2. Devise
@jlucasps
Solução flexível para autenticação de usuários
Segue o padrão MVC
Totalmente integrada com o Rails
Permite várias roles autenticadas ao mesmo
tempo (user, admin, member)
Baseada em conceitos de módulos
3. ● Database Authenticatable
○ Criptografar a senha e armazenar em
banco de dados
○ Auntenticação pode ser feita via POST ou
HTTP Basic Authentication
Devise
@jlucasps
4. ● Token Authenticatable
○ Autenticar o usuário baseado em um token
de acesso
○ Token pode ser enviado via query string ou
HTTP Basic Authentication
Devise
@jlucasps
5. Devise
@jlucasps
● Omniauthable
○ Framework de autenticação compatível
com diversos providers (facebook, twitter,
openId, google, github), além dos
tradicionais username e password
6. Devise
@jlucasps
● Confirmable
○ Enviar email com as instruções de
confirmação de cadastro
● Recoverable
○ Alterar password do usuário e enviar
instruções de alteração
8. Devise
@jlucasps
● Rememberable
○ Mecanimo para salvar cookies e permitir
manter usuário autenticado na aplicação
● Trackable
○ Registrar quantidade de acessos, hora do
acesso e IP de origem
9. Devise
@jlucasps
● Timeoutable
○ Expirar a sessão caso o usuário fique um
período inativo
● Validatable
○ Validação de email e password
○ Mecanismo opcional e customizável
10. Devise
@jlucasps
● Lockable
○ Bloquear a conta do usuário caso haja um
certo número de tentativas frustradas de
acesso
○ Desbloqueio pode ser feito via email ou
após um período de tempo
12. MailCatcher
@jlucasps
# https://github.com/sj26/mailcatcher
# Catches mail and serves it through a dream.
gem 'mailcatcher'
Adicionar MailCatcher ao Gemfile
Atualizar config/environments/development.rb
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = { :address => "localhost", :
port => 1025 }
jlucasps@lotus:/media/truecrypt1/handsonrails/first_app$ mailcatcher
Starting MailCatcher
==> smtp://127.0.0.1:1025
==> http://127.0.0.1:1080
*** MailCatcher runs as a daemon by default. Go to the web interface to
quit.
13. Devise
@jlucasps
Após adicionar Devise ao Gemfile, execute o
generator
jlucasps@lotus:/media/truecrypt1/handsonrails/first_app$ rails g devise:
install
create config/initializers/devise.rb
create config/locales/devise.en.yml
===========================================
====================
Some setup you must do manually if you haven't yet:
....
15. Devise
@jlucasps
jlucasps@lotus:/media/truecrypt1/handsonrails/first_app$ rails g devise
User
invoke active_record
create db/migrate/20130619172147_add_devise_to_users.rb
insert app/models/user.rb
route devise_for :users
Adicionar Devise a algum model
Configurar Migration gerada e executá-la
jlucasps@lotus:/media/truecrypt1/handsonrails/first_app$ rake db:migrate
== AddDeviseToUsers: migrating
===============================================
-- change_table(:users)
-> 0.0590s
-- add_index(:users, :reset_password_token, {:unique=>true})
-> 0.0007s
== AddDeviseToUsers: migrated (0.0600s)
======================================
16. Devise
@jlucasps
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :authenticate_user!
end
Configurar ApplicationController
Configurar arquivo /config/initializers/devise.rb
# ==> Scopes configuration
# Turn scoped views on. Before rendering "sessions/new", it will first check for
# "users/sessions/new". It's turned off by default because it's slower if you
# are using only default views.
config.scoped_views = true
19. Devise
@jlucasps
class WelcomeController < ApplicationController
before_filter :authenticate_user!, :except => [:index, :about, :contact]
def index
end
def black
render :layout => 'application_black'
end
def about
end
def contact
end
end
Caso queira liberar acesso para actions do WelcomeController
22. Devise
@jlucasps
RSpec.configure do |config|
# ## Mock Framework
#
# If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
#
# config.mock_with :mocha
# config.mock_with :flexmock
# config.mock_with :rr
config.include Capybara::DSL
config.include Devise::TestHelpers, :type => :controller
config.include Rails.application.routes.url_helpers
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
#config.fixture_path = "#{::Rails.root}/spec/fixtures"
Testes automatizados com Devise: /spec/spec_helper.rb
23. Devise
@jlucasps
include Warden::Test::Helpers
def create_logged_in_user(user_sym)
user = FactoryGirl.find_or_create(user_sym)
login_as(user, scope: :user)
user
end
class ActiveRecord::Base
mattr_accessor :shared_connection
@@shared_connection = nil
def self.connection
@@shared_connection || retrieve_connection
end
end
ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection
Devise support /spec/support/devise.rb
24. FactoryGirl
@jlucasps
# Factory_girl is a fixtures replacement with a straightforward
definition syntax
# https://github.com/thoughtbot/factory_girl_rails
gem 'factory_girl_rails', "~> 4.0"
Adicionar factory_girl ao Gemfile
Importar factory_girl no spec_helper
# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'rspec/autorun'
require 'factory_girl'
require 'factory_girl_patch'
require 'capybara/rails'
require 'capybara/rspec'
FactoryGirl.register_strategy(:find_or_create, FactoryGirlPatch)
25. FactoryGirl
@jlucasps
FactoryGirl.define do
factory :user_bart, :class => User do
name "Bart Simpson"
email "bart@simpson.com"
password "dirty_boy"
password_confirmation "dirty_boy"
encrypted_password BCrypt::Password.create("dirty_boy", :cost => 10)
end
factory :user_lisa, :class => User do
name "Lisa Simpson"
email "lisa@simpson.com"
password "smart_girl"
password_confirmation "smart_girl"
encrypted_password BCrypt::Password.create("smart_girl", :cost => 10)
end
end
Criar factories
26. FactoryGirl
@jlucasps
class FactoryGirlPatch
def association(runner)
runner.run
end
def result(evaluation)
evaluation.object.tap do |instance|
evaluation.notify(:after_build, instance)
evaluation.notify(:before_create, instance)
saved_object = instance.class.where(instance.attributes.except("id", "created_at", "updated_at")).first
if saved_object.present?
instance.id = saved_object.id
instance.created_at = saved_object.created_at if instance.respond_to?(:created_at)
instance.updated_at = saved_object.updated_at if instance.respond_to?(:updated_at)
else
evaluation.create(instance)
evaluation.notify(:after_create, instance)
end
end
end
end
Estratégia find_or_create: /lib/factory_girl_patch.rb
27. Devise
@jlucasps
require 'spec_helper'
describe UsersController do
let(:user_bart) { FactoryGirl.find_or_create(:user_bart)}
before(:each) do
sign_in user_bart
end
describe "GET index" do
it "assigns @users" do
saved_users = [FactoryGirl.find_or_create(:user_bart), FactoryGirl.find_or_create(:
user_lisa)]
get :index
assigns(:users).should eq(saved_users)
end
end
end
Testes no controller /spec/controllers/users_controller_spec.rb
28. Devise
@jlucasps
Testes no controller /spec/controllers/users_controller_spec.rb
jlucasps@lotus:/media/first_app$ rspec
spec/controllers/users_controller_spec.rb
.
Finished in 0.331 seconds
1 example, 0 failures
Randomized with seed 5290
29. Desenvolvimento
Web com Ruby on
Rails
João Lucas Pereira de Santana
gtalk | linkedin | twitter: jlucasps
Obrigado!