Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Router scopes and permissions #1075

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 0 additions & 10 deletions config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,6 @@ config :arrow,
migrate_synchronously?: true,
redirect_http?: true,
ueberauth_provider: :keycloak,
required_roles: %{
view_disruption: ["read-only", "admin"],
create_disruption: ["admin"],
update_disruption: ["admin"],
delete_disruption: ["admin"],
create_note: ["admin"],
view_change_feed: ["admin"],
publish_notice: ["admin"],
db_dump: ["admin"]
},
time_zone: "America/New_York",
ex_aws_requester: {Fake.ExAws, :admin_group_request},
http_client: HTTPoison,
Expand Down
2 changes: 1 addition & 1 deletion config/dev.exs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ config :arrow, ArrowWeb.Endpoint,

config :ueberauth, Ueberauth,
providers: [
keycloak: {Arrow.Ueberauth.Strategy.Fake, [groups: ["admin"]]}
keycloak: {Arrow.Ueberauth.Strategy.Fake, [roles: ["admin"]]}
]

config :arrow, :redirect_http?, false
Expand Down
5 changes: 5 additions & 0 deletions config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,8 @@ config :wallaby,
otp_app: :arrow,
screenshot_dir: "test/integration/screenshots",
screenshot_on_failure: true

config :ueberauth, Ueberauth,
providers: [
keycloak: {Arrow.Ueberauth.Strategy.Fake, [roles: ["admin"]]}
]
40 changes: 0 additions & 40 deletions lib/arrow/permissions.ex

This file was deleted.

5 changes: 2 additions & 3 deletions lib/arrow/ueberauth/strategy/fake.ex
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ defmodule Arrow.Ueberauth.Strategy.Fake do
token: "fake_access_token",
refresh_token: "fake_refresh_token",
expires: true,
expires_at: System.system_time(:second) + 60 * 60,
other: %{groups: ["arrow-admin"]}
expires_at: System.system_time(:second) + 60 * 60
}
end

Expand All @@ -44,7 +43,7 @@ defmodule Arrow.Ueberauth.Strategy.Fake do
"iat" => System.system_time(:second)
},
userinfo: %{
"roles" => Ueberauth.Strategy.Helpers.options(conn)[:groups]
"roles" => Ueberauth.Strategy.Helpers.options(conn)[:roles]
}
}
}
Expand Down
22 changes: 22 additions & 0 deletions lib/arrow_web/auth_manager/ensure_arrow_admin.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
defmodule ArrowWeb.EnsureArrowAdmin do
@moduledoc """
Verify user is an Arrow admin.
"""

use ArrowWeb, :verified_routes

import Plug.Conn, only: [halt: 1]
import Phoenix.Controller, only: [redirect: 2]

def init(options), do: options

def call(%{assigns: %{current_user: user}} = conn, _opts) do
if "admin" in user.roles do
conn
else
conn
|> redirect(to: ~p"/unauthorized")
|> halt()
end
end
end
24 changes: 24 additions & 0 deletions lib/arrow_web/auth_manager/ensure_arrow_read_only.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
defmodule ArrowWeb.EnsureArrowReadOnly do
@moduledoc """
Verify user has read-only access to Arrow.
"""

use ArrowWeb, :verified_routes

import Plug.Conn, only: [halt: 1]
import Phoenix.Controller, only: [redirect: 2]

@read_only_roles MapSet.new(["read-only", "admin"])

def init(options), do: options

def call(%{assigns: %{current_user: user}} = conn, _opts) do
if MapSet.disjoint?(user.roles, @read_only_roles) do
conn
|> redirect(to: ~p"/unauthorized")
|> halt()
else
conn
end
end
end
3 changes: 0 additions & 3 deletions lib/arrow_web/controllers/api/db_dump_controller.ex
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
defmodule ArrowWeb.API.DBDumpController do
alias ArrowWeb.Plug.Authorize
use ArrowWeb, :controller

plug(Authorize, :db_dump)

@spec show(Plug.Conn.t(), map()) :: Plug.Conn.t()
def show(conn, _params) do
json(conn, Arrow.DBStructure.dump_data())
Expand Down
3 changes: 0 additions & 3 deletions lib/arrow_web/controllers/api/notice_controller.ex
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
defmodule ArrowWeb.API.NoticeController do
alias ArrowWeb.Plug.Authorize
use ArrowWeb, :controller
require Logger

plug(Authorize, :publish_notice)

@spec publish(Plug.Conn.t(), map()) :: Plug.Conn.t()
def publish(conn, params) do
revision_ids = params["revision_ids"] |> String.split(",") |> Enum.map(&String.to_integer/1)
Expand Down
14 changes: 6 additions & 8 deletions lib/arrow_web/controllers/disruption_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,9 @@ defmodule ArrowWeb.DisruptionController do
alias __MODULE__.{Filters, Index}
alias Arrow.{Adjustment, Disruption, DisruptionRevision}
alias ArrowWeb.ErrorHelpers
alias ArrowWeb.Plug.Authorize
alias Ecto.Changeset
alias Plug.Conn

plug(Authorize, :view_disruption when action in [:index, :show])
plug(Authorize, :create_disruption when action in [:new, :create])
plug(Authorize, :update_disruption when action in [:edit, :update, :update_row_status])
plug(Authorize, :delete_disruption when action in [:delete])

@spec update_row_status(Conn.t(), Conn.params()) :: Conn.t()
def update_row_status(%{assigns: %{current_user: user}} = conn, %{
"id" => id,
Expand All @@ -29,13 +23,17 @@ defmodule ArrowWeb.DisruptionController do
def index(%{assigns: %{current_user: user}} = conn, params) do
filters = Filters.from_params(params)

render(conn, "index.html", disruptions: Index.all(filters), filters: filters, user: user)
render(conn, "index.html",
disruptions: Index.all(filters),
filters: filters,
roles: user.roles
)
end

@spec show(Conn.t(), Conn.params()) :: Conn.t()
def show(%{assigns: %{current_user: user}} = conn, %{"id" => id}) do
%{id: id, revisions: [revision], notes: notes} = Disruption.get!(id)
render(conn, "show.html", id: id, revision: revision, user: user, notes: notes)
render(conn, "show.html", id: id, revision: revision, roles: user.roles, notes: notes)
end

@spec new(Conn.t(), Conn.params()) :: Conn.t()
Expand Down
2 changes: 1 addition & 1 deletion lib/arrow_web/controllers/disruption_html.ex
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
defmodule ArrowWeb.DisruptionView do
use ArrowWeb, :html

alias Arrow.{Adjustment, DisruptionRevision, Permissions}
alias Arrow.{Adjustment, DisruptionRevision}
alias __MODULE__.{DaysOfWeek, Form}
alias __MODULE__.Calendar, as: DCalendar
alias ArrowWeb.DisruptionController.Filters
Expand Down
2 changes: 1 addition & 1 deletion lib/arrow_web/controllers/disruption_html/index.html.heex
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div class="row my-3">
<div class="col">
<%= if Permissions.authorize?(:create_disruption, @user) do %>
<%= if "admin" in @roles do %>
<a class="btn btn-primary" href="/disruptions/new">+ create new</a>
<% end %>
</div>
Expand Down
4 changes: 1 addition & 3 deletions lib/arrow_web/controllers/disruption_html/show.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@
<section class="col-lg-5">
<%= if @revision.is_active do %>
<div class="m-disruption-details_flex align-items-center">
<%= if Permissions.authorize?(:update_disruption, @user) do %>
<%= if "admin" in @roles do %>
<%= form_tag Routes.disruption_path(@conn, :update_row_status, @revision.disruption_id), method: "put" do %>
{hidden_input(:revision, :row_approved, value: !@revision.row_approved)}
{submit(mark_as_approved_or_pending(@revision.row_approved),
Expand All @@ -98,8 +98,6 @@
<.link class="btn btn-tertiary" navigate={Routes.disruption_path(@conn, :edit, @id)}>
<._button_description conn={@conn} kind={:edit} />
</.link>
<% end %>
<%= if Permissions.authorize?(:delete_disruption, @user) do %>
<%= button class: "btn btn-tertiary",
method: :delete,
to: Routes.disruption_path(@conn, :delete, @id),
Expand Down
2 changes: 0 additions & 2 deletions lib/arrow_web/controllers/feed_controller.ex
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
defmodule ArrowWeb.FeedController do
use ArrowWeb, :controller

plug(ArrowWeb.Plug.Authorize, :view_change_feed)

@spec index(Plug.Conn.t(), Plug.Conn.params()) :: Plug.Conn.t()
def index(conn, _params) do
disruptions = Arrow.Disruption.latest_vs_published()
Expand Down
3 changes: 0 additions & 3 deletions lib/arrow_web/controllers/note_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ defmodule ArrowWeb.NoteController do

alias Arrow.Disruption
alias ArrowWeb.ErrorHelpers
alias ArrowWeb.Plug.Authorize

plug(Authorize, :create_note when action in [:create])

def create(%{assigns: %{current_user: user}} = conn, %{
"id" => disruption_id,
Expand Down
6 changes: 0 additions & 6 deletions lib/arrow_web/controllers/shape_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,6 @@ defmodule ArrowWeb.ShapeController do
use ArrowWeb, :controller

alias Arrow.Shuttles
alias ArrowWeb.Plug.Authorize

plug(Authorize, :view_disruption when action in [:index, :show, :download])
plug(Authorize, :create_disruption when action in [:new, :create])
plug(Authorize, :update_disruption when action in [:edit, :update, :update_row_status])
plug(Authorize, :delete_disruption when action in [:delete])

def index(conn, _params) do
shapes = Shuttles.list_shapes()
Expand Down
2 changes: 1 addition & 1 deletion lib/arrow_web/plug/assign_user.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ defmodule ArrowWeb.Plug.AssignUser do
def init(options), do: options

@spec call(Plug.Conn.t(), Plug.opts()) :: Plug.Conn.t()
def call(conn, _opts) do
def call(conn, _opts \\ []) do
%{"sub" => user_id, "roles" => roles} = Guardian.Plug.current_claims(conn)

assign(conn, :current_user, %User{
Expand Down
26 changes: 0 additions & 26 deletions lib/arrow_web/plug/authorize.ex

This file was deleted.

38 changes: 31 additions & 7 deletions lib/arrow_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -40,24 +40,44 @@ defmodule ArrowWeb.Router do
plug(ArrowWeb.Plug.AssignUser)
end

pipeline :ensure_arrow_admin do
plug(ArrowWeb.EnsureArrowAdmin)
end

pipeline :ensure_arrow_read_only do
plug(ArrowWeb.EnsureArrowReadOnly)
end

scope "/", ArrowWeb do
pipe_through([:redirect_prod_http, :browser, :authenticate])

get("/unauthorized", UnauthorizedController, :index)
end

scope "/", ArrowWeb do
pipe_through([:redirect_prod_http, :browser, :authenticate, :ensure_arrow_admin])

get("/feed", FeedController, :index)
get("/mytoken", MyTokenController, :show)
get("/", DisruptionController, :index)
resources("/disruptions", DisruptionController, except: [:index])
resources("/disruptions", DisruptionController, except: [:index, :show])
put("/disruptions/:id/row_status", DisruptionController, :update_row_status)
post("/disruptions/:id/notes", NoteController, :create)
delete("/shapes/:id", ShapeController, :delete)
get("/shapes_upload", ShapeController, :new)
post("/shapes_upload", ShapeController, :create)
end

scope "/", ArrowWeb do
pipe_through([:redirect_prod_http, :browser, :authenticate, :ensure_arrow_read_only])

get("/mytoken", MyTokenController, :show)
get("/", DisruptionController, :index)
get("/disruptions/:id", DisruptionController, :show)
live("/stops/new", StopViewLive, :new)
live("/stops/:id/edit", StopViewLive, :edit)
get("/stops", StopController, :index)
put("/stops/:id", StopController, :update)
post("/stops", StopController, :create)
resources("/shapes", ShapeController, only: [:delete, :index, :show])
get("/shapes_upload", ShapeController, :new)
post("/shapes_upload", ShapeController, :create)
resources("/shapes", ShapeController, only: [:index, :show])
get("/shapes/:id/download", ShapeController, :download)
live("/shuttles/new", ShuttleViewLive, :new)
live("/shuttles/:id/edit", ShuttleViewLive, :edit)
Expand Down Expand Up @@ -89,10 +109,14 @@ defmodule ArrowWeb.Router do
end

scope "/api", ArrowWeb.API do
pipe_through([:redirect_prod_http, :api, :authenticate_api])
pipe_through([:redirect_prod_http, :api, :authenticate_api, :ensure_arrow_admin])

post("/publish_notice", NoticeController, :publish)
get("/db_dump", DBDumpController, :show)
end

scope "/api", ArrowWeb.API do
pipe_through([:redirect_prod_http, :api, :authenticate_api])

scope "/gtfs", alias: false do
post("/import", GtfsImportController, :enqueue_import)
Expand Down
Loading
Loading