Challah Rolls
Challah (pronounced HAH-lah) Rolls is an extension to the Challah gem that brings basic authorization support in the form of user roles and permissions.
** Note: Prior to Challah v0.8, this functionality was baked in to the main gem. Since then, the roles and permissions are included into this extension instead. **
Requirements
- Ruby 1.9.2+
- Bundler
- Rails 3.1+
- Challah Gem 0.8+
Installation
gem install challah
gem install challah-rolls
Or, in your Gemfile
gem 'challah', '>= 0.8'
gem 'challah-rolls'
Set up
Once the gem has been set up and installed, run the following command to set up the database migrations:
rake challah:rolls:setup
This will copy over the necessary migrations to your app, migrate the database and add some seed data. You will be prompted to add the first user as the last step in this process.
Creating permissions and roles
Since Challah doesn't provide any controller and views for permissions and roles there are a few handy rake tasks you can use to create new records.
The following tasks will prompt for the various attributes in each model:
rake challah:permissions:create # => Create a new Permission record
rake challah:roles:create # => Create a new Role record
Models
Challah Rolls provides two additional models to your app: Permission
, Role
. By default, these models are hidden away in the Challah Rolls gem engine, but you can always copy the models into your app to make further modifications to the functionality.
Permission
A permission is used to identify something within your application that you would like to restrict to certain users. A permission does not inherently have any functionality of its own and is just used as a reference point for pieces of functionality in your app. A permission record requires the presence of a name and key.
A permission's key is used throughout Challah to refer to this permission. Each key (and name) must be unique and will be used later to restrict access to functionality. Permission keys must be lowercase and contain only letters, numbers, and underscores.
If there is a role named 'Administrator' in your app, all permissions will be available to that role. Any new permissions that are added will also be automatically added to the 'Administrator' role, so this is a great role to use for anyone that needs to be able to do everything within your app.
The default Challah installation creates two permissions by default: admin
and manage_users
.
To unpack the Permission
model into your app so you can extend it further, run:
rake challah:unpack:permission # => Copy the Permission model into your app
Role
A role is used to group together various permissions and assign them to a user. Roles can also be thought of as user groups. Each role record requires a unique name.
Roles should only be used within your app to consolidate various permissions into logical groups. Roles are not intended to be used to restrict functionality, use permissions instead.
The default Challah installation creates two roles by default: 'Administrator' and 'Default'. Administrators have all permissions, now and in the future. Default users have no permissions other than being able to log in.
Once you've added a few other permissions, you can easily add them to a role. In this case, the moderator
permission key is added to the default role:
role = Role[:default]
role.permission_keys = %w( moderator )
role.save
To unpack the Role
model into your app so you can extend it further, run:
rake challah:unpack:role # => Copy the Role model into your app
User
Several modifications are made to the user model by Challah Rolls:
Each user is assigned to exactly one Role
and can also be assigned to multiple Permission
objects as needed. Because a user can be assigned to a role (and therefore its permissions) and permissions on an ad-hoc basis, it is important to always check a user record for restrictions based on permissions and not to use roles as a mechanism for restricting functionality in your app.
There are a few helpful scopes to help find users by role and for a particular role:
User.find_all_by_role(:administrator) # => Finds all users that are administrators
User.find_all_by_permission(:manage_users) # => Finds all users that have the :manage_users permission.
Restricted access
One of the main reasons to use a user- and permission-based system is to restrict access to certain portions of your application. Challah Rolls provides basic restriction methods for your controllers, views and directly from any User instance.
Checking for a permission
Since Challah is a permissions-based system, all restricted access should be performed by testing a user for the given permission.
Anywhere you can access a user instance, you can use the has
method and pass in a single permission key to test that user for access:
<ul>
<li><a href="/">Home</a></li>
<% if current_user? and current_user.has(:secret_stuff) %>
<li><a href="/secret-stuff">Secret Stuff</a></li>
<% end %>
<li><a href="/public-stuff">Not-so-secret Stuff</a></li>
</ul>
Notice that we checked for existence of the user before we checked to see if the user has a permission. If you used the restrict_to_authenticated
method in your controller, you can likely skip this step.
Note: current_user
will return nil
if there is no user available, so checking for current_user?
prevents you from calling has
on nil
.
For controller restrictions, use the restrict_to_permission
method:
class WidgetsController < ApplicationController
restrict_to_permission :manage_widgets
# ...
end
The restrict_to_permission
method will also fail if there is no user currently authenticated.
And, just as before, we can use the Rails filter options to limit the restriction to certain actions.
class WidgetsController < ApplicationController
restrict_to_permission :admin, :only => [ :destroy ]
# ...
end
And of course, you can stack up multiple restrictions get very specific about what your users can do:
# Everyone can view index,
# :manage_widgets users can perform basic editing
# and, only :admins can delete
#
class WidgetsController < ApplicationController
restrict_to_authenticated :only => [ :index ]
restrict_to_permission :manage_widgets, :except => [ :index, :destroy ]
restrict_to_permission :admin, :only => [ :destroy ]
# ...
end
Whichever method you use will yield the same results. Just make sure you are checking for a permission key, and not checking for a role. Checking for roles (i.e.: user.role_id == 1
) is shameful practice. Use permissions!
Full documentation
Documentation is available at: http://rubydoc.info/gems/challah-rolls
Issues
If you have any issues or find bugs running Challah Rolls, please report them on Github. While most functions should be stable, Challah is still in its infancy and certain issues may be present.
Testing
Challah Rolls is fully tested using Test Unit, Shoulda and Mocha. To run the test suite, bundle install
then run:
rake test
License
Challah Rolls is released under the MIT license
Contributions and pull-requests are more than welcome.