Inicialmente é importante diferenciar Autorização e Autenticação.
A autenticação é o processo que verifica a identidade de uma pessoa, por sua vez, a autorização verifica se esta pessoa possui permissão para executar determinadas operações. Por este motivo, a autenticação sempre precede a autorização. (retirado da Wikipédia)
Vamos em frente, para o processo de autenticação usaremos o Devise por ser simples e fácil de implementar, além de ser a Gem mais popular para tal tarefa. Existem algumas alternativas para Autorização, como o CanCan, a questão é que queria algo mais “simples”.
Bem, gostaria de deixar toda a tarefa de mapear Grupos de Acesso(roles) e Funcionalidades(features) no Banco de Dados, cabendo a mim somente verificar se este acesso é permitido.
Então fiz o seguinte:
O model User, criado pelo Devise ganha o relacionamento com roles e os métodos can? e cannot?:
class User < ActiveRecord::Base
has_many :users_roles, :class_name => "UsersRoles"
has_many :roles, :through => :users_roles
...
def can? controller, action
admin? || allowed_actions.include?([controller, action])
end
def cannot? controller, action
!can? controller, action
end
def allowed_actions
roles.includes(:features).map{|x| x.features}.flatten.map{|x| [x.controller, x.action]}
end
end
ps. Uso o “admin” como o usuário que terá acesso a TUDO, ele não será uma Role.
Criei as entidades Role e Feature:
class Role < ActiveRecord::Base
attr_accessible :name
has_many :users_roles, :class_name => "UsersRoles"
has_many :users, :through => :users_roles
has_many :roles_features, :class_name => "RolesFeatures"
has_many :features, :through => :roles_features
end
class Feature < ActiveRecord::Base
attr_accessible :action, :controller, :description
has_many :roles_features, :class_name => "RolesFeatures"
has_many :roles, :through => :roles_features
end
E seus Relacionamentos:
class RolesFeatures < ActiveRecord::Base
belongs_to :role
belongs_to :feature
end
class UsersRoles < ActiveRecord::Base
belongs_to :user
belongs_to :role
end
Por fim, suas migrations:
create_table "roles", :force => true do |t|
t.string "name"
end
create_table "features", :force => true do |t|
t.string "controller"
t.string "action"
t.string "description"
end
create_table "roles_features", :force => true do |t|
t.integer "role_id"
t.integer "feature_id"
end
create_table "users_roles", :force => true do |t|
t.integer "user_id"
t.integer "role_id"
end
Com a modelagem de dados montada, fica somente a tarefa de verificar a cada ação. No ApplicationController teremos:
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :authenticate_user!, :authorize_user!
skip_filter :authorize_user!, :if => lambda {|controller| !user_signed_in?}
def authorize_user!
if !is_a?(DeviseController) && current_user.cannot?(self.controller_name, self.action_name)
render :text => 'Sem acesso'
end
end
end
Onde :authenticate_user! é um método do devise.
ps. Se você quiser criar uma Gem que automatize esta tarefa, nem precisa pedir autorização, me mande o link somente para que eu possa contribuir. :)
Tags:auth, rails