README.md in action_access-0.0.3 vs README.md in action_access-0.1.0
- old
+ new
@@ -4,11 +4,11 @@
Action Access is an access control system for Ruby on Rails. It provides a
modular and easy way to secure applications and handle permissions.
It works at controller level focusing on what **actions** are accessible for
-the current user instead of handling models and their attributes.
+the current user instead of messing with models and their attributes.
It also provides utilities for thorough control and some useful view helpers.
## Installation
@@ -22,60 +22,77 @@
```
## Basic configuration
-The most important setting is the way to get the **clearance level** (role,
-user group, etc.), other than that it works out of the box.
+The most important setting is how to get the **clearance levels** (roles,
+credentials, user groups, etc.) for the current session, other than that it
+works out of the box.
Action Access doesn't require users or authentication at all to function so
you can get creative with the way you set and identify clearance levels.
-It only needs a `current_clearance_level` method that returns the proper
-clearance level for the current request. It can be a string or symbol and
-it doesn't matter if it's singular or plural, it'll be singularized.
+It only needs a `current_clearance_levels` method that returns the
+clearance levels granted for the current request. It can be a single clearance
+level (string or symbol) or a list of them (array), and it doesn't matter if
+they're singular or plural (they'll be singularized).
* With `current_user`:
- The default `current_clearance_level` method tests if it can get
- `current_user.clearance_level` and defaults to `:guest` if not.
+ The default `current_clearance_levels` method tests if it can get
+ `current_user.clearance_levels` and defaults to `:guest` if not.
So, if you already have a `current_user` method you just need to add a
- `clearance_level` method to the user. With a role based authorization you
+ `clearance_levels` method to the user. With a role based authorization you
may add the following to your `User` model:
```ruby
class User < ActiveRecord::Base
belongs_to :role
- def clearance_level
+ def clearance_levels
+ # Single role name
role.name
end
end
```
+ or
+
+ ```ruby
+ class User < ActiveRecord::Base
+ has_and_belongs_to_many :roles
+
+ def clearance_levels
+ # Array of role names
+ roles.pluck(:name)
+ end
+ end
+ ```
+
+
* No `current_user`:
- If there's no `current_user` you need to override `current_clearance_level`
- with whatever logic that applies to your application.
+ If there's no `current_user` you need to override `current_clearance_levels`
+ with whatever logic applies to your application.
Continuing with the role based example, you might do something like this:
```ruby
class ApplicationController < ActionController::Base
- def current_clearance_level
+ def current_clearance_levels
session[:role] || :guest
end
end
```
## Setting permissions
Permissions are set through authorization statements using the **let** class
-method available in every controller. The first parameter is the clearance
-level (plural or singular) and the second is the action or list of actions.
+method available in every controller. It takes the clearance levels (plural or
+singular) first and the action or list of actions (array) as the last parameter.
As a simple example, to allow administrators (and only administrators in this
case) to delete articles you'd add the following to `ArticlesController`:
```ruby
@@ -92,16 +109,18 @@
This will automatically **lock** the controller and only allow administrators
accessing the destroy action. **Every other request** pointing to the controller
**will be rejected** and redirected with an alert.
+
### Real-life example:
```ruby
class ArticlesController < ApplicationController
let :admins, :all
- let :editors, [:index, :show, :edit, :update]
+ let :editors, :reviewers, [:edit, :update]
+ let :editors, :destroy
let :all, [:index, :show]
def index
# ...
end
@@ -110,27 +129,33 @@
end
```
These statements lock the controller and set the following:
* _Administrators_ (admins) are authorized to access any action.
- * _Editors_ can list, view and edit articles.
+ * _Editors_ can list, view, edit and destroy articles (can't create).
+ * _Reviewers_ can list, view and edit articles.
* _Anyone else_ can **only** list and view articles.
This case uses the special keyword `:all`, it means everyone if passed as the
-first argument or every action if passed as the second one.
+first argument or every action if passed as the last one.
Again, any unauthorized request will be rejected and redirected with an alert.
+
### Note about clearance levels
Notice that in the previous examples we didn't need to define clearance levels
-or roles anywhere else in the application. With the authorization statement you
-both **define** them and **set their permissions**. The only requirement is
-that the clearance levels from the authorizations match the one returned by
-`current_clearance_level`.
+or roles anywhere else in the application. With the authorization statements
+you both **define** them and **set their permissions**. The only requirement is
+that the clearance levels from the authorizations match at least one from the
+list returned by `current_clearance_levels`.
+This makes it easier to embrace modular designs, makes controllers
+self-contained because everything related to a controller is within the
+controller and avoids leaving unnecessary or unused code after refactoring.
+
## Advanced configuration
### Locked by default
The `lock_access` class method forces controllers to be locked even if no
@@ -149,10 +174,11 @@
```
To **unlock** a single controller (to make it "public") add `let :all, :all`,
this will allow anyone to access any action in the controller.
+
### Redirection path
By default any unauthorized (or not explicitly authorized) access will be
redirected to the **root path**.
@@ -160,22 +186,25 @@
very clear `unauthorized_access_redirection_path` method.
```ruby
class ApplicationController < ActionController::Base
- def unathorized_access_redirection_path
- case current_user.clearance_level.to_sym
- when :admin then admin_root_path
- when :user then user_root_path
- else root_path
- end
+ def unauthorized_access_redirection_path
+ # Ensure an array of symbols
+ clearance_levels = Array(current_user.clearance_levels).map(&:to_sym)
+
+ # Choose a redirection path
+ return admin_root_path if clearance_levels.include?(:admin)
+ return user_root_path if clearance_levels.include?(:user)
+ root_path
end
# ...
end
```
+
### Alert message
Redirections have a default alert message of "Not authorized.". To customize it
or use translations set `action_access.redirection_message` in your locales.
@@ -213,10 +242,11 @@
```
There are better ways to handle this particular case but it serves to outline
the use of `not_authorized!` inside actions.
+
### Model additions
Action Access is bundled with some model utilities too. By calling
`add_access_utilities` in any model it will extend it with a `can?` instance
method that checks if the entity (commonly a user) is authorized to perform a
@@ -231,24 +261,28 @@
```ruby
@user.can? :edit, :articles, namespace: :admin
@user.can? :edit, @admin_article # Admin::Article instance
@user.can? :edit, Admin::ArticlesController
-# True if the user's clearance level allows her to access 'admin/articles#edit'.
+# True if the user's clearance levels allow her to access 'admin/articles#edit'.
```
-`can?` depends on a `clearance_level` method in the model so don't forget it.
-Continuing with the `User` model from before:
+Just like the default `current_clearance_levels` in controllers, `can?`
+depends on the `clearance_levels` method in the model too.
+Following up the `User` model from before:
+
```ruby
class User < ActiveRecord::Base
add_access_utilities
- belongs_to :role
+ has_and_belongs_to_many :roles
- def clearance_level
- role.name
+ # Don't forget this!
+ def clearance_levels
+ # Array of role names
+ roles.pluck(:name)
end
end
```
```erb
@@ -262,16 +296,16 @@
The **keeper** is the core of Action Access, it's the one that registers
permissions and who decides if a clearance level grants access or not.
It's available as `keeper` within controllers and views and as
`ActionAccess::Keeper.instance` anywhere else. You can use it to check
-permissions with the `lets?` method, which takes the clearance level as the
-first argument and the rest are the same as for `can?`.
+permissions with the `lets?` method, which takes the clearance level (only one)
+as the first argument and the rest are the same as for `can?`.
```ruby
-# Filter a list of users to only those allowed to edit articles.
-@users.select { |user| keeper.lets? user.role.name, :edit, :articles }
+# Filter a list of roles to only those that allow to edit articles.
+roles.select { |role| keeper.lets? role, :edit, :articles }
```
## License
@@ -280,6 +314,14 @@
Copyright (c) 2014 MatÃas A. Gagliano.
## Contributing
-See [CONTRIBUTING.md](CONTRIBUTING.md).
+If you have *questions*, found an *issue* or want to submit a *pull request* you
+must read the [CONTRIBUTING file](CONTRIBUTING.md).
+
+- **DO NOT** use the issue tracker for **questions** or to require help, there
+are other means for that (see the CONTRIBUTING file).
+
+- **ALWAYS** open an issue before submitting a **pull request**, it won't be
+accepted otherwise. Discussing changes beforehand will make your work much
+more relevant.