I just released LetMe 1.0, marking the first stable release.
LetMe is an authorization DSL with introspection capabilities.
Why?
There are plenty of authorization libraries, but when an application grows and with it the number of authorization rules, it becomes harder to see at a glance what is allowed under which circumstances. I wanted a solution that a) provides me an easily readable format to define authorization rules, and b) allows me to list and filter those rules, so that I can dynamically generate documentation pages and help texts for permission forms.
Example
A simple policy module would look like this:
defmodule MyApp.Policy do
use LetMe.Policy
object :article do
action :create do
desc "allows a user to create a new article"
allow role: :editor
allow role: :writer
end
action :read do
desc "allows a user to read an article and to see a list of articles"
allow true
deny :banned
end
action :update do
allow role: :editor
allow [:own_resource, role: :writer]
end
action :delete do
allow role: :editor
end
end
end
The allow
and deny
conditions only reference check functions which you have to define on your own. This means the DSL is much simpler than a full-fledged policy language. In the end it’s just a means of combining custom checks.
defmodule MyApp.Policy.Checks do
alias MyApp.Accounts.User
@doc """
Returns `true` if the `banned` flag is set on the user.
"""
def banned(%User{banned: banned}, _, _), do: banned
@doc """
Checks whether the user ID of the object matches the ID of the current user.
Assumes that the object has a `:user_id` field.
"""
def own_resource(%User{id: id}, %{user_id: id}, _opts) when is_binary(id), do: true
def own_resource(_, _, _), do: false
@doc """
Checks whether the user role matches the role passed as an option.
## Usage
allow role: :editor
or
allow {:role, :editor}
"""
def role(%User{role: role}, _object, role), do: true
def role(_, _, _), do: false
end
LetMe compiles the defined rules into authorization and introspection functions.
iex> MyApp.Policy.authorize?(:article_read, current_user)
true
iex> MyApp.Policy.get_rule(:article_create)
%LetMe.Rule{
action: :create,
allow: [
[role: :admin],
[role: :writer]
],
name: :article_create,
object: :article,
# ...
}
You can also get a list of rules and apply filters on them. There are some more features including a Schema
behaviour for query scoping and field redactions.