Autenticação – Permitir login com nome de usuário ou email

I – Introdução

II – Principais tecnologias usadas neste artigo

  • Linux Ubuntu 11.10
  • Ruby 1.9.2
  • Rails 3.2.1
  • gem Devise 2.0

III – O que não precisamos instalar

IV – O que precisa ser feito antes

V – Sequência de passos para implementação

Passo 01 – Entre na aplicação que criamos anteriormente

$ cd service_desk

Passo 02 – Corrigir mensagens

  • Para que o app tenha menos código e fique mais DRY (Don’t repeat yourself), vamos corrigir duas views.
  • Em app/views/tickets/show.html.erb, retire a seguinte linha
<p id="notice"><%= notice %></p>
  • Em app/views/layouts/application.html.erb, modifique a “section main” para que fique da seguinte maneira:
<section id="main">
   <p class="notice"><%= notice %></p>
   <p class="alert"><%= alert %></p>
   <%= yield %>
</section>

Passo 03 – Inclua a coluna username na tabela de usuários (Users)

  • A coluna username será única (uniq) na tabela Users.
  • A migration irá gerar um índice na mesma tabela Users, para otimizar os acessos pela chave username.
$ rails g migration add_username_to_users username:string:uniq
$ bundle exec rake db:migrate

Passo 04 – Gere as views do Devise

  • Para incluir o campo username, que terá a label “login” nas views do Devise, é necessário personalizar as mesmas views.
  • Vamos, agora, gerar todas a views do Devise para que seja possível personalizar as telas de registro de usuário, de login de usuário, e tela de modificação de senha.
$ rails g devise:views
  • Veja abaixo que foram criados a pasta “devise” e as pastas e arquivos (views) do Devise.
$ tree app/views/devise/
app/views/devise/
├── confirmations
│   └── new.html.erb
├── _links.erb
├── mailer
│   ├── confirmation_instructions.html.erb
│   ├── reset_password_instructions.html.erb
│   └── unlock_instructions.html.erb
├── passwords
│   ├── edit.html.erb
│   └── new.html.erb
├── registrations
│   ├── edit.html.erb
│   └── new.html.erb
├── sessions
│   └── new.html.erb
└── unlocks
    └── new.html.erb

6 directories, 11 files

Passo 05 – Modifique as views do Devise a seguir

  • Na view app/views/devise/sessions/new.html.erb:
<div><%= f.label :email %><br /> # delete essa linha
<%= f.email_field :email %></div> # delete essa linha
<div><%= f.label :login %><br /> # insira essa linha
<%= f.text_field :login %></div>  # insira essa linha
  • Na view app/views/devise/registrations/new.html.erb:
<div><%= f.label :username %><br /> # insira essa linha
<%= f.text_field :username %></div> # insira essa linha
<div><%= f.label :email %><br /> # mantenha essa linha
<%= f.email_field :email %></div> # mantenha essa linha
  • Na view app/views/devise/registrations/edit.html.erb:
<div><%= f.label :username %><br /> # insira essa linha
<%= f.text_field :username %></div> # insira essa linha
<div><%= f.label :email %><br /> # mantenha essa linha
<%= f.email_field :email %></div> # mantenha essa linha
  • Na view app/views/devise/passwords/new.html.erb:
<div><%= f.label :email %><br /> # delete essa linha
<%= f.email_field :email %></div> # delete essa linha
<div><%= f.label :login %><br /> # insira essa linha
<%= f.text_field :login %></div> # insira essa linha

Passo 06 – Modifique a label das views para que mostre “username or email” ao usuário

  • Modifique o arquivo /config/locales/en.yml para que fique parecido com o trecho abaixo:
en:
  hello: "Hello world"            

  activerecord:
    attributes:
      user:
        login: "Username or email"

Passo 07 – Indique ao Devise para que use :login no authentication_keys

  • Modifique o arquivo /config/initializers/devise.rb para que tenha:
config.authentication_keys = [ :login ]

Passo 08 – Agora várias alterações do model User serão necessárias

  • Modifique o arquivo /app/model/user.rb para que tenha:
attr_accessible :username, :login

# Virtual attribute for authenticating by either username or email
# This is in addition to a real persisted field like 'username'
attr_accessor :login

# Overwrite Devise’s find_for_database_authentication method
def self.find_for_database_authentication(warden_conditions)
   conditions = warden_conditions.dup
   login = conditions.delete(:login)
   where(conditions).where(["lower(username) = :value OR lower(email) = :value", { :value =>
     login.strip.downcase }]).first
end

protected

 # Attempt to find a user by it's email. If a record is found, send new
 # password instructions to it. If not user is found, returns a new user
 # with an email not found error.
 def self.send_reset_password_instructions(attributes={})
   recoverable = find_recoverable_or_initialize_with_errors(reset_password_keys, attributes, :not_found)
   recoverable.send_reset_password_instructions if recoverable.persisted?
   recoverable
 end 

 def self.find_recoverable_or_initialize_with_errors(required_attributes, attributes, error=:invalid)
   (case_insensitive_keys || []).each { |k| attributes[k].try(:downcase!) }

   attributes = attributes.slice(*required_attributes)
   attributes.delete_if { |key, value| value.blank? }

   if attributes.size == required_attributes.size
     if attributes.has_key?(:login)
        login = attributes[:login]
        record = find_record(login)
     else
       record = where(attributes).first
     end
   end  

   unless record
     record = new

     required_attributes.each do |key|
       value = attributes[key]
       record.send("#{key}=", value)
       record.errors.add(key, value.present? ? error : :blank)
     end
   end
   record
 end

 def self.find_record(login)
   where(["username = :value OR email = :value", { :value => login }]).first
 end

Passo 09 – Alimentar dados automaticamente no sistema

  • Vamos usar comandos de inserção de dados, dentro do arquivo “seed.rb”
  • Cada vez que alguém iniciar este projeto do zero, será possível inicia-lo com esses dados de exemplo.
  • Desta forma, teremos quatro usuários já cadastrados no sistema, todos com a senha 123456:
    • superuser
    • admin
    • user
    • user1
  • Modifique o arquivo /db/migrate/seeds.rb para que tenha:
superuser_user = User.create!(:username => 'superuser',
                              :email => 'superuser@example.com',
                              :password => '123456',
                              :password_confirmation => '123456',
                              :confirmed_at => Time.now )
admin_user = User.create!(:username => 'admin',
                          :email => 'admin@example.com',
                          :password => '123456',
                          :password_confirmation => '123456',
                          :confirmed_at => Time.now )
user_user = User.create!(:username => 'user',
                         :email => 'user@example.com',
                         :password => '123456',
                         :password_confirmation => '123456',
                         :confirmed_at => Time.now )
user1_user = User.create!(:username => 'user1',
                          :email => 'user1@example.com',
                          :password => '123456',
                          :password_confirmation => '123456',
                          :confirmed_at => Time.now )                               

# create ticket #1
tkt1 = Ticket.create!( ticket_number: "00001", title: "ticket number 00001" )
Task.create!( description: "task 001 of ticket 00001", ticket_id: tkt1.id )         

# create ticket #2
tkt2 = Ticket.create!( ticket_number: "00002", title: "ticket number 00002" )
Task.create!( description: "task 001 of ticket 00002", ticket_id: tkt2.id )
Task.create!( description: "task 002 of ticket 00002", ticket_id: tkt2.id )         

# create ticket #3
tkt3 = Ticket.create!( ticket_number: "00003", title: "ticket number 00003" )
Task.create!( description: "task 001 of ticket 00003", ticket_id: tkt3.id )
Task.create!( description: "task 002 of ticket 00003", ticket_id: tkt3.id )
Task.create!( description: "task 003 of ticket 00003", ticket_id: tkt3.id )
  • Para que o seed acima seja executado, execute o comando:
$ bundle exec rake db:seed

Passo 10 – Conferencia dos Models

  • Agora o seu model Ticket (app/model/ticket.rb) terá que ficar assim:
 class Ticket < ActiveRecord::Base
  has_many :tasks    validates :ticket_number, presence: true
  accepts_nested_attributes_for :tasks, :reject_if => proc { |a| a[:description].blank? }, allow_destroy:
 ...true
   attr_accessible :ticket_number, :title
 end
  • E seu model Task (app/model/task.rb) assim:
class Task < ActiveRecord::Base
  belongs_to :ticket
  attr_accessible :description, :ticket_id
end

Passo 11 – Execute a aplicação localmente

$ rails server

VI – Final Feliz

Pronto!
Fizemos o básico para que nosso app web aceite username ou o email do usuário.
Também criamos um arquivo “seed” que poderá ser usado para criar dados iniciais (inclusive usuários) de exemplo no sistema.

VII – Referências

  1. Instalação do Ruby On Rails 3.1.1 no Linux Ubuntu 11.10
  2. Ruby On Rails 3.1.3 no Linux Ubuntu 11.10: Iniciando um novo projeto – versão 2
  3. Aplicação “mestre detalhe” em Rails 3.2.1
  4. Autenticação em Ruby On Rails 3.2.1 com Devise
  5. How To: Allow users to sign in using their username or email address
Anúncios

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s