sorcery 0.7.6 → 0.8.0
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/.travis.yml +3 -0
- data/Gemfile +9 -4
- data/Gemfile.lock +99 -58
- data/README.rdoc +33 -8
- data/Rakefile +6 -7
- data/VERSION +1 -1
- data/lib/generators/sorcery/install_generator.rb +9 -7
- data/lib/generators/sorcery/templates/initializer.rb +324 -127
- data/lib/generators/sorcery/templates/migration/brute_force_protection.rb +2 -0
- data/lib/sorcery/controller/submodules/brute_force_protection.rb +1 -2
- data/lib/sorcery/controller/submodules/external/protocols/oauth2.rb +2 -2
- data/lib/sorcery/controller/submodules/external/providers/facebook.rb +16 -4
- data/lib/sorcery/controller/submodules/external/providers/github.rb +6 -4
- data/lib/sorcery/controller/submodules/external/providers/google.rb +4 -4
- data/lib/sorcery/controller/submodules/external/providers/linkedin.rb +101 -0
- data/lib/sorcery/controller/submodules/external/providers/liveid.rb +1 -1
- data/lib/sorcery/controller/submodules/external/providers/vkontakte.rb +94 -0
- data/lib/sorcery/controller/submodules/external.rb +83 -15
- data/lib/sorcery/controller/submodules/http_basic_auth.rb +1 -1
- data/lib/sorcery/controller/submodules/remember_me.rb +11 -2
- data/lib/sorcery/controller.rb +14 -6
- data/lib/sorcery/crypto_providers/bcrypt.rb +1 -0
- data/lib/sorcery/model/adapters/active_record.rb +9 -4
- data/lib/sorcery/model/adapters/mongo_mapper.rb +16 -14
- data/lib/sorcery/model/adapters/mongoid.rb +12 -6
- data/lib/sorcery/model/submodules/brute_force_protection.rb +44 -13
- data/lib/sorcery/model/submodules/remember_me.rb +4 -6
- data/lib/sorcery/model/submodules/reset_password.rb +14 -7
- data/lib/sorcery/model/submodules/user_activation.rb +14 -6
- data/lib/sorcery/model.rb +40 -39
- data/lib/sorcery/railties/tasks.rake +2 -0
- data/lib/sorcery.rb +3 -1
- data/sorcery.gemspec +31 -141
- data/spec/Gemfile +1 -1
- data/spec/Gemfile.lock +20 -19
- data/spec/README.md +8 -8
- data/spec/rails3/Gemfile +1 -0
- data/spec/rails3/Gemfile.lock +27 -24
- data/spec/rails3/app/controllers/application_controller.rb +54 -0
- data/spec/rails3/app/mailers/sorcery_mailer.rb +7 -0
- data/spec/rails3/app/views/sorcery_mailer/send_unlock_token_email.text.erb +1 -0
- data/spec/rails3/db/migrate/brute_force_protection/20101224223626_add_brute_force_protection_to_users.rb +2 -0
- data/spec/rails3/spec/controller_activity_logging_spec.rb +3 -0
- data/spec/rails3/spec/controller_brute_force_protection_spec.rb +23 -1
- data/spec/rails3/spec/controller_oauth2_spec.rb +162 -24
- data/spec/rails3/spec/controller_oauth_spec.rb +111 -6
- data/spec/rails3/spec/controller_spec.rb +7 -0
- data/spec/rails3_mongo_mapper/Gemfile +2 -1
- data/spec/rails3_mongo_mapper/Gemfile.lock +37 -39
- data/spec/rails3_mongo_mapper/app/controllers/application_controller.rb +14 -0
- data/spec/rails3_mongo_mapper/spec/controller_spec.rb +7 -0
- data/spec/rails3_mongoid/Gemfile +1 -0
- data/spec/rails3_mongoid/Gemfile.lock +28 -25
- data/spec/rails3_mongoid/app/controllers/application_controller.rb +14 -0
- data/spec/rails3_mongoid/config/mongoid.yml +1 -1
- data/spec/rails3_mongoid/spec/controller_activity_logging_spec.rb +11 -11
- data/spec/rails3_mongoid/spec/controller_spec.rb +7 -0
- data/spec/shared_examples/controller_oauth2_shared_examples.rb +20 -1
- data/spec/shared_examples/controller_oauth_shared_examples.rb +18 -0
- data/spec/shared_examples/user_activation_shared_examples.rb +71 -41
- data/spec/shared_examples/user_reset_password_shared_examples.rb +76 -31
- data/spec/sorcery_crypto_providers_spec.rb +9 -0
- metadata +159 -246
|
@@ -34,6 +34,7 @@ module Sorcery
|
|
|
34
34
|
:auth_path,
|
|
35
35
|
:token_path,
|
|
36
36
|
:site,
|
|
37
|
+
:scope,
|
|
37
38
|
:user_info_path,
|
|
38
39
|
:user_info_mapping
|
|
39
40
|
attr_reader :access_token
|
|
@@ -43,6 +44,7 @@ module Sorcery
|
|
|
43
44
|
def init
|
|
44
45
|
@site = "https://github.com/"
|
|
45
46
|
@user_info_path = "https://api.github.com/user"
|
|
47
|
+
@scope = nil
|
|
46
48
|
@auth_path = "/login/oauth/authorize"
|
|
47
49
|
@token_path = "/login/oauth/access_token"
|
|
48
50
|
@user_info_mapping = {}
|
|
@@ -51,7 +53,7 @@ module Sorcery
|
|
|
51
53
|
def get_user_hash
|
|
52
54
|
user_hash = {}
|
|
53
55
|
response = @access_token.get(@user_info_path)
|
|
54
|
-
user_hash[:user_info] = JSON.parse(response)
|
|
56
|
+
user_hash[:user_info] = JSON.parse(response.body)
|
|
55
57
|
user_hash[:uid] = user_hash[:user_info]['id']
|
|
56
58
|
user_hash
|
|
57
59
|
end
|
|
@@ -63,7 +65,7 @@ module Sorcery
|
|
|
63
65
|
# calculates and returns the url to which the user should be redirected,
|
|
64
66
|
# to get authenticated at the external provider's site.
|
|
65
67
|
def login_url(params,session)
|
|
66
|
-
self.authorize_url({:
|
|
68
|
+
self.authorize_url({:authorize_url => @auth_path})
|
|
67
69
|
end
|
|
68
70
|
|
|
69
71
|
# tries to login the user from access token
|
|
@@ -71,8 +73,8 @@ module Sorcery
|
|
|
71
73
|
args = {}
|
|
72
74
|
args.merge!({:code => params[:code]}) if params[:code]
|
|
73
75
|
options = {
|
|
74
|
-
:
|
|
75
|
-
:
|
|
76
|
+
:token_url => @token_path,
|
|
77
|
+
:token_method => :post
|
|
76
78
|
}
|
|
77
79
|
@access_token = self.get_access_token(args, options)
|
|
78
80
|
end
|
|
@@ -44,7 +44,7 @@ module Sorcery
|
|
|
44
44
|
def init
|
|
45
45
|
@site = "https://accounts.google.com"
|
|
46
46
|
@auth_url = "/o/oauth2/auth"
|
|
47
|
-
@
|
|
47
|
+
@token_url = "/o/oauth2/token"
|
|
48
48
|
@user_info_url = "https://www.googleapis.com/oauth2/v1/userinfo"
|
|
49
49
|
@scope = "https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile"
|
|
50
50
|
@user_info_mapping = {}
|
|
@@ -53,7 +53,7 @@ module Sorcery
|
|
|
53
53
|
def get_user_hash
|
|
54
54
|
user_hash = {}
|
|
55
55
|
response = @access_token.get(@user_info_url)
|
|
56
|
-
user_hash[:user_info] = JSON.parse(response)
|
|
56
|
+
user_hash[:user_info] = JSON.parse(response.body)
|
|
57
57
|
user_hash[:uid] = user_hash[:user_info]['id']
|
|
58
58
|
user_hash
|
|
59
59
|
end
|
|
@@ -73,8 +73,8 @@ module Sorcery
|
|
|
73
73
|
args = {}
|
|
74
74
|
args.merge!({:code => params[:code]}) if params[:code]
|
|
75
75
|
options = {
|
|
76
|
-
:
|
|
77
|
-
:
|
|
76
|
+
:token_url => @token_url,
|
|
77
|
+
:token_method => :post
|
|
78
78
|
}
|
|
79
79
|
@access_token = self.get_access_token(args, options)
|
|
80
80
|
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
module Sorcery
|
|
2
|
+
module Controller
|
|
3
|
+
module Submodules
|
|
4
|
+
module External
|
|
5
|
+
module Providers
|
|
6
|
+
# This module adds support for OAuth with Linkedin.com.
|
|
7
|
+
# When included in the 'config.providers' option, it adds a new option, 'config.linkedin'.
|
|
8
|
+
# Via this new option you can configure Linkedin specific settings like your app's key and secret.
|
|
9
|
+
#
|
|
10
|
+
# config.linkedin.key = <key>
|
|
11
|
+
# config.linkedin.secret = <secret>
|
|
12
|
+
# ...
|
|
13
|
+
#
|
|
14
|
+
module Linkedin
|
|
15
|
+
def self.included(base)
|
|
16
|
+
base.module_eval do
|
|
17
|
+
class << self
|
|
18
|
+
attr_reader :linkedin
|
|
19
|
+
|
|
20
|
+
def merge_linkedin_defaults!
|
|
21
|
+
@defaults.merge!(:@linkedin => LinkedinClient)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
merge_linkedin_defaults!
|
|
25
|
+
update!
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
module LinkedinClient
|
|
30
|
+
class << self
|
|
31
|
+
attr_accessor :key,
|
|
32
|
+
:secret,
|
|
33
|
+
:callback_url,
|
|
34
|
+
:site,
|
|
35
|
+
:authorize_path,
|
|
36
|
+
:request_token_path,
|
|
37
|
+
:access_token_path,
|
|
38
|
+
:user_info_path,
|
|
39
|
+
:user_info_mapping,
|
|
40
|
+
:user_info_fields,
|
|
41
|
+
:access_permissions
|
|
42
|
+
attr_reader :access_token
|
|
43
|
+
|
|
44
|
+
include Protocols::Oauth1
|
|
45
|
+
|
|
46
|
+
# Override included get_consumer method to provide authorize_path
|
|
47
|
+
def get_consumer
|
|
48
|
+
# Add access permissions to request token path
|
|
49
|
+
@configuration[:request_token_path] += "?scope=" + self.access_permissions.join('+') unless self.access_permissions.blank? or @configuration[:request_token_path].include? "?scope="
|
|
50
|
+
::OAuth::Consumer.new(@key, @secret, @configuration)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def init
|
|
54
|
+
@configuration = {
|
|
55
|
+
site: "https://api.linkedin.com",
|
|
56
|
+
authorize_path: '/uas/oauth/authenticate',
|
|
57
|
+
request_token_path: '/uas/oauth/requestToken',
|
|
58
|
+
access_token_path: '/uas/oauth/accessToken'
|
|
59
|
+
}
|
|
60
|
+
@user_info_path = "/v1/people/~"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def get_user_hash
|
|
64
|
+
user_hash = {}
|
|
65
|
+
fields = self.user_info_fields.join(',')
|
|
66
|
+
response = @access_token.get("#{@user_info_path}:(#{fields})", 'x-li-format' => 'json')
|
|
67
|
+
user_hash[:user_info] = JSON.parse(response.body)
|
|
68
|
+
user_hash[:uid] = user_hash[:user_info]['id'].to_s
|
|
69
|
+
user_hash
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def has_callback?
|
|
73
|
+
true
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# calculates and returns the url to which the user should be redirected,
|
|
77
|
+
# to get authenticated at the external provider's site.
|
|
78
|
+
def login_url(params,session)
|
|
79
|
+
req_token = self.get_request_token
|
|
80
|
+
session[:request_token] = req_token.token
|
|
81
|
+
session[:request_token_secret] = req_token.secret
|
|
82
|
+
self.authorize_url({:request_token => req_token.token, :request_token_secret => req_token.secret})
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# tries to login the user from access token
|
|
86
|
+
def process_callback(params,session)
|
|
87
|
+
args = {}
|
|
88
|
+
args.merge!({:oauth_verifier => params[:oauth_verifier], :request_token => session[:request_token], :request_token_secret => session[:request_token_secret]})
|
|
89
|
+
args.merge!({:code => params[:code]}) if params[:code]
|
|
90
|
+
@access_token = self.get_access_token(args)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
end
|
|
94
|
+
init
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -54,7 +54,7 @@ module Sorcery
|
|
|
54
54
|
user_hash = {}
|
|
55
55
|
@access_token.token_param = "access_token"
|
|
56
56
|
response = @access_token.get(@user_info_url)
|
|
57
|
-
user_hash[:user_info] = JSON.parse(response)
|
|
57
|
+
user_hash[:user_info] = JSON.parse(response.body)
|
|
58
58
|
user_hash[:uid] = user_hash[:user_info]['id']
|
|
59
59
|
user_hash
|
|
60
60
|
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
module Sorcery
|
|
2
|
+
module Controller
|
|
3
|
+
module Submodules
|
|
4
|
+
module External
|
|
5
|
+
module Providers
|
|
6
|
+
# This module adds support for OAuth with vkontakte.com.
|
|
7
|
+
# When included in the 'config.providers' option, it adds a new option, 'config.vkontakte'.
|
|
8
|
+
# Via this new option you can configure Vkontakte specific settings like your app's key and secret.
|
|
9
|
+
#
|
|
10
|
+
# config.vkontakte.key = <key>
|
|
11
|
+
# config.vkontakte.secret = <secret>
|
|
12
|
+
# ...
|
|
13
|
+
#
|
|
14
|
+
module Vkontakte
|
|
15
|
+
def self.included(base)
|
|
16
|
+
base.module_eval do
|
|
17
|
+
class << self
|
|
18
|
+
attr_reader :vkontakte # access to vkontakte_client.
|
|
19
|
+
|
|
20
|
+
def merge_vkontakte_defaults!
|
|
21
|
+
@defaults.merge!(:@vkontakte => VkontakteClient)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
merge_vkontakte_defaults!
|
|
25
|
+
update!
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
module VkontakteClient
|
|
30
|
+
class << self
|
|
31
|
+
attr_accessor :key,
|
|
32
|
+
:secret,
|
|
33
|
+
:callback_url,
|
|
34
|
+
:auth_path,
|
|
35
|
+
:token_path,
|
|
36
|
+
:site,
|
|
37
|
+
:scope,
|
|
38
|
+
:user_info_path,
|
|
39
|
+
:user_info_mapping
|
|
40
|
+
attr_reader :access_token
|
|
41
|
+
|
|
42
|
+
include Protocols::Oauth2
|
|
43
|
+
|
|
44
|
+
def init
|
|
45
|
+
@site = "https://oauth.vk.com/"
|
|
46
|
+
@user_info_url = "https://api.vk.com/method/getUserInfo"
|
|
47
|
+
@scope = nil
|
|
48
|
+
@auth_path = "/authorize"
|
|
49
|
+
@token_path = "/access_token"
|
|
50
|
+
@user_info_mapping = {}
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def get_user_hash
|
|
54
|
+
user_hash = {}
|
|
55
|
+
response = @access_token.get("#{@user_info_url}?access_token=#{@access_token.token}")
|
|
56
|
+
user_hash[:user_info] = JSON.parse(response.body)
|
|
57
|
+
if user_hash[:user_info]
|
|
58
|
+
user_hash[:user_info] = user_hash[:user_info]["response"]
|
|
59
|
+
end
|
|
60
|
+
user_hash[:uid] = user_hash[:user_info]['user_id']
|
|
61
|
+
user_hash
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def has_callback?
|
|
65
|
+
true
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# calculates and returns the url to which the user should be redirected,
|
|
69
|
+
# to get authenticated at the external provider's site.
|
|
70
|
+
def login_url(params,session)
|
|
71
|
+
self.authorize_url({:authorize_url => @auth_path})
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# tries to login the user from access token
|
|
75
|
+
def process_callback(params,session)
|
|
76
|
+
args = {}
|
|
77
|
+
args.merge!({:code => params[:code]}) if params[:code]
|
|
78
|
+
options = {
|
|
79
|
+
:token_url => @token_path,
|
|
80
|
+
:token_method => :post
|
|
81
|
+
}
|
|
82
|
+
@access_token = self.get_access_token(args, options)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
end
|
|
86
|
+
init
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -10,12 +10,12 @@ module Sorcery
|
|
|
10
10
|
class << self
|
|
11
11
|
attr_reader :external_providers # external providers like twitter.
|
|
12
12
|
attr_accessor :ca_file # path to ca_file. By default use a internal ca-bundle.crt.
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
def merge_external_defaults!
|
|
15
15
|
@defaults.merge!(:@external_providers => [],
|
|
16
16
|
:@ca_file => File.join(File.expand_path(File.dirname(__FILE__)), 'external/protocols/certs/ca-bundle.crt'))
|
|
17
17
|
end
|
|
18
|
-
|
|
18
|
+
|
|
19
19
|
def external_providers=(providers)
|
|
20
20
|
providers.each do |provider|
|
|
21
21
|
include Providers.const_get(provider.to_s.split("_").map {|p| p.capitalize}.join(""))
|
|
@@ -28,26 +28,37 @@ module Sorcery
|
|
|
28
28
|
|
|
29
29
|
module InstanceMethods
|
|
30
30
|
protected
|
|
31
|
-
|
|
31
|
+
|
|
32
32
|
# sends user to authenticate at the provider's website.
|
|
33
33
|
# after authentication the user is redirected to the callback defined in the provider config
|
|
34
34
|
def login_at(provider, args = {})
|
|
35
35
|
@provider = Config.send(provider)
|
|
36
|
+
if @provider.callback_url.present? && @provider.callback_url[0] == '/'
|
|
37
|
+
uri = URI.parse(request.url.gsub(/\?.*$/,''))
|
|
38
|
+
uri.path = ''
|
|
39
|
+
uri.query = nil
|
|
40
|
+
uri.scheme = 'https' if(request.env['HTTP_X_FORWARDED_PROTO'] == 'https')
|
|
41
|
+
host = uri.to_s
|
|
42
|
+
@provider.callback_url = "#{host}#{@provider.callback_url}"
|
|
43
|
+
end
|
|
36
44
|
if @provider.has_callback?
|
|
37
45
|
redirect_to @provider.login_url(params,session)
|
|
38
46
|
else
|
|
39
47
|
#@provider.login(args)
|
|
40
48
|
end
|
|
41
49
|
end
|
|
42
|
-
|
|
50
|
+
|
|
43
51
|
# tries to login the user from provider's callback
|
|
44
52
|
def login_from(provider)
|
|
45
53
|
@provider = Config.send(provider)
|
|
46
54
|
@provider.process_callback(params,session)
|
|
47
55
|
@user_hash = @provider.get_user_hash
|
|
48
|
-
if user = user_class.load_from_provider(provider,@user_hash[:uid])
|
|
56
|
+
if user = user_class.load_from_provider(provider,@user_hash[:uid].to_s)
|
|
57
|
+
return_to_url = session[:return_to_url]
|
|
49
58
|
reset_session
|
|
59
|
+
session[:return_to_url] = return_to_url
|
|
50
60
|
auto_login(user)
|
|
61
|
+
after_login!(user)
|
|
51
62
|
user
|
|
52
63
|
end
|
|
53
64
|
end
|
|
@@ -57,7 +68,48 @@ module Sorcery
|
|
|
57
68
|
@provider = Config.send(provider)
|
|
58
69
|
@provider.access_token
|
|
59
70
|
end
|
|
60
|
-
|
|
71
|
+
|
|
72
|
+
# If user is logged, he can add all available providers into his account
|
|
73
|
+
def add_provider_to_user(provider)
|
|
74
|
+
provider_name = provider.to_sym
|
|
75
|
+
@provider = Config.send(provider_name)
|
|
76
|
+
@provider.process_callback(params,session)
|
|
77
|
+
@user_hash = @provider.get_user_hash
|
|
78
|
+
config = user_class.sorcery_config
|
|
79
|
+
|
|
80
|
+
# first check to see if user has a particular authentication already
|
|
81
|
+
unless (current_user.send(config.authentications_class.to_s.downcase.pluralize).send("find_by_#{config.provider_attribute_name}_and_#{config.provider_uid_attribute_name}", provider, @user_hash[:uid].to_s))
|
|
82
|
+
user = current_user.send(config.authentications_class.to_s.downcase.pluralize).build(config.provider_uid_attribute_name => @user_hash[:uid], config.provider_attribute_name => provider_name.to_s)
|
|
83
|
+
user.save(:validate => false)
|
|
84
|
+
else
|
|
85
|
+
user = false
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
return user
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Initialize new user from provider informations.
|
|
92
|
+
# If a provider doesn't give required informations or username/email is already taken,
|
|
93
|
+
# we store provider/user infos into a session and can be rendered into registration form
|
|
94
|
+
def create_and_validate_from(provider)
|
|
95
|
+
provider = provider.to_sym
|
|
96
|
+
@provider = Config.send(provider)
|
|
97
|
+
@user_hash = @provider.get_user_hash
|
|
98
|
+
config = user_class.sorcery_config
|
|
99
|
+
|
|
100
|
+
attrs = user_attrs(@provider.user_info_mapping, @user_hash)
|
|
101
|
+
|
|
102
|
+
user = user_class.new(attrs)
|
|
103
|
+
user.send(config.authentications_class.to_s.downcase.pluralize).build(config.provider_uid_attribute_name => @user_hash[:uid], config.provider_attribute_name => provider)
|
|
104
|
+
|
|
105
|
+
session[:incomplete_user] = {
|
|
106
|
+
:provider => {config.provider_uid_attribute_name => @user_hash[:uid], config.provider_attribute_name => provider},
|
|
107
|
+
:user_hash => attrs
|
|
108
|
+
} unless user.save
|
|
109
|
+
|
|
110
|
+
return user
|
|
111
|
+
end
|
|
112
|
+
|
|
61
113
|
# this method automatically creates a new user from the data in the external user hash.
|
|
62
114
|
# The mappings from user hash fields to user db fields are set at controller config.
|
|
63
115
|
# If the hash field you would like to map is nested, use slashes. For example, Given a hash like:
|
|
@@ -70,30 +122,46 @@ module Sorcery
|
|
|
70
122
|
#
|
|
71
123
|
# And this will cause 'moishe' to be set as the value of :username field.
|
|
72
124
|
# Note: Be careful. This method skips validations model.
|
|
125
|
+
# Instead you can pass a block, if the block returns false the user will not be created
|
|
126
|
+
#
|
|
127
|
+
# create_from(provider) {|user| user.some_check }
|
|
128
|
+
#
|
|
73
129
|
def create_from(provider)
|
|
74
130
|
provider = provider.to_sym
|
|
75
131
|
@provider = Config.send(provider)
|
|
76
132
|
@user_hash = @provider.get_user_hash
|
|
77
133
|
config = user_class.sorcery_config
|
|
78
|
-
|
|
79
|
-
@provider.user_info_mapping
|
|
80
|
-
|
|
81
|
-
attribute_value = varr.inject(@user_hash[:user_info]) {|hsh,v| hsh[v] } rescue nil
|
|
82
|
-
attribute_value.nil? ? attrs : attrs.merge!(k => attribute_value)
|
|
83
|
-
else
|
|
84
|
-
attrs.merge!(k => @user_hash[:user_info][v])
|
|
85
|
-
end
|
|
86
|
-
end
|
|
134
|
+
|
|
135
|
+
attrs = user_attrs(@provider.user_info_mapping, @user_hash)
|
|
136
|
+
|
|
87
137
|
user_class.transaction do
|
|
88
138
|
@user = user_class.new()
|
|
89
139
|
attrs.each do |k,v|
|
|
90
140
|
@user.send(:"#{k}=", v)
|
|
91
141
|
end
|
|
142
|
+
|
|
143
|
+
if block_given?
|
|
144
|
+
return false unless yield @user
|
|
145
|
+
end
|
|
146
|
+
|
|
92
147
|
@user.save(:validate => false)
|
|
93
148
|
user_class.sorcery_config.authentications_class.create!({config.authentications_user_id_attribute_name => @user.id, config.provider_attribute_name => provider, config.provider_uid_attribute_name => @user_hash[:uid]})
|
|
94
149
|
end
|
|
95
150
|
@user
|
|
96
151
|
end
|
|
152
|
+
|
|
153
|
+
def user_attrs(user_info_mapping, user_hash)
|
|
154
|
+
attrs = {}
|
|
155
|
+
user_info_mapping.each do |k,v|
|
|
156
|
+
if (varr = v.split("/")).size > 1
|
|
157
|
+
attribute_value = varr.inject(user_hash[:user_info]) {|hash, value| hash[value]} rescue nil
|
|
158
|
+
attribute_value.nil? ? attrs : attrs.merge!(k => attribute_value)
|
|
159
|
+
else
|
|
160
|
+
attrs.merge!(k => user_hash[:user_info][v])
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
return attrs
|
|
164
|
+
end
|
|
97
165
|
end
|
|
98
166
|
end
|
|
99
167
|
end
|
|
@@ -58,7 +58,7 @@ module Sorcery
|
|
|
58
58
|
while current_controller != ActionController::Base
|
|
59
59
|
result = Config.controller_to_realm_map[current_controller.controller_name]
|
|
60
60
|
return result if result
|
|
61
|
-
current_controller =
|
|
61
|
+
current_controller = current_controller.superclass
|
|
62
62
|
end
|
|
63
63
|
nil
|
|
64
64
|
else
|
|
@@ -8,6 +8,15 @@ module Sorcery
|
|
|
8
8
|
module RememberMe
|
|
9
9
|
def self.included(base)
|
|
10
10
|
base.send(:include, InstanceMethods)
|
|
11
|
+
Config.module_eval do
|
|
12
|
+
class << self
|
|
13
|
+
attr_accessor :remember_me_httponly
|
|
14
|
+
def merge_remember_me_defaults!
|
|
15
|
+
@defaults.merge!(:@remember_me_httponly => true)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
merge_remember_me_defaults!
|
|
19
|
+
end
|
|
11
20
|
Config.login_sources << :login_from_cookie
|
|
12
21
|
Config.after_login << :remember_me_if_asked_to
|
|
13
22
|
Config.after_logout << :forget_me!
|
|
@@ -60,7 +69,7 @@ module Sorcery
|
|
|
60
69
|
cookies.signed[:remember_me_token] = {
|
|
61
70
|
:value => user.send(user.sorcery_config.remember_me_token_attribute_name),
|
|
62
71
|
:expires => user.send(user.sorcery_config.remember_me_token_expires_at_attribute_name),
|
|
63
|
-
:httponly =>
|
|
72
|
+
:httponly => Config.remember_me_httponly,
|
|
64
73
|
:domain => Config.cookie_domain
|
|
65
74
|
}
|
|
66
75
|
end
|
|
@@ -69,4 +78,4 @@ module Sorcery
|
|
|
69
78
|
end
|
|
70
79
|
end
|
|
71
80
|
end
|
|
72
|
-
end
|
|
81
|
+
end
|
data/lib/sorcery/controller.rb
CHANGED
|
@@ -21,19 +21,22 @@ module Sorcery
|
|
|
21
21
|
# If all attempts to auto-login fail, the failure callback will be called.
|
|
22
22
|
def require_login
|
|
23
23
|
if !logged_in?
|
|
24
|
-
session[:return_to_url] = request.url if Config.save_return_to_url
|
|
24
|
+
session[:return_to_url] = request.url if Config.save_return_to_url && request.get?
|
|
25
25
|
self.send(Config.not_authenticated_action)
|
|
26
26
|
end
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
# Takes credentials and returns a user on successful authentication.
|
|
30
30
|
# Runs hooks after login or failed login.
|
|
31
|
-
|
|
31
|
+
def login(*credentials)
|
|
32
|
+
@current_user = nil
|
|
32
33
|
user = user_class.authenticate(*credentials)
|
|
33
34
|
if user
|
|
34
|
-
|
|
35
|
+
old_session = session.dup
|
|
35
36
|
reset_session # protect from session fixation attacks
|
|
36
|
-
|
|
37
|
+
old_session.to_hash.each_pair do |k,v|
|
|
38
|
+
session[k.to_sym] = v
|
|
39
|
+
end
|
|
37
40
|
auto_login(user)
|
|
38
41
|
after_login!(user, credentials)
|
|
39
42
|
current_user
|
|
@@ -71,6 +74,7 @@ module Sorcery
|
|
|
71
74
|
# and we want to return him back to the page he originally wanted.
|
|
72
75
|
def redirect_back_or_to(url, flash_hash = {})
|
|
73
76
|
redirect_to(session[:return_to_url] || url, :flash => flash_hash)
|
|
77
|
+
session[:return_to_url] = nil
|
|
74
78
|
end
|
|
75
79
|
|
|
76
80
|
# The default action for denying non-authenticated users.
|
|
@@ -108,10 +112,14 @@ module Sorcery
|
|
|
108
112
|
end
|
|
109
113
|
|
|
110
114
|
def login_from_session
|
|
111
|
-
@current_user = (user_class.
|
|
115
|
+
@current_user = (user_class.find(session[:user_id]) if session[:user_id]) || false
|
|
116
|
+
rescue => exception
|
|
117
|
+
return false if defined?(Mongoid) and exception.is_a?(Mongoid::Errors::DocumentNotFound)
|
|
118
|
+
return false if defined?(ActiveRecord) and exception.is_a?(ActiveRecord::RecordNotFound)
|
|
119
|
+
raise exception
|
|
112
120
|
end
|
|
113
121
|
|
|
114
|
-
def after_login!(user, credentials)
|
|
122
|
+
def after_login!(user, credentials = [])
|
|
115
123
|
Config.after_login.each {|c| self.send(c, user, credentials)}
|
|
116
124
|
end
|
|
117
125
|
|
|
@@ -8,11 +8,16 @@ module Sorcery
|
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
module InstanceMethods
|
|
11
|
-
def
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
def update_many_attributes(attrs)
|
|
12
|
+
attrs.each do |name, value|
|
|
13
|
+
self.send(:"#{name}=", value)
|
|
14
|
+
end
|
|
14
15
|
primary_key = self.class.primary_key
|
|
15
|
-
self.class.where(:"#{primary_key}" => self.send(:"#{primary_key}")).update_all(
|
|
16
|
+
self.class.where(:"#{primary_key}" => self.send(:"#{primary_key}")).update_all(attrs)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def update_single_attribute(name, value)
|
|
20
|
+
update_many_attributes(name => value)
|
|
16
21
|
end
|
|
17
22
|
end
|
|
18
23
|
|
|
@@ -3,19 +3,21 @@ module Sorcery
|
|
|
3
3
|
module Adapters
|
|
4
4
|
module MongoMapper
|
|
5
5
|
extend ActiveSupport::Concern
|
|
6
|
-
|
|
6
|
+
|
|
7
7
|
included do
|
|
8
8
|
include Sorcery::Model
|
|
9
9
|
end
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
10
|
+
|
|
11
|
+
def increment(attr)
|
|
12
|
+
self.class.increment(id, attr => 1)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def save!(options = {})
|
|
16
|
+
save(options)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def update_many_attributes(attrs)
|
|
20
|
+
update_attributes(attrs)
|
|
19
21
|
end
|
|
20
22
|
|
|
21
23
|
module ClassMethods
|
|
@@ -31,19 +33,19 @@ module Sorcery
|
|
|
31
33
|
end
|
|
32
34
|
@user
|
|
33
35
|
end
|
|
34
|
-
|
|
36
|
+
|
|
35
37
|
def find_by_id(id)
|
|
36
38
|
find(id)
|
|
37
39
|
end
|
|
38
|
-
|
|
40
|
+
|
|
39
41
|
def find_by_activation_token(token)
|
|
40
42
|
where(sorcery_config.activation_token_attribute_name => token).first
|
|
41
43
|
end
|
|
42
|
-
|
|
44
|
+
|
|
43
45
|
def transaction(&blk)
|
|
44
46
|
tap(&blk)
|
|
45
47
|
end
|
|
46
|
-
|
|
48
|
+
|
|
47
49
|
def find_by_sorcery_token(token_attr_name, token)
|
|
48
50
|
where(token_attr_name => token).first
|
|
49
51
|
end
|
|
@@ -11,11 +11,17 @@ module Sorcery
|
|
|
11
11
|
def increment(attr)
|
|
12
12
|
self.inc(attr,1)
|
|
13
13
|
end
|
|
14
|
-
|
|
14
|
+
|
|
15
|
+
def update_many_attributes(attrs)
|
|
16
|
+
attrs.each do |name, value|
|
|
17
|
+
attrs[name] = value.utc if value.is_a?(ActiveSupport::TimeWithZone)
|
|
18
|
+
self.send(:"#{name}=", value)
|
|
19
|
+
end
|
|
20
|
+
self.class.where(:_id => self.id).update_all(attrs)
|
|
21
|
+
end
|
|
22
|
+
|
|
15
23
|
def update_single_attribute(name, value)
|
|
16
|
-
|
|
17
|
-
self.send(:"#{name}=", value)
|
|
18
|
-
self.class.where(:_id => self.id).update_all(name => value)
|
|
24
|
+
update_many_attributes(name => value)
|
|
19
25
|
end
|
|
20
26
|
end
|
|
21
27
|
|
|
@@ -72,8 +78,8 @@ module Sorcery
|
|
|
72
78
|
def get_current_users
|
|
73
79
|
config = sorcery_config
|
|
74
80
|
where(config.last_activity_at_attribute_name.ne => nil) \
|
|
75
|
-
.
|
|
76
|
-
.
|
|
81
|
+
.where("this.#{config.last_logout_at_attribute_name} == null || this.#{config.last_activity_at_attribute_name} > this.#{config.last_logout_at_attribute_name}") \
|
|
82
|
+
.where(config.last_activity_at_attribute_name.gt => config.activity_timeout.seconds.ago.utc).order_by([:_id,:asc])
|
|
77
83
|
end
|
|
78
84
|
end
|
|
79
85
|
end
|