Sheriff
Build simple and robust authorization systems with Elixir and Plug.
Installation
If available in Hex, the package can be installed as:
Add
sheriff
to your list of dependencies inmix.exs
:def deps do [{:sheriff, "~> 0.1.0"}] end
Ensure
sheriff
is started before your application:def application do [applications: [:sheriff]] end
Current User
Sheriff defaults to :current_user
being set as a key in the Plug.Conn.private
map, but you can specify your own in your config:
config :sheriff,
resource_key: :your_desired_resource_key
Resource Loading
Resource loaders are responsible for retrieving the targetted resource provided for a specific request. A global loader can be can be specified in your application configuration or individual loaders can be supplied on a per plug basis.
Sheriff ships with a convenient Sheriff.ResourceLoader
behaviour:
defmodule Example.UserLoader do
@behaviour Sheriff.ResourceLoader
def fetch_resource(:show, %{"id" => id}), do: Repo.get(User, id)
def fetch_resource(:index, _params), do: Repo.all(User)
end
Policies
Policies are modules that implement the Sheriff.Policy
behaviour:
defmodule Example.UserPolicy do
@behaviour Sheriff.Policy
alias Example.User
# Admins can see all the things!
def permitted?(%User{role: "admin"}, _request, _resource), do: true
# Users can access themselves
def permitted?(%User{id: id}, _request, %User{id: id}), do: true
# Team admin can view team members
def permitted?(%User{role: "team_admin", team_id: id}, :show, resources) do
Enum.all?(resources, &(&1.team_id == team_id)
end
# Not match, no access
def permitted?(_, _, _), do: false
end
Plugs
There are two plugs that Sheriff relies upon of which should occur after Plug.Parser
:
Sheriff.Plug.LoadResource
- Uses the configuredResourceLoader
to fetch the target resourceSheriff.Plug.EnforcePolicy
- Apply a givenPolicy
against the current user, target resource, and request.
When setting up our pipeline, we can use something like this:
plug Sheriff.Plug.LoadResource, resource_loader: Example.UserLoader
plug Sheriff.Plug.EnforcePolicy, policy: Example.UserPolicy
Error Handling
Within Sheriff there are three error scenerios we want to address:
- The request resource is missing
- The current user is not authenticated
- The current user is not authorized to perform the requested action
We’ll want to provide a handler to Sheriff. A handler is any module that
implements resource_missing/1
, unauthenticated/1
, and unauthorized/1
;
you may can use the Sheriff.Handler
behaviour if you’d like.
If you do this, you will need to add it to your config as well:
config :sheriff,
handler: YourApp.ErrorHandler
That’s it!