mongoid-devise 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.rdoc +333 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +260 -0
- data/Rakefile +53 -0
- data/TODO +2 -0
- data/app/controllers/confirmations_controller.rb +33 -0
- data/app/controllers/passwords_controller.rb +42 -0
- data/app/controllers/registrations_controller.rb +55 -0
- data/app/controllers/sessions_controller.rb +45 -0
- data/app/controllers/unlocks_controller.rb +33 -0
- data/app/models/devise_mailer.rb +68 -0
- data/app/views/confirmations/new.html.erb +12 -0
- data/app/views/devise_mailer/confirmation_instructions.html.erb +5 -0
- data/app/views/devise_mailer/reset_password_instructions.html.erb +8 -0
- data/app/views/devise_mailer/unlock_instructions.html.erb +7 -0
- data/app/views/passwords/edit.html.erb +16 -0
- data/app/views/passwords/new.html.erb +12 -0
- data/app/views/registrations/edit.html.erb +25 -0
- data/app/views/registrations/new.html.erb +17 -0
- data/app/views/sessions/new.html.erb +17 -0
- data/app/views/shared/_devise_links.erb +19 -0
- data/app/views/unlocks/new.html.erb +12 -0
- data/generators/devise/USAGE +5 -0
- data/generators/devise/devise_generator.rb +15 -0
- data/generators/devise/lib/route_devise.rb +32 -0
- data/generators/devise/templates/migration.rb +23 -0
- data/generators/devise/templates/model.rb +9 -0
- data/generators/devise_install/USAGE +3 -0
- data/generators/devise_install/devise_install_generator.rb +15 -0
- data/generators/devise_install/templates/README +18 -0
- data/generators/devise_install/templates/devise.rb +102 -0
- data/generators/devise_views/USAGE +3 -0
- data/generators/devise_views/devise_views_generator.rb +21 -0
- data/init.rb +2 -0
- data/lib/devise.rb +253 -0
- data/lib/devise/controllers/helpers.rb +200 -0
- data/lib/devise/controllers/internal_helpers.rb +129 -0
- data/lib/devise/controllers/url_helpers.rb +41 -0
- data/lib/devise/encryptors/authlogic_sha512.rb +21 -0
- data/lib/devise/encryptors/base.rb +20 -0
- data/lib/devise/encryptors/bcrypt.rb +21 -0
- data/lib/devise/encryptors/clearance_sha1.rb +19 -0
- data/lib/devise/encryptors/restful_authentication_sha1.rb +22 -0
- data/lib/devise/encryptors/sha1.rb +27 -0
- data/lib/devise/encryptors/sha512.rb +27 -0
- data/lib/devise/failure_app.rb +65 -0
- data/lib/devise/hooks/activatable.rb +15 -0
- data/lib/devise/hooks/rememberable.rb +30 -0
- data/lib/devise/hooks/timeoutable.rb +18 -0
- data/lib/devise/hooks/trackable.rb +18 -0
- data/lib/devise/locales/en.yml +35 -0
- data/lib/devise/mapping.rb +131 -0
- data/lib/devise/models.rb +112 -0
- data/lib/devise/models/activatable.rb +16 -0
- data/lib/devise/models/authenticatable.rb +146 -0
- data/lib/devise/models/confirmable.rb +172 -0
- data/lib/devise/models/http_authenticatable.rb +21 -0
- data/lib/devise/models/lockable.rb +160 -0
- data/lib/devise/models/recoverable.rb +80 -0
- data/lib/devise/models/registerable.rb +8 -0
- data/lib/devise/models/rememberable.rb +94 -0
- data/lib/devise/models/timeoutable.rb +28 -0
- data/lib/devise/models/token_authenticatable.rb +89 -0
- data/lib/devise/models/trackable.rb +16 -0
- data/lib/devise/models/validatable.rb +48 -0
- data/lib/devise/orm/active_record.rb +41 -0
- data/lib/devise/orm/data_mapper.rb +83 -0
- data/lib/devise/orm/mongo_mapper.rb +51 -0
- data/lib/devise/orm/mongoid.rb +60 -0
- data/lib/devise/rails.rb +14 -0
- data/lib/devise/rails/routes.rb +125 -0
- data/lib/devise/rails/warden_compat.rb +25 -0
- data/lib/devise/schema.rb +65 -0
- data/lib/devise/strategies/authenticatable.rb +36 -0
- data/lib/devise/strategies/base.rb +16 -0
- data/lib/devise/strategies/http_authenticatable.rb +49 -0
- data/lib/devise/strategies/rememberable.rb +37 -0
- data/lib/devise/strategies/token_authenticatable.rb +37 -0
- data/lib/devise/test_helpers.rb +86 -0
- data/lib/devise/version.rb +3 -0
- data/test/controllers/helpers_test.rb +177 -0
- data/test/controllers/internal_helpers_test.rb +55 -0
- data/test/controllers/url_helpers_test.rb +47 -0
- data/test/devise_test.rb +69 -0
- data/test/encryptors_test.rb +31 -0
- data/test/failure_app_test.rb +44 -0
- data/test/integration/authenticatable_test.rb +271 -0
- data/test/integration/confirmable_test.rb +97 -0
- data/test/integration/http_authenticatable_test.rb +44 -0
- data/test/integration/lockable_test.rb +83 -0
- data/test/integration/recoverable_test.rb +141 -0
- data/test/integration/registerable_test.rb +130 -0
- data/test/integration/rememberable_test.rb +63 -0
- data/test/integration/timeoutable_test.rb +68 -0
- data/test/integration/token_authenticatable_test.rb +55 -0
- data/test/integration/trackable_test.rb +64 -0
- data/test/mailers/confirmation_instructions_test.rb +80 -0
- data/test/mailers/reset_password_instructions_test.rb +68 -0
- data/test/mailers/unlock_instructions_test.rb +62 -0
- data/test/mapping_test.rb +153 -0
- data/test/models/authenticatable_test.rb +180 -0
- data/test/models/confirmable_test.rb +228 -0
- data/test/models/lockable_test.rb +202 -0
- data/test/models/recoverable_test.rb +138 -0
- data/test/models/rememberable_test.rb +135 -0
- data/test/models/timeoutable_test.rb +28 -0
- data/test/models/token_authenticatable_test.rb +51 -0
- data/test/models/trackable_test.rb +5 -0
- data/test/models/validatable_test.rb +106 -0
- data/test/models_test.rb +56 -0
- data/test/orm/active_record.rb +31 -0
- data/test/orm/mongo_mapper.rb +20 -0
- data/test/orm/mongoid.rb +22 -0
- data/test/rails_app/app/active_record/admin.rb +7 -0
- data/test/rails_app/app/active_record/user.rb +7 -0
- data/test/rails_app/app/controllers/admins_controller.rb +6 -0
- data/test/rails_app/app/controllers/application_controller.rb +10 -0
- data/test/rails_app/app/controllers/home_controller.rb +4 -0
- data/test/rails_app/app/controllers/users_controller.rb +16 -0
- data/test/rails_app/app/helpers/application_helper.rb +3 -0
- data/test/rails_app/app/mongo_mapper/admin.rb +9 -0
- data/test/rails_app/app/mongo_mapper/user.rb +8 -0
- data/test/rails_app/app/mongoid/admin.rb +9 -0
- data/test/rails_app/app/mongoid/user.rb +8 -0
- data/test/rails_app/config/boot.rb +110 -0
- data/test/rails_app/config/environment.rb +42 -0
- data/test/rails_app/config/environments/development.rb +17 -0
- data/test/rails_app/config/environments/production.rb +28 -0
- data/test/rails_app/config/environments/test.rb +28 -0
- data/test/rails_app/config/initializers/devise.rb +79 -0
- data/test/rails_app/config/initializers/inflections.rb +2 -0
- data/test/rails_app/config/initializers/new_rails_defaults.rb +24 -0
- data/test/rails_app/config/initializers/session_store.rb +15 -0
- data/test/rails_app/config/routes.rb +21 -0
- data/test/routes_test.rb +110 -0
- data/test/support/assertions_helper.rb +37 -0
- data/test/support/integration_tests_helper.rb +71 -0
- data/test/support/test_silencer.rb +5 -0
- data/test/support/tests_helper.rb +39 -0
- data/test/test_helper.rb +21 -0
- data/test/test_helpers_test.rb +57 -0
- metadata +216 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require "digest/sha1"
|
|
2
|
+
|
|
3
|
+
module Devise
|
|
4
|
+
module Encryptors
|
|
5
|
+
# = RestfulAuthenticationSha1
|
|
6
|
+
# Simulates Restful Authentication's default encryption mechanism.
|
|
7
|
+
# Warning: it uses Devise's pepper to port the concept of REST_AUTH_SITE_KEY
|
|
8
|
+
# Warning: it uses Devise's stretches configuration to port the concept of REST_AUTH_DIGEST_STRETCHES. Should be set to 10 in
|
|
9
|
+
# the initializer to silumate the default behavior.
|
|
10
|
+
class RestfulAuthenticationSha1 < Base
|
|
11
|
+
|
|
12
|
+
# Gererates a default password digest based on salt, pepper and the
|
|
13
|
+
# incoming password.
|
|
14
|
+
def self.digest(password, stretches, salt, pepper)
|
|
15
|
+
digest = pepper
|
|
16
|
+
stretches.times { digest = Digest::SHA1.hexdigest([digest, salt, password, pepper].flatten.join('--')) }
|
|
17
|
+
digest
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require "digest/sha1"
|
|
2
|
+
|
|
3
|
+
module Devise
|
|
4
|
+
module Encryptors
|
|
5
|
+
# = Sha1
|
|
6
|
+
# Uses the Sha1 hash algorithm to encrypt passwords.
|
|
7
|
+
class Sha1 < Base
|
|
8
|
+
|
|
9
|
+
# Gererates a default password digest based on stretches, salt, pepper and the
|
|
10
|
+
# incoming password.
|
|
11
|
+
def self.digest(password, stretches, salt, pepper)
|
|
12
|
+
digest = pepper
|
|
13
|
+
stretches.times { digest = self.secure_digest(salt, digest, password, pepper) }
|
|
14
|
+
digest
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
# Generate a SHA1 digest joining args. Generated token is something like
|
|
20
|
+
# --arg1--arg2--arg3--argN--
|
|
21
|
+
def self.secure_digest(*tokens)
|
|
22
|
+
::Digest::SHA1.hexdigest('--' << tokens.flatten.join('--') << '--')
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require "digest/sha2"
|
|
2
|
+
|
|
3
|
+
module Devise
|
|
4
|
+
module Encryptors
|
|
5
|
+
# = Sha512
|
|
6
|
+
# Uses the Sha512 hash algorithm to encrypt passwords.
|
|
7
|
+
class Sha512 < Base
|
|
8
|
+
|
|
9
|
+
# Gererates a default password digest based on salt, pepper and the
|
|
10
|
+
# incoming password.
|
|
11
|
+
def self.digest(password, stretches, salt, pepper)
|
|
12
|
+
digest = pepper
|
|
13
|
+
stretches.times { digest = self.secure_digest(salt, digest, password, pepper) }
|
|
14
|
+
digest
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
# Generate a Sha512 digest joining args. Generated token is something like
|
|
20
|
+
# --arg1--arg2--arg3--argN--
|
|
21
|
+
def self.secure_digest(*tokens)
|
|
22
|
+
::Digest::SHA512.hexdigest('--' << tokens.flatten.join('--') << '--')
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
module Devise
|
|
2
|
+
# Failure application that will be called every time :warden is thrown from
|
|
3
|
+
# any strategy or hook. Responsible for redirect the user to the sign in
|
|
4
|
+
# page based on current scope and mapping. If no scope is given, redirect
|
|
5
|
+
# to the default_url.
|
|
6
|
+
class FailureApp
|
|
7
|
+
attr_reader :env
|
|
8
|
+
include Warden::Mixins::Common
|
|
9
|
+
|
|
10
|
+
cattr_accessor :default_url, :default_message, :instance_writer => false
|
|
11
|
+
@@default_message = :unauthenticated
|
|
12
|
+
|
|
13
|
+
def self.call(env)
|
|
14
|
+
new(env).respond!
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def initialize(env)
|
|
18
|
+
@env = env
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def respond!
|
|
22
|
+
options = @env['warden.options']
|
|
23
|
+
scope = options[:scope]
|
|
24
|
+
|
|
25
|
+
redirect_path = if mapping = Devise.mappings[scope]
|
|
26
|
+
"#{mapping.parsed_path}/#{mapping.path_names[:sign_in]}"
|
|
27
|
+
else
|
|
28
|
+
"/#{default_url}"
|
|
29
|
+
end
|
|
30
|
+
query_string = query_string_for(options)
|
|
31
|
+
store_location!(scope)
|
|
32
|
+
|
|
33
|
+
headers = {}
|
|
34
|
+
headers["Location"] = redirect_path
|
|
35
|
+
headers["Location"] << "?" << query_string unless query_string.empty?
|
|
36
|
+
headers["Content-Type"] = 'text/plain'
|
|
37
|
+
|
|
38
|
+
[302, headers, ["You are being redirected to #{redirect_path}"]]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Build the proper query string based on the given message.
|
|
42
|
+
def query_string_for(options)
|
|
43
|
+
message = @env['warden'].try(:message) || options[:message] || default_message
|
|
44
|
+
|
|
45
|
+
params = case message
|
|
46
|
+
when Symbol
|
|
47
|
+
{ message => true }
|
|
48
|
+
when String
|
|
49
|
+
{ :message => message }
|
|
50
|
+
else
|
|
51
|
+
{}
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
Rack::Utils.build_query(params)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Stores requested uri to redirect the user after signing in. We cannot use
|
|
58
|
+
# scoped session provided by warden here, since the user is not authenticated
|
|
59
|
+
# yet, but we still need to store the uri based on scope, so different scopes
|
|
60
|
+
# would never use the same uri to redirect.
|
|
61
|
+
def store_location!(scope)
|
|
62
|
+
session[:"#{scope}.return_to"] = request.request_uri if request && request.get?
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Deny user access whenever his account is not active yet.
|
|
2
|
+
Warden::Manager.after_set_user do |record, warden, options|
|
|
3
|
+
if record && record.respond_to?(:active?) && !record.active?
|
|
4
|
+
scope = options[:scope]
|
|
5
|
+
warden.logout(scope)
|
|
6
|
+
|
|
7
|
+
# If winning strategy was set, this is being called after authenticate and
|
|
8
|
+
# there is no need to force a redirect.
|
|
9
|
+
if warden.winning_strategy
|
|
10
|
+
warden.winning_strategy.fail!(record.inactive_message)
|
|
11
|
+
else
|
|
12
|
+
throw :warden, :scope => scope, :message => record.inactive_message
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# After authenticate hook to verify if the user in the given scope asked to be
|
|
2
|
+
# remembered while he does not sign out. Generates a new remember token for
|
|
3
|
+
# that specific user and adds a cookie with this user info to sign in this user
|
|
4
|
+
# automatically without asking for credentials. Refer to rememberable strategy
|
|
5
|
+
# for more info.
|
|
6
|
+
Warden::Manager.after_authentication do |record, warden, options|
|
|
7
|
+
scope = options[:scope]
|
|
8
|
+
remember_me = warden.params[scope].try(:fetch, :remember_me, nil)
|
|
9
|
+
|
|
10
|
+
if Devise::TRUE_VALUES.include?(remember_me) &&
|
|
11
|
+
warden.authenticated?(scope) && record.respond_to?(:remember_me!)
|
|
12
|
+
record.remember_me!
|
|
13
|
+
|
|
14
|
+
warden.response.set_cookie "remember_#{scope}_token", {
|
|
15
|
+
:value => record.class.serialize_into_cookie(record),
|
|
16
|
+
:expires => record.remember_expires_at,
|
|
17
|
+
:path => "/"
|
|
18
|
+
}
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Before logout hook to forget the user in the given scope, only if rememberable
|
|
23
|
+
# is activated for this scope. Also clear remember token to ensure the user
|
|
24
|
+
# won't be remembered again.
|
|
25
|
+
Warden::Manager.before_logout do |record, warden, scope|
|
|
26
|
+
if record.respond_to?(:forget_me!)
|
|
27
|
+
record.forget_me!
|
|
28
|
+
warden.response.delete_cookie "remember_#{scope}_token"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Each time a record is set we check whether it's session has already timed out
|
|
2
|
+
# or not, based on last request time. If so, the record is logged out and
|
|
3
|
+
# redirected to the sign in page. Also, each time the request comes and the
|
|
4
|
+
# record is set, we set the last request time inside it's scoped session to
|
|
5
|
+
# verify timeout in the following request.
|
|
6
|
+
Warden::Manager.after_set_user do |record, warden, options|
|
|
7
|
+
scope = options[:scope]
|
|
8
|
+
if record && record.respond_to?(:timedout?) && warden.authenticated?(scope)
|
|
9
|
+
last_request_at = warden.session(scope)['last_request_at']
|
|
10
|
+
|
|
11
|
+
if record.timedout?(last_request_at)
|
|
12
|
+
warden.logout(scope)
|
|
13
|
+
throw :warden, :scope => scope, :message => :timeout
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
warden.session(scope)['last_request_at'] = Time.now.utc
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# After each sign in, update sign in time, sign in count and sign in IP.
|
|
2
|
+
Warden::Manager.after_set_user :except => :fetch do |record, warden, options|
|
|
3
|
+
scope = options[:scope]
|
|
4
|
+
if Devise.mappings[scope].try(:trackable?) && warden.authenticated?(scope)
|
|
5
|
+
old_current, new_current = record.current_sign_in_at, Time.now
|
|
6
|
+
record.last_sign_in_at = old_current || new_current
|
|
7
|
+
record.current_sign_in_at = new_current
|
|
8
|
+
|
|
9
|
+
old_current, new_current = record.current_sign_in_ip, warden.request.remote_ip
|
|
10
|
+
record.last_sign_in_ip = old_current || new_current
|
|
11
|
+
record.current_sign_in_ip = new_current
|
|
12
|
+
|
|
13
|
+
record.sign_in_count ||= 0
|
|
14
|
+
record.sign_in_count += 1
|
|
15
|
+
|
|
16
|
+
record.save(false)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
en:
|
|
2
|
+
devise:
|
|
3
|
+
sessions:
|
|
4
|
+
link: 'Sign in'
|
|
5
|
+
signed_in: 'Signed in successfully.'
|
|
6
|
+
signed_out: 'Signed out successfully.'
|
|
7
|
+
unauthenticated: 'You need to sign in or sign up before continuing.'
|
|
8
|
+
unconfirmed: 'You have to confirm your account before continuing.'
|
|
9
|
+
locked: 'Your account is locked.'
|
|
10
|
+
invalid: 'Invalid email or password.'
|
|
11
|
+
invalid_token: 'Invalid authentication token.'
|
|
12
|
+
timeout: 'Your session expired, please sign in again to continue.'
|
|
13
|
+
inactive: 'Your account was not activated yet.'
|
|
14
|
+
passwords:
|
|
15
|
+
link: 'Forgot password?'
|
|
16
|
+
send_instructions: 'You will receive an email with instructions about how to reset your password in a few minutes.'
|
|
17
|
+
updated: 'Your password was changed successfully. You are now signed in.'
|
|
18
|
+
confirmations:
|
|
19
|
+
link: "Didn't receive confirmation instructions?"
|
|
20
|
+
send_instructions: 'You will receive an email with instructions about how to confirm your account in a few minutes.'
|
|
21
|
+
confirmed: 'Your account was successfully confirmed. You are now signed in.'
|
|
22
|
+
registrations:
|
|
23
|
+
link: 'Sign up'
|
|
24
|
+
signed_up: 'You have signed up successfully.'
|
|
25
|
+
updated: 'You updated your account successfully.'
|
|
26
|
+
destroyed: 'Bye! Your account was successfully cancelled. We hope to see you again soon.'
|
|
27
|
+
unlocks:
|
|
28
|
+
link: "Didn't receive unlock instructions?"
|
|
29
|
+
send_instructions: 'You will receive an email with instructions about how to unlock your account in a few minutes.'
|
|
30
|
+
unlocked: 'Your account was successfully unlocked. You are now signed in.'
|
|
31
|
+
mailer:
|
|
32
|
+
confirmation_instructions: 'Confirmation instructions'
|
|
33
|
+
reset_password_instructions: 'Reset password instructions'
|
|
34
|
+
unlock_instructions: 'Unlock Instructions'
|
|
35
|
+
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
module Devise
|
|
2
|
+
# Responsible for handling devise mappings and routes configuration. Each
|
|
3
|
+
# resource configured by devise_for in routes is actually creating a mapping
|
|
4
|
+
# object. You can refer to devise_for in routes for usage options.
|
|
5
|
+
#
|
|
6
|
+
# The required value in devise_for is actually not used internally, but it's
|
|
7
|
+
# inflected to find all other values.
|
|
8
|
+
#
|
|
9
|
+
# map.devise_for :users
|
|
10
|
+
# mapping = Devise.mappings[:user]
|
|
11
|
+
#
|
|
12
|
+
# mapping.name #=> :user
|
|
13
|
+
# # is the scope used in controllers and warden, given in the route as :singular.
|
|
14
|
+
#
|
|
15
|
+
# mapping.as #=> "users"
|
|
16
|
+
# # how the mapping should be search in the path, given in the route as :as.
|
|
17
|
+
#
|
|
18
|
+
# mapping.to #=> User
|
|
19
|
+
# # is the class to be loaded from routes, given in the route as :class_name.
|
|
20
|
+
#
|
|
21
|
+
# mapping.for #=> [:authenticatable]
|
|
22
|
+
# # is the modules included in the class
|
|
23
|
+
#
|
|
24
|
+
class Mapping #:nodoc:
|
|
25
|
+
attr_reader :name, :as, :path_names, :path_prefix, :route_options
|
|
26
|
+
|
|
27
|
+
# Loop through all mappings looking for a map that matches with the requested
|
|
28
|
+
# path (ie /users/sign_in). If a path prefix is given, it's taken into account.
|
|
29
|
+
def self.find_by_path(path)
|
|
30
|
+
Devise.mappings.each_value do |mapping|
|
|
31
|
+
route = path.split("/")[mapping.as_position]
|
|
32
|
+
return mapping if route && mapping.as == route.to_sym
|
|
33
|
+
end
|
|
34
|
+
nil
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Find a mapping by a given class. It takes into account single table inheritance as well.
|
|
38
|
+
def self.find_by_class(klass)
|
|
39
|
+
Devise.mappings.each_value do |mapping|
|
|
40
|
+
return mapping if klass <= mapping.to
|
|
41
|
+
end
|
|
42
|
+
nil
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Receives an object and find a scope for it. If a scope cannot be found,
|
|
46
|
+
# raises an error. If a symbol is given, it's considered to be the scope.
|
|
47
|
+
def self.find_scope!(duck)
|
|
48
|
+
case duck
|
|
49
|
+
when String, Symbol
|
|
50
|
+
duck
|
|
51
|
+
else
|
|
52
|
+
klass = duck.is_a?(Class) ? duck : duck.class
|
|
53
|
+
mapping = Devise::Mapping.find_by_class(klass)
|
|
54
|
+
raise "Could not find a valid mapping for #{duck}" unless mapping
|
|
55
|
+
mapping.name
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Default url options which can be used as prefix.
|
|
60
|
+
def self.default_url_options
|
|
61
|
+
{}
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def initialize(name, options) #:nodoc:
|
|
65
|
+
@as = (options.delete(:as) || name).to_sym
|
|
66
|
+
@klass = (options.delete(:class_name) || name.to_s.classify).to_s
|
|
67
|
+
@name = (options.delete(:scope) || name.to_s.singularize).to_sym
|
|
68
|
+
|
|
69
|
+
@path_prefix = "/#{options.delete(:path_prefix)}/".squeeze("/")
|
|
70
|
+
@route_options = options || {}
|
|
71
|
+
|
|
72
|
+
@path_names = Hash.new { |h,k| h[k] = k.to_s }
|
|
73
|
+
@path_names.merge!(options.delete(:path_names) || {})
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Return modules for the mapping.
|
|
77
|
+
def for
|
|
78
|
+
@for ||= to.devise_modules
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Reload mapped class each time when cache_classes is false.
|
|
82
|
+
def to
|
|
83
|
+
return @to if @to
|
|
84
|
+
klass = @klass.constantize
|
|
85
|
+
@to = klass if Rails.configuration.cache_classes
|
|
86
|
+
klass
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Check if the respective controller has a module in the mapping class.
|
|
90
|
+
def allows?(controller)
|
|
91
|
+
(self.for & CONTROLLERS[controller.to_sym]).present?
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Return in which position in the path prefix devise should find the as mapping.
|
|
95
|
+
def as_position
|
|
96
|
+
self.path_prefix.count("/")
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Returns the raw path using path_prefix and as.
|
|
100
|
+
def raw_path
|
|
101
|
+
path_prefix + as.to_s
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Returns the parsed path taking into account the relative url root and raw path.
|
|
105
|
+
def parsed_path
|
|
106
|
+
returning (ActionController::Base.relative_url_root.to_s + raw_path) do |path|
|
|
107
|
+
self.class.default_url_options.each do |key, value|
|
|
108
|
+
path.gsub!(key.inspect, value.to_param)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Create magic predicates for verifying what module is activated by this map.
|
|
114
|
+
# Example:
|
|
115
|
+
#
|
|
116
|
+
# def confirmable?
|
|
117
|
+
# self.for.include?(:confirmable)
|
|
118
|
+
# end
|
|
119
|
+
#
|
|
120
|
+
def self.register(*modules)
|
|
121
|
+
modules.each do |m|
|
|
122
|
+
class_eval <<-METHOD, __FILE__, __LINE__ + 1
|
|
123
|
+
def #{m}?
|
|
124
|
+
self.for.include?(:#{m})
|
|
125
|
+
end
|
|
126
|
+
METHOD
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
Devise::Mapping.register *ALL
|
|
130
|
+
end
|
|
131
|
+
end
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
module Devise
|
|
2
|
+
module Models
|
|
3
|
+
autoload :Activatable, 'devise/models/activatable'
|
|
4
|
+
autoload :Authenticatable, 'devise/models/authenticatable'
|
|
5
|
+
autoload :Confirmable, 'devise/models/confirmable'
|
|
6
|
+
autoload :Lockable, 'devise/models/lockable'
|
|
7
|
+
autoload :Recoverable, 'devise/models/recoverable'
|
|
8
|
+
autoload :Rememberable, 'devise/models/rememberable'
|
|
9
|
+
autoload :Registerable, 'devise/models/registerable'
|
|
10
|
+
autoload :Timeoutable, 'devise/models/timeoutable'
|
|
11
|
+
autoload :Trackable, 'devise/models/trackable'
|
|
12
|
+
autoload :Validatable, 'devise/models/validatable'
|
|
13
|
+
|
|
14
|
+
# Creates configuration values for Devise and for the given module.
|
|
15
|
+
#
|
|
16
|
+
# Devise::Models.config(Devise::Authenticable, :stretches, 10)
|
|
17
|
+
#
|
|
18
|
+
# The line above creates:
|
|
19
|
+
#
|
|
20
|
+
# 1) An accessor called Devise.stretches, which value is used by default;
|
|
21
|
+
#
|
|
22
|
+
# 2) Some class methods for your model Model.stretches and Model.stretches=
|
|
23
|
+
# which have higher priority than Devise.stretches;
|
|
24
|
+
#
|
|
25
|
+
# 3) And an instance method stretches.
|
|
26
|
+
#
|
|
27
|
+
# To add the class methods you need to have a module ClassMethods defined
|
|
28
|
+
# inside the given class.
|
|
29
|
+
#
|
|
30
|
+
def self.config(mod, *accessors) #:nodoc:
|
|
31
|
+
accessors.each do |accessor|
|
|
32
|
+
mod.class_eval <<-METHOD, __FILE__, __LINE__ + 1
|
|
33
|
+
def #{accessor}
|
|
34
|
+
if defined?(@#{accessor})
|
|
35
|
+
@#{accessor}
|
|
36
|
+
elsif superclass.respond_to?(:#{accessor})
|
|
37
|
+
superclass.#{accessor}
|
|
38
|
+
else
|
|
39
|
+
Devise.#{accessor}
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def #{accessor}=(value)
|
|
44
|
+
@#{accessor} = value
|
|
45
|
+
end
|
|
46
|
+
METHOD
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Include the chosen devise modules in your model:
|
|
51
|
+
#
|
|
52
|
+
# devise :authenticatable, :confirmable, :recoverable
|
|
53
|
+
#
|
|
54
|
+
# You can also give any of the devise configuration values in form of a hash,
|
|
55
|
+
# with specific values for this model. Please check your Devise initializer
|
|
56
|
+
# for a complete description on those values.
|
|
57
|
+
#
|
|
58
|
+
def devise(*modules)
|
|
59
|
+
raise "You need to give at least one Devise module" if modules.empty?
|
|
60
|
+
options = modules.extract_options!
|
|
61
|
+
|
|
62
|
+
@devise_modules = Devise::ALL & modules.map(&:to_sym).uniq
|
|
63
|
+
|
|
64
|
+
Devise.orm_class.included_modules_hook(self) do
|
|
65
|
+
devise_modules.each do |m|
|
|
66
|
+
include Devise::Models.const_get(m.to_s.classify)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
options.each { |key, value| send(:"#{key}=", value) }
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Stores all modules included inside the model, so we are able to verify
|
|
74
|
+
# which routes are needed.
|
|
75
|
+
def devise_modules
|
|
76
|
+
@devise_modules ||= []
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Find an initialize a record setting an error if it can't be found.
|
|
80
|
+
def find_or_initialize_with_error_by(attribute, value, error=:invalid)
|
|
81
|
+
if value.present?
|
|
82
|
+
conditions = { attribute => value }
|
|
83
|
+
record = find(:first, :conditions => conditions)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
unless record
|
|
87
|
+
record = new
|
|
88
|
+
|
|
89
|
+
if value.present?
|
|
90
|
+
record.send(:"#{attribute}=", value)
|
|
91
|
+
else
|
|
92
|
+
error, skip_default = :blank, true
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
add_error_on(record, attribute, error, !skip_default)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
record
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Wraps add error logic in a method that works for different frameworks.
|
|
102
|
+
def add_error_on(record, attribute, error, add_default=true)
|
|
103
|
+
options = add_default ? { :default => error.to_s.gsub("_", " ") } : {}
|
|
104
|
+
|
|
105
|
+
begin
|
|
106
|
+
record.errors.add(attribute, error, options)
|
|
107
|
+
rescue ArgumentError
|
|
108
|
+
record.errors.add(attribute, error.to_s.gsub("_", " "))
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|