I've sucessfully rolled my own roles and permissions based authorisation system:
- Roles and Permissions are both models in the database that have a many-to-many relationship through another table.
- Users belong_to a role and have_many permissions through that role
- Admin users are able to create new roles and define which permissions go with which roles.
- Controllers and Views check the current user has particular permissions before acting or rendering certain things.
It all works wonderfully. Here's a key part of the code which gets included into application_controller,rb
:
def permitted_action (permission_names)
# if the users permissions do not instersect with those given then redirect to root
redirect_to(root_url) if (permission_names & current_user.permissions.map(&:name)).empty?
end
and here's a controller validating user permissions:
before_action only: [:new, :create] do
permitted_action ['create_user']
end
My problem is that permissions are DB rows with unique string names, and I use the names when I want to identify the permissions. So I seed the DB with all the permissions that I need, and then when a View or Controller needs to check the permission I need to get the permission name right at that point. If I check for permission "add_user" but in the db the permission name is "add_users" it goes wrong.
Then in testing I have to put all the permissions in again as fixtures, and get all the names right again.
When I add functionality that requires more permissions I have to add these permissions in with the same string in at least 3 different places. That seems like the wrong thing to me.
Ideally the controllers would define what the permissions are that they use, and the db/seeds.rb file would pick up those and seed the db, and the test fixtures would also pick them up, but I'm not sure if this is possible.
How should I structure this?
Edit:
I think what would help me is a fixtures parser that feeds the db/seeds.rb file. Taking something like this from permissions.yml
:
archive_tally:
name: archive_tally
description: Archive or make active any tally
category: 3
And spitting out something like this:
archive_tally = Permission.find_or_initiate_by_name("archive_tally")
archive_tally.description = "Archive or make active any tally"
archive_tally.category = 3
archive_tally.save!
I don't think this sort of thing exists yet.