cbsorcery 0.8.6

Sign up to get free protection for your applications and to get access to all the features.
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