cbsorcery 0.8.6

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 (135) hide show
  1. data/.document +5 -0
  2. data/.gitignore +56 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +40 -0
  5. data/CHANGELOG.md +263 -0
  6. data/Gemfile +6 -0
  7. data/LICENSE.txt +20 -0
  8. data/README.md +360 -0
  9. data/Rakefile +6 -0
  10. data/gemfiles/active_record-rails40.gemfile +7 -0
  11. data/gemfiles/active_record-rails41.gemfile +7 -0
  12. data/lib/generators/sorcery/USAGE +22 -0
  13. data/lib/generators/sorcery/helpers.rb +40 -0
  14. data/lib/generators/sorcery/install_generator.rb +95 -0
  15. data/lib/generators/sorcery/templates/initializer.rb +451 -0
  16. data/lib/generators/sorcery/templates/migration/activity_logging.rb +10 -0
  17. data/lib/generators/sorcery/templates/migration/brute_force_protection.rb +9 -0
  18. data/lib/generators/sorcery/templates/migration/core.rb +13 -0
  19. data/lib/generators/sorcery/templates/migration/external.rb +12 -0
  20. data/lib/generators/sorcery/templates/migration/remember_me.rb +8 -0
  21. data/lib/generators/sorcery/templates/migration/reset_password.rb +9 -0
  22. data/lib/generators/sorcery/templates/migration/user_activation.rb +9 -0
  23. data/lib/sorcery.rb +85 -0
  24. data/lib/sorcery/adapters/active_record_adapter.rb +120 -0
  25. data/lib/sorcery/adapters/base_adapter.rb +30 -0
  26. data/lib/sorcery/controller.rb +157 -0
  27. data/lib/sorcery/controller/config.rb +65 -0
  28. data/lib/sorcery/controller/submodules/activity_logging.rb +82 -0
  29. data/lib/sorcery/controller/submodules/brute_force_protection.rb +38 -0
  30. data/lib/sorcery/controller/submodules/external.rb +199 -0
  31. data/lib/sorcery/controller/submodules/http_basic_auth.rb +74 -0
  32. data/lib/sorcery/controller/submodules/remember_me.rb +81 -0
  33. data/lib/sorcery/controller/submodules/session_timeout.rb +56 -0
  34. data/lib/sorcery/crypto_providers/aes256.rb +51 -0
  35. data/lib/sorcery/crypto_providers/bcrypt.rb +97 -0
  36. data/lib/sorcery/crypto_providers/common.rb +35 -0
  37. data/lib/sorcery/crypto_providers/md5.rb +19 -0
  38. data/lib/sorcery/crypto_providers/sha1.rb +28 -0
  39. data/lib/sorcery/crypto_providers/sha256.rb +36 -0
  40. data/lib/sorcery/crypto_providers/sha512.rb +36 -0
  41. data/lib/sorcery/engine.rb +21 -0
  42. data/lib/sorcery/model.rb +183 -0
  43. data/lib/sorcery/model/config.rb +96 -0
  44. data/lib/sorcery/model/submodules/activity_logging.rb +70 -0
  45. data/lib/sorcery/model/submodules/brute_force_protection.rb +125 -0
  46. data/lib/sorcery/model/submodules/external.rb +100 -0
  47. data/lib/sorcery/model/submodules/remember_me.rb +62 -0
  48. data/lib/sorcery/model/submodules/reset_password.rb +131 -0
  49. data/lib/sorcery/model/submodules/user_activation.rb +149 -0
  50. data/lib/sorcery/model/temporary_token.rb +30 -0
  51. data/lib/sorcery/protocols/certs/ca-bundle.crt +5182 -0
  52. data/lib/sorcery/protocols/oauth.rb +42 -0
  53. data/lib/sorcery/protocols/oauth2.rb +47 -0
  54. data/lib/sorcery/providers/base.rb +27 -0
  55. data/lib/sorcery/providers/facebook.rb +63 -0
  56. data/lib/sorcery/providers/github.rb +51 -0
  57. data/lib/sorcery/providers/google.rb +51 -0
  58. data/lib/sorcery/providers/jira.rb +77 -0
  59. data/lib/sorcery/providers/linkedin.rb +66 -0
  60. data/lib/sorcery/providers/liveid.rb +53 -0
  61. data/lib/sorcery/providers/twitter.rb +59 -0
  62. data/lib/sorcery/providers/vk.rb +63 -0
  63. data/lib/sorcery/providers/xing.rb +64 -0
  64. data/lib/sorcery/railties/tasks.rake +6 -0
  65. data/lib/sorcery/test_helpers/internal.rb +78 -0
  66. data/lib/sorcery/test_helpers/internal/rails.rb +68 -0
  67. data/lib/sorcery/test_helpers/rails/controller.rb +21 -0
  68. data/lib/sorcery/test_helpers/rails/integration.rb +26 -0
  69. data/lib/sorcery/version.rb +3 -0
  70. data/sorcery.gemspec +34 -0
  71. data/spec/active_record/user_activation_spec.rb +18 -0
  72. data/spec/active_record/user_activity_logging_spec.rb +17 -0
  73. data/spec/active_record/user_brute_force_protection_spec.rb +16 -0
  74. data/spec/active_record/user_oauth_spec.rb +16 -0
  75. data/spec/active_record/user_remember_me_spec.rb +16 -0
  76. data/spec/active_record/user_reset_password_spec.rb +16 -0
  77. data/spec/active_record/user_spec.rb +37 -0
  78. data/spec/controllers/controller_activity_logging_spec.rb +124 -0
  79. data/spec/controllers/controller_brute_force_protection_spec.rb +43 -0
  80. data/spec/controllers/controller_http_basic_auth_spec.rb +68 -0
  81. data/spec/controllers/controller_oauth2_spec.rb +407 -0
  82. data/spec/controllers/controller_oauth_spec.rb +240 -0
  83. data/spec/controllers/controller_remember_me_spec.rb +117 -0
  84. data/spec/controllers/controller_session_timeout_spec.rb +80 -0
  85. data/spec/controllers/controller_spec.rb +215 -0
  86. data/spec/orm/active_record.rb +21 -0
  87. data/spec/rails_app/app/active_record/authentication.rb +3 -0
  88. data/spec/rails_app/app/active_record/user.rb +5 -0
  89. data/spec/rails_app/app/active_record/user_provider.rb +3 -0
  90. data/spec/rails_app/app/controllers/sorcery_controller.rb +265 -0
  91. data/spec/rails_app/app/helpers/application_helper.rb +2 -0
  92. data/spec/rails_app/app/mailers/sorcery_mailer.rb +32 -0
  93. data/spec/rails_app/app/views/application/index.html.erb +17 -0
  94. data/spec/rails_app/app/views/layouts/application.html.erb +14 -0
  95. data/spec/rails_app/app/views/sorcery_mailer/activation_email.html.erb +17 -0
  96. data/spec/rails_app/app/views/sorcery_mailer/activation_email.text.erb +9 -0
  97. data/spec/rails_app/app/views/sorcery_mailer/activation_needed_email.html.erb +17 -0
  98. data/spec/rails_app/app/views/sorcery_mailer/activation_success_email.html.erb +17 -0
  99. data/spec/rails_app/app/views/sorcery_mailer/activation_success_email.text.erb +9 -0
  100. data/spec/rails_app/app/views/sorcery_mailer/reset_password_email.html.erb +16 -0
  101. data/spec/rails_app/app/views/sorcery_mailer/reset_password_email.text.erb +8 -0
  102. data/spec/rails_app/app/views/sorcery_mailer/send_unlock_token_email.text.erb +1 -0
  103. data/spec/rails_app/config.ru +4 -0
  104. data/spec/rails_app/config/application.rb +56 -0
  105. data/spec/rails_app/config/boot.rb +4 -0
  106. data/spec/rails_app/config/database.yml +22 -0
  107. data/spec/rails_app/config/environment.rb +5 -0
  108. data/spec/rails_app/config/environments/test.rb +37 -0
  109. data/spec/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  110. data/spec/rails_app/config/initializers/inflections.rb +10 -0
  111. data/spec/rails_app/config/initializers/mime_types.rb +5 -0
  112. data/spec/rails_app/config/initializers/secret_token.rb +7 -0
  113. data/spec/rails_app/config/initializers/session_store.rb +12 -0
  114. data/spec/rails_app/config/locales/en.yml +5 -0
  115. data/spec/rails_app/config/routes.rb +48 -0
  116. data/spec/rails_app/db/migrate/activation/20101224223622_add_activation_to_users.rb +17 -0
  117. data/spec/rails_app/db/migrate/activity_logging/20101224223624_add_activity_logging_to_users.rb +19 -0
  118. data/spec/rails_app/db/migrate/brute_force_protection/20101224223626_add_brute_force_protection_to_users.rb +13 -0
  119. data/spec/rails_app/db/migrate/core/20101224223620_create_users.rb +16 -0
  120. data/spec/rails_app/db/migrate/external/20101224223628_create_authentications_and_user_providers.rb +22 -0
  121. data/spec/rails_app/db/migrate/remember_me/20101224223623_add_remember_me_token_to_users.rb +15 -0
  122. data/spec/rails_app/db/migrate/reset_password/20101224223622_add_reset_password_to_users.rb +13 -0
  123. data/spec/rails_app/db/schema.rb +23 -0
  124. data/spec/rails_app/db/seeds.rb +7 -0
  125. data/spec/shared_examples/user_activation_shared_examples.rb +242 -0
  126. data/spec/shared_examples/user_activity_logging_shared_examples.rb +97 -0
  127. data/spec/shared_examples/user_brute_force_protection_shared_examples.rb +156 -0
  128. data/spec/shared_examples/user_oauth_shared_examples.rb +36 -0
  129. data/spec/shared_examples/user_remember_me_shared_examples.rb +57 -0
  130. data/spec/shared_examples/user_reset_password_shared_examples.rb +263 -0
  131. data/spec/shared_examples/user_shared_examples.rb +467 -0
  132. data/spec/sorcery_crypto_providers_spec.rb +198 -0
  133. data/spec/spec.opts +2 -0
  134. data/spec/spec_helper.rb +41 -0
  135. metadata +350 -0
@@ -0,0 +1,65 @@
1
+ module Sorcery
2
+ module Controller
3
+ module Config
4
+ class << self
5
+ attr_accessor :submodules,
6
+ :user_class, # what class to use as the user class.
7
+ :not_authenticated_action, # what controller action to call for non-authenticated users.
8
+
9
+ :save_return_to_url, # when a non logged in user tries to enter a page that requires
10
+ # login, save the URL he wanted to reach,
11
+ # and send him there after login.
12
+
13
+ :cookie_domain, # set domain option for cookies
14
+
15
+ :login_sources,
16
+ :after_login,
17
+ :after_failed_login,
18
+ :before_logout,
19
+ :after_logout
20
+
21
+ def init!
22
+ @defaults = {
23
+ :@user_class => nil,
24
+ :@submodules => [],
25
+ :@not_authenticated_action => :not_authenticated,
26
+ :@login_sources => [],
27
+ :@after_login => [],
28
+ :@after_failed_login => [],
29
+ :@before_logout => [],
30
+ :@after_logout => [],
31
+ :@save_return_to_url => true,
32
+ :@cookie_domain => nil
33
+ }
34
+ end
35
+
36
+ # Resets all configuration options to their default values.
37
+ def reset!
38
+ @defaults.each do |k,v|
39
+ instance_variable_set(k,v)
40
+ end
41
+ end
42
+
43
+ def update!
44
+ @defaults.each do |k,v|
45
+ instance_variable_set(k,v) if !instance_variable_defined?(k)
46
+ end
47
+ end
48
+
49
+ def user_config(&blk)
50
+ block_given? ? @user_config = blk : @user_config
51
+ end
52
+
53
+ def configure(&blk)
54
+ @configure_blk = blk
55
+ end
56
+
57
+ def configure!
58
+ @configure_blk.call(self) if @configure_blk
59
+ end
60
+ end
61
+ init!
62
+ reset!
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,82 @@
1
+ module Sorcery
2
+ module Controller
3
+ module Submodules
4
+ # This submodule keeps track of events such as login, logout,
5
+ # and last activity time, per user.
6
+ # It helps in estimating which users are active now in the site.
7
+ # This cannot be determined absolutely because a user might be
8
+ # reading a page without clicking anything for a while.
9
+ # This is the controller part of the submodule, which adds hooks
10
+ # to register user events,
11
+ # and methods to collect active users data for use in the app.
12
+ # see Socery::Model::Submodules::ActivityLogging for configuration
13
+ # options.
14
+ module ActivityLogging
15
+ def self.included(base)
16
+ base.send(:include, InstanceMethods)
17
+ Config.module_eval do
18
+ class << self
19
+ attr_accessor :register_login_time
20
+ attr_accessor :register_logout_time
21
+ attr_accessor :register_last_activity_time
22
+ attr_accessor :register_last_ip_address
23
+
24
+ def merge_activity_logging_defaults!
25
+ @defaults.merge!(:@register_login_time => true,
26
+ :@register_logout_time => true,
27
+ :@register_last_activity_time => true,
28
+ :@register_last_ip_address => true
29
+ )
30
+ end
31
+ end
32
+ merge_activity_logging_defaults!
33
+ end
34
+ Config.after_login << :register_login_time_to_db
35
+ Config.after_login << :register_last_ip_address
36
+ Config.before_logout << :register_logout_time_to_db
37
+ base.after_filter :register_last_activity_time_to_db
38
+ end
39
+
40
+ module InstanceMethods
41
+ # Returns an array of the active users.
42
+ def current_users
43
+ ActiveSupport::Deprecation.warn("Sorcery: `current_users` method is deprecated. Read more on Github: https://github.com/NoamB/sorcery/issues/602")
44
+
45
+ user_class.current_users
46
+ end
47
+
48
+ protected
49
+
50
+ # registers last login time on every login.
51
+ # This runs as a hook just after a successful login.
52
+ def register_login_time_to_db(user, credentials)
53
+ return unless Config.register_login_time
54
+ user.set_last_login_at(Time.now.in_time_zone)
55
+ end
56
+
57
+ # registers last logout time on every logout.
58
+ # This runs as a hook just before a logout.
59
+ def register_logout_time_to_db(user)
60
+ return unless Config.register_logout_time
61
+ user.set_last_logout_at(Time.now.in_time_zone)
62
+ end
63
+
64
+ # Updates last activity time on every request.
65
+ # The only exception is logout - we do not update activity on logout
66
+ def register_last_activity_time_to_db
67
+ return unless Config.register_last_activity_time
68
+ return unless logged_in?
69
+ current_user.set_last_activity_at(Time.now.in_time_zone)
70
+ end
71
+
72
+ # Updates IP address on every login.
73
+ # This runs as a hook just after a successful login.
74
+ def register_last_ip_address(user, credentials)
75
+ return unless Config.register_last_ip_address
76
+ current_user.set_last_ip_addess(request.remote_ip)
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,38 @@
1
+ module Sorcery
2
+ module Controller
3
+ module Submodules
4
+ # This module helps protect user accounts by locking them down after too
5
+ # many failed attemps to login were detected.
6
+ # This is the controller part of the submodule which takes care of
7
+ # updating the failed logins and resetting them.
8
+ # See Sorcery::Model::Submodules::BruteForceProtection for configuration
9
+ # options.
10
+ module BruteForceProtection
11
+ def self.included(base)
12
+ base.send(:include, InstanceMethods)
13
+
14
+ Config.after_login << :reset_failed_logins_count!
15
+ Config.after_failed_login << :update_failed_logins_count!
16
+ end
17
+
18
+ module InstanceMethods
19
+
20
+ protected
21
+
22
+ # Increments the failed logins counter on every failed login.
23
+ # Runs as a hook after a failed login.
24
+ def update_failed_logins_count!(credentials)
25
+ user = user_class.sorcery_adapter.find_by_credentials(credentials)
26
+ user.register_failed_login! if user
27
+ end
28
+
29
+ # Resets the failed logins counter.
30
+ # Runs as a hook after a successful login.
31
+ def reset_failed_logins_count!(user, credentials)
32
+ user.sorcery_adapter.update_attribute(user_class.sorcery_config.failed_logins_count_attribute_name, 0)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,199 @@
1
+ module Sorcery
2
+ module Controller
3
+ module Submodules
4
+ # This submodule helps you login users from external auth providers such as Twitter.
5
+ # This is the controller part which handles the http requests and tokens passed between the app and the @provider.
6
+ module External
7
+ def self.included(base)
8
+ base.send(:include, InstanceMethods)
9
+
10
+ require 'sorcery/providers/base'
11
+ require 'sorcery/providers/facebook'
12
+ require 'sorcery/providers/twitter'
13
+ require 'sorcery/providers/vk'
14
+ require 'sorcery/providers/linkedin'
15
+ require 'sorcery/providers/liveid'
16
+ require 'sorcery/providers/xing'
17
+ require 'sorcery/providers/github'
18
+ require 'sorcery/providers/google'
19
+ require 'sorcery/providers/jira'
20
+
21
+ Config.module_eval do
22
+ class << self
23
+ attr_reader :external_providers
24
+ attr_accessor :ca_file
25
+
26
+ def external_providers=(providers)
27
+ @external_providers = providers
28
+
29
+ providers.each do |name|
30
+ class_eval <<-E
31
+ def self.#{name}
32
+ @#{name} ||= Sorcery::Providers.const_get('#{name}'.to_s.capitalize).new
33
+ end
34
+ E
35
+ end
36
+ end
37
+
38
+ def merge_external_defaults!
39
+ @defaults.merge!(:@external_providers => [],
40
+ :@ca_file => File.join(File.expand_path(File.dirname(__FILE__)), '../../protocols/certs/ca-bundle.crt'))
41
+ end
42
+ end
43
+ merge_external_defaults!
44
+ end
45
+ end
46
+
47
+ module InstanceMethods
48
+ protected
49
+
50
+ # save the singleton ProviderClient instance into @provider
51
+ def sorcery_get_provider(provider_name)
52
+ return unless Config.external_providers.include?(provider_name.to_sym)
53
+ Config.send(provider_name.to_sym)
54
+ end
55
+
56
+ # get the login URL from the provider, if applicable. Returns nil if the provider
57
+ # does not provide a login URL. (as of v0.8.1 all providers provide a login URL)
58
+ def sorcery_login_url(provider_name, args = {})
59
+ @provider = sorcery_get_provider provider_name
60
+ sorcery_fixup_callback_url @provider
61
+ if @provider.respond_to?(:login_url) && @provider.has_callback?
62
+ @provider.state = args[:state] if args[:state]
63
+ return @provider.login_url(params, session)
64
+ else
65
+ return nil
66
+ end
67
+ end
68
+
69
+ # get the user hash from a provider using information from the params and session.
70
+ def sorcery_fetch_user_hash(provider_name)
71
+ # the application should never ask for user hashes from two different providers
72
+ # on the same request. But if they do, we should be ready: on the second request,
73
+ # clear out the instance variables if the provider is different
74
+ provider = sorcery_get_provider provider_name
75
+ if @provider.nil? || @provider != provider
76
+ @provider = provider
77
+ @access_token = nil
78
+ @user_hash = nil
79
+ end
80
+
81
+ # delegate to the provider for the access token and the user hash.
82
+ # cache them in instance variables.
83
+ @access_token ||= @provider.process_callback(params, session) # sends request to oauth agent to get the token
84
+ @user_hash ||= @provider.get_user_hash(@access_token) # uses the token to send another request to the oauth agent requesting user info
85
+ end
86
+
87
+ # for backwards compatibility
88
+ def access_token(*args)
89
+ @access_token
90
+ end
91
+
92
+
93
+ # this method should be somewhere else. It only does something once per application per provider.
94
+ def sorcery_fixup_callback_url(provider)
95
+ provider.original_callback_url ||= provider.callback_url
96
+ if provider.original_callback_url.present? && provider.original_callback_url[0] == '/'
97
+ uri = URI.parse(request.url.gsub(/\?.*$/,''))
98
+ uri.path = ''
99
+ uri.query = nil
100
+ uri.scheme = 'https' if(request.env['HTTP_X_FORWARDED_PROTO'] == 'https')
101
+ host = uri.to_s
102
+ provider.callback_url = "#{host}#{@provider.original_callback_url}"
103
+ end
104
+ end
105
+
106
+ # sends user to authenticate at the provider's website.
107
+ # after authentication the user is redirected to the callback defined in the provider config
108
+ def login_at(provider_name, args = {})
109
+ redirect_to sorcery_login_url(provider_name, args)
110
+ end
111
+
112
+ # tries to login the user from provider's callback
113
+ def login_from(provider_name, should_remember = false)
114
+ sorcery_fetch_user_hash provider_name
115
+
116
+ if user = user_class.load_from_provider(provider_name, @user_hash[:uid].to_s)
117
+ # we found the user.
118
+ # clear the session
119
+ return_to_url = session[:return_to_url]
120
+ reset_sorcery_session
121
+ session[:return_to_url] = return_to_url
122
+
123
+ # sign in the user
124
+ auto_login(user, should_remember)
125
+ after_login!(user)
126
+
127
+ # return the user
128
+ user
129
+ end
130
+ end
131
+
132
+ # If user is logged, he can add all available providers into his account
133
+ def add_provider_to_user(provider_name)
134
+ sorcery_fetch_user_hash provider_name
135
+ config = user_class.sorcery_config
136
+
137
+ current_user.add_provider_to_user(provider_name.to_s, @user_hash[:uid].to_s)
138
+ end
139
+
140
+ # Initialize new user from provider informations.
141
+ # If a provider doesn't give required informations or username/email is already taken,
142
+ # we store provider/user infos into a session and can be rendered into registration form
143
+ def create_and_validate_from(provider_name)
144
+ sorcery_fetch_user_hash provider_name
145
+ config = user_class.sorcery_config
146
+
147
+ attrs = user_attrs(@provider.user_info_mapping, @user_hash)
148
+
149
+ user, saved = user_class.create_and_validate_from_provider(provider_name, @user_hash[:uid], attrs)
150
+
151
+ session[:incomplete_user] = {
152
+ :provider => {config.provider_uid_attribute_name => @user_hash[:uid], config.provider_attribute_name => provider_name},
153
+ :user_hash => attrs
154
+ } unless saved
155
+
156
+ return user
157
+ end
158
+
159
+ # this method automatically creates a new user from the data in the external user hash.
160
+ # The mappings from user hash fields to user db fields are set at controller config.
161
+ # If the hash field you would like to map is nested, use slashes. For example, Given a hash like:
162
+ #
163
+ # "user" => {"name"=>"moishe"}
164
+ #
165
+ # You will set the mapping:
166
+ #
167
+ # {:username => "user/name"}
168
+ #
169
+ # And this will cause 'moishe' to be set as the value of :username field.
170
+ # Note: Be careful. This method skips validations model.
171
+ # Instead you can pass a block, if the block returns false the user will not be created
172
+ #
173
+ # create_from(provider) {|user| user.some_check }
174
+ #
175
+ def create_from(provider_name, &block)
176
+ sorcery_fetch_user_hash provider_name
177
+ config = user_class.sorcery_config
178
+
179
+ attrs = user_attrs(@provider.user_info_mapping, @user_hash)
180
+ @user = user_class.create_from_provider(provider_name, @user_hash[:uid], attrs, &block)
181
+ end
182
+
183
+ def user_attrs(user_info_mapping, user_hash)
184
+ attrs = {}
185
+ user_info_mapping.each do |k,v|
186
+ if (varr = v.split("/")).size > 1
187
+ attribute_value = varr.inject(user_hash[:user_info]) {|hash, value| hash[value]} rescue nil
188
+ attribute_value.nil? ? attrs : attrs.merge!(k => attribute_value)
189
+ else
190
+ attrs.merge!(k => user_hash[:user_info][v])
191
+ end
192
+ end
193
+ return attrs
194
+ end
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,74 @@
1
+ module Sorcery
2
+ module Controller
3
+ module Submodules
4
+ # This submodule integrates HTTP Basic authentication into sorcery.
5
+ # You are provided with a before filter, require_login_from_http_basic,
6
+ # which requests the browser for authentication.
7
+ # Then the rest of the submodule takes care of logging the user in
8
+ # into the session, so that the next requests will keep him logged in.
9
+ module HttpBasicAuth
10
+ def self.included(base)
11
+ base.send(:include, InstanceMethods)
12
+ Config.module_eval do
13
+ class << self
14
+ attr_accessor :controller_to_realm_map # What realm to display for which controller name.
15
+
16
+ def merge_http_basic_auth_defaults!
17
+ @defaults.merge!(:@controller_to_realm_map => {"application" => "Application"})
18
+ end
19
+ end
20
+ merge_http_basic_auth_defaults!
21
+ end
22
+ Config.login_sources << :login_from_basic_auth
23
+ end
24
+
25
+ module InstanceMethods
26
+
27
+ protected
28
+
29
+ # to be used as a before_filter.
30
+ # The method sets a session when requesting the user's credentials.
31
+ # This is a trick to overcome the way HTTP authentication works (explained below):
32
+ #
33
+ # Once the user fills the credentials once, the browser will always send it to the
34
+ # server when visiting the website, until the browser is closed.
35
+ # This causes wierd behaviour if the user logs out. The session is reset, yet the
36
+ # user is re-logged in by the before_filter calling 'login_from_basic_auth'.
37
+ # To overcome this, we set a session when requesting the password, which logout will
38
+ # reset, and that's how we know if we need to request for HTTP auth again.
39
+ def require_login_from_http_basic
40
+ (request_http_basic_authentication(realm_name_by_controller) and (session[:http_authentication_used] = true) and return) if (request.authorization.nil? || session[:http_authentication_used].nil?)
41
+ require_login
42
+ session[:http_authentication_used] = nil unless logged_in?
43
+ end
44
+
45
+ # given to main controller module as a login source callback
46
+ def login_from_basic_auth
47
+ authenticate_with_http_basic do |username, password|
48
+ @current_user = (user_class.authenticate(username, password) if session[:http_authentication_used]) || false
49
+ auto_login(@current_user) if @current_user
50
+ @current_user
51
+ end
52
+ end
53
+
54
+ # Sets the realm name by searching the controller name in the hash given at configuration time.
55
+ def realm_name_by_controller
56
+ if defined?(ActionController::Base)
57
+ current_controller = self.class
58
+ while current_controller != ActionController::Base
59
+ result = Config.controller_to_realm_map[current_controller.controller_name]
60
+ return result if result
61
+ current_controller = current_controller.superclass
62
+ end
63
+ nil
64
+ else
65
+ Config.controller_to_realm_map["application"]
66
+ end
67
+ end
68
+
69
+ end
70
+
71
+ end
72
+ end
73
+ end
74
+ end