Lightning.Policies.Permissions (Lightning v2.20.1)

Copy Markdown View Source

This module defines a unique interface managing authorizations in Lightning.

Users in Lightning have instance-wide and project-wide roles which determine their level of access to resources in the application. For more details see the documentation.

Policy Modules

Authorization policies are implemented under lib/lightning/policies/:

  • users.ex - Instance-wide access levels
  • project_users.ex - Project-wide access levels
  • credentials.ex - Credential management permissions
  • workflows.ex - Workflow-related permissions
  • collections.ex - Collection access permissions
  • dataclips.ex - Dataclip permissions
  • exports.ex - Export functionality permissions
  • provisioning.ex - Resource provisioning permissions

Interface

This module provides the can/4 and can?/4 interface, which wraps Bodyguard.permit/4 to harmonize policy usage across the application.

Policy Resolution: You can reference policy modules in two ways:

Functions

  • can(policy, action, actor, resource) - Returns :ok or {:error, :unauthorized}
  • can?(policy, action, actor, resource) - Returns true or false

Examples

Using full module names:

can_edit = Lightning.Policies.ProjectUsers 
           |> Lightning.Policies.Permissions.can?(:edit_workflow, user, project)

Using atom shortcuts:

can_create = Permissions.can?(:credentials, :create_keychain_credential, user, project)
can_delete = Permissions.can?(:project_users, :delete_project, user, project)

All policies are comprehensively tested in test/lightning/policies/.

Summary

Functions

checks if user has the permissions to apply action using some policy module

same as can/4 but returns true if user can apply action and false otherwise

Resolves actor's scope on the subject's project and passes it to decide, for a policy handed an actor rather than a %Scope{}. Bodyguard's contract is boolean, and channel and LiveView callers match {:error, :unauthorized}, so a missing project, no standing and a scheduled project are all false. Only callers going through permit/4 with a %Scope{} are told apart.

Whether the caller's grant already refuses action, before any target is loaded. A pass here is not permission. The action still has to ask permit/4 about its target.

Whether the caller described by scope may apply action to subject.

Functions

can(policy, action, user, params \\ [])

checks if user has the permissions to apply action using some policy module

Returns :ok if user can apply action and {:error, :unauthorized} otherwise

Examples

iex> can(Lightning.Policies.Users, :create_workflow, user, project)
:ok

iex> can(Lightning.Policies.Users, :create_project, user, %{})
{:error, :unauthorized}

can?(policy, action, user, params \\ [])

same as can/4 but returns true if user can apply action and false otherwise

Examples

iex> can(Lightning.Policies.Users, :create_workflow, user, project)
true

iex> can(Lightning.Policies.Users, :create_project, user, %{})
false

for_actor(actor, subject, decide)

@spec for_actor(Lightning.Scope.actor(), term(), (Lightning.Scope.t() -> term())) ::
  term()

Resolves actor's scope on the subject's project and passes it to decide, for a policy handed an actor rather than a %Scope{}. Bodyguard's contract is boolean, and channel and LiveView callers match {:error, :unauthorized}, so a missing project, no standing and a scheduled project are all false. Only callers going through permit/4 with a %Scope{} are told apart.

permit(policy, action, scope)

@spec permit(atom(), atom(), Lightning.Scope.t()) ::
  :ok
  | {:error,
     :forbidden
     | Lightning.Policies.Decision.insufficient_scope()
     | :no_standing
     | :scheduled_for_deletion}

Whether the caller's grant already refuses action, before any target is loaded. A pass here is not permission. The action still has to ask permit/4 about its target.

permit(policy, action, scope, subject)

@spec permit(atom(), atom(), Lightning.Scope.t(), term()) ::
  :ok
  | {:error,
     :forbidden
     | Lightning.Policies.Decision.insufficient_scope()
     | :no_standing
     | :scheduled_for_deletion}

Whether the caller described by scope may apply action to subject.

The one entry point for API callers. Unlike can/4, a refusal is {:error, :forbidden}, so it renders as 403 rather than 401, and a policy's more specific answer ({:insufficient_scope, grant}, :no_standing, :scheduled_for_deletion) reaches the caller unchanged.