sorcery 0.7.4 → 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 +103 -62
- data/README.rdoc +33 -8
- data/Rakefile +6 -7
- data/VERSION +1 -1
- data/lib/generators/sorcery/install_generator.rb +15 -10
- data/lib/generators/sorcery/templates/initializer.rb +343 -125
- data/lib/generators/sorcery/templates/migration/brute_force_protection.rb +2 -0
- data/lib/generators/sorcery/templates/migration/reset_password.rb +1 -1
- data/lib/sorcery/controller/submodules/activity_logging.rb +3 -3
- data/lib/sorcery/controller/submodules/brute_force_protection.rb +1 -2
- data/lib/sorcery/controller/submodules/external/protocols/oauth2.rb +19 -19
- data/lib/sorcery/controller/submodules/external/providers/facebook.rb +20 -5
- data/lib/sorcery/controller/submodules/external/providers/github.rb +15 -4
- data/lib/sorcery/controller/submodules/external/providers/google.rb +90 -0
- data/lib/sorcery/controller/submodules/external/providers/linkedin.rb +101 -0
- data/lib/sorcery/controller/submodules/external/providers/liveid.rb +91 -0
- data/lib/sorcery/controller/submodules/external/providers/twitter.rb +2 -1
- data/lib/sorcery/controller/submodules/external/providers/vkontakte.rb +94 -0
- data/lib/sorcery/controller/submodules/external.rb +89 -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 +21 -1
- data/lib/sorcery/model/adapters/mongo_mapper.rb +22 -15
- data/lib/sorcery/model/adapters/mongoid.rb +20 -3
- data/lib/sorcery/model/submodules/activity_logging.rb +4 -4
- data/lib/sorcery/model/submodules/brute_force_protection.rb +46 -15
- data/lib/sorcery/model/submodules/remember_me.rb +4 -6
- data/lib/sorcery/model/submodules/reset_password.rb +18 -12
- data/lib/sorcery/model/submodules/user_activation.rb +15 -8
- data/lib/sorcery/model.rb +57 -43
- data/lib/sorcery/railties/tasks.rake +2 -0
- data/lib/sorcery.rb +5 -1
- data/sorcery.gemspec +33 -21
- data/spec/Gemfile +1 -1
- data/spec/Gemfile.lock +22 -21
- data/spec/README.md +8 -8
- data/spec/rails3/Gemfile +3 -3
- data/spec/rails3/Gemfile.lock +55 -33
- data/spec/rails3/app/controllers/application_controller.rb +85 -2
- 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/config/environments/in_memory.rb +35 -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 +10 -0
- data/spec/rails3/spec/controller_brute_force_protection_spec.rb +23 -1
- data/spec/rails3/spec/controller_oauth2_spec.rb +260 -22
- data/spec/rails3/spec/controller_oauth_spec.rb +111 -6
- data/spec/rails3/spec/controller_spec.rb +37 -2
- data/spec/rails3/spec/integration_spec.rb +15 -15
- data/spec/rails3/spec/spec_helper.rb +1 -1
- data/spec/rails3_mongo_mapper/Gemfile +2 -1
- data/spec/rails3_mongo_mapper/Gemfile.lock +40 -42
- data/spec/rails3_mongo_mapper/app/controllers/application_controller.rb +14 -0
- data/spec/rails3_mongo_mapper/spec/controller_spec.rb +41 -1
- data/spec/rails3_mongoid/Gemfile +1 -0
- data/spec/rails3_mongoid/Gemfile.lock +30 -27
- data/spec/rails3_mongoid/app/controllers/application_controller.rb +19 -0
- data/spec/rails3_mongoid/config/mongoid.yml +1 -1
- data/spec/rails3_mongoid/spec/controller_activity_logging_spec.rb +18 -11
- data/spec/rails3_mongoid/spec/controller_spec.rb +41 -1
- 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 +80 -27
- data/spec/sorcery_crypto_providers_spec.rb +14 -1
- metadata +74 -46
|
@@ -34,8 +34,11 @@ module Sorcery
|
|
|
34
34
|
:site,
|
|
35
35
|
:user_info_path,
|
|
36
36
|
:scope,
|
|
37
|
-
:user_info_mapping
|
|
38
|
-
|
|
37
|
+
:user_info_mapping,
|
|
38
|
+
:display,
|
|
39
|
+
:access_permissions
|
|
40
|
+
attr_reader :access_token
|
|
41
|
+
|
|
39
42
|
include Protocols::Oauth2
|
|
40
43
|
|
|
41
44
|
def init
|
|
@@ -43,12 +46,17 @@ module Sorcery
|
|
|
43
46
|
@user_info_path = "/me"
|
|
44
47
|
@scope = "email,offline_access"
|
|
45
48
|
@user_info_mapping = {}
|
|
49
|
+
@display = "page"
|
|
50
|
+
@token_url = "oauth/access_token"
|
|
51
|
+
@mode = :query
|
|
52
|
+
@parse = :query
|
|
53
|
+
@param_name = "access_token"
|
|
46
54
|
end
|
|
47
55
|
|
|
48
56
|
def get_user_hash
|
|
49
57
|
user_hash = {}
|
|
50
58
|
response = @access_token.get(@user_info_path)
|
|
51
|
-
user_hash[:user_info] = JSON.parse(response)
|
|
59
|
+
user_hash[:user_info] = JSON.parse(response.body)
|
|
52
60
|
user_hash[:uid] = user_hash[:user_info]['id']
|
|
53
61
|
user_hash
|
|
54
62
|
end
|
|
@@ -62,12 +70,19 @@ module Sorcery
|
|
|
62
70
|
def login_url(params,session)
|
|
63
71
|
self.authorize_url
|
|
64
72
|
end
|
|
65
|
-
|
|
73
|
+
|
|
74
|
+
# overrides oauth2#authorize_url to allow customized scope.
|
|
75
|
+
def authorize_url
|
|
76
|
+
@scope = self.access_permissions.present? ? self.access_permissions.join(",") : @scope
|
|
77
|
+
super
|
|
78
|
+
end
|
|
79
|
+
|
|
66
80
|
# tries to login the user from access token
|
|
67
81
|
def process_callback(params,session)
|
|
68
82
|
args = {}
|
|
83
|
+
options = { :token_url => @token_url, :mode => @mode, :param_name => @param_name, :parse => @parse }
|
|
69
84
|
args.merge!({:code => params[:code]}) if params[:code]
|
|
70
|
-
@access_token = self.get_access_token(args)
|
|
85
|
+
@access_token = self.get_access_token(args, options)
|
|
71
86
|
end
|
|
72
87
|
|
|
73
88
|
end
|
|
@@ -31,22 +31,29 @@ module Sorcery
|
|
|
31
31
|
attr_accessor :key,
|
|
32
32
|
:secret,
|
|
33
33
|
:callback_url,
|
|
34
|
+
:auth_path,
|
|
35
|
+
:token_path,
|
|
34
36
|
:site,
|
|
37
|
+
:scope,
|
|
35
38
|
:user_info_path,
|
|
36
39
|
:user_info_mapping
|
|
40
|
+
attr_reader :access_token
|
|
37
41
|
|
|
38
42
|
include Protocols::Oauth2
|
|
39
43
|
|
|
40
44
|
def init
|
|
41
45
|
@site = "https://github.com/"
|
|
42
|
-
@user_info_path = "
|
|
46
|
+
@user_info_path = "https://api.github.com/user"
|
|
47
|
+
@scope = nil
|
|
48
|
+
@auth_path = "/login/oauth/authorize"
|
|
49
|
+
@token_path = "/login/oauth/access_token"
|
|
43
50
|
@user_info_mapping = {}
|
|
44
51
|
end
|
|
45
52
|
|
|
46
53
|
def get_user_hash
|
|
47
54
|
user_hash = {}
|
|
48
55
|
response = @access_token.get(@user_info_path)
|
|
49
|
-
user_hash[:user_info] = JSON.parse(response)
|
|
56
|
+
user_hash[:user_info] = JSON.parse(response.body)
|
|
50
57
|
user_hash[:uid] = user_hash[:user_info]['id']
|
|
51
58
|
user_hash
|
|
52
59
|
end
|
|
@@ -58,14 +65,18 @@ module Sorcery
|
|
|
58
65
|
# calculates and returns the url to which the user should be redirected,
|
|
59
66
|
# to get authenticated at the external provider's site.
|
|
60
67
|
def login_url(params,session)
|
|
61
|
-
self.authorize_url({:
|
|
68
|
+
self.authorize_url({:authorize_url => @auth_path})
|
|
62
69
|
end
|
|
63
70
|
|
|
64
71
|
# tries to login the user from access token
|
|
65
72
|
def process_callback(params,session)
|
|
66
73
|
args = {}
|
|
67
74
|
args.merge!({:code => params[:code]}) if params[:code]
|
|
68
|
-
|
|
75
|
+
options = {
|
|
76
|
+
:token_url => @token_path,
|
|
77
|
+
:token_method => :post
|
|
78
|
+
}
|
|
79
|
+
@access_token = self.get_access_token(args, options)
|
|
69
80
|
end
|
|
70
81
|
|
|
71
82
|
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
module Sorcery
|
|
2
|
+
module Controller
|
|
3
|
+
module Submodules
|
|
4
|
+
module External
|
|
5
|
+
module Providers
|
|
6
|
+
# This module adds support for OAuth with google.com.
|
|
7
|
+
# When included in the 'config.providers' option, it adds a new option, 'config.google'.
|
|
8
|
+
# Via this new option you can configure Google specific settings like your app's key and secret.
|
|
9
|
+
#
|
|
10
|
+
# config.google.key = <key>
|
|
11
|
+
# config.google.secret = <secret>
|
|
12
|
+
# ...
|
|
13
|
+
#
|
|
14
|
+
module Google
|
|
15
|
+
def self.included(base)
|
|
16
|
+
base.module_eval do
|
|
17
|
+
class << self
|
|
18
|
+
attr_reader :google # access to google_client.
|
|
19
|
+
|
|
20
|
+
def merge_google_defaults!
|
|
21
|
+
@defaults.merge!(:@google => GoogleClient)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
merge_google_defaults!
|
|
25
|
+
update!
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
module GoogleClient
|
|
30
|
+
class << self
|
|
31
|
+
attr_accessor :key,
|
|
32
|
+
:secret,
|
|
33
|
+
:callback_url,
|
|
34
|
+
:site,
|
|
35
|
+
:auth_url,
|
|
36
|
+
:token_path,
|
|
37
|
+
:user_info_url,
|
|
38
|
+
:scope,
|
|
39
|
+
:user_info_mapping
|
|
40
|
+
attr_reader :access_token
|
|
41
|
+
|
|
42
|
+
include Protocols::Oauth2
|
|
43
|
+
|
|
44
|
+
def init
|
|
45
|
+
@site = "https://accounts.google.com"
|
|
46
|
+
@auth_url = "/o/oauth2/auth"
|
|
47
|
+
@token_url = "/o/oauth2/token"
|
|
48
|
+
@user_info_url = "https://www.googleapis.com/oauth2/v1/userinfo"
|
|
49
|
+
@scope = "https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile"
|
|
50
|
+
@user_info_mapping = {}
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def get_user_hash
|
|
54
|
+
user_hash = {}
|
|
55
|
+
response = @access_token.get(@user_info_url)
|
|
56
|
+
user_hash[:user_info] = JSON.parse(response.body)
|
|
57
|
+
user_hash[:uid] = user_hash[:user_info]['id']
|
|
58
|
+
user_hash
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def has_callback?
|
|
62
|
+
true
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# calculates and returns the url to which the user should be redirected,
|
|
66
|
+
# to get authenticated at the external provider's site.
|
|
67
|
+
def login_url(params,session)
|
|
68
|
+
self.authorize_url({:authorize_url => @auth_url})
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# tries to login the user from access token
|
|
72
|
+
def process_callback(params,session)
|
|
73
|
+
args = {}
|
|
74
|
+
args.merge!({:code => params[:code]}) if params[:code]
|
|
75
|
+
options = {
|
|
76
|
+
:token_url => @token_url,
|
|
77
|
+
:token_method => :post
|
|
78
|
+
}
|
|
79
|
+
@access_token = self.get_access_token(args, options)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
init
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
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
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
module Sorcery
|
|
2
|
+
module Controller
|
|
3
|
+
module Submodules
|
|
4
|
+
module External
|
|
5
|
+
module Providers
|
|
6
|
+
# This module adds support for OAuth with microsoft liveid
|
|
7
|
+
# When included in the 'config.providers' option, it adds a new option, 'config.liveid'.
|
|
8
|
+
# Via this new option you can configure LiveId specific settings like your app's key and secret.
|
|
9
|
+
#
|
|
10
|
+
# config.liveid.key = <key>
|
|
11
|
+
# config.liveid.secret = <secret>
|
|
12
|
+
# ...
|
|
13
|
+
#
|
|
14
|
+
module Liveid
|
|
15
|
+
def self.included(base)
|
|
16
|
+
base.module_eval do
|
|
17
|
+
class << self
|
|
18
|
+
attr_reader :liveid # access to liveid_client.
|
|
19
|
+
|
|
20
|
+
def merge_liveid_defaults!
|
|
21
|
+
@defaults.merge!(:@liveid => LiveidClient)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
merge_liveid_defaults!
|
|
25
|
+
update!
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
module LiveidClient
|
|
30
|
+
class << self
|
|
31
|
+
attr_accessor :key,
|
|
32
|
+
:secret,
|
|
33
|
+
:callback_url,
|
|
34
|
+
:site,
|
|
35
|
+
:auth_url,
|
|
36
|
+
:token_path,
|
|
37
|
+
:user_info_url,
|
|
38
|
+
:scope,
|
|
39
|
+
:user_info_mapping
|
|
40
|
+
attr_reader :access_token
|
|
41
|
+
|
|
42
|
+
include Protocols::Oauth2
|
|
43
|
+
|
|
44
|
+
def init
|
|
45
|
+
@site = "https://oauth.live.com/"
|
|
46
|
+
@auth_url = "/authorize"
|
|
47
|
+
@token_path = "/token"
|
|
48
|
+
@user_info_url = "https://apis.live.net/v5.0/me"
|
|
49
|
+
@scope = "wl.basic wl.emails wl.offline_access"
|
|
50
|
+
@user_info_mapping = {}
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def get_user_hash
|
|
54
|
+
user_hash = {}
|
|
55
|
+
@access_token.token_param = "access_token"
|
|
56
|
+
response = @access_token.get(@user_info_url)
|
|
57
|
+
user_hash[:user_info] = JSON.parse(response.body)
|
|
58
|
+
user_hash[:uid] = user_hash[:user_info]['id']
|
|
59
|
+
user_hash
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def has_callback?
|
|
63
|
+
true
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# calculates and returns the url to which the user should be redirected,
|
|
67
|
+
# to get authenticated at the external provider's site.
|
|
68
|
+
def login_url(params,session)
|
|
69
|
+
self.authorize_url({:authorize_url => @auth_url})
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# tries to login the user from access token
|
|
73
|
+
def process_callback(params,session)
|
|
74
|
+
args = {}
|
|
75
|
+
args.merge!({:code => params[:code]}) if params[:code]
|
|
76
|
+
options = {
|
|
77
|
+
:access_token_path => @token_path,
|
|
78
|
+
:access_token_method => :post
|
|
79
|
+
}
|
|
80
|
+
@access_token = self.get_access_token(args, options)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
init
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
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,30 +28,88 @@ 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
|
|
54
|
-
|
|
65
|
+
|
|
66
|
+
# get provider access account
|
|
67
|
+
def access_token(provider)
|
|
68
|
+
@provider = Config.send(provider)
|
|
69
|
+
@provider.access_token
|
|
70
|
+
end
|
|
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
|
+
|
|
55
113
|
# this method automatically creates a new user from the data in the external user hash.
|
|
56
114
|
# The mappings from user hash fields to user db fields are set at controller config.
|
|
57
115
|
# If the hash field you would like to map is nested, use slashes. For example, Given a hash like:
|
|
@@ -64,30 +122,46 @@ module Sorcery
|
|
|
64
122
|
#
|
|
65
123
|
# And this will cause 'moishe' to be set as the value of :username field.
|
|
66
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
|
+
#
|
|
67
129
|
def create_from(provider)
|
|
68
130
|
provider = provider.to_sym
|
|
69
131
|
@provider = Config.send(provider)
|
|
70
132
|
@user_hash = @provider.get_user_hash
|
|
71
133
|
config = user_class.sorcery_config
|
|
72
|
-
|
|
73
|
-
@provider.user_info_mapping
|
|
74
|
-
|
|
75
|
-
attribute_value = varr.inject(@user_hash[:user_info]) {|hsh,v| hsh[v] } rescue nil
|
|
76
|
-
attribute_value.nil? ? attrs : attrs.merge!(k => attribute_value)
|
|
77
|
-
else
|
|
78
|
-
attrs.merge!(k => @user_hash[:user_info][v])
|
|
79
|
-
end
|
|
80
|
-
end
|
|
134
|
+
|
|
135
|
+
attrs = user_attrs(@provider.user_info_mapping, @user_hash)
|
|
136
|
+
|
|
81
137
|
user_class.transaction do
|
|
82
138
|
@user = user_class.new()
|
|
83
139
|
attrs.each do |k,v|
|
|
84
140
|
@user.send(:"#{k}=", v)
|
|
85
141
|
end
|
|
142
|
+
|
|
143
|
+
if block_given?
|
|
144
|
+
return false unless yield @user
|
|
145
|
+
end
|
|
146
|
+
|
|
86
147
|
@user.save(:validate => false)
|
|
87
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]})
|
|
88
149
|
end
|
|
89
150
|
@user
|
|
90
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
|
|
91
165
|
end
|
|
92
166
|
end
|
|
93
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
|