Packages

Ash adapter for Parc: one declared authorization policy, gating writes and row-scoping reads across every resource.

Current section

Files

Jump to
ash_parc README.md
Raw

README.md

# AshParc
Ash adapter for `Parc`, the framework-agnostic PARC authorization
contract. AshParc binds Ash's policy DSL to one `Parc` policy, so a single
declared policy authorizes every action across every resource.
## Enforcement points
AshParc names one policy at two points in a resource's `policies` block:
- `AshParc.can_perform/2`: an `Ash.Policy.SimpleCheck` gating a write or
generic action on the policy's verdict.
- `AshParc.build_request/3`: builds the `Parc.Request` for a
resource-owned `Ash.Policy.FilterCheck` that row-scopes a read.
Both points project the action's arguments and instance attributes into the
request through the `:arguments` and `:attributes` options; `:context` lifts
a chosen argument or attribute into `request.context` under a normalized key
the preparers and decider read.
## Usage
Declare the policy as a `Parc.Pipeline` (see `parc`) with
`AshParc.ResetAshContext` as its first preparer, then gate a write from
the resource's `policies` block:
```elixir
policy action(:update) do
authorize_if AshParc.can_perform(MyApp.AuthzPolicy, attributes: [:organization_id])
end
```
Row-scope a read with a resource-owned `Ash.Policy.FilterCheck` that runs
the policy and turns the verdict into a filter, and name it the same way:
```elixir
policy action(:read) do
authorize_if MyApp.Widget.ReadScope
end
```
That check builds its request with `AshParc.build_request/3`, and the read
scoping needs
```elixir
config :ash, policies: [no_filter_static_forbidden_reads?: false]
```
so a `{:deny, _}` scopes the read to no rows instead of raising
`Ash.Error.Forbidden`.
## Installation
Add `ash_parc` to your dependencies in `mix.exs`:
```elixir
def deps do
[
{:ash_parc, "~> 0.1"}
]
end
```
The API reference is at [hexdocs.pm/ash_parc](https://hexdocs.pm/ash_parc).