towerops/lib/towerops/accounts/scope.ex
Graham McIntire 872bef3512
feat: add 12/24-hour time format setting with consistent timezone display
wire time_format into scope, add settings UI selector, change defaults
to 24h, and replace all user-facing Calendar.strftime calls with
centralized TimeHelpers using the user's timezone and time format.
2026-02-13 15:16:29 -06:00

84 lines
2.5 KiB
Elixir

defmodule Towerops.Accounts.Scope do
@moduledoc """
Defines the scope of the caller to be used throughout the app.
The `Towerops.Accounts.Scope` allows public interfaces to receive
information about the caller, such as if the call is initiated from an
end-user, and if so, which user. Additionally, such a scope can carry fields
such as "super user" or other privileges for use as authorization, or to
ensure specific code paths can only be access for a given scope.
It is useful for logging as well as for scoping pubsub subscriptions and
broadcasts when a caller subscribes to an interface or performs a particular
action.
Feel free to extend the fields on this struct to fit the needs of
growing application requirements.
"""
alias Towerops.Accounts.User
alias Towerops.Organizations.Organization
defstruct user: nil, superuser: nil, impersonating?: false, organization: nil, timezone: "UTC", time_format: "24h"
@doc """
Creates a scope for the given user.
Returns nil if no user is given.
"""
def for_user(%User{} = user) do
%__MODULE__{
user: user,
superuser: nil,
impersonating?: false,
timezone: user.timezone || "UTC",
time_format: user.time_format || "24h"
}
end
def for_user(nil), do: nil
@doc """
Creates a scope for a superuser impersonating another user.
The `user` field contains the impersonated user (what the superuser sees as),
while the `superuser` field contains the actual superuser performing the impersonation.
"""
def for_impersonation(%User{} = superuser, %User{} = target_user) do
%__MODULE__{
user: target_user,
superuser: superuser,
impersonating?: true,
timezone: target_user.timezone || "UTC",
time_format: target_user.time_format || "24h"
}
end
@doc """
Adds organization context to an existing scope.
Returns updated scope with organization field set.
"""
def put_organization(%__MODULE__{} = scope, %Organization{} = organization) do
%{scope | organization: organization}
end
def put_organization(%__MODULE__{} = scope, nil) do
%{scope | organization: nil}
end
def put_organization(nil, _organization), do: nil
@doc """
Returns true if the scope has superuser privileges.
This includes both:
- Regular users with is_superuser = true
- Superusers who are currently impersonating another user
"""
def superuser?(%__MODULE__{user: user, superuser: superuser}) do
(user && user.is_superuser) || (superuser && superuser.is_superuser)
end
def superuser?(nil), do: false
end