sso 0.1.0.alpha5 → 0.1.0.alpha6
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.
- checksums.yaml +4 -4
- data/lib/sso/client/README.md +17 -2
- data/lib/sso/client/authentications/passport.rb +176 -0
- data/lib/sso/client/passport.rb +7 -3
- data/lib/sso/client/passport_verifier.rb +130 -0
- data/lib/sso/client/warden/hooks/after_fetch.rb +32 -81
- data/lib/sso/client/warden/strategies/passport.rb +43 -0
- data/lib/sso/client.rb +3 -0
- data/lib/sso/server/authentications/passport.rb +16 -49
- data/lib/sso/server/configuration.rb +36 -6
- data/lib/sso/server/doorkeeper/access_token_marker.rb +7 -7
- data/lib/sso/server/middleware/passport_destruction.rb +40 -0
- data/lib/sso/server/middleware/{passport_creation.rb → passport_exchange.rb} +15 -11
- data/lib/sso/server/middleware/passport_verification.rb +2 -2
- data/lib/sso/server/passport.rb +43 -15
- data/lib/sso/server/passports.rb +59 -31
- data/lib/sso/server/warden/hooks/after_authentication.rb +1 -1
- data/lib/sso/server/warden/hooks/before_logout.rb +1 -1
- data/lib/sso/server/warden/strategies/passport.rb +8 -6
- data/lib/sso/server.rb +2 -3
- data/spec/dummy/app/controllers/sessions_controller.rb +1 -1
- data/spec/dummy/config/application.rb +6 -0
- data/spec/dummy/config/initializers/sso.rb +10 -6
- data/spec/dummy/config/initializers/warden.rb +3 -11
- data/spec/dummy/db/migrate/20150303132931_create_passports_table.rb +29 -15
- data/spec/dummy/db/schema.rb +10 -5
- data/spec/integration/oauth/authorization_code_spec.rb +80 -10
- data/spec/integration/oauth/{password_verification_spec.rb → password_spec.rb} +39 -3
- data/spec/lib/sso/client/authentications/passport_spec.rb +92 -0
- data/spec/{integration/oauth → lib/sso/client/warden/hooks}/after_fetch_spec.rb +4 -3
- data/spec/lib/sso/server/middleware/passport_destruction_spec.rb +33 -0
- data/spec/lib/sso/server/passports_spec.rb +104 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/support/factories/doorkeeper/application.rb +0 -3
- data/spec/support/factories/server/passport.rb +5 -1
- data/spec/support/factories/server/user.rb +1 -1
- metadata +31 -21
@@ -32,8 +32,10 @@ module SSO
|
|
32
32
|
return Operations.failure :invalid_signature, object: failure_rack_array
|
33
33
|
end
|
34
34
|
|
35
|
-
debug { 'The request was properly signed, I found the corresponding passport.' }
|
35
|
+
debug { 'The request was properly signed, I found the corresponding passport. Updating activity...' }
|
36
36
|
update_passport
|
37
|
+
debug { 'Attaching user to passport' }
|
38
|
+
passport.load_user!
|
37
39
|
|
38
40
|
if passport.state == state
|
39
41
|
Operations.success :signature_approved_no_changes, object: success_same_state_rack_array
|
@@ -44,12 +46,14 @@ module SSO
|
|
44
46
|
end
|
45
47
|
|
46
48
|
def check_arguments
|
49
|
+
debug { 'Checking arguments...' }
|
47
50
|
yield Operations.failure :missing_verb if verb.blank?
|
48
51
|
yield Operations.failure :missing_passport_id if passport_id.blank?
|
49
52
|
yield Operations.failure :missing_state if state.blank?
|
50
53
|
yield Operations.failure :passport_not_found if passport.blank?
|
51
54
|
yield Operations.failure :passport_revoked if passport.invalid?
|
52
|
-
|
55
|
+
debug { 'Arguments are fine.' }
|
56
|
+
#yield Operations.failure :user_not_encapsulated if passport.user.blank?
|
53
57
|
Operations.success :arguments_are_valid
|
54
58
|
end
|
55
59
|
|
@@ -74,16 +78,24 @@ module SSO
|
|
74
78
|
end
|
75
79
|
|
76
80
|
def passport
|
77
|
-
@passport ||=
|
81
|
+
@passport ||= passport!
|
82
|
+
end
|
83
|
+
|
84
|
+
def passport!
|
85
|
+
return unless record = backend.find_by_id(passport_id)
|
86
|
+
debug { "Successfully loaded Passport #{passport_id} from database." }
|
87
|
+
record
|
78
88
|
end
|
79
89
|
|
80
90
|
def passport_id
|
81
91
|
signature_request.authenticate { |passport_id| return passport_id }
|
92
|
+
|
82
93
|
rescue Signature::AuthenticationError
|
83
94
|
nil
|
84
95
|
end
|
85
96
|
|
86
97
|
def valid_signature?
|
98
|
+
debug { 'Checking request signature...' }
|
87
99
|
signature_request.authenticate { Signature::Token.new passport_id, passport.secret }
|
88
100
|
true
|
89
101
|
rescue Signature::AuthenticationError
|
@@ -96,52 +108,7 @@ module SSO
|
|
96
108
|
end
|
97
109
|
|
98
110
|
def update_passport
|
99
|
-
|
100
|
-
if passport.ip.to_s == ip.to_s && passport.agent.to_s == user_agent.to_s
|
101
|
-
debug { "No changes in IP or User Agent so I won't perform an update now..." }
|
102
|
-
Operations.success :already_up_to_date
|
103
|
-
else
|
104
|
-
debug { "Yes, it is necessary, updating activity of Passport #{passport.id}" }
|
105
|
-
passport.update_attributes ip: ip.to_s, agent: user_agent, activity_at: Time.now
|
106
|
-
Operations.success :passport_metadata_updated
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
def application
|
111
|
-
passport.application
|
112
|
-
end
|
113
|
-
|
114
|
-
def app_scopes
|
115
|
-
application.scopes
|
116
|
-
end
|
117
|
-
|
118
|
-
def insider?
|
119
|
-
if app_scopes.empty?
|
120
|
-
warn { "Doorkeeper::Application #{application.name} with ID #{application.id} has no scope restrictions. Assuming 'outsider' for now." }
|
121
|
-
return false
|
122
|
-
end
|
123
|
-
|
124
|
-
app_scopes.has_scopes? [:insider]
|
125
|
-
end
|
126
|
-
|
127
|
-
def ip
|
128
|
-
if insider?
|
129
|
-
params['ip']
|
130
|
-
else
|
131
|
-
request_ip
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
def user_agent
|
136
|
-
if insider?
|
137
|
-
params['user_agent']
|
138
|
-
else
|
139
|
-
request.user_agent
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
def request_ip
|
144
|
-
request.env['action_dispatch.remote_ip'] || fail('Whoops, I thought you were using Rails, but action_dispatch.remote_ip is empty!')
|
111
|
+
::SSO::Server::Passports.update_activity passport_id: passport.id, request: request
|
145
112
|
end
|
146
113
|
|
147
114
|
def verb
|
@@ -3,16 +3,13 @@ require 'logger'
|
|
3
3
|
module SSO
|
4
4
|
class Configuration
|
5
5
|
|
6
|
+
# Server
|
7
|
+
|
6
8
|
def human_readable_location_for_ip
|
7
9
|
@human_readable_location_for_ip || default_human_readable_location_for_ip
|
8
10
|
end
|
9
11
|
attr_writer :human_readable_location_for_ip
|
10
12
|
|
11
|
-
def exception_handler
|
12
|
-
@exception_handler || default_exception_handler
|
13
|
-
end
|
14
|
-
attr_writer :exception_handler
|
15
|
-
|
16
13
|
def user_state_base
|
17
14
|
@user_state_base || fail('You need to configure user_state_base, see SSO::Configuration for more info.')
|
18
15
|
end
|
@@ -28,6 +25,30 @@ module SSO
|
|
28
25
|
end
|
29
26
|
attr_writer :user_state_key
|
30
27
|
|
28
|
+
# Client
|
29
|
+
|
30
|
+
def session_backend
|
31
|
+
@session_backend || default_session_backend
|
32
|
+
end
|
33
|
+
attr_writer :session_backend
|
34
|
+
|
35
|
+
def passport_verification_timeout_ms
|
36
|
+
@passport_verification_timeout_ms || default_passport_verification_timeout_ms
|
37
|
+
end
|
38
|
+
attr_writer :passport_verification_timeout_ms
|
39
|
+
|
40
|
+
# Both
|
41
|
+
|
42
|
+
def exception_handler
|
43
|
+
@exception_handler || default_exception_handler
|
44
|
+
end
|
45
|
+
attr_writer :exception_handler
|
46
|
+
|
47
|
+
def passport_chip_key
|
48
|
+
@passport_chip_key || fail('You need to configure a secret passport_chip_key, see SSO::Configuration for more info.')
|
49
|
+
end
|
50
|
+
attr_writer :passport_chip_key
|
51
|
+
|
31
52
|
def logger
|
32
53
|
@logger ||= default_logger
|
33
54
|
end
|
@@ -62,7 +83,7 @@ module SSO
|
|
62
83
|
end
|
63
84
|
|
64
85
|
def default_exception_handler
|
65
|
-
proc do
|
86
|
+
proc do |exception|
|
66
87
|
return unless ::SSO.config.logger
|
67
88
|
::SSO.config.logger.error(self.class) do
|
68
89
|
"An internal error occured #{exception.class.name} #{exception.message} #{exception.backtrace[0..5].join(' ') if exception.backtrace}"
|
@@ -76,5 +97,14 @@ module SSO
|
|
76
97
|
end
|
77
98
|
end
|
78
99
|
|
100
|
+
def default_session_backend
|
101
|
+
fail('You need to configure session_backend, see SSO::Configuration for more info.') unless %w(developmen test).include?(environment)
|
102
|
+
# Moneta.new :Memory
|
103
|
+
end
|
104
|
+
|
105
|
+
def default_passport_verification_timeout_ms
|
106
|
+
100
|
107
|
+
end
|
108
|
+
|
79
109
|
end
|
80
110
|
end
|
@@ -38,25 +38,25 @@ module SSO
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def handle_authorization_grant_flow
|
41
|
-
# We cannot rely on session[:passport_id] here because the end-user might have cookies disabled.
|
42
|
-
# The only thing we can rely on to identify the
|
41
|
+
# We cannot rely on looking up session[:passport_id] here because the end-user might have cookies disabled.
|
42
|
+
# The only thing we can really rely on to identify the Passport is the incoming grant token.
|
43
43
|
debug { %(Detected outgoing "Access Token" #{outgoing_access_token.inspect} of the "Authorization Code Grant" flow) }
|
44
44
|
debug { %(This Access Token belongs to "Authorization Grant Token" #{grant_token.inspect}. Augmenting related Passport with it...) }
|
45
45
|
registration = ::SSO::Server::Passports.register_access_token_from_grant grant_token: grant_token, access_token: outgoing_access_token
|
46
46
|
|
47
47
|
return if registration.success?
|
48
|
-
warn { 'The passport could not be augmented. Destroying warden session.' }
|
48
|
+
warn { 'The passport could not be augmented via the authorizaton grant. Destroying warden session.' }
|
49
49
|
warden.logout
|
50
50
|
end
|
51
51
|
|
52
52
|
def handle_password_flow
|
53
|
-
local_passport_id = session[:passport_id] # <- We know this
|
53
|
+
local_passport_id = session[:passport_id] # <- We know this always exists because it was set in this very response
|
54
54
|
debug { %(Detected outgoing "Access Token" #{outgoing_access_token.inspect} of the "Resource Owner Password Credentials Grant" flow.) }
|
55
55
|
debug { %(Augmenting local Passport #{local_passport_id.inspect} with this outgoing Access Token...) }
|
56
|
-
|
56
|
+
registration = ::SSO::Server::Passports.register_access_token_from_id passport_id: local_passport_id, access_token: outgoing_access_token
|
57
57
|
|
58
|
-
return if
|
59
|
-
warn { 'The passport could not be
|
58
|
+
return if registration.success?
|
59
|
+
warn { 'The passport could not be augmented via the access token. Destroying warden session.' }
|
60
60
|
warden.logout
|
61
61
|
end
|
62
62
|
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module SSO
|
2
|
+
module Server
|
3
|
+
module Middleware
|
4
|
+
class PassportDestruction
|
5
|
+
include ::SSO::Logging
|
6
|
+
|
7
|
+
def initialize(app)
|
8
|
+
@app = app
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(env)
|
12
|
+
request = Rack::Request.new(env)
|
13
|
+
|
14
|
+
if !(request.delete? && request.path.start_with?(passports_path))
|
15
|
+
debug { "I'm not interested in this #{request.request_method.inspect} request to #{request.path.inspect} I only care for DELETE #{passports_path.inspect}" }
|
16
|
+
return @app.call(env)
|
17
|
+
end
|
18
|
+
|
19
|
+
passport_id = request.path.to_s.split('/').last
|
20
|
+
revocation = ::SSO::Server::Passports.logout passport_id: passport_id
|
21
|
+
env['warden'].logout
|
22
|
+
|
23
|
+
payload = { success: true, code: revocation.code }
|
24
|
+
debug { "Revoked Passport with ID #{passport_id.inspect}" }
|
25
|
+
|
26
|
+
return [200, { 'Content-Type' => 'application/json' }, [payload.to_json]]
|
27
|
+
end
|
28
|
+
|
29
|
+
def json_code(code)
|
30
|
+
[200, { 'Content-Type' => 'application/json' }, [{ success: true, code: code }.to_json]]
|
31
|
+
end
|
32
|
+
|
33
|
+
def passports_path
|
34
|
+
OmniAuth::Strategies::SSO.passports_path
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -1,7 +1,9 @@
|
|
1
1
|
module SSO
|
2
2
|
module Server
|
3
3
|
module Middleware
|
4
|
-
|
4
|
+
# Hands out the Passport when presented with the corresponding Access Token.
|
5
|
+
#
|
6
|
+
class PassportExchange
|
5
7
|
include ::SSO::Logging
|
6
8
|
|
7
9
|
def initialize(app)
|
@@ -11,9 +13,10 @@ module SSO
|
|
11
13
|
def call(env)
|
12
14
|
request = Rack::Request.new(env)
|
13
15
|
remote_ip = request.env['action_dispatch.remote_ip'].to_s
|
16
|
+
device_id = request.params['device_id']
|
14
17
|
|
15
18
|
if !(request.post? && request.path == passports_path)
|
16
|
-
debug { "I'm not interested in this request to #{request.path}" }
|
19
|
+
debug { "I'm not interested in this #{request.request_method.inspect} request to #{request.path.inspect} I only care for POST #{passports_path.inspect}" }
|
17
20
|
return @app.call(env)
|
18
21
|
end
|
19
22
|
|
@@ -29,18 +32,19 @@ module SSO
|
|
29
32
|
return json_code :access_token_invalid
|
30
33
|
end
|
31
34
|
|
32
|
-
|
33
|
-
passport_id = creation.object
|
34
|
-
finding = ::SSO::Server::Passports.find(passport_id)
|
35
|
-
|
35
|
+
finding = ::SSO::Server::Passports.find_by_access_token_id(access_token.id)
|
36
36
|
if finding.failure?
|
37
|
-
|
38
|
-
return json_code :
|
37
|
+
# This should never happen. Every Access Token should be connected to a Passport.
|
38
|
+
return json_code :passport_not_found
|
39
39
|
end
|
40
|
-
|
41
40
|
passport = finding.object
|
42
|
-
|
43
|
-
|
41
|
+
|
42
|
+
::SSO::Server::Passports.update_activity passport_id: passport.id, request: request
|
43
|
+
|
44
|
+
debug { "Attaching user and chip to passport #{passport.inspect}" }
|
45
|
+
passport.load_user!
|
46
|
+
passport.create_chip!
|
47
|
+
|
44
48
|
payload = { success: true, code: :here_is_your_passport, passport: passport.export }
|
45
49
|
debug { "Created Passport #{passport.id}, sending it including user #{passport.user.inspect}}"}
|
46
50
|
|
@@ -15,13 +15,13 @@ module SSO
|
|
15
15
|
debug { 'Detected incoming Passport verification request.' }
|
16
16
|
env['warden'].authenticate! :passport
|
17
17
|
else
|
18
|
-
debug { "I'm not interested in this request to #{request.path}" }
|
18
|
+
debug { "I'm not interested in this #{request.request_method.inspect} request to #{request.path.inspect} I only care for GET #{passports_path.inspect}" }
|
19
19
|
@app.call(env)
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
23
|
def passports_path
|
24
|
-
OmniAuth::Strategies::SSO.passports_path
|
24
|
+
::OmniAuth::Strategies::SSO.passports_path
|
25
25
|
end
|
26
26
|
|
27
27
|
end
|
data/lib/sso/server/passport.rb
CHANGED
@@ -5,23 +5,21 @@ module SSO
|
|
5
5
|
# This could be MongoDB or whatever
|
6
6
|
class Passport < ActiveRecord::Base
|
7
7
|
include ::SSO::Logging
|
8
|
+
include ::SSO::Benchmarking
|
8
9
|
|
9
10
|
self.table_name = 'passports'
|
10
11
|
|
11
12
|
before_validation :ensure_secret
|
12
|
-
before_validation :ensure_group_id
|
13
13
|
before_validation :ensure_activity_at
|
14
14
|
|
15
15
|
before_save :update_location
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
validates :secret, :group_id, presence: true
|
17
|
+
validates :secret, presence: true
|
20
18
|
validates :oauth_access_token_id, uniqueness: { scope: [:owner_id, :revoked_at], allow_blank: true }
|
21
19
|
validates :revoke_reason, allow_blank: true, format: { with: /\A[a-z_]+\z/ }
|
22
|
-
validates :application_id, presence: true, numericality: { only_integer: true, greater_than_or_equal_to: 0 }
|
23
20
|
|
24
21
|
attr_accessor :user
|
22
|
+
attr_reader :chip
|
25
23
|
|
26
24
|
def export
|
27
25
|
debug { "Exporting Passport #{id} including the encapsulated user." }
|
@@ -29,6 +27,7 @@ module SSO
|
|
29
27
|
id: id,
|
30
28
|
secret: secret,
|
31
29
|
state: state,
|
30
|
+
chip: chip,
|
32
31
|
user: user,
|
33
32
|
}
|
34
33
|
end
|
@@ -47,19 +46,52 @@ module SSO
|
|
47
46
|
end
|
48
47
|
|
49
48
|
def state!
|
50
|
-
|
51
|
-
|
52
|
-
|
49
|
+
benchmark 'Passport user state calculation' do
|
50
|
+
OpenSSL::HMAC.hexdigest user_state_digest, user_state_key, user_state_base
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def load_user!
|
55
|
+
@user = SSO.config.find_user_for_passport.call passport: self.reload
|
56
|
+
end
|
57
|
+
|
58
|
+
def create_chip!
|
59
|
+
@chip = chip!
|
60
|
+
end
|
61
|
+
|
62
|
+
def chip!
|
63
|
+
benchmark 'Passport chip encryption' do
|
64
|
+
ensure_secret
|
65
|
+
cipher = chip_digest
|
66
|
+
cipher.encrypt
|
67
|
+
cipher.key = chip_key
|
68
|
+
chip_iv = cipher.random_iv
|
69
|
+
ciphertext = cipher.update chip_plaintext
|
70
|
+
ciphertext << cipher.final
|
71
|
+
debug { "The Passport chip plaintext #{chip_plaintext.inspect} was encrypted using key #{chip_key.inspect} and IV #{chip_iv.inspect} and resultet in ciphertext #{ciphertext.inspect}" }
|
72
|
+
chip = [Base64.encode64(ciphertext).strip(), Base64.encode64(chip_iv).strip()].join('|')
|
73
|
+
logger.debug { "Augmented passport #{id.inspect} with chip #{chip.inspect}" }
|
74
|
+
chip
|
53
75
|
end
|
54
|
-
debug { "The user state digest is #{result.inspect}" }
|
55
|
-
debug { "Calculating the user state took #{(time * 1000).round(2)}ms" }
|
56
|
-
result
|
57
76
|
end
|
58
77
|
|
59
78
|
def user_state_digest
|
60
79
|
OpenSSL::Digest.new 'sha1'
|
61
80
|
end
|
62
81
|
|
82
|
+
def chip_digest
|
83
|
+
OpenSSL::Cipher::AES256.new :CBC
|
84
|
+
end
|
85
|
+
|
86
|
+
def chip_key
|
87
|
+
SSO.config.passport_chip_key
|
88
|
+
end
|
89
|
+
|
90
|
+
# Don't get confused, the chip plaintext is the passport secret
|
91
|
+
def chip_plaintext
|
92
|
+
secret
|
93
|
+
end
|
94
|
+
|
63
95
|
def user_state_key
|
64
96
|
::SSO.config.user_state_key
|
65
97
|
end
|
@@ -74,10 +106,6 @@ module SSO
|
|
74
106
|
self.secret ||= SecureRandom.uuid
|
75
107
|
end
|
76
108
|
|
77
|
-
def ensure_group_id
|
78
|
-
self.group_id ||= SecureRandom.uuid
|
79
|
-
end
|
80
|
-
|
81
109
|
def ensure_activity_at
|
82
110
|
self.activity_at ||= Time.now
|
83
111
|
end
|
data/lib/sso/server/passports.rb
CHANGED
@@ -17,10 +17,20 @@ module SSO
|
|
17
17
|
Operations.failure :backend_error, object: exception
|
18
18
|
end
|
19
19
|
|
20
|
-
def self.
|
21
|
-
|
20
|
+
def self.find_by_access_token_id(id)
|
21
|
+
record = backend.where(revoked_at: nil).find_by_oauth_access_token_id(id)
|
22
22
|
|
23
|
-
record
|
23
|
+
if record
|
24
|
+
Operations.success(:record_found, object: record)
|
25
|
+
else
|
26
|
+
Operations.failure :record_not_found
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.generate(owner_id:, ip:, agent:, device: nil)
|
31
|
+
debug { "Generating Passport for user ID #{owner_id.inspect} and IP #{ip.inspect} and Agent #{agent.inspect} and Device #{device.inspect}" }
|
32
|
+
|
33
|
+
record = backend.create owner_id: owner_id, ip: ip, agent: agent, device: device
|
24
34
|
|
25
35
|
if record.persisted?
|
26
36
|
debug { "Successfully generated passport with ID #{record.id}" }
|
@@ -30,6 +40,33 @@ module SSO
|
|
30
40
|
end
|
31
41
|
end
|
32
42
|
|
43
|
+
def self.update_activity(passport_id:, request:)
|
44
|
+
record = find_valid_passport(passport_id) { |failure| return failure }
|
45
|
+
|
46
|
+
immediate_ip = request.respond_to?(:remote_ip) ? request.remote_ip : request.ip
|
47
|
+
if record.insider?
|
48
|
+
proxied_ip = request['ip']
|
49
|
+
unless proxied_ip
|
50
|
+
warn { "There should have been a proxied IP param, but there was none. I will use the immediare IP #{immediate_ip} now." }
|
51
|
+
proxied_ip = immediate_ip
|
52
|
+
end
|
53
|
+
attributes = { ip: proxied_ip, agent: request['agent'], device: request['device_id'] }
|
54
|
+
else
|
55
|
+
attributes = { ip: immediate_ip, agent: request.user_agent, device: request.params['device_id'] }
|
56
|
+
end
|
57
|
+
attributes.merge! activity_at: Time.now
|
58
|
+
|
59
|
+
record.stamps ||= {} # <- Not thread-safe, this may potentially delete all existing stamps, I guess
|
60
|
+
record.stamps[attributes[:ip]] = Time.now.to_i
|
61
|
+
|
62
|
+
debug { "Updating activity of #{record.insider? ? :insider : :outsider} Passport #{passport_id.inspect} using IP #{attributes[:ip]} agent #{attributes[:agent]} and device #{attributes[:device]}" }
|
63
|
+
if record.update_attributes(attributes)
|
64
|
+
Operations.success :passport_metadata_updated
|
65
|
+
else
|
66
|
+
Operations.failure :could_not_update_passport_activity, object: record.errors.to_hash
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
33
70
|
def self.register_authorization_grant(passport_id:, token:)
|
34
71
|
record = find_valid_passport(passport_id) { |failure| return failure }
|
35
72
|
access_grant = find_valid_access_grant(token) { |failure| return failure }
|
@@ -47,7 +84,9 @@ module SSO
|
|
47
84
|
access_token = find_valid_access_token(access_token) { |failure| return failure }
|
48
85
|
record = find_valid_passport_by_grant_id(access_grant.id) { |failure| return failure }
|
49
86
|
|
50
|
-
|
87
|
+
is_insider = access_token.scopes.include? 'insider'
|
88
|
+
|
89
|
+
if record.update_attributes oauth_access_token_id: access_token.id, insider: is_insider
|
51
90
|
debug { "Successfully augmented Passport #{record.id} with Access Token ID #{access_token.id} which is #{access_token.token}" }
|
52
91
|
Operations.success :passport_known_by_grant_augmented_with_access_token
|
53
92
|
else
|
@@ -55,31 +94,33 @@ module SSO
|
|
55
94
|
end
|
56
95
|
end
|
57
96
|
|
58
|
-
def self.
|
97
|
+
def self.register_access_token_from_id(passport_id:, access_token:)
|
59
98
|
access_token = find_valid_access_token(access_token) { |failure| return failure }
|
60
99
|
record = find_valid_passport(passport_id) { |failure| return failure }
|
61
100
|
|
62
|
-
|
63
|
-
|
101
|
+
is_insider = access_token.scopes.include? 'insider'
|
102
|
+
|
103
|
+
if record.update_attributes oauth_access_token_id: access_token.id, insider: is_insider
|
104
|
+
debug { "Successfully augmented #{is_insider ? :insider : :outsider} Passport #{record.id} with Access Token ID #{access_token.id} which is #{access_token.token}" }
|
64
105
|
Operations.success :passport_augmented_with_access_token
|
65
106
|
else
|
66
107
|
Operations.failure :could_not_augment_passport_with_access_token
|
67
108
|
end
|
68
109
|
end
|
69
110
|
|
70
|
-
def self.logout(passport_id
|
71
|
-
|
72
|
-
debug { "Attemting to logout Passport groups of Passport IDs #{passport_id.inspect} and #{provider_passport_id.inspect}..." }
|
73
|
-
else
|
74
|
-
debug { "Should logout Passport groups now, but don't know which ones. Moving on..." }
|
75
|
-
return Operations.success :nothing_to_revoke_from
|
76
|
-
end
|
111
|
+
def self.logout(passport_id:)
|
112
|
+
return Operations.failure(:missing_passport_id) if passport_id.blank?
|
77
113
|
|
78
|
-
|
79
|
-
|
80
|
-
|
114
|
+
debug { "Logging out Passport with ID #{passport_id.inspect}" }
|
115
|
+
record = backend.find_by_id passport_id
|
116
|
+
return Operations.success(:passport_does_not_exist) unless record
|
117
|
+
return Operations.success(:passport_already_revoked) if record.revoked_at
|
81
118
|
|
82
|
-
|
119
|
+
if record.update_attributes revoked_at: Time.now, revoke_reason: :logout
|
120
|
+
Operations.success :passport_revoked
|
121
|
+
else
|
122
|
+
Operations.failure :backend_could_not_revoke_passport
|
123
|
+
end
|
83
124
|
end
|
84
125
|
|
85
126
|
private
|
@@ -89,7 +130,6 @@ module SSO
|
|
89
130
|
return record if record
|
90
131
|
|
91
132
|
debug { "Could not find valid passport with ID #{id.inspect}" }
|
92
|
-
debug { "All I have is #{backend.all.inspect}" }
|
93
133
|
yield Operations.failure :passport_not_found if block_given?
|
94
134
|
nil
|
95
135
|
end
|
@@ -127,18 +167,6 @@ module SSO
|
|
127
167
|
end
|
128
168
|
end
|
129
169
|
|
130
|
-
def self.logout_cluster(passport_id)
|
131
|
-
unless passport_id.present? && record = backend.find_by_id(passport_id)
|
132
|
-
debug { "Cannot revoke Passport group of Passport ID #{passport_id.inspect} because it does not exist." }
|
133
|
-
return 0
|
134
|
-
end
|
135
|
-
|
136
|
-
debug { "Revoking Passport group #{record.group_id.inspect} of Passport ID #{passport_id.inspect}" }
|
137
|
-
affected_row_count = backend.where(group_id: record.group_id).update_all revoked_at: Time.now, revoke_reason: :logout
|
138
|
-
debug { "Successfully revoked #{affected_row_count.inspect} Passports." }
|
139
|
-
affected_row_count
|
140
|
-
end
|
141
|
-
|
142
170
|
def self.backend
|
143
171
|
::SSO::Server::Passport
|
144
172
|
end
|
@@ -27,7 +27,7 @@ module SSO
|
|
27
27
|
request = warden.request
|
28
28
|
session = warden.env['rack.session']
|
29
29
|
|
30
|
-
debug { "Generating a passport for user #{user.id.inspect} for the session
|
30
|
+
debug { "Generating a passport for user #{user.id.inspect} for the session with the SSO server..." }
|
31
31
|
attributes = { owner_id: user.id, ip: request.ip, agent: request.user_agent }
|
32
32
|
|
33
33
|
generation = SSO::Server::Passports.generate attributes
|
@@ -26,7 +26,7 @@ module SSO
|
|
26
26
|
|
27
27
|
def call
|
28
28
|
debug { 'Before warden destroys the passport in the cookie, it will revoke all connected Passports as well.' }
|
29
|
-
revoking = Passports.logout passport_id: params['passport_id']
|
29
|
+
revoking = Passports.logout passport_id: params['passport_id']
|
30
30
|
|
31
31
|
error { 'Could not revoke the Passports.' } if revoking.failure?
|
32
32
|
debug { 'Finished.' }
|
@@ -4,6 +4,7 @@ module SSO
|
|
4
4
|
module Strategies
|
5
5
|
class Passport < ::Warden::Strategies::Base
|
6
6
|
include ::SSO::Logging
|
7
|
+
include ::SSO::Benchmarking
|
7
8
|
|
8
9
|
def valid?
|
9
10
|
params['auth_version'].to_s != '' && params['state'] != ''
|
@@ -12,12 +13,7 @@ module SSO
|
|
12
13
|
def authenticate!
|
13
14
|
debug { 'Authenticating from Passport...' }
|
14
15
|
|
15
|
-
authentication =
|
16
|
-
time = Benchmark.realtime do
|
17
|
-
authentication = ::SSO::Server::Authentications::Passport.new(request).authenticate
|
18
|
-
end
|
19
|
-
|
20
|
-
info { "The Passport verification took #{(time * 1000).round}ms" }
|
16
|
+
authentication = passport_authentication
|
21
17
|
|
22
18
|
if authentication.success?
|
23
19
|
debug { 'Authentication from Passport successful.' }
|
@@ -32,6 +28,12 @@ module SSO
|
|
32
28
|
::SSO.config.exception_handler.call exception
|
33
29
|
end
|
34
30
|
|
31
|
+
def passport_authentication
|
32
|
+
benchmark 'Passport verification' do
|
33
|
+
::SSO::Server::Authentications::Passport.new(request).authenticate
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
35
37
|
end
|
36
38
|
end
|
37
39
|
end
|
data/lib/sso/server.rb
CHANGED
@@ -11,13 +11,12 @@ require 'sso/server/errors'
|
|
11
11
|
require 'sso/server/passport'
|
12
12
|
require 'sso/server/passports'
|
13
13
|
require 'sso/server/geolocations'
|
14
|
-
require 'sso/server/configuration'
|
15
|
-
require 'sso/server/configure'
|
16
14
|
require 'sso/server/engine'
|
17
15
|
|
18
16
|
require 'sso/server/authentications/passport'
|
19
17
|
require 'sso/server/middleware/passport_verification'
|
20
|
-
require 'sso/server/middleware/
|
18
|
+
require 'sso/server/middleware/passport_destruction'
|
19
|
+
require 'sso/server/middleware/passport_exchange'
|
21
20
|
|
22
21
|
require 'sso/server/warden/hooks/after_authentication'
|
23
22
|
require 'sso/server/warden/hooks/before_logout'
|
@@ -16,7 +16,7 @@ class SessionsController < ApplicationController
|
|
16
16
|
warden.authenticate! :password
|
17
17
|
|
18
18
|
if session[:return_path]
|
19
|
-
debug { "Sending
|
19
|
+
debug { "Sending you back to #{session[:return_path]}" }
|
20
20
|
redirect_to session[:return_path]
|
21
21
|
session[:return_path] = nil
|
22
22
|
else
|
@@ -32,5 +32,11 @@ module Dummy
|
|
32
32
|
manager.serialize_from_session { |id| User.find_by_id(id) }
|
33
33
|
end
|
34
34
|
|
35
|
+
config.middleware.insert_after ::Warden::Manager, ::SSO::Server::Middleware::PassportVerification
|
36
|
+
config.middleware.insert_after ::Warden::Manager, ::SSO::Server::Middleware::PassportDestruction
|
37
|
+
config.middleware.insert_after ::Warden::Manager, ::SSO::Server::Middleware::PassportExchange
|
38
|
+
config.middleware.insert_after ::Warden::Manager, ::SSO::Server::Doorkeeper::GrantMarker
|
39
|
+
config.middleware.insert_after ::Warden::Manager, ::SSO::Server::Doorkeeper::AccessTokenMarker
|
40
|
+
|
35
41
|
end
|
36
42
|
end
|