mirror of
https://github.com/heartcombo/devise.git
synced 2022-11-09 12:18:31 -05:00
Create authenticatable base model and strategy.
This commit is contained in:
parent
1c5d4771ff
commit
65b8908960
18 changed files with 130 additions and 160 deletions
|
@ -54,15 +54,23 @@ module Devise
|
|||
# Keys used when authenticating an user.
|
||||
mattr_accessor :authentication_keys
|
||||
@@authentication_keys = [ :email ]
|
||||
|
||||
# Range validation for password length
|
||||
mattr_accessor :password_length
|
||||
@@password_length = 6..20
|
||||
|
||||
|
||||
# If http authentication is enabled by default.
|
||||
mattr_accessor :http_authenticatable
|
||||
@@http_authenticatable = true
|
||||
|
||||
# The realm used in Http Basic Authentication.
|
||||
mattr_accessor :http_authentication_realm
|
||||
@@http_authentication_realm = "Application"
|
||||
|
||||
# Email regex used to validate email formats. Adapted from authlogic.
|
||||
mattr_accessor :email_regexp
|
||||
@@email_regexp = /^([\w\.%\+\-]+)@([\w\-]+\.)+([\w]{2,})$/i
|
||||
|
||||
|
||||
# Range validation for password length
|
||||
mattr_accessor :password_length
|
||||
@@password_length = 6..20
|
||||
|
||||
# Time interval where the remember me token is valid.
|
||||
mattr_accessor :remember_for
|
||||
@@remember_for = 2.weeks
|
||||
|
@ -122,10 +130,6 @@ module Devise
|
|||
mattr_accessor :token_authentication_key
|
||||
@@token_authentication_key = :auth_token
|
||||
|
||||
# The realm used in Http Basic Authentication.
|
||||
mattr_accessor :http_authentication_realm
|
||||
@@http_authentication_realm = "Application"
|
||||
|
||||
# Private methods to interface with Warden.
|
||||
mattr_reader :warden_config
|
||||
@@warden_config = nil
|
||||
|
|
|
@ -53,6 +53,10 @@ module Devise
|
|||
modules << :database_authenticatable
|
||||
end
|
||||
|
||||
if modules.delete(:http_authenticatable)
|
||||
ActiveSupport::Deprecation.warn ":http_authenticatable as module is deprecated and is on by default. Revert by setting :http_authenticatable => false.", caller
|
||||
end
|
||||
|
||||
@devise_modules = Devise::ALL & modules.map(&:to_sym).uniq
|
||||
|
||||
devise_modules_hook! do
|
||||
|
|
38
lib/devise/models/authenticatable.rb
Normal file
38
lib/devise/models/authenticatable.rb
Normal file
|
@ -0,0 +1,38 @@
|
|||
module Devise
|
||||
module Models
|
||||
# Authenticable module. Holds common settings for authentication.
|
||||
#
|
||||
# Configuration:
|
||||
#
|
||||
# You can overwrite configuration values by setting in globally in Devise,
|
||||
# using devise method or overwriting the respective instance method.
|
||||
#
|
||||
# authentication_keys: parameters used for authentication. By default [:email].
|
||||
#
|
||||
# http_authenticatable: if this model allows http authentication. By default true.
|
||||
#
|
||||
module Authenticatable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
module ClassMethods
|
||||
Devise::Models.config(self, :authentication_keys, :http_authenticatable)
|
||||
|
||||
alias :http_authenticatable? :http_authenticatable
|
||||
|
||||
# Find first record based on conditions given (ie by the sign in form).
|
||||
# Overwrite to add customized conditions, create a join, or maybe use a
|
||||
# namedscope to filter records while authenticating.
|
||||
# Example:
|
||||
#
|
||||
# def self.find_for_authentication(conditions={})
|
||||
# conditions[:active] = true
|
||||
# super
|
||||
# end
|
||||
#
|
||||
def find_for_authentication(conditions)
|
||||
find(:first, :conditions => conditions)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,3 +1,4 @@
|
|||
require 'devise/models/authenticatable'
|
||||
require 'devise/strategies/database_authenticatable'
|
||||
|
||||
module Devise
|
||||
|
@ -19,8 +20,6 @@ module Devise
|
|||
#
|
||||
# encryptor: the encryptor going to be used. By default :sha1.
|
||||
#
|
||||
# authentication_keys: parameters used for authentication. By default [:email]
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# User.authenticate('email@test.com', 'password123') # returns authenticated user or nil
|
||||
|
@ -30,6 +29,8 @@ module Devise
|
|||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
include Devise::Models::Authenticatable
|
||||
|
||||
attr_reader :password, :current_password
|
||||
attr_accessor :password_confirmation
|
||||
end
|
||||
|
@ -89,15 +90,13 @@ module Devise
|
|||
end
|
||||
|
||||
module ClassMethods
|
||||
Devise::Models.config(self, :pepper, :stretches, :encryptor, :authentication_keys)
|
||||
Devise::Models.config(self, :pepper, :stretches, :encryptor)
|
||||
|
||||
# Authenticate a user based on configured attribute keys. Returns the
|
||||
# authenticated user if it's valid or nil.
|
||||
def authenticate(attributes={})
|
||||
return unless authentication_keys.all? { |k| attributes[k].present? }
|
||||
conditions = attributes.slice(*authentication_keys)
|
||||
resource = find_for_authentication(conditions)
|
||||
resource if resource.try(:valid_for_authentication?, attributes)
|
||||
def authenticate(conditions)
|
||||
resource = find_for_database_authentication(conditions.except(:password))
|
||||
resource if resource.try(:valid_for_authentication?, conditions)
|
||||
end
|
||||
|
||||
# Returns the class for the configured encryptor.
|
||||
|
@ -105,20 +104,8 @@ module Devise
|
|||
@encryptor_class ||= ::Devise::Encryptors.const_get(encryptor.to_s.classify)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Find first record based on conditions given (ie by the sign in form).
|
||||
# Overwrite to add customized conditions, create a join, or maybe use a
|
||||
# namedscope to filter records while authenticating.
|
||||
# Example:
|
||||
#
|
||||
# def self.find_for_authentication(conditions={})
|
||||
# conditions[:active] = true
|
||||
# find(:first, :conditions => conditions)
|
||||
# end
|
||||
#
|
||||
def find_for_authentication(conditions)
|
||||
find(:first, :conditions => conditions)
|
||||
def find_for_database_authentication(*args)
|
||||
find_for_authentication(*args)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
require 'devise/strategies/http_authenticatable'
|
||||
|
||||
module Devise
|
||||
module Models
|
||||
# Adds HttpAuthenticatable behavior to your model. It expects that your
|
||||
# model class responds to authenticate and authentication_keys methods
|
||||
# (which for example are defined in authenticatable).
|
||||
module HttpAuthenticatable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
module ClassMethods
|
||||
# Authenticate an user using http.
|
||||
def authenticate_with_http(username, password)
|
||||
authenticate(authentication_keys.first => username, :password => password)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -68,7 +68,7 @@ module Devise
|
|||
#
|
||||
# def self.find_for_token_authentication(token, conditions = {})
|
||||
# conditions = {:active => true}
|
||||
# self.find_by_authentication_token(token, :conditions => conditions)
|
||||
# super
|
||||
# end
|
||||
#
|
||||
def find_for_token_authentication(token)
|
||||
|
|
|
@ -2,9 +2,8 @@ require 'active_support/core_ext/object/with_options'
|
|||
|
||||
Devise.with_options :model => true do |d|
|
||||
# Strategies first
|
||||
d.with_options :strategy => true do |s|
|
||||
d.with_options :strategy => true do |s|
|
||||
s.add_module :database_authenticatable, :controller => :sessions, :flash => :invalid, :route => :session
|
||||
s.add_module :http_authenticatable
|
||||
s.add_module :token_authenticatable, :controller => :sessions, :flash => :invalid_token, :route => :session
|
||||
s.add_module :rememberable
|
||||
end
|
||||
|
|
55
lib/devise/strategies/authenticatable.rb
Normal file
55
lib/devise/strategies/authenticatable.rb
Normal file
|
@ -0,0 +1,55 @@
|
|||
require 'devise/strategies/base'
|
||||
|
||||
module Devise
|
||||
module Strategies
|
||||
class Authenticatable < Base
|
||||
attr_accessor :authentication_hash, :password
|
||||
|
||||
def valid?
|
||||
valid_for_http_auth? || valid_for_params_auth?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def valid_for_http_auth?
|
||||
mapping.to.http_authenticatable? && request.authorization && set_http_auth_hash
|
||||
end
|
||||
|
||||
def valid_for_params_auth?
|
||||
valid_controller? && valid_params? && set_params_auth_hash
|
||||
end
|
||||
|
||||
def valid_controller?
|
||||
mapping.controllers[:sessions] == params[:controller]
|
||||
end
|
||||
|
||||
def valid_params?
|
||||
params[scope].is_a?(Hash)
|
||||
end
|
||||
|
||||
def set_http_auth_hash
|
||||
keys = [authentication_keys.first, :password]
|
||||
with_authentication_hash Hash[*keys.zip(decode_credentials).flatten]
|
||||
end
|
||||
|
||||
def decode_credentials
|
||||
username_and_password = request.authorization.split(' ', 2).last || ''
|
||||
ActiveSupport::Base64.decode64(username_and_password).split(/:/, 2)
|
||||
end
|
||||
|
||||
def set_params_auth_hash
|
||||
with_authentication_hash params[scope]
|
||||
end
|
||||
|
||||
def with_authentication_hash(hash)
|
||||
self.authentication_hash = hash.slice(*authentication_keys)
|
||||
self.password = hash[:password]
|
||||
authentication_keys.all?{ |k| authentication_hash[k].present? } && password.present?
|
||||
end
|
||||
|
||||
def authentication_keys
|
||||
@authentication_keys ||= mapping.to.authentication_keys
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -12,6 +12,7 @@ module Devise
|
|||
end
|
||||
end
|
||||
|
||||
# TODO Move to a module
|
||||
def success!(record)
|
||||
if record.respond_to?(:active?) && !record.active?
|
||||
fail!(record.inactive_message)
|
||||
|
|
|
@ -1,19 +1,15 @@
|
|||
require 'devise/strategies/base'
|
||||
require 'devise/strategies/authenticatable'
|
||||
|
||||
module Devise
|
||||
module Strategies
|
||||
# Default strategy for signing in a user, based on his email and password.
|
||||
# Redirects to sign_in page if it's not authenticated
|
||||
class DatabaseAuthenticatable < Base
|
||||
def valid?
|
||||
valid_controller? && valid_params?
|
||||
end
|
||||
|
||||
class DatabaseAuthenticatable < Authenticatable
|
||||
# Authenticate a user based on email and password params, returning to warden
|
||||
# success and the authenticated user if everything is okay. Otherwise redirect
|
||||
# to sign in page.
|
||||
def authenticate!
|
||||
if resource = mapping.to.authenticate(params[scope])
|
||||
if resource = mapping.to.authenticate(authentication_hash.merge(:password => password))
|
||||
success!(resource)
|
||||
else
|
||||
fail(:invalid)
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
require 'devise/strategies/base'
|
||||
|
||||
module Devise
|
||||
module Strategies
|
||||
# Sign in an user using HTTP authentication.
|
||||
class HttpAuthenticatable < Base
|
||||
def valid?
|
||||
request.authorization
|
||||
end
|
||||
|
||||
def authenticate!
|
||||
username, password = username_and_password
|
||||
|
||||
if resource = mapping.to.authenticate_with_http(username, password)
|
||||
success!(resource)
|
||||
else
|
||||
fail!(:invalid)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def username_and_password
|
||||
decode_credentials(request).split(/:/, 2)
|
||||
end
|
||||
|
||||
def decode_credentials(request)
|
||||
ActiveSupport::Base64.decode64(request.authorization.split(' ', 2).last || '')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Warden::Strategies.add(:http_authenticatable, Devise::Strategies::HttpAuthenticatable)
|
|
@ -12,6 +12,9 @@ Devise.setup do |config|
|
|||
# session. If you need permissions, you should implement that in a before filter.
|
||||
# config.authentication_keys = [ :email ]
|
||||
|
||||
# Tell if authentication for http is enabled. True by default.
|
||||
# config.http_authenticatable = true
|
||||
|
||||
# The realm used in Http Basic Authentication
|
||||
# config.http_authentication_realm = "Application"
|
||||
|
||||
|
|
|
@ -30,8 +30,7 @@ class MappingTest < ActiveSupport::TestCase
|
|||
end
|
||||
|
||||
test 'has strategies depending on the model declaration' do
|
||||
assert_equal [:rememberable, :token_authenticatable,
|
||||
:http_authenticatable, :database_authenticatable], Devise.mappings[:user].strategies
|
||||
assert_equal [:rememberable, :token_authenticatable, :database_authenticatable], Devise.mappings[:user].strategies
|
||||
assert_equal [:database_authenticatable], Devise.mappings[:admin].strategies
|
||||
end
|
||||
|
||||
|
|
|
@ -98,38 +98,6 @@ class DatabaseAuthenticatableTest < ActiveSupport::TestCase
|
|||
assert_not user.valid_password?('654321')
|
||||
end
|
||||
|
||||
test 'should authenticate a valid user with email and password and return it' do
|
||||
user = create_user
|
||||
user.confirm!
|
||||
authenticated_user = User.authenticate(:email => user.email, :password => user.password)
|
||||
assert_equal authenticated_user, user
|
||||
end
|
||||
|
||||
test 'should return nil when authenticating an invalid user by email' do
|
||||
user = create_user
|
||||
authenticated_user = User.authenticate(:email => 'another.email@email.com', :password => user.password)
|
||||
assert_nil authenticated_user
|
||||
end
|
||||
|
||||
test 'should return nil when authenticating an invalid user by password' do
|
||||
user = create_user
|
||||
authenticated_user = User.authenticate(:email => user.email, :password => 'another_password')
|
||||
assert_nil authenticated_user
|
||||
end
|
||||
|
||||
test 'should use authentication keys to retrieve users' do
|
||||
swap Devise, :authentication_keys => [:username] do
|
||||
user = create_user
|
||||
assert_nil User.authenticate(:email => user.email, :password => user.password)
|
||||
assert_not_nil User.authenticate(:username => user.username, :password => user.password)
|
||||
end
|
||||
end
|
||||
|
||||
test 'should allow overwriting find for authentication conditions' do
|
||||
admin = Admin.create!(valid_attributes)
|
||||
assert_not_nil Admin.authenticate(:email => admin.email, :password => admin.password)
|
||||
end
|
||||
|
||||
test 'should respond to current password' do
|
||||
assert new_user.respond_to?(:current_password)
|
||||
end
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
require 'test_helper'
|
||||
|
||||
class HttpAuthenticatableTest < ActiveSupport::TestCase
|
||||
test 'should authenticate a valid user with email and password and return it' do
|
||||
user = create_user
|
||||
user.confirm!
|
||||
|
||||
authenticated_user = User.authenticate_with_http(user.email, user.password)
|
||||
assert_equal authenticated_user, user
|
||||
end
|
||||
|
||||
test 'should return nil when authenticating an invalid user by email' do
|
||||
user = create_user
|
||||
user.confirm!
|
||||
|
||||
authenticated_user = User.authenticate_with_http('another.email@email.com', user.password)
|
||||
assert_nil authenticated_user
|
||||
end
|
||||
end
|
|
@ -1,7 +1,3 @@
|
|||
class Admin < ActiveRecord::Base
|
||||
devise :authenticatable, :registerable, :timeoutable, :recoverable
|
||||
|
||||
def self.find_for_authentication(conditions)
|
||||
last(:conditions => conditions)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,10 +5,6 @@ class Admin
|
|||
property :username, String
|
||||
|
||||
devise :authenticatable, :registerable, :timeoutable, :recoverable
|
||||
|
||||
def self.find_for_authentication(conditions)
|
||||
last(conditions)
|
||||
end
|
||||
|
||||
def self.create!(*args)
|
||||
create(*args)
|
||||
|
|
|
@ -2,10 +2,6 @@ class Admin
|
|||
include Mongoid::Document
|
||||
|
||||
devise :authenticatable, :timeoutable, :registerable, :recoverable
|
||||
|
||||
def self.find_for_authentication(conditions)
|
||||
last(:conditions => conditions, :sort => [[:email, :asc]])
|
||||
end
|
||||
|
||||
def self.last(options={})
|
||||
options.delete(:order) if options[:order] == "id"
|
||||
|
|
Loading…
Reference in a new issue