Skip to content
Merged
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
4 changes: 4 additions & 0 deletions lib/algora/organizations/organizations.ex
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ defmodule Algora.Organizations do
)
end

def fetch_member(org_id, user_id) do
Repo.fetch_by(Member, org_id: org_id, user_id: user_id)
end

def list_org_contractors(org) do
Repo.all(
from u in User,
Expand Down
6 changes: 5 additions & 1 deletion lib/algora/organizations/schemas/member.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@ defmodule Algora.Organizations.Member do

alias Algora.Accounts.User

@roles [:admin, :mod, :expert]

typed_schema "members" do
field :role, Ecto.Enum, values: [:admin, :mod, :expert]
field :role, Ecto.Enum, values: @roles

belongs_to :org, User
belongs_to :user, User

timestamps()
end

def roles, do: @roles

def changeset(member, params) do
member
|> cast(params, [:role])
Expand Down
25 changes: 25 additions & 0 deletions lib/algora_web/controllers/org_auth.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
defmodule AlgoraWeb.OrgAuth do
@moduledoc false
import Phoenix.LiveView

alias Algora.Organizations

def on_mount(:ensure_admin, params, session, socket) do
ensure_role([:admin], params, session, socket)
end

def on_mount(:ensure_mod, params, session, socket) do
ensure_role([:mod, :admin], params, session, socket)
end

defp ensure_role(allowed_roles, _params, _session, socket) do
%{current_org: current_org, current_user: current_user} = socket.assigns

with {:ok, member} <- Organizations.fetch_member(current_org.id, current_user.id),
true <- member.role in allowed_roles do
{:cont, socket}
else
_ -> {:halt, redirect(socket, to: "/org/#{current_org.handle}")}
end
end
end
14 changes: 12 additions & 2 deletions lib/algora_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,23 @@ defmodule AlgoraWeb.Router do
# live "/org/:org_handle/projects/:id", Project.ViewLive
live "/org/:org_handle/jobs", Org.JobsLive, :index
live "/org/:org_handle/jobs/:id", Org.JobLive, :index
live "/org/:org_handle/transactions", Org.TransactionsLive, :index
live "/org/:org_handle/chat", ChatLive, :index
live "/org/:org_handle/settings", Org.SettingsLive, :edit
live "/org/:org_handle/team", Org.TeamLive, :index
live "/org/:org_handle/leaderboard", Org.LeaderboardLive, :index
end

live_session :org_admin,
layout: {AlgoraWeb.Layouts, :org},
on_mount: [
{AlgoraWeb.UserAuth, :ensure_authenticated},
{AlgoraWeb.UserAuth, :current_user},
AlgoraWeb.Org.Nav,
{AlgoraWeb.OrgAuth, :ensure_admin}
] do
live "/org/:org_handle/settings", Org.SettingsLive, :edit
live "/org/:org_handle/transactions", Org.TransactionsLive, :index
end

live_session :org2,
on_mount: [{AlgoraWeb.UserAuth, :current_user}, AlgoraWeb.Org.Nav] do
live "/org/:org_handle/projects/:id", DevLive
Expand Down
59 changes: 59 additions & 0 deletions test/algora_web/live/org_auth_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
defmodule AlgoraWeb.Org.SettingsLiveTest do
use AlgoraWeb.ConnCase, async: true

import Algora.Factory
import Ecto.Changeset
import Phoenix.LiveViewTest

alias Algora.Organizations.Member
alias Algora.Repo

setup %{conn: conn} do
conn = Phoenix.ConnTest.init_test_session(conn, %{})

%{
conn: conn,
org: insert!(:organization)
}
end

# Helper function to test auth requirements for org routes
defp assert_org_route_auth(conn, org, path, allowed_roles) do
test_path = "/org/#{org.handle}#{path}"

# Test unauthorized access
assert {:error, {:redirect, %{to: to}}} = live(conn, test_path)
assert to == ~p"/auth/login?return_to=#{test_path}"

# # Test non-member access
user = insert!(:user)
conn_with_user = AlgoraWeb.UserAuth.put_current_user(conn, user)
assert {:error, {:redirect, %{to: to}}} = live(conn_with_user, test_path)
assert to == "/org/#{org.handle}"

# # Test access for each role
member = insert!(:member, user: user, org: org)

for role <- Member.roles() do
member |> change(role: role) |> Repo.update!()

if role in allowed_roles do
assert {:ok, _view, _html} = live(conn_with_user, test_path)
else
assert {:error, {:redirect, %{to: to}}} = live(conn_with_user, test_path)
assert to == "/org/#{org.handle}"
end
end
end

describe "protected org routes" do
for {path, roles} <- %{
"/settings" => [:admin],
"/transactions" => [:admin]
} do
test "#{path} page", %{conn: conn, org: org} do
assert_org_route_auth(conn, org, unquote(path), unquote(Macro.escape(roles)))
end
end
end
end