Extract Activatable from Confirmable, so if you need to active your account through other means, you can still do so and ensure cherry pick works as expected.

This commit is contained in:
José Valim 2009-12-20 13:53:53 +01:00
parent f26e6a269f
commit e1c2e45f97
16 changed files with 143 additions and 38 deletions

View File

@ -1,3 +1,7 @@
* enhancements
* Extract Activatable from Confirmable
* Decouple Serializers from Devise modules
== 0.7.3
* bug fix

View File

@ -13,6 +13,7 @@ Right now it's composed of seven mainly modules:
* Confirmable: responsible for verifying whether an account is already confirmed to sign in, and to send emails with confirmation instructions.
* Recoverable: takes care of reseting the user password and send reset instructions.
* Rememberable: manages generating and clearing token for remember the user from a saved cookie.
* Activatable: if you need to activate accounts by other means, which are not through confirmation, use this module.
* Timeoutable: expires sessions without activity in a certain period of time.
* Trackable: tracks sign in count, timestamps and ip.
* Validatable: creates all needed validations for email and password. It's totally optional, so you're able to to customize validations by yourself.

4
TODO
View File

@ -1,3 +1,5 @@
* Make test run with different ORMs
* Add registerable support
* Add http authentication support
* Add http authentication support
* Extract SessionSerializer tests from Authenticatable
* Extract Activatable tests from Confirmable

View File

@ -1,10 +1,14 @@
# Use this hook to configure devise mailer, warden hooks and so forth. The first
# four configuration values can also be set straight in your models.
Devise.setup do |config|
# Configure the frameworks used by default. You should always set this value
# because if Devise add a new strategy, it won't be added to your application
# Configure Devise modules used by default. You should always set this value
# because if Devise adds a new strategy, it won't be added to your application
# by default, unless you configure it here.
config.all = <%= Devise::ALL.inspect %>
#
# Remember that Devise includes other modules on its own (like :activatable
# and :timeoutable) which are not included here and also plugins. So be sure
# to check the docs for a complete set.
config.all = [:authenticatable, :confirmable, :recoverable, :rememberable, :trackable, :validatable]
# Invoke `rake secret` and use the printed value to setup a pepper to generate
# the encrypted password. By default no pepper is used.

View File

@ -24,7 +24,7 @@ module Devise
autoload :MongoMapper, 'devise/orm/mongo_mapper'
end
ALL = [:authenticatable, :confirmable, :recoverable, :rememberable,
ALL = [:authenticatable, :activatable, :confirmable, :recoverable, :rememberable,
:timeoutable, :trackable, :validatable]
# Maps controller names to devise modules
@ -38,9 +38,8 @@ module Devise
SERIALIZERS = [:session, :cookie]
TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE']
# Maps the messages types that are used in flash message. This array is not
# frozen, so you can add messages from your own strategies.
FLASH_MESSAGES = [ :unauthenticated, :unconfirmed, :invalid, :timeout ]
# Maps the messages types that are used in flash message.
FLASH_MESSAGES = [ :unauthenticated, :unconfirmed, :invalid, :timeout, :inactive ]
# Declare encryptors length which are used in migrations.
ENCRYPTORS_LENGTH = {
@ -51,6 +50,9 @@ module Devise
:authlogic_sha512 => 128
}
# Email regex used to validate email formats. Retrieved from authlogic.
EMAIL_REGEX = /\A[\w\.%\+\-]+@(?:[A-Z0-9\-]+\.)+(?:[A-Z]{2,4}|museum|travel)\z/i
# Used to encrypt password. Please generate one with rake secret.
mattr_accessor :pepper
@@pepper = nil

View File

@ -6,12 +6,13 @@ Warden::Manager.after_set_user do |record, warden, options|
if record && record.respond_to?(:active?) && !record.active?
scope = options[:scope]
warden.logout(scope)
# If winning strategy was set, this is being called after authenticate and
# there is no need to force a redirect.
if warden.winning_strategy
# If winning strategy was set, this is being called after authenticate and
# there is no need to force a redirect.
warden.winning_strategy.fail!(:unconfirmed)
warden.winning_strategy.fail!(record.inactive_message)
else
throw :warden, :scope => scope, :message => :unconfirmed
throw :warden, :scope => scope, :message => record.inactive_message
end
end
end

View File

@ -7,6 +7,7 @@ en:
unconfirmed: 'You have to confirm your account before continuing.'
invalid: 'Invalid email or password.'
timeout: 'Your session expired, please sign in again to continue.'
inactive: 'Your account was not activated yet.'
passwords:
send_instructions: 'You will receive an email with instructions about how to reset your password in a few minutes.'
updated: 'Your password was changed successfully. You are now signed in.'

View File

@ -1,5 +1,6 @@
module Devise
module Models
autoload :Activatable, 'devise/models/activatable'
autoload :Authenticatable, 'devise/models/authenticatable'
autoload :Confirmable, 'devise/models/confirmable'
autoload :Recoverable, 'devise/models/recoverable'
@ -81,9 +82,9 @@ module Devise
raise "You need to give at least one Devise module" if modules.empty?
options = modules.extract_options!
modules = Devise.all if modules.include?(:all)
modules += Devise.all if modules.delete(:all)
modules -= Array(options.delete(:except))
modules = Devise::ALL & modules
modules = Devise::ALL & modules.uniq
Devise.orm_class.included_modules_hook(self, modules) do
modules.each do |m|

View File

@ -0,0 +1,16 @@
require 'devise/hooks/activatable'
module Devise
module Models
# This module implements the default API required in activatable hook.
module Activatable
def active?
raise NotImplementedError
end
def inactive_message
:inactive
end
end
end
end

View File

@ -1,4 +1,4 @@
require 'devise/hooks/confirmable'
require 'devise/models/activatable'
module Devise
module Models
@ -29,6 +29,7 @@ module Devise
# User.find(1).send_confirmation_instructions # manually send instructions
# User.find(1).resend_confirmation! # generates a new token and resent it
module Confirmable
include Devise::Models::Activatable
def self.included(base)
base.class_eval do
@ -70,14 +71,19 @@ module Devise
end
end
# Verify whether a user is active to sign in or not. If the user is
# already confirmed, it should never be blocked. Otherwise we need to
# calculate if the confirm time has not expired for this user, in other
# words, if the confirmation is still valid.
# Overwrites active? from Devise::Models::Activatable for confirmation
# by verifying whether an user is active to sign in or not. If the user
# is already confirmed, it should never be blocked. Otherwise we need to
# calculate if the confirm time has not expired for this user.
def active?
confirmed? || confirmation_period_valid?
end
# The message to be shown if the account is inactive.
def inactive_message
:unconfirmed
end
# If you don't want confirmation to be sent on create, neither a code
# to be generated, call skip_confirmation!
def skip_confirmation!

View File

@ -6,10 +6,6 @@ module Devise
# Automatically validate if the email is present, unique and it's format is
# valid. Also tests presence of password, confirmation and length
module Validatable
# Email regex used to validate email formats. Retrieved from authlogic.
EMAIL_REGEX = /\A[\w\.%\+\-]+@(?:[A-Z0-9\-]+\.)+(?:[A-Z]{2,4}|museum|travel)\z/i
# All validations used by this module.
VALIDATIONS = [ :validates_presence_of, :validates_uniqueness_of, :validates_format_of,
:validates_confirmation_of, :validates_length_of ].freeze

View File

@ -37,11 +37,11 @@ class Exceptable < User
end
class Configurable < User
devise :all, :stretches => 15,
:pepper => 'abcdef',
:confirm_within => 5.days,
:remember_for => 7.days,
:timeout_in => 15.minutes
devise :all, :timeoutable, :stretches => 15,
:pepper => 'abcdef',
:confirm_within => 5.days,
:remember_for => 7.days,
:timeout_in => 15.minutes
end
class ActiveRecordTest < ActiveSupport::TestCase
@ -60,7 +60,7 @@ class ActiveRecordTest < ActiveSupport::TestCase
end
end
test 'include by default authenticatable only' do
test 'add authenticatable module only' do
assert_include_modules Authenticatable, :authenticatable
end
@ -90,11 +90,11 @@ class ActiveRecordTest < ActiveSupport::TestCase
test 'add all modules' do
assert_include_modules Devisable,
:authenticatable, :confirmable, :recoverable, :rememberable, :trackable, :timeoutable, :validatable
:authenticatable, :confirmable, :recoverable, :rememberable, :trackable, :validatable
end
test 'configure modules with except option' do
assert_include_modules Exceptable, :authenticatable, :confirmable, :trackable, :timeoutable
assert_include_modules Exceptable, :authenticatable, :confirmable, :trackable
end
test 'set a default value for stretches' do

View File

@ -1,5 +1,5 @@
class Admin < ActiveRecord::Base
devise :all, :except => [:recoverable, :confirmable, :rememberable, :validatable, :trackable]
devise :all, :timeoutable, :except => [:recoverable, :confirmable, :rememberable, :validatable, :trackable]
def self.find_for_authentication(conditions)
last(:conditions => conditions)

View File

@ -1,4 +1,4 @@
class User < ActiveRecord::Base
devise :all
devise :all, :timeoutable
attr_accessible :username, :email, :password, :password_confirmation
end

View File

@ -0,0 +1,76 @@
# Use this hook to configure devise mailer, warden hooks and so forth. The first
# four configuration values can also be set straight in your models.
Devise.setup do |config|
# Configure Devise modules used by default. You should always set this value
# because if Devise adds a new strategy, it won't be added to your application
# by default, unless you configure it here.
#
# Remember that Devise includes other modules on its own (like :activatable
# and :timeoutable) which are not included here and also plugins. So be sure
# to check the docs for a complete set.
config.all = [:authenticatable, :confirmable, :recoverable, :rememberable, :trackable, :validatable]
# Invoke `rake secret` and use the printed value to setup a pepper to generate
# the encrypted password. By default no pepper is used.
# config.pepper = "rake secret output"
# Configure how many times you want the password is reencrypted. Default is 10.
# config.stretches = 10
# Define which will be the encryption algorithm. Supported algorithms are :sha1
# (default) and :sha512. Devise also supports encryptors from others authentication
# frameworks as :clearance_sha1, :authlogic_sha512 (then you should set stretches
# above to 20 for default behavior) and :restful_authentication_sha1 (then you
# should set stretches to 10, and copy REST_AUTH_SITE_KEY to pepper)
# config.encryptor = :sha1
# Configure which keys are used when authenticating an user. By default is
# just :email. You can configure it to use [:username, :subdomain], so for
# authenticating an user, both parameters are required. Remember that those
# parameters are used only when authenticating and not when retrieving from
# session. If you need permissions, you should implement that in a before filter.
# config.authentication_keys = [ :email ]
# The time you want give to your user to confirm his account. During this time
# he will be able to access your application without confirming. Default is nil.
# config.confirm_within = 2.days
# The time the user will be remembered without asking for credentials again.
# config.remember_for = 2.weeks
# The time you want to timeout the user session without activity. After this
# time the user will be asked for credentials again.
# config.timeout_in = 10.minutes
# Configure the e-mail address which will be shown in DeviseMailer.
# config.mailer_sender = "foo.bar@yourapp.com"
# Load and configure the ORM. Supports :active_record, :data_mapper and :mongo_mapper.
# require 'devise/orm/mongo_mapper'
# config.orm = :mongo_mapper
# Turn scoped views on. Before rendering "sessions/new", it will first check for
# "sessions/users/new". It's turned off by default because it's slower if you
# are using only default views.
# config.scoped_views = true
# If you want to use other strategies, that are not (yet) supported by Devise,
# you can configure them inside the config.warden block. The example below
# allows you to setup OAuth, using http://github.com/roman/warden_oauth
#
# config.warden do |manager|
# manager.oauth(:twitter) do |twitter|
# twitter.consumer_secret = <YOUR CONSUMER SECRET>
# twitter.consumer_key = <YOUR CONSUMER KEY>
# twitter.options :site => 'http://twitter.com'
# end
# manager.default_strategies.unshift :twitter_oauth
# end
# Configure default_url_options if you are using dynamic segments in :path_prefix
# for devise_for.
#
# config.default_url_options do
# { :locale => I18n.locale }
# end
end

View File

@ -1,5 +0,0 @@
# Sample localization file for English. Add more files in this directory for other locales.
# See http://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
en:
hello: "Hello world"