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.
- data/.document +5 -0
- data/.gitignore +56 -0
- data/.rspec +1 -0
- data/.travis.yml +40 -0
- data/CHANGELOG.md +263 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +20 -0
- data/README.md +360 -0
- data/Rakefile +6 -0
- data/gemfiles/active_record-rails40.gemfile +7 -0
- data/gemfiles/active_record-rails41.gemfile +7 -0
- data/lib/generators/sorcery/USAGE +22 -0
- data/lib/generators/sorcery/helpers.rb +40 -0
- data/lib/generators/sorcery/install_generator.rb +95 -0
- data/lib/generators/sorcery/templates/initializer.rb +451 -0
- data/lib/generators/sorcery/templates/migration/activity_logging.rb +10 -0
- data/lib/generators/sorcery/templates/migration/brute_force_protection.rb +9 -0
- data/lib/generators/sorcery/templates/migration/core.rb +13 -0
- data/lib/generators/sorcery/templates/migration/external.rb +12 -0
- data/lib/generators/sorcery/templates/migration/remember_me.rb +8 -0
- data/lib/generators/sorcery/templates/migration/reset_password.rb +9 -0
- data/lib/generators/sorcery/templates/migration/user_activation.rb +9 -0
- data/lib/sorcery.rb +85 -0
- data/lib/sorcery/adapters/active_record_adapter.rb +120 -0
- data/lib/sorcery/adapters/base_adapter.rb +30 -0
- data/lib/sorcery/controller.rb +157 -0
- data/lib/sorcery/controller/config.rb +65 -0
- data/lib/sorcery/controller/submodules/activity_logging.rb +82 -0
- data/lib/sorcery/controller/submodules/brute_force_protection.rb +38 -0
- data/lib/sorcery/controller/submodules/external.rb +199 -0
- data/lib/sorcery/controller/submodules/http_basic_auth.rb +74 -0
- data/lib/sorcery/controller/submodules/remember_me.rb +81 -0
- data/lib/sorcery/controller/submodules/session_timeout.rb +56 -0
- data/lib/sorcery/crypto_providers/aes256.rb +51 -0
- data/lib/sorcery/crypto_providers/bcrypt.rb +97 -0
- data/lib/sorcery/crypto_providers/common.rb +35 -0
- data/lib/sorcery/crypto_providers/md5.rb +19 -0
- data/lib/sorcery/crypto_providers/sha1.rb +28 -0
- data/lib/sorcery/crypto_providers/sha256.rb +36 -0
- data/lib/sorcery/crypto_providers/sha512.rb +36 -0
- data/lib/sorcery/engine.rb +21 -0
- data/lib/sorcery/model.rb +183 -0
- data/lib/sorcery/model/config.rb +96 -0
- data/lib/sorcery/model/submodules/activity_logging.rb +70 -0
- data/lib/sorcery/model/submodules/brute_force_protection.rb +125 -0
- data/lib/sorcery/model/submodules/external.rb +100 -0
- data/lib/sorcery/model/submodules/remember_me.rb +62 -0
- data/lib/sorcery/model/submodules/reset_password.rb +131 -0
- data/lib/sorcery/model/submodules/user_activation.rb +149 -0
- data/lib/sorcery/model/temporary_token.rb +30 -0
- data/lib/sorcery/protocols/certs/ca-bundle.crt +5182 -0
- data/lib/sorcery/protocols/oauth.rb +42 -0
- data/lib/sorcery/protocols/oauth2.rb +47 -0
- data/lib/sorcery/providers/base.rb +27 -0
- data/lib/sorcery/providers/facebook.rb +63 -0
- data/lib/sorcery/providers/github.rb +51 -0
- data/lib/sorcery/providers/google.rb +51 -0
- data/lib/sorcery/providers/jira.rb +77 -0
- data/lib/sorcery/providers/linkedin.rb +66 -0
- data/lib/sorcery/providers/liveid.rb +53 -0
- data/lib/sorcery/providers/twitter.rb +59 -0
- data/lib/sorcery/providers/vk.rb +63 -0
- data/lib/sorcery/providers/xing.rb +64 -0
- data/lib/sorcery/railties/tasks.rake +6 -0
- data/lib/sorcery/test_helpers/internal.rb +78 -0
- data/lib/sorcery/test_helpers/internal/rails.rb +68 -0
- data/lib/sorcery/test_helpers/rails/controller.rb +21 -0
- data/lib/sorcery/test_helpers/rails/integration.rb +26 -0
- data/lib/sorcery/version.rb +3 -0
- data/sorcery.gemspec +34 -0
- data/spec/active_record/user_activation_spec.rb +18 -0
- data/spec/active_record/user_activity_logging_spec.rb +17 -0
- data/spec/active_record/user_brute_force_protection_spec.rb +16 -0
- data/spec/active_record/user_oauth_spec.rb +16 -0
- data/spec/active_record/user_remember_me_spec.rb +16 -0
- data/spec/active_record/user_reset_password_spec.rb +16 -0
- data/spec/active_record/user_spec.rb +37 -0
- data/spec/controllers/controller_activity_logging_spec.rb +124 -0
- data/spec/controllers/controller_brute_force_protection_spec.rb +43 -0
- data/spec/controllers/controller_http_basic_auth_spec.rb +68 -0
- data/spec/controllers/controller_oauth2_spec.rb +407 -0
- data/spec/controllers/controller_oauth_spec.rb +240 -0
- data/spec/controllers/controller_remember_me_spec.rb +117 -0
- data/spec/controllers/controller_session_timeout_spec.rb +80 -0
- data/spec/controllers/controller_spec.rb +215 -0
- data/spec/orm/active_record.rb +21 -0
- data/spec/rails_app/app/active_record/authentication.rb +3 -0
- data/spec/rails_app/app/active_record/user.rb +5 -0
- data/spec/rails_app/app/active_record/user_provider.rb +3 -0
- data/spec/rails_app/app/controllers/sorcery_controller.rb +265 -0
- data/spec/rails_app/app/helpers/application_helper.rb +2 -0
- data/spec/rails_app/app/mailers/sorcery_mailer.rb +32 -0
- data/spec/rails_app/app/views/application/index.html.erb +17 -0
- data/spec/rails_app/app/views/layouts/application.html.erb +14 -0
- data/spec/rails_app/app/views/sorcery_mailer/activation_email.html.erb +17 -0
- data/spec/rails_app/app/views/sorcery_mailer/activation_email.text.erb +9 -0
- data/spec/rails_app/app/views/sorcery_mailer/activation_needed_email.html.erb +17 -0
- data/spec/rails_app/app/views/sorcery_mailer/activation_success_email.html.erb +17 -0
- data/spec/rails_app/app/views/sorcery_mailer/activation_success_email.text.erb +9 -0
- data/spec/rails_app/app/views/sorcery_mailer/reset_password_email.html.erb +16 -0
- data/spec/rails_app/app/views/sorcery_mailer/reset_password_email.text.erb +8 -0
- data/spec/rails_app/app/views/sorcery_mailer/send_unlock_token_email.text.erb +1 -0
- data/spec/rails_app/config.ru +4 -0
- data/spec/rails_app/config/application.rb +56 -0
- data/spec/rails_app/config/boot.rb +4 -0
- data/spec/rails_app/config/database.yml +22 -0
- data/spec/rails_app/config/environment.rb +5 -0
- data/spec/rails_app/config/environments/test.rb +37 -0
- data/spec/rails_app/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/rails_app/config/initializers/inflections.rb +10 -0
- data/spec/rails_app/config/initializers/mime_types.rb +5 -0
- data/spec/rails_app/config/initializers/secret_token.rb +7 -0
- data/spec/rails_app/config/initializers/session_store.rb +12 -0
- data/spec/rails_app/config/locales/en.yml +5 -0
- data/spec/rails_app/config/routes.rb +48 -0
- data/spec/rails_app/db/migrate/activation/20101224223622_add_activation_to_users.rb +17 -0
- data/spec/rails_app/db/migrate/activity_logging/20101224223624_add_activity_logging_to_users.rb +19 -0
- data/spec/rails_app/db/migrate/brute_force_protection/20101224223626_add_brute_force_protection_to_users.rb +13 -0
- data/spec/rails_app/db/migrate/core/20101224223620_create_users.rb +16 -0
- data/spec/rails_app/db/migrate/external/20101224223628_create_authentications_and_user_providers.rb +22 -0
- data/spec/rails_app/db/migrate/remember_me/20101224223623_add_remember_me_token_to_users.rb +15 -0
- data/spec/rails_app/db/migrate/reset_password/20101224223622_add_reset_password_to_users.rb +13 -0
- data/spec/rails_app/db/schema.rb +23 -0
- data/spec/rails_app/db/seeds.rb +7 -0
- data/spec/shared_examples/user_activation_shared_examples.rb +242 -0
- data/spec/shared_examples/user_activity_logging_shared_examples.rb +97 -0
- data/spec/shared_examples/user_brute_force_protection_shared_examples.rb +156 -0
- data/spec/shared_examples/user_oauth_shared_examples.rb +36 -0
- data/spec/shared_examples/user_remember_me_shared_examples.rb +57 -0
- data/spec/shared_examples/user_reset_password_shared_examples.rb +263 -0
- data/spec/shared_examples/user_shared_examples.rb +467 -0
- data/spec/sorcery_crypto_providers_spec.rb +198 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +41 -0
- 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
|