dorgan
Hi,
I’m working on an authorization system for my phoenix app, and I need some help designing it..
I want it to be based on roles and permissions, and I also want to be able to create, update and delete roles at runtime and have them persisted in the database.
Permissions wouldn’t change very often as they would imply changes to the codebase, so I think they can be hardcoded.
I’ve seen bodyguard, canary and policy_wonk but neither of them seem to suit my needs so I prefer to roll my own.
This is the approach I would follow:
- Create
rolesandrole_usertables for a many_to_many association between users and roles - As the permissions are hardcoded, the roles table would have a column for each of the permissions(ie: a boolean
create_postcolumn) or a comma delimitedpermissionsstring column. I don’t really know which is better, but I like the latter because I don’t need to run migrations if my permissions ever change. - Write an authorization module that exposes an API like
BodyGuard’s, for example:Authorization.can(:create_post, user)
Is this, in general, a good approach I should go for? How would you approach this?
Thanks!
Trending in Questions
I having some trouble figuring out if I have set myself too strict of standards for my production server. Currently I can handle 75% of r...
New
Hello,
I’m trying to build a basic Phoenix web-app, and I’d like to use Tailwind.
However, when I launch mix phx.server, I get an error...
New
Hi everyone,
I am toying with the idea of building a “match maker” for giving personal help to people that wants to start coding.
I sta...
New
I’m working on a small exercise involving update_in/3, and I came up with this solution:
data = %{
name: "Periodic Table",
category:...
New
I’ve got trouble wrapping my head around the order in which functions are called in this snippet (from Phoenix’s authentication):
toke...
New
Is there any way to avoid the Hologram compiler running when using iex? It seems like the front-end code could potentially be disregarded...
New
** (ArgumentError) expected :max_attempts to be a positive integer, got: {:@, [line: 10, column: 19], [{:max_attempts, [line: 10, column:...
New
Other Trending Topics
Edit: 2026 May 15 - This post is archived.
Mob is alive!!
Main docs: mob v0.7.11 — Documentation
A bit of explanation for the slightly c...
New
I am happy to introduce the very α version of the new programming language compiled to BEAM.
Welcome Cure.
It has literally three kille...
New
Hobbes is a low-level distributed database for the Elixir programming language.
Hobbes provides a simple, safe, and scalable storage lay...
New
A little off-topic, but I feel like people here have a good head on their shoulders.
I used to be quite good at making software. Was luc...
New
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ai
- #ecto-query
- #elixirconf-us
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #elixirconf-eu
- #api
- #forms
- #metaprogramming
- #hex











Showing Posts 1 to 10- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
OvermindDL1
Similar to what I wrote at work it sounds like (I really should generify it and put it out as a library someday…).
Essentially what I did was this:
MyServer.Permissions.SomeBlahPermissionfor example), each of which has at least an ‘action’ field, among other (also a ‘default’ function for displaying in the UI for default values, which are different from default values when instancing it in code).can/can?set of functions (and others) that takes anenvironment (usually aconnorsocketor so that grabs the information from a protocol).can?returns a boolean (great forEnum.filter!), thecanreturns either the environment itself if it passes or an exception structure (not raising it, returning it, I use the Exceptional library a lot, that’s where~>comes from, it pipes into if the value is ‘good’ otherwise returns it directly). Thecan’s tend to be called many times in a single call so efficient cache’ing is pretty important (it can add an inline cache to the, for example, ‘conn’ for faster lookup on future calls as well so there is no ETS hit too via Cachex)canaccesses a Cachex cache that calls into the database. All Cachex caches of a given account_id are wiped everywhere anytime the permissions are written.One big change I’d make would probably make the format in the database a bit different, right now the permissions tables for account_id/group is the account_id/group_id, the structure name, and a jsonb of the structure data. I’d probably change it to allow for direct record comparisons more easily in ‘some’ cases in the database so I wouldn’t have to filter in-code so often, though sadly I wouldn’t actually be able to do that in ‘most’ of my cases because so much data comes from the Oracle and LDAP systems, but would be more useful for setups that are more usual.
But with this style I can test to the logged in user, I can test via a variety of other things, all with simple
can/2/can?/2calls. It’s efficient enough that even looping over 2k records for a report is still less than a second with the database lookup and all, which is significantly faster than the system it was replacing anyway.On the admin side the permissions are taken a structure to display to the user based on the defaults call on each permissions module, so I can set permissions from the UI in detail. In addition there is a ‘matrix’ view for mass editing of many account permissions via some pre-built sets with ease, all dynamically generated based on the permission structures that are detected in the system (based on their
@behaviourmodule attributes).EDIT: Oh, also thanks to the permission_ex library there is an admin lookup for easy overriding and a blacklist key for deny’ing, even if other permissions would allow for it. It makes it very easy.
dorgan
I took a look at your permission_ex, in essence it’s pretty similar to the module I was coding, and I also lookup for an admin permission for easy overriding too
So, if I understood correctly, you store the permissions per user and per group(what I call roles), serialized in a jsonb column, then retrieve every permission for a given user and match against them. As you match against a list of permissions, you can also get permissions from other sources(eg ldap). You also cache the permissions per user to improve performance.
Is this correct?
If so then I think I will go that way, your approach has enlightened me
Right now I’m storing the permissions per group and assigning groups to users, I also wrote a plug that checks in the
connstruct for apermsassign and match the permissions list against it. Since the user is retrieved from the db on every request and by extension it’s permissions, I guess it’s a good idea to cache it as you do as an improvement, but I think that overall yes, we’re doing similar thingsThank you!
OvermindDL1
In essence. I just have Cachex with a fallback function do all the lookups when there is a cache miss, it’s a nice singular place to put it all.
If it’s just hitting the database once per connection it’s not really a big deal to not cache it, I’m mostly caching it because initial lookup can be crazy slow at times because of the way old servers I have to interact with. It’s still good to put it all behind a module API so you can always swap it out with a cache later if necessary without needing to change your API, that is why I pass the 'env’ironment in/out of each function so I can cache on it as well transparently without changing the API.
mythicalprogrammer
I just use Guardian.Permissions.Bitwise and add the roles into table hardcoded in a permission field in user table (probably should do many to many like yours). Since I’m using Guardian for token anyway mind as well use their Permissions functionality.
dorgan
Interesting, I overlooked Guardian because I wasn’t interested in token based authentication and didn’t know it included an authorization module. I’ll take a closer look at it
dorgan
Great! So it’s basically what I was looking for, but I didn’t know if I was going on a good path
Thanks for sharing your approach!
OvermindDL1
Guardian is mostly a JWT-style wrapper over JWT or token auth. It’s permissions are simple boolean flags, last I checked (could be changed now) it didn’t support arbitrary permissions on things, like, per-account_id permissions or so, only simple boolean flags.
maz
FYI, the links to Online Documentation are broken here:
https://hexdocs.pm/permission_ex/getting_started.html
OvermindDL1
Ugh, must have happened when I updated ex_doc, wonder how that happened as the docs haven’t changed other than just updating that… ^.^;
And fixed it, looks like ex_doc removed a feature I was using, so that was fun… >.>
maz
Awesome, Works For Me now.