Lotus.Web.Authorization (Lotus Web v1.2.0)

Copy Markdown View Source

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

ActionResource
:querythe data source name
:exportthe data source name
:ai_generatethe data source name
:discoverthe data source name
:create_querynil
:update_querythe stored %Lotus.Storage.Query{}
:delete_querythe %Lotus.Storage.Query{}
:share_querythe %Lotus.Storage.Query{} (no dashboard control asks it yet)
:share_dashboardthe %Lotus.Storage.Dashboard{} whose public link changes
:view_dashboardthe %Lotus.Storage.Dashboard{}
:manage_dashboardnil for a new dashboard, else the %Lotus.Storage.Dashboard{}
:manage_sourcenil
:manage_cachenil

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

decision()

@type decision() :: :allow | {:deny, String.t()}

A decision: allow, or deny with a reason the dashboard shows the user.

Functions

actions()

@spec actions() :: [Lotus.Web.Resolver.action(), ...]

The action vocabulary, in a fixed order.

allowed?(assigns, action, resource \\ nil)

@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.

authorize(assigns, action, resource \\ nil)

@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.

decide(resolver, user, access, action, resource)

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.

default_decision(arg1, action)

The decision for an access level when the resolver does not implement Lotus.Web.Resolver.authorize/3.

AccessDecision
: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.

discoverable?(assigns, source)

@spec discoverable?(map(), String.t()) :: boolean()

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.

permissions(assigns)

@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.

reason(atom)

@spec reason(Lotus.Web.Resolver.action()) :: String.t()

The reason the dashboard shows when it denies action.