Current section
Files
Jump to
Current section
Files
lib/let_me/rule.ex
defmodule LetMe.Rule do
@moduledoc """
A struct for an authorization rule.
"""
@typedoc """
Struct for an authorization rule.
- `action` - The action (verb) to be performed on the object, e.g. `:update`.
- `allow` - A list of lists of checks to run to determine whether the action
is allowed. The outer list contains the alternatives (one for each `allow`
call; combined with `OR`). The inner lists are the checks for each `allow`
(combined with `AND`).
- `description` - A human-readable description of the action.
- `deny` - A list of lists of checks to run to determine whether the action is
explicitly denied. Same format as in `allow`. If any of these checks returns
`true`, the end result of the authorization request is immediately `false`,
even if any of the checks in the `allow` field would return `true`.
- `name` - The name of the rule. Is always `{object}_{action}`.
- `object` - The object that the action is performed on, e.g. `:article`.
- `pre_hooks` - Functions to run in order to hydrate the subject and/or object
before running the allow and deny checks.
- `metadata` - A list of relevant metadata useful for extending functionality.
The list entries in the outer list of the `allow` and `deny` fields are
combined with a logical `OR`. If one of the entries is a list of checks, those
checks are combined with a logical `AND`.
## Examples
- `[{role: :editor}, {role: :writer}]` - role is editor OR role is writer
- `[[{role: :editor}], [{role: :writer}]]` - same as above
- `[[{role: :editor}], [{role: :writer}, {:own_resource}]]` -
(role is editor OR (role is writer AND object is the user's own resource))
"""
@type t :: %__MODULE__{
action: atom,
allow: [check | [check]],
deny: [check | [check]],
description: String.t() | nil,
name: atom,
object: atom,
pre_hooks: [hook],
metadata: Keyword.t()
}
@typedoc """
A `check` references a function in the configured check module.
Can be either one of:
- A function name as an atom. The function must be a 2-arity function that
takes the subject (usually the current user) and the object as
arguments.
- A tuple with the function name as an atom and a value of any type. The
function must be a 3-arity function that takes the subject, the object, and
the given value as arguments.
"""
@type check :: atom | {atom, any}
@typedoc """
A hook can be registered to hydrate the subject and/or object before passing
them to the check functions.
"Hydration" in this context means enriching or preparing the data by adding
or transforming necessary information. For instance, you might fetch related
data from the database, calculate derived properties, or format the data in a
certain way.
A hook can be one of the following:
- The name of a function defined in the configured check module as an atom.
- A `{module, function}` tuple.
- A `{module, function, arguments}` tuple.
In either case, the function must take the subject as the first argument, the
object as the second argument, and return a tuple with the updated subject and
object. If an MFA tuple is passed, the given arguments are appended to the
default arguments.
"""
@type hook :: atom | {module, atom} | {module, atom, any}
@enforce_keys [:action, :name, :object]
defstruct action: nil,
allow: [],
deny: [],
description: nil,
name: nil,
object: nil,
pre_hooks: [],
metadata: []
end