gpb
Authorization packages
So, I’m starting to evaluate the main Elixir authorization packages for use in Phoenix, and a quick query on hex reveals this list:
authorize - Rule based authorization for Elixir
bodyguard - Bodyguard is a simple, flexibile authorization library with a focus on Phoenix 1.3+ apps.
canada - A DSL for declarative permissions
canary - An authorization library to restrict what resources the current user is allowed to access, and load those resources for you.
policy_wonk - Plug based authorization and resource loading. Aimed at Phoenix, but depends only on Plug. MIT license Updated to compile clean with Elixir 1.4
As well as OvermindDL1’s
permission_ex - Permission management and checking library for Elixir.
Just curious what people’s experience here have been for ease of use and performance.
For my purposes, I’m mainly interested in relatively coarse-grained permissions.
…for some trivia of the day, here in Japan we refer to this as:
Authorization = 承認 (shonin)
Authentication = 認証 (ninsho)
First Post!
OvermindDL1
This is purely a checking library currently. I keep permissions in the database and combine them from there and other sources (LDAP, an oracle database) to make a structure that is cached for short periods of time that I then rapidly do comparisons to using this library. It does nothing else, but it does that well. ![]()
For Authentication I use uberauth though. For authorization I have a custom built thing that uses my permission_ex internally. I do things like:
def report(conn, %{"report" => "facesheets" = report} = params) do
conn
|> can(%Permissions.Report{action: :index, report: report})
~> case do conn ->
...
end
end
Potentially multiple times, like checking that this person is allowed to access each row of a given returned table from a query and filtering it out (I have a can?/2 variant that just returns a boolean, where can/2 returns either the original passed in ‘environment’, many different environments are supported, or it returns an exception, yes returns not throws, the ~> is from expede’s exceptional library that only runs/pipes-into it’s second argument if it is not an exception structure). It’s fairly simple overall but has a massive amount of detail and refinement of access permissions. I can show individual columns for individual student for different students for different users and such, highly detailed. I did not find any library that supported such detail at the time I made it, and I’m unsure about the recently made libraries. ^.^;
Most Liked
dwahyudi
Hi, I build my own authorization code. So, for my needs, I have 3 kinds of authorization:
- Role authorization (specific roles for specific actions, admin or customer service can look users or orders data, etc).
- Ownership authorization (user can only reads and updates his/her own orders, profile, password, posts, etc).
- Combination of above.
I previously used canary, but now I just write my own authorization code.
I have this very first iteration of code. There are 3 public functions. Note, that I previously set current user and its role beforehand (via a plug). (I have a user table and a role table, user table has role_id).
verify_role, to verify if user has a role included as permitted roles.
verify_owner, to verify ownership of user’s data.
verify_owner_or_role, combination of above, just for convenience.
They all return {:ok}, if verified, otherwise they will return {:error, status}, which will be received by FallbackController.
For my case, I prefer checking the authorization on the controller. Something like this:
def show(conn, %{"id" => id}) do
with {:ok} <- verify_owner_or_role(conn, id, ["admin", "customer_service"]) do
user = Accounts.get_user!(id)
render(conn, "show.json", user: user)
end
end
It means that to show a specific user data with id 1, conn must have current_user to be the user with id 1, OR current_user is an admin or a customer service. Your case may vary.
defmodule WshopWeb.ApiSecurity.UserVerification do
def verify_role(conn, permitted_roles) do
with {:ok, conn} <- authenticate(conn),
{:ok, conn} <- authorize(conn, permitted_roles) do
{:ok}
else
{:error, status} ->
{:error, status}
end
end
def verify_owner_or_role(conn, user_id, permitted_roles) do
case verify_role(conn, permitted_roles) == {:ok} || verify_owner(conn, user_id) == {:ok} do
true -> {:ok}
false -> {:error, :forbidden}
end
end
def verify_owner(conn, user_id) do
with {:ok, conn} <- authenticate(conn),
{:ok, conn} <- do_verify_ownership(conn, user_id) do
{:ok}
else
{:error, status} ->
{:error, status}
end
end
defp do_verify_ownership(conn, user_id) do
case conn.assigns.current_user.id == user_id do
true -> {:ok, conn}
false -> {:error, :forbidden}
end
end
defp authenticate(conn) do
if conn.assigns.current_user do
{:ok, conn}
else
{:error, :unauthorized}
end
end
defp authorize(conn, permitted_roles) when permitted_roles == [], do: {:ok, conn}
defp authorize(conn, permitted_roles) do
case role_permitted?(conn, permitted_roles) do
true -> {:ok, conn}
false -> {:error, :forbidden}
end
end
defp role_permitted?(conn, permitted_roles) do
assigns = conn.assigns
Map.has_key?(assigns, :role) && assigns.role in permitted_roles
end
end
There are also 2 other cases:
- For a case which a model can be updated by different user roles, like in Uber/Lyft case, when an order is placed by a customer who can also change the state of the order (create, cancel, etc) but the states (from pickup to finished) are updated by a driver. I could write another function, but I haven’t. Something like
verify_picked_order. I should have renamed above module to be likeUserJourneyVerificationwhile thatverify_picked_ordercould be placed in another module. Note that code above is my first iteration, I should split the authorization for admin/customer service or whatever other than the customer, into another module. - For a case of which an authorization is done on grouping. Let say a teacher can look into his students data. I should create
verify_role_and_owner, which means a teacher must have a role of teacher (or whatever the business requirements demand) and we query the database to check if a teacher teach specific students, so we can show the students data.
I don’t know if this approach is good or not. I like this approach, it is just a group(s) of functions.
Critics and suggestions are welcome.
AstonJ
henry-hz
@dwahyudi, I found what should be the most flexible and powerful model reading the code in python, java, node, etc… of this lib: https://casbin.org/ . Let me know if you and others are willing to join and write in elixir.
Last Post!
krlsdu
Popular in Discussions
Other popular topics
Chat & Discussions>Discussions
Latest on Elixir Forum
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #elixirconf-us
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #security
- #hex









