# `Lightning.Policies.Permissions`
[🔗](https://github.com/OpenFn/lightning/blob/main/lib/lightning/policies/permissions.ex#L1)

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](https://docs.openfn.org/documentation/about-lightning#roles-and-permissions).

## 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:
- Full module names: `Lightning.Policies.Users`
- Atom shortcuts for sub-modules: `:users`, `:project_users`, `:credentials`

## 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:**
```elixir
can_edit = Lightning.Policies.ProjectUsers 
           |> Lightning.Policies.Permissions.can?(:edit_workflow, user, project)
```

**Using atom shortcuts:**
```elixir  
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/`.

# `can`

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?`

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`

```elixir
@spec for_actor(Lightning.Scope.actor(), term(), (Lightning.Scope.t() -&gt; 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`

```elixir
@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`

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

---

*Consult [api-reference.md](api-reference.md) for complete listing*
