zachallaun
Hi friends,
I wanted an authorization utility for an existing project I’m working on where I have a fair amount of permissions logic duplicated between functions that check whether a person can do something to a given resource and query utilities that filter a query based on what a person is allowed to do.
After spending a bit of time looking at existing solutions, I decided to spend a couple days putting something together myself.
The library is extremely alpha and not yet released on hex. It’s basically at the “works enough to test in my own project” stage, but I’d like to hear from folks on the approach I’ve taken and whether it’s worth pursuing further. Basically: I (think I) like it and will use it myself, but I’m not sure whether it makes sense to invest in bits that may make it more useful for others.
If you have a spare 5-10 minutes, please consider reading through the example in the README. I’d love to hear any thoughts or feedback. Thanks!
Trending in RFCs
Other Trending Topics
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
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #blog-post
- #elixir-ls
- #ai
- #elixirconf-us
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming











Showing Posts 1 to 10- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
csadewa
I just looked at
Janus, and i love the ease and flexibility for defining rule for security!I have a bit of concern though, how about the performance of
Janus? I look at code for a bit and it seems to depend on DB (via ecto query), so i have concern on latency and throughput of it.zachallaun
Thanks for taking a look!
As for performance: calls to
allows?andforbids?do not hit the database, but operate only on the in-memory resource.filtercreates an Ecto query, the performance of which will depend on the indexes, etc. that you have set up for your database — this is not really any different than if you had written the query yourself.I’ve not benchmarked anything, but I’m fairly certain that performance will be acceptable for the vast majority of use-cases.
al2o3cr
Some quick reactions:
what about preloads?
filter?could help, but it’s going to take a lot more code than “normal” preloadswhat about conditions that involve other records? For instance, “admin can only edit a post if it doesn’t have any replies”
zachallaun
Thanks for the questions.
Can you give a quick example of what you mean by this vs. “normal” preloads?
filterdoes no preloading, it just filters the query using joins. I don’t think it makes sense to couple preloading to authorization, since the concerns are separate — you may need more/less/different associations preloaded than are needed for auth.allows?also does no preloading for now and just raises if a required association isn’t loaded. This is not optimal though, as your code checking permissions withallows?shouldn’t need to know what needs to be preloaded. It might make sense to accept a repo as an option, so you could do something likeIf you’re checking auth in a loop this could lead to N+1 queries, but in that case you should likely be using
filterto filter the set of resources once.Good question and something I’ve thought about a bit. Two possibilities:
allow,forbid, etc. that lets you pass two functions, one that will run for authorizing a single resource and one that will filter a query. This way you can get as complex as needed. This would be very easy to add support for.allows?andfilterin your policy module — this is possible now but requires some boilerplate.I think I prefer the first option.
al2o3cr
Sticking to the models in the README for concreteness, imagine you wanted to load:
A no-auth approach (the “normal” preloads case) would be straightforward:
zachallaun
Got it.
This kind of API should be fairly straightforward to implement, and I can certainly see the utility:
This would basically be sugar for:
zachallaun
This proved a little trickier, but the result is actually pretty dang cool. I’m not quite done implementing this yet but I’m confident it can be done.
To take your example a little further: Let’s say you were implementing user search. In the results, you wanted to display the user’s last thread, the first post in that thread, and user’s latest post in any thread.
Normal users can’t see banned users or archived threads or posts, but moderators can see both.
Here’s how it might look like using Janus:
The result of this would be the users that the current user can read, preloaded with the latest thread created by that user (and that thread’s first post) that the current user can read, and the latest post by that user that the current user can read. So a normal user would see non-banned users and non-archived threads/posts, while a moderator may see banned users and archived threads/posts mixed into the results. This all done with a single query and includes users that may have no threads or posts.
Man, Ecto is really cool.
PJUllrich
Loving this
It’s late here, so I’ll get back to this tomorrow, but have you also looked into implementing query restrictions using “Tenancy” instead of implementing another DDL for Ecto? Personally, I’d want the impact of my RAP as minimal as possible on the “normal” syntax like Ecto’s query language. Also, it should be easily replaceable/removable. That’s why I love our RAP at Remote. You can remove it by removing the plug and that’s it! As a dev, I wasn’t even aware that we had a RAP for a whole year 
See here for example: Multi tenancy with foreign keys — Ecto v3.14.0
zachallaun
Thanks for the suggestion! I’m using this exact approach in one of my own applications (based on the same guide that you linked). I see tenancy as somewhat separate/tangential to authorization, however – in my eyes, tenancy is about creating a hard separation between entities that should never interact, whereas authorization is about giving permission to actors within an entity to operate on resources also within that entity.
There’s certainly a chance I’ve misunderstood you, however, so I’m definitely eager for you to expand on this idea when you are able!
I think that we’re in total agreement here. The DDL used by Janus (in
:whereclauses, for instance) is meant to be essentially a simplified subset of Ecto’s keyword syntax. It’s simplified so that it can be used to both authorize a single resource and build up an Ecto query. I toyed with the idea of somehow usingEcto.Query.API, but I think that adds too much complexity for too little benefit.To my eyes, the impact of Janus on the normal way of querying Ecto is extremely small. From outside of your policy module, you don’t do anything differently while building up your query except for appending a call that composes with your query to filter to authorized resources.
Here’s a sketch of what a context function might look like that loads authorized resources:
I think this approach is similarly replaceable/removable. Since you “own” your policy module and no code outside of that module should reference
Janus, the impact of removing/replacing is limited. I’m trying to keep the API very small (currentlyauthorize,any_authorized?andauthorized[which I think I’m going to rename tofilter_authorizedor similar]).Excited to hear more of your thoughts tomorrow!
zachallaun
@PJUllrich Just read part 2 and updated my gist with notes on how I might implement that using Janus!
I see now what you mean by using the “tenancy” approach! I think it translates nicely in Janus using
filter_authorizedand a process-dictionary-cached policy: