Asks the host whether the current user may perform an action.
The dashboard asks one question in one place: may this user do this action, on this resource? It asks before a control renders, so a control the user may not use is not shown, and again in the event handler, so a crafted event is refused too.
The host answers through Lotus.Web.Resolver.authorize/3. A resolver
that does not implement it gets a decision derived from
Lotus.Web.Resolver.resolve_access/1, see default_decision/2.
Actions and resources
| Action | Resource |
|---|---|
:query | the data source name |
:export | the data source name |
:ai_generate | the data source name |
:discover | the data source name |
:create_query | nil |
:update_query | the stored %Lotus.Storage.Query{} |
:delete_query | the %Lotus.Storage.Query{} |
:share_query | the %Lotus.Storage.Query{} (no dashboard control asks it yet) |
:share_dashboard | the %Lotus.Storage.Dashboard{} whose public link changes |
:view_dashboard | the %Lotus.Storage.Dashboard{} |
:manage_dashboard | nil for a new dashboard, else the %Lotus.Storage.Dashboard{} |
:manage_source | nil |
:manage_cache | nil |
Cost
Templates ask on every render. At mount the dashboard computes the actions
that take no resource once, into the :permissions assign, and
allowed?/3 reads that map. Actions with a resource are asked on demand, so
the callback must not do I/O per call; cache the policy in the host.
Summary
Types
A decision: allow, or deny with a reason the dashboard shows the user.
Functions
The action vocabulary, in a fixed order.
Return true when the user in assigns may perform action on resource.
Decide whether the user in assigns may perform action on resource.
Decide for an explicit resolver, user and access level.
The decision for an access level when the resolver does not implement
Lotus.Web.Resolver.authorize/3.
Return true when the user in assigns may browse source: see it in the
source list, and see its schemas, tables and columns.
Compute the decisions for the actions without a resource, as a map of
action to boolean. The dashboard stores it as the :permissions assign.
The reason the dashboard shows when it denies action.
Types
@type decision() :: :allow | {:deny, String.t()}
A decision: allow, or deny with a reason the dashboard shows the user.
Functions
@spec actions() :: [Lotus.Web.Resolver.action(), ...]
The action vocabulary, in a fixed order.
@spec allowed?(map(), Lotus.Web.Resolver.action(), term()) :: boolean()
Return true when the user in assigns may perform action on resource.
For an action without a resource, reads the :permissions assign when the
dashboard computed it at mount. Use it in templates.
@spec authorize(map(), Lotus.Web.Resolver.action(), term()) :: decision()
Decide whether the user in assigns may perform action on resource.
Reads :resolver, :user, :access and :public_view from assigns.
Always asks, and never reads the :permissions cache. Use it in event
handlers, and show the reason of a {:deny, reason} to the user.
A public dashboard has no user, so the host is not asked: the decision
derives from :read_only access.
Assigns with no :resolver or :access key mean the dashboard never handed
them down. The error is logged and the decision derives from :read_only
access, so the mistake fails closed. Under
config :lotus_web, strict_actor: true it raises instead, the same as
Lotus.Web.Actor.opts/1 does for a missing actor.
@spec decide( module() | nil, Lotus.Web.Resolver.user(), Lotus.Web.Resolver.access_level(), Lotus.Web.Resolver.action(), term() ) :: decision()
Decide for an explicit resolver, user and access level.
Calls Lotus.Web.Resolver.authorize/3 when the resolver implements it,
else returns default_decision/2 for access. Use it where no socket
assigns exist, such as a controller.
@spec default_decision(Lotus.Web.Resolver.access_level(), Lotus.Web.Resolver.action()) :: decision()
The decision for an access level when the resolver does not implement
Lotus.Web.Resolver.authorize/3.
| Access | Decision |
|---|---|
:all | :allow for every action |
:read_only | :allow for :query, :export, :discover and :view_dashboard, {:deny, reason} for the rest |
:forbidden, {:forbidden, path} | {:deny, reason} for every action |
A host resolver can call it to keep the default for some actions.
Return true when the user in assigns may browse source: see it in the
source list, and see its schemas, tables and columns.
Browsing needs :discover or :query on the source. Running a query on it
still needs :query.
@spec permissions(map()) :: %{required(Lotus.Web.Resolver.action()) => boolean()}
Compute the decisions for the actions without a resource, as a map of
action to boolean. The dashboard stores it as the :permissions assign.
@spec reason(Lotus.Web.Resolver.action()) :: String.t()
The reason the dashboard shows when it denies action.