Bouncer is a simple library that allow to create your permission policy using a simple behavior. It was built with Phoenix framework in mind but can be adapted to many more situations.
Built With Elixir
Report Bug
·
Request Feature
The package is curently not available in hexpm but we are planning to add it in a near future.
In the meantime you can add it to your project dependancies using git.
def deps do
[
{:bouncer, git: "https://github.com/lenra-io/bouncer.git", tag: "vx.y.z"}
]
end
Bouncer is designed around the Bouncer.Policy
behavior. You just have to create a MyApp.Policy
module and implement the authorize/3
function.
The following example are used with Phoenix controllers.
# First we define the Policy using the behavior for a specific Controller
defmodule MyApp.MyController.Policy do
@behaviour Bouncer.Policy
@impl true
# The authorize/3 function take:
# - the atom representing the current user action
# - the user struct/map or anything that represent a resource/user/account
# - Any metadata useful to check permissions
# If the user is an admin, he can do everything
def authorize(_, %User{role: :admin}, _), do: true
# any one can acces the index page
def authorize(:index, _, _), do: true
# Only verified user can create the resource
def authorize(:create, %User{role: :verified_user}, _), do: true
# Only the owner of the resource can update or delete the resource.
# We use pattern matching on id to ensure that the id is the same.
# If the id of the user and the user owner is different, it will not match.
# We also use guard to group the rule of :create and :delete
def authorize(action, %User{id: id}, %Resource{owner: User{id: id}}) when action in [:create, :delete], do: true
# Good practice, deny everything else baseline.
def authorize(_, _, _), do: false
end
Now that the policy is defined, you just have to use Bouncer.allow/4
or Bouncer.allow?/4
to check if the given resource can do the given action with a given metadata.
defmodule MyApp do
# The allow/3 function is designed to be used inside the `with` flow control.
# the allow?/3 function can be use in Enum.filter, if...
def index do
with {:ok, user} <- fetch_my_user(),
:ok <- Bouncer.allow(MyApp.Policy, :index, user, nil) do
do_my_stuff()
end
end
end
Since this can add a lot of repetitive code such as fetching user, specify the module and the action, you can also create some macro to help you shorten the Bouncer.allow
call.
In this example, we will create a macro that will automatically extract the user from the conn and pass the Policy module and the controller action. This will transform the allow/4 function into a allow/2 function that take the conn and the metadata.
defmodule MyApp.Policy do
defmacro __using__(opts \\ []) do
policy_module = Keyword.get(opts, :module)
quote do
@spec allow(any(), any()) :: :ok | {:error, atom()}
def allow(conn, params \\ nil) do
Bouncer.allow(
unquote(policy_module),
Plug.action_name(conn),
get_user(conn),
params
)
end
end
end
end
Then, use it on the Phoenix controller like so
defmodule MyApp.MyController do
use MyApp, :controller
use MyApp.Policy,
module: MyApp.MyController.Policy
def index(conn, %{id: resource_id}) do
with {:ok, resource} <- fetchResource(resource_id)
:ok <- allow(conn, resource) do
doStuff()
end
end
end
Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.
If you have a suggestion that would make this better, please open an issue with the tag "enhancement". Don't forget to give the project a star if you liked it! Thanks again!
Distributed under the MIT License. See LICENSE for more information.
Lenra - @lenra_dev - contact@lenra.io
Project Link: https://github.com/lenra-io/bouncer