defmodule MyApp.Authorization do
@moduledoc """
Provides authorization functionality
"""
require Logger
alias MyApp.Accounts.User
alias MyApp.AdminNotifications
alias MyApp.Organizations
alias MyApp.Organizations.Organization
alias MyApp.Repo
@type error :: :unauthorized | :invalid_resource | :role_not_found
@permissions %{
# Organization permissions
organization_view: [:member, :admin, :owner],
organization_edit: [:admin, :owner],
organization_delete: [:owner],
organization_invite_members: [:admin, :owner],
organization_manage_members: [:admin, :owner],
# Announcement permissions
announcement_view: [:member, :admin, :owner],
announcement_create: [:member, :admin, :owner],
announcement_edit: [:member, :admin, :owner],
announcement_delete: [:member, :admin, :owner]
}
@type permission ::
:organization_view
| :organization_edit
| :organization_delete
| :organization_invite_members
| :organization_manage_members
| :announcement_view
| :announcement_create
| :announcement_edit
| :announcement_delete
@doc """
Authorizes a user for a specific action on a resource.
"""
@spec authorize(User.t(), resource :: struct(), permission()) ::
:ok | {:error, error()}
def authorize(user, resource, permission) do
with {:ok, organization} <- get_organization_from_resource(resource),
{:ok, role} <- Organizations.get_user_role(user, organization),
true <- has_permission?(role, permission) do
:ok
else
{:error, :invalid_resource} = error ->
log_authorization_failure(user, resource, permission, error)
error
{:error, :not_found} ->
error = {:error, :unauthorized}
log_authorization_failure(user, resource, permission, error)
error
false ->
error = {:error, :unauthorized}
log_authorization_failure(user, resource, permission, error)
error
end
end
# Private functions
def get_organization_from_resource(resource) do
case resource do
%Organization{} = organization -> {:ok, organization}
%{organization_id: organization_id} -> {:ok, Repo.get(Organization, organization_id)}
_ -> {:error, :invalid_resource}
end
end
def has_permission?(role, permission) do
allowed_roles = @permissions[permission] || []
role in allowed_roles
end
defp log_authorization_failure(user, resource, permission, error) do
AdminNotifications.notify(:authorization_error, %{
user: user,
resource: resource,
permission: permission,
error: error
})
end
end