Skip to content

Commit

Permalink
Refactored permission handling so it's easier to follow.
Browse files Browse the repository at this point in the history
  • Loading branch information
cmaddox5 committed Jan 7, 2025
1 parent e4cdde4 commit 505d1fc
Show file tree
Hide file tree
Showing 23 changed files with 154 additions and 183 deletions.
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

0 comments on commit 505d1fc

Please sign in to comment.