devise-edge 1.2.rc

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.
Files changed (161) hide show
  1. data/CHANGELOG.rdoc +500 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +335 -0
  4. data/app/controllers/devise/confirmations_controller.rb +33 -0
  5. data/app/controllers/devise/oauth_callbacks_controller.rb +4 -0
  6. data/app/controllers/devise/passwords_controller.rb +41 -0
  7. data/app/controllers/devise/registrations_controller.rb +75 -0
  8. data/app/controllers/devise/sessions_controller.rb +23 -0
  9. data/app/controllers/devise/unlocks_controller.rb +34 -0
  10. data/app/helpers/devise_helper.rb +17 -0
  11. data/app/mailers/devise/mailer.rb +88 -0
  12. data/app/views/devise/confirmations/new.html.erb +12 -0
  13. data/app/views/devise/mailer/confirmation_instructions.html.erb +5 -0
  14. data/app/views/devise/mailer/reset_password_instructions.html.erb +8 -0
  15. data/app/views/devise/mailer/unlock_instructions.html.erb +7 -0
  16. data/app/views/devise/passwords/edit.html.erb +16 -0
  17. data/app/views/devise/passwords/new.html.erb +12 -0
  18. data/app/views/devise/registrations/edit.html.erb +25 -0
  19. data/app/views/devise/registrations/new.html.erb +18 -0
  20. data/app/views/devise/sessions/new.html.erb +17 -0
  21. data/app/views/devise/shared/_links.erb +25 -0
  22. data/app/views/devise/unlocks/new.html.erb +12 -0
  23. data/config/locales/en.yml +42 -0
  24. data/lib/devise.rb +371 -0
  25. data/lib/devise/controllers/helpers.rb +261 -0
  26. data/lib/devise/controllers/internal_helpers.rb +113 -0
  27. data/lib/devise/controllers/scoped_views.rb +33 -0
  28. data/lib/devise/controllers/url_helpers.rb +39 -0
  29. data/lib/devise/encryptors/authlogic_sha512.rb +19 -0
  30. data/lib/devise/encryptors/base.rb +20 -0
  31. data/lib/devise/encryptors/clearance_sha1.rb +17 -0
  32. data/lib/devise/encryptors/restful_authentication_sha1.rb +22 -0
  33. data/lib/devise/encryptors/sha1.rb +25 -0
  34. data/lib/devise/encryptors/sha512.rb +25 -0
  35. data/lib/devise/failure_app.rb +126 -0
  36. data/lib/devise/hooks/activatable.rb +11 -0
  37. data/lib/devise/hooks/forgetable.rb +12 -0
  38. data/lib/devise/hooks/rememberable.rb +45 -0
  39. data/lib/devise/hooks/timeoutable.rb +22 -0
  40. data/lib/devise/hooks/trackable.rb +9 -0
  41. data/lib/devise/mapping.rb +105 -0
  42. data/lib/devise/models.rb +66 -0
  43. data/lib/devise/models/authenticatable.rb +143 -0
  44. data/lib/devise/models/confirmable.rb +160 -0
  45. data/lib/devise/models/database_authenticatable.rb +94 -0
  46. data/lib/devise/models/encryptable.rb +65 -0
  47. data/lib/devise/models/lockable.rb +168 -0
  48. data/lib/devise/models/oauthable.rb +49 -0
  49. data/lib/devise/models/recoverable.rb +83 -0
  50. data/lib/devise/models/registerable.rb +21 -0
  51. data/lib/devise/models/rememberable.rb +122 -0
  52. data/lib/devise/models/timeoutable.rb +33 -0
  53. data/lib/devise/models/token_authenticatable.rb +72 -0
  54. data/lib/devise/models/trackable.rb +30 -0
  55. data/lib/devise/models/validatable.rb +60 -0
  56. data/lib/devise/modules.rb +30 -0
  57. data/lib/devise/oauth.rb +41 -0
  58. data/lib/devise/oauth/config.rb +33 -0
  59. data/lib/devise/oauth/helpers.rb +18 -0
  60. data/lib/devise/oauth/internal_helpers.rb +182 -0
  61. data/lib/devise/oauth/test_helpers.rb +29 -0
  62. data/lib/devise/oauth/url_helpers.rb +35 -0
  63. data/lib/devise/orm/active_record.rb +36 -0
  64. data/lib/devise/orm/mongo_mapper.rb +46 -0
  65. data/lib/devise/orm/mongoid.rb +29 -0
  66. data/lib/devise/path_checker.rb +18 -0
  67. data/lib/devise/rails.rb +67 -0
  68. data/lib/devise/rails/routes.rb +260 -0
  69. data/lib/devise/rails/warden_compat.rb +42 -0
  70. data/lib/devise/schema.rb +96 -0
  71. data/lib/devise/strategies/authenticatable.rb +150 -0
  72. data/lib/devise/strategies/base.rb +15 -0
  73. data/lib/devise/strategies/database_authenticatable.rb +21 -0
  74. data/lib/devise/strategies/rememberable.rb +51 -0
  75. data/lib/devise/strategies/token_authenticatable.rb +53 -0
  76. data/lib/devise/test_helpers.rb +100 -0
  77. data/lib/devise/version.rb +3 -0
  78. data/lib/generators/active_record/devise_generator.rb +28 -0
  79. data/lib/generators/active_record/templates/migration.rb +30 -0
  80. data/lib/generators/devise/devise_generator.rb +17 -0
  81. data/lib/generators/devise/install_generator.rb +24 -0
  82. data/lib/generators/devise/orm_helpers.rb +24 -0
  83. data/lib/generators/devise/views_generator.rb +63 -0
  84. data/lib/generators/mongoid/devise_generator.rb +17 -0
  85. data/lib/generators/templates/README +25 -0
  86. data/lib/generators/templates/devise.rb +168 -0
  87. data/test/controllers/helpers_test.rb +220 -0
  88. data/test/controllers/internal_helpers_test.rb +56 -0
  89. data/test/controllers/url_helpers_test.rb +59 -0
  90. data/test/devise_test.rb +65 -0
  91. data/test/encryptors_test.rb +30 -0
  92. data/test/failure_app_test.rb +148 -0
  93. data/test/integration/authenticatable_test.rb +424 -0
  94. data/test/integration/confirmable_test.rb +104 -0
  95. data/test/integration/database_authenticatable_test.rb +38 -0
  96. data/test/integration/http_authenticatable_test.rb +64 -0
  97. data/test/integration/lockable_test.rb +109 -0
  98. data/test/integration/oauthable_test.rb +258 -0
  99. data/test/integration/recoverable_test.rb +141 -0
  100. data/test/integration/registerable_test.rb +179 -0
  101. data/test/integration/rememberable_test.rb +179 -0
  102. data/test/integration/timeoutable_test.rb +80 -0
  103. data/test/integration/token_authenticatable_test.rb +99 -0
  104. data/test/integration/trackable_test.rb +64 -0
  105. data/test/mailers/confirmation_instructions_test.rb +84 -0
  106. data/test/mailers/reset_password_instructions_test.rb +72 -0
  107. data/test/mailers/unlock_instructions_test.rb +66 -0
  108. data/test/mapping_test.rb +95 -0
  109. data/test/models/confirmable_test.rb +221 -0
  110. data/test/models/database_authenticatable_test.rb +82 -0
  111. data/test/models/encryptable_test.rb +65 -0
  112. data/test/models/lockable_test.rb +204 -0
  113. data/test/models/oauthable_test.rb +21 -0
  114. data/test/models/recoverable_test.rb +155 -0
  115. data/test/models/rememberable_test.rb +271 -0
  116. data/test/models/timeoutable_test.rb +28 -0
  117. data/test/models/token_authenticatable_test.rb +37 -0
  118. data/test/models/trackable_test.rb +5 -0
  119. data/test/models/validatable_test.rb +99 -0
  120. data/test/models_test.rb +77 -0
  121. data/test/oauth/config_test.rb +44 -0
  122. data/test/oauth/url_helpers_test.rb +47 -0
  123. data/test/orm/active_record.rb +9 -0
  124. data/test/orm/mongoid.rb +10 -0
  125. data/test/rails_app/app/active_record/admin.rb +6 -0
  126. data/test/rails_app/app/active_record/shim.rb +2 -0
  127. data/test/rails_app/app/active_record/user.rb +8 -0
  128. data/test/rails_app/app/controllers/admins/sessions_controller.rb +6 -0
  129. data/test/rails_app/app/controllers/admins_controller.rb +6 -0
  130. data/test/rails_app/app/controllers/application_controller.rb +9 -0
  131. data/test/rails_app/app/controllers/home_controller.rb +12 -0
  132. data/test/rails_app/app/controllers/publisher/registrations_controller.rb +2 -0
  133. data/test/rails_app/app/controllers/publisher/sessions_controller.rb +2 -0
  134. data/test/rails_app/app/controllers/users_controller.rb +18 -0
  135. data/test/rails_app/app/helpers/application_helper.rb +3 -0
  136. data/test/rails_app/app/mongoid/admin.rb +9 -0
  137. data/test/rails_app/app/mongoid/shim.rb +24 -0
  138. data/test/rails_app/app/mongoid/user.rb +10 -0
  139. data/test/rails_app/config/application.rb +35 -0
  140. data/test/rails_app/config/boot.rb +13 -0
  141. data/test/rails_app/config/environment.rb +5 -0
  142. data/test/rails_app/config/environments/development.rb +19 -0
  143. data/test/rails_app/config/environments/production.rb +33 -0
  144. data/test/rails_app/config/environments/test.rb +33 -0
  145. data/test/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  146. data/test/rails_app/config/initializers/devise.rb +172 -0
  147. data/test/rails_app/config/initializers/inflections.rb +2 -0
  148. data/test/rails_app/config/initializers/secret_token.rb +2 -0
  149. data/test/rails_app/config/routes.rb +54 -0
  150. data/test/rails_app/db/migrate/20100401102949_create_tables.rb +31 -0
  151. data/test/rails_app/db/schema.rb +52 -0
  152. data/test/rails_app/lib/shared_admin.rb +9 -0
  153. data/test/rails_app/lib/shared_user.rb +48 -0
  154. data/test/routes_test.rb +189 -0
  155. data/test/support/assertions.rb +24 -0
  156. data/test/support/helpers.rb +60 -0
  157. data/test/support/integration.rb +88 -0
  158. data/test/support/webrat/integrations/rails.rb +24 -0
  159. data/test/test_helper.rb +23 -0
  160. data/test/test_helpers_test.rb +101 -0
  161. metadata +335 -0
@@ -0,0 +1,33 @@
1
+ require 'devise/hooks/timeoutable'
2
+
3
+ module Devise
4
+ module Models
5
+ # Timeoutable takes care of veryfing whether a user session has already
6
+ # expired or not. When a session expires after the configured time, the user
7
+ # will be asked for credentials again, it means, he/she will be redirected
8
+ # to the sign in page.
9
+ #
10
+ # == Options
11
+ #
12
+ # Timeoutable adds the following options to devise_for:
13
+ #
14
+ # * +timeout_in+: the interval to timeout the user session without activity.
15
+ #
16
+ # == Examples
17
+ #
18
+ # user.timedout?(30.minutes.ago)
19
+ #
20
+ module Timeoutable
21
+ extend ActiveSupport::Concern
22
+
23
+ # Checks whether the user session has expired based on configured time.
24
+ def timedout?(last_access)
25
+ last_access && last_access <= self.class.timeout_in.ago
26
+ end
27
+
28
+ module ClassMethods
29
+ Devise::Models.config(self, :timeout_in)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,72 @@
1
+ require 'devise/strategies/token_authenticatable'
2
+
3
+ module Devise
4
+ module Models
5
+ # The TokenAuthenticatable module is responsible for generating an authentication token and
6
+ # validating the authenticity of the same while signing in.
7
+ #
8
+ # This module only provides a few helpers to help you manage the token, but it is up to you
9
+ # to choose how to use it. For example, if you want to have a new token every time the user
10
+ # saves his account, you can do the following:
11
+ #
12
+ # before_save :reset_authentication_token
13
+ #
14
+ # On the other hand, if you want to generate token unless one exists, you should use instead:
15
+ #
16
+ # before_save :ensure_authentication_token
17
+ #
18
+ # If you want to delete the token after it is used, you can do so in the
19
+ # after_token_authentication callback.
20
+ #
21
+ # == Options
22
+ #
23
+ # TokenAuthenticable adds the following options to devise_for:
24
+ #
25
+ # * +token_authentication_key+: Defines name of the authentication token params key. E.g. /users/sign_in?some_key=...
26
+ #
27
+ # * +stateless_token+: By default, when you sign up with a token, Devise will store the user in session
28
+ # as any other authentication strategy. You can set stateless_token to true to avoid this.
29
+ #
30
+ module TokenAuthenticatable
31
+ extend ActiveSupport::Concern
32
+
33
+ # Generate new authentication token (a.k.a. "single access token").
34
+ def reset_authentication_token
35
+ self.authentication_token = self.class.authentication_token
36
+ end
37
+
38
+ # Generate new authentication token and save the record.
39
+ def reset_authentication_token!
40
+ reset_authentication_token
41
+ self.save(:validate => false)
42
+ end
43
+
44
+ # Generate authentication token unless already exists.
45
+ def ensure_authentication_token
46
+ self.reset_authentication_token if self.authentication_token.blank?
47
+ end
48
+
49
+ # Generate authentication token unless already exists and save the record.
50
+ def ensure_authentication_token!
51
+ self.reset_authentication_token! if self.authentication_token.blank?
52
+ end
53
+
54
+ # Hook called after token authentication.
55
+ def after_token_authentication
56
+ end
57
+
58
+ module ClassMethods
59
+ def find_for_token_authentication(conditions)
60
+ find_for_authentication(:authentication_token => conditions[token_authentication_key])
61
+ end
62
+
63
+ # Generate a token checking if one does not already exist in the database.
64
+ def authentication_token
65
+ generate_token(:authentication_token)
66
+ end
67
+
68
+ ::Devise::Models.config(self, :token_authentication_key, :stateless_token)
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,30 @@
1
+ require 'devise/hooks/trackable'
2
+
3
+ module Devise
4
+ module Models
5
+ # Track information about your user sign in. It tracks the following columns:
6
+ #
7
+ # * sign_in_count - Increased every time a sign in is made (by form, openid, oauth)
8
+ # * current_sign_in_at - A tiemstamp updated when the user signs in
9
+ # * last_sign_in_at - Holds the timestamp of the previous sign in
10
+ # * current_sign_in_ip - The remote ip updated when the user sign in
11
+ # * last_sign_in_at - Holds the remote ip of the previous sign in
12
+ #
13
+ module Trackable
14
+ def update_tracked_fields!(request)
15
+ old_current, new_current = self.current_sign_in_at, Time.now
16
+ self.last_sign_in_at = old_current || new_current
17
+ self.current_sign_in_at = new_current
18
+
19
+ old_current, new_current = self.current_sign_in_ip, request.remote_ip
20
+ self.last_sign_in_ip = old_current || new_current
21
+ self.current_sign_in_ip = new_current
22
+
23
+ self.sign_in_count ||= 0
24
+ self.sign_in_count += 1
25
+
26
+ save(:validate => false)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,60 @@
1
+ module Devise
2
+ module Models
3
+ # Validatable creates all needed validations for a user email and password.
4
+ # It's optional, given you may want to create the validations by yourself.
5
+ # Automatically validate if the email is present, unique and it's format is
6
+ # valid. Also tests presence of password, confirmation and length.
7
+ #
8
+ # == Options
9
+ #
10
+ # Validatable adds the following options to devise_for:
11
+ #
12
+ # * +email_regexp+: the regular expression used to validate e-mails;
13
+ # * +password_length+: a range expressing password length. Defaults to 6..20.
14
+ #
15
+ module Validatable
16
+ # All validations used by this module.
17
+ VALIDATIONS = [ :validates_presence_of, :validates_uniqueness_of, :validates_format_of,
18
+ :validates_confirmation_of, :validates_length_of ].freeze
19
+
20
+ def self.included(base)
21
+ base.extend ClassMethods
22
+ assert_validations_api!(base)
23
+
24
+ base.class_eval do
25
+ validates_presence_of :email
26
+ validates_uniqueness_of :email, :scope => authentication_keys[1..-1], :case_sensitive => false, :allow_blank => true
27
+ validates_format_of :email, :with => email_regexp, :allow_blank => true
28
+
29
+ with_options :if => :password_required? do |v|
30
+ v.validates_presence_of :password
31
+ v.validates_confirmation_of :password
32
+ v.validates_length_of :password, :within => password_length, :allow_blank => true
33
+ end
34
+ end
35
+ end
36
+
37
+ def self.assert_validations_api!(base) #:nodoc:
38
+ unavailable_validations = VALIDATIONS.select { |v| !base.respond_to?(v) }
39
+
40
+ unless unavailable_validations.empty?
41
+ raise "Could not use :validatable module since #{base} does not respond " <<
42
+ "to the following methods: #{unavailable_validations.to_sentence}."
43
+ end
44
+ end
45
+
46
+ protected
47
+
48
+ # Checks whether a password is needed or not. For validations only.
49
+ # Passwords are always required if it's a new record, or if the password
50
+ # or confirmation are being set somewhere.
51
+ def password_required?
52
+ !persisted? || !password.nil? || !password_confirmation.nil?
53
+ end
54
+
55
+ module ClassMethods
56
+ Devise::Models.config(self, :email_regexp, :password_length)
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,30 @@
1
+ require 'active_support/core_ext/object/with_options'
2
+
3
+ Devise.with_options :model => true do |d|
4
+ # Strategies first
5
+ d.with_options :strategy => true do |s|
6
+ routes = [nil, :new, :destroy]
7
+ s.add_module :database_authenticatable, :controller => :sessions, :route => { :session => routes }
8
+ s.add_module :token_authenticatable, :controller => :sessions, :route => { :session => routes }
9
+ s.add_module :rememberable
10
+ end
11
+
12
+ # Other authentications
13
+ d.add_module :encryptable
14
+ d.add_module :oauthable, :controller => :oauth_callbacks, :route => :oauth_callback
15
+
16
+ # Misc after
17
+ routes = [nil, :new, :edit]
18
+ d.add_module :recoverable, :controller => :passwords, :route => { :password => routes }
19
+ d.add_module :registerable, :controller => :registrations, :route => { :registration => (routes << :cancel) }
20
+ d.add_module :validatable
21
+
22
+ # The ones which can sign out after
23
+ routes = [nil, :new]
24
+ d.add_module :confirmable, :controller => :confirmations, :route => { :confirmation => routes }
25
+ d.add_module :lockable, :controller => :unlocks, :route => { :unlock => routes }
26
+ d.add_module :timeoutable
27
+
28
+ # Stats for last, so we make sure the user is really signed in
29
+ d.add_module :trackable
30
+ end
@@ -0,0 +1,41 @@
1
+ begin
2
+ require "oauth2"
3
+ rescue LoadError => e
4
+ warn "Could not load 'oauth2'. Please ensure you have the gem installed and listed in your Gemfile."
5
+ raise
6
+ end
7
+
8
+ module Devise
9
+ module Oauth
10
+ autoload :Config, "devise/oauth/config"
11
+ autoload :Helpers, "devise/oauth/helpers"
12
+ autoload :InternalHelpers, "devise/oauth/internal_helpers"
13
+ autoload :UrlHelpers, "devise/oauth/url_helpers"
14
+ autoload :TestHelpers, "devise/oauth/test_helpers"
15
+
16
+ class << self
17
+ delegate :short_circuit_authorizers!, :unshort_circuit_authorizers!, :to => "Devise::Oauth::TestHelpers"
18
+
19
+ def test_mode!
20
+ Faraday.default_adapter = :test
21
+ ActiveSupport.on_load(:action_controller) { include Devise::Oauth::TestHelpers }
22
+ ActiveSupport.on_load(:action_view) { include Devise::Oauth::TestHelpers }
23
+ end
24
+
25
+ def stub!(provider, stubs=nil, &block)
26
+ raise "You either need to pass stubs as a block or as a parameter" unless block_given? || stubs
27
+ stubs ||= Faraday::Adapter::Test::Stubs.new(&block)
28
+ Devise.oauth_configs[provider].build_connection do |b|
29
+ b.adapter :test, stubs
30
+ end
31
+ end
32
+
33
+ def reset_stubs!(*providers)
34
+ target = providers.any? ? Devise.oauth_configs.slice(*providers) : Devise.oauth_configs
35
+ target.each_value do |v|
36
+ v.build_connection { |b| b.adapter Faraday.default_adapter }
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,33 @@
1
+ require 'active_support/core_ext/array/wrap'
2
+
3
+ module Devise
4
+ module Oauth
5
+ # A configuration object that holds the OAuth2::Client object
6
+ # and all configuration values given config.oauth.
7
+ class Config
8
+ attr_reader :scope, :client
9
+
10
+ def initialize(app_id, app_secret, options)
11
+ @scope = Array.wrap(options.delete(:scope))
12
+ @client = OAuth2::Client.new(app_id, app_secret, options)
13
+ end
14
+
15
+ def authorize_url(options)
16
+ options[:scope] ||= @scope.join(',')
17
+ client.web_server.authorize_url(options)
18
+ end
19
+
20
+ def access_token_by_code(code, redirect_uri=nil)
21
+ client.web_server.get_access_token(code, :redirect_uri => redirect_uri)
22
+ end
23
+
24
+ def access_token_by_token(token)
25
+ OAuth2::AccessToken.new(client, token)
26
+ end
27
+
28
+ def build_connection(&block)
29
+ client.connection.build(&block)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,18 @@
1
+ module Devise
2
+ module Oauth
3
+ # Provides a few helpers that are included in ActionController::Base
4
+ # for convenience.
5
+ module Helpers
6
+
7
+ protected
8
+
9
+ # Overwrite expire_session_data_after_sign_in! so it removes all
10
+ # oauth tokens from session ensuring registrations done in a row
11
+ # do not try to store the same token in the database.
12
+ def expire_session_data_after_sign_in!
13
+ super
14
+ session.keys.grep(/_oauth_token$/).each { |k| session.delete(k) }
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,182 @@
1
+ module Devise
2
+ module Oauth
3
+ module InternalHelpers
4
+ extend ActiveSupport::Concern
5
+
6
+ def self.define_oauth_helpers(name) #:nodoc:
7
+ alias_method(name, :callback_action)
8
+ public name
9
+ end
10
+
11
+ included do
12
+ helpers = %w(oauth_callback oauth_provider oauth_config)
13
+ hide_action *helpers
14
+ helper_method *helpers
15
+ before_filter :valid_oauth_callback?, :oauth_error_happened?
16
+ end
17
+
18
+ # Returns the oauth_callback (also aliased as oauth_provider) as a symbol.
19
+ # For example: :github.
20
+ def oauth_callback
21
+ @oauth_callback ||= action_name.to_sym
22
+ end
23
+ alias :oauth_provider :oauth_callback
24
+
25
+ # Returns the configuration object for this oauth callback.
26
+ def oauth_config
27
+ @oauth_client ||= resource_class.oauth_configs[oauth_callback]
28
+ end
29
+
30
+ protected
31
+
32
+ # This method checks three things:
33
+ #
34
+ # * If the URL being accessed is a valid provider for the given scope;
35
+ # * If code or error was streamed back from the server;
36
+ # * If the resource class implements the required hook;
37
+ #
38
+ def valid_oauth_callback?
39
+ unless oauth_config
40
+ unknown_action! "Skipping #{oauth_callback} OAuth because configuration " <<
41
+ "could not be found for model #{resource_name}."
42
+ end
43
+
44
+ unless params[:code] || params[:error] || params[:error_reason]
45
+ unknown_action! "Skipping #{oauth_callback} OAuth because code nor error were sent."
46
+ end
47
+
48
+ unless resource_class.respond_to?(oauth_model_callback)
49
+ raise "#{resource_class.name} does not respond to #{oauth_model_callback}. " <<
50
+ "Check the OAuth section in the README for more information."
51
+ end
52
+ end
53
+
54
+ # Check if an error was sent by the authorizer. If it happened, we redirect
55
+ # to url specified by after_oauth_failure_path_for, which defaults to new_session_path.
56
+ #
57
+ # By default, Devise shows a custom message from I18n saying the user could
58
+ # not be authenticated and the reason:
59
+ #
60
+ # en:
61
+ # devise:
62
+ # oauth_callbacks:
63
+ # failure: 'Could not authorize you from %{kind} because "%{reason}".'
64
+ #
65
+ # Let's suppose the reason returned by a Github was "access_denied". It will show:
66
+ #
67
+ # Could not authorize you from Github because "Access denied"
68
+ #
69
+ # And it will also be logged on console:
70
+ #
71
+ # github oauth failed: "access_denied".
72
+ #
73
+ # However, each specific error message can be customized using I18n:
74
+ #
75
+ # en:
76
+ # devise:
77
+ # oauth_callbacks:
78
+ # access_denied: 'You did not give access to our application on %{kind}.'
79
+ #
80
+ # Note "access_denied" follows the same lookup rule described in set_oauth_flash_message
81
+ # method. Besides, is important to remember most errors are specified by OAuth 2
82
+ # specification. But a few providers do not use them yet.
83
+ #
84
+ # TODO: Currently, Facebook is returning error_reason=user_denied when
85
+ # the user denies, but the specification defines error=access_denied instead.
86
+ def oauth_error_happened?
87
+ if error = params[:error] || params[:error_reason]
88
+ # Some providers returns access-denied instead of access_denied.
89
+ error = error.to_s.gsub("-", "_")
90
+ logger.warn "[Devise] #{oauth_callback} oauth failed: #{error.inspect}."
91
+
92
+ set_oauth_flash_message :alert, error[0,25], :default => :failure, :reason => error.humanize
93
+ redirect_to after_oauth_failure_path_for(resource_name)
94
+ end
95
+ end
96
+
97
+ # The model method used as hook.
98
+ def oauth_model_callback #:nodoc:
99
+ "find_for_#{oauth_callback}_oauth"
100
+ end
101
+
102
+ # The session key to store the token.
103
+ def oauth_session_key #:nodoc:
104
+ "#{resource_name}_#{oauth_callback}_oauth_token"
105
+ end
106
+
107
+ # The callback redirect uri. Used to request the access token.
108
+ def oauth_redirect_uri #:nodoc:
109
+ oauth_callback_url(resource_name, oauth_callback)
110
+ end
111
+
112
+ # This is the implementation for all OAuth actions.
113
+ def callback_action
114
+ access_token = oauth_config.access_token_by_code(params[:code], oauth_redirect_uri)
115
+ self.resource = resource_class.send(oauth_model_callback, access_token, signed_in_resource)
116
+
117
+ if resource.persisted? && resource.errors.empty?
118
+ set_oauth_flash_message :notice, :success
119
+ sign_in_and_redirect resource_name, resource, :event => :authentication
120
+ else
121
+ session[oauth_session_key] = access_token.token
122
+ clean_up_passwords(resource)
123
+ render_for_oauth
124
+ end
125
+ end
126
+
127
+ # Handles oauth flash messages by adding a cascade. The default messages
128
+ # are always in the controller namespace:
129
+ #
130
+ # en:
131
+ # devise:
132
+ # oauth_callbacks:
133
+ # success: 'Successfully authorized from %{kind} account.'
134
+ # failure: 'Could not authorize you from %{kind} because "%{reason}".'
135
+ # skipped: 'Skipped Oauth authorization for %{kind}.'
136
+ #
137
+ # But they can also be nested according to the oauth provider:
138
+ #
139
+ # en:
140
+ # devise:
141
+ # oauth_callbacks:
142
+ # github:
143
+ # success: 'Hello coder! Welcome to our app!'
144
+ #
145
+ # And finally by Devise scope:
146
+ #
147
+ # en:
148
+ # devise:
149
+ # oauth_callbacks:
150
+ # admin:
151
+ # github:
152
+ # success: 'Hello coder with high permissions! Can I get a raise?'
153
+ #
154
+ def set_oauth_flash_message(key, type, options={})
155
+ options[:kind] = oauth_callback.to_s.titleize
156
+ options[:default] = Array(options[:default]).unshift(type.to_sym)
157
+ set_flash_message(key, "#{oauth_callback}.#{type}", options)
158
+ end
159
+
160
+ # Choose which template to render when a not persisted resource is
161
+ # returned in the find_for_x_oauth. By default, it renders registrations/new.
162
+ def render_for_oauth
163
+ render_with_scope :new, devise_mapping.controllers[:registrations]
164
+ end
165
+
166
+ # The default hook used by oauth to specify the redirect url for success.
167
+ def after_oauth_success_path_for(resource_or_scope)
168
+ after_sign_in_path_for(resource_or_scope)
169
+ end
170
+
171
+ # The default hook used by oauth to specify the redirect url for failure.
172
+ def after_oauth_failure_path_for(scope)
173
+ new_session_path(scope)
174
+ end
175
+
176
+ # Overwrite redirect_for_sign_in so it takes uses after_oauth_success_path_for.
177
+ def redirect_for_sign_in(scope, resource) #:nodoc:
178
+ redirect_to stored_location_for(scope) || after_oauth_success_path_for(resource)
179
+ end
180
+ end
181
+ end
182
+ end