27 de set. de 2007

Login e Controle de Acesso em Ruby on Rails

Tempos atrás estava procurando algo que facilitasse minha vida com autenticação e controle de acesso em RoR. Passei algum tempo quebrando um galho com a engine do Goldberg. Boa, mas muito gigante para aplicações simples. Cheguei nesse tutorial rápido e espero que ajude, não inventei nada, só reescrevi a roda: 1. Crie uma nova aplicação "rails novosite" 2. "cd novosite" 3. Instale o plugin "gem install login_generator" e em seguida gere os arquivos de autenticação com "ruby script/generate login LoginSystem" onde LoginSystem será o nome do seu controller de autenticação. 4. Crie um migrate para criar a estrutura do controle de acesso "ruby script/generate migration create_acl_schema". 5. Coloque o conteudo abaixo no migrate criado
class CreateAclSchema < ActiveRecord::Migration
  def self.up
    create_table :users do |t|
      t.column :login, :string, :limit=>40, :null=>false
      t.column :name, :string, :limit=>40
      t.column :password, :string, :limit=>40, :null=>false
      t.column :updated_at, :datetime
      t.column :created_at, :datetime
      t.column :access, :datetime
      t.column :nick, :string
    end
    create_table :roles do |t|
      t.column :name, :string, :limit=>40, :null=>false
      t.column :info, :string, :limit=>80
    end
    create_table :permissions do |t|
      t.column :name, :string, :limit=>40, :null=>false
      t.column :info, :string, :limit=>80
    end
    create_table :roles_users do |t|
      t.column :user_id, :integer, :null=>false
      t.column :role_id, :integer, :null=>false
    end
    create_table :permissions_roles do |t|
      t.column :role_id, :integer, :null=>false
      t.column :permission_id, :integer, :null=>false
    end
  end
def self.down
    drop_table :users
    drop_table :roles
    drop_table :permissions
    drop_table :roles_users
    drop_table :permissions_roles
  end
end
6. Crie outro migrate para a entidade que iremos testar "ruby script/generate migration carro" 7. Coloque o conteudo abaixo no migrate criado
class CreateCarros < ActiveRecord::Migration
  def self.up
    create_table :carros do |t|
      t.column :user_id, :integer
      t.column :name, :string
      t.column :age, :integer
      t.column :updated_at, :datetime
      t.column :created_at, :datetime
    end
  end

  def self.down
    drop_table :carros
  end
end
8. Configure o config\database.yml e crie a base recomendada 9. execute "rake migrate" para criar tabelas na base de dados e depois "ruby script\generate scaffold Carro" para criar um CRUD de teste para a entidade Carro. 10. Crie os seguintes arquivos: app/models/permission.rb
class Permission < ActiveRecord::Base
  has_and_belongs_to_many :roles
end
app/models/role.rb
class Role < ActiveRecord::Base
  has_and_belongs_to_many :permissions
  has_and_belongs_to_many :users
end
lib/acl_system.rb
module ACLSystem
  include LoginSystem

  protected

  def authorize?(user)
    required_perm = "%s/%s" % [ params['controller'], params['action'] ]

    if user.authorized? required_perm
      return true
    end

    return false
  end
end
11. Em app/models/user.rb modifique adicionando os seguintes métos e relacionamento:
class User < ActiveRecord::Base
has_and_belongs_to_many :roles

  # Return true/false if User is authorized for resource.
def authorized?(resource)
    match=false
    permission_strings.each do |p|
      r = Regexp.new(p)
      match = match || ((r =~ resource) != nil)
    end
    return match
end


  # Load permission strings 
  def permission_strings
    a = []
    self.roles.each{|r| r.permissions.each{|p| a<< p.name }}
    a
  end
E em app/controllers/application.rb para:
require_dependency "acl_system" 
class ApplicationController < ActionController::Base
include ACLSystem 
model :user
E em app/controllers/carros_controllers.rb para:
class CarrosController < ActiveRecord::Base
before_filter :login_required
Obs: "before_filter :login_required" exige que o usuário esteja logado e tenha permissao de acesso. 12. Inicie o servidor e acesse "localhost:3000/login_system/signup" crie dois usuários de teste 13. Utilize o script para criar as permissões, regras e associar ao usuário:
INSERT INTO roles (id, name, info) VALUES (1, 'admin', 'Admistrador'); 
INSERT INTO roles (id, name, info) VALUES (2, 'normal', 'Usuario Comum'); 
INSERT INTO roles_users (user_id, role_id) VALUES (1, 1); 
INSERT INTO roles_users (user_id, role_id) VALUES (2, 2); 
INSERT INTO permissions(name,info) VALUES('.*/.*', 'Acesso Total'); 
INSERT INTO permissions(name,info) VALUES('carros/index', 'Acesso reduzido'); 
INSERT INTO permissions(name,info) VALUES('carros/list', 'Acesso reduzido'); 
INSERT INTO permissions_roles (permission_id, role_id) VALUES (1, 1); 
INSERT INTO permissions_roles (permission_id, role_id) VALUES (2, 2); 
INSERT INTO permissions_roles (permission_id, role_id) VALUES (3, 2); 
14. Pronto... agora é só testar Acesse "http://localhost:3000/carros/" com o segundo usuário criado e verifique que existe acesso. Mas não é possível criar um novo carro. Para o primeiro usuário criado o acesso é total. Referências: LoginGeneratorACLSystem LoginGenerator

Um comentário:

Unknown disse...

Vale a pena lembra que em tabelas associativas ( habtm ) não é necessário, e até mesmo aconselhável a não utilizar o id. Portanto utilize ao criar as tabelas:

create_table :roles_users, :id => false do |t|

create_table :permissions_roles, :id => false do |t|

Google