Permit - an authorization library for Phoenix, LiveView and Ecto

Hello!
We’ve released Permit - a new open-source project at Curiosum aiming to make it easier to manage permission-based access control in Elixir applications, supporting Ecto, Phoenix and LiveView. Permit exposes a mostly plain-Elixir, DSL-free syntax to reduce developer confusion and avoid a steep learning curve.

Through Permit.Ecto, Permit is the first authorization library for Elixir to automatically convert authorization conditions to Ecto queries, as well as being the first to integrate seamlessly with Phoenix LiveView with Permit.Phoenix .

If you’re interested in securing your controllers and making resource loading and authorization easier, or you used to like cancancan popular with Rails developers, you might find Permit especially useful (it is very far from being a clone of anything, though). See README for usage examples.

Permit consists of three packages:

Links:

Feel free to contribute and enjoy the usage!

15 Likes

This looks like a really great library for those outside of the Ash ecosystem. Congratulations!

Not quite; Ash has had it’s SAT solving policy authorizer which automatically constrains read and write queries to PostgreSQL (as well as any other supported DataLayer) for several years now.

3 Likes

I’ve seen a similar feature in the LetMe library by @woylie , named “scoped queries” :grinning:

Very neat never the less!

1 Like

We’ve taken a look at this some time ago, and it looks to me that scoped queries still require you to define Ecto queries manually - this is good from a specific standpoint, but the approach here is that most conditions should be convertible automatically.

1 Like

Thank you for pointing us to this, James.

This escaped our attention somehow, and looks to be tightly coupled with Ash altogether, but we’ll update the documentation and other descriptions to take it into account.

Thank you for contributing!

Would you mind to also write few words how it compares to Janus ?

I think you have a copypasta in your README, there are 2 functions called show/2

I wrote Janus :slight_smile:

On the surface, it looks like Permit and Janus are very similar! They both prefer plain Elixir and a single source of truth – the same authz rules are used for individual resources as well as scoped Ecto queries.

Permit seems much more feature-complete (Phoenix/LiveView integration) and has corporate backing, which makes it very attractive! I need to look at Permit more carefully, but I’d love to see if there’s anything novel in Janus that could be ported to Permit and potentially retire Janus if they’re as similar as I think they are.

5 Likes

Hi,

I just tried installing the latest versions of {:permit_ecto, “~> 0.2.0”} and {:permit_phoenix, “~> 0.1.0”} and am getting a dependency conflict:

Because permit_ecto >= 0.2.0 depends on permit ~> 0.2 and permit_phoenix >= 0.1.0 depends on permit ~> 0.1.3, permit_ecto >= 0.2.0 is incompatible with permit_phoenix >= 0.1.0.
And because your app depends on permit_ecto ~> 0.2.0, permit_phoenix >= 0.1.0 is forbidden.
So, because your app depends on permit_phoenix ~> 0.1.0, version solving failed.

Looks like permit_ecto just got bumped. Can I expect an update to permit_phoenix soon?

Thank you

I’ve started using permit_ecto and the accessible_by! function is working. E.g., it is restricting which schema objects a user can read but am having trouble when checking authorization.

Background
In my case a User Participates in Games. The participant table is a join of the game_id and user_id. In my Ecto schema, Game has a belongs_to :user for the owner and has_many :participants for the players.

What works
Authorization.accessible_by! and Authorization.read? agree if I make a permit read on the user_id field of the game. That is checking the read? authorization returns true for games returned with accessible_by!

    permit()
    |> read(Game, user_id: id)

What’s not working
When I set the read permit based on the participant association, Authorization.accessible_by! returns the correctly restricted list of games but the read? authorization returns false.

    permit()
    |> read(Game, participants: [user_id: id])

Should this work or should I be declaring permit.can some other way?