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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/lib/sso/client/README.md +17 -2
  3. data/lib/sso/client/authentications/passport.rb +176 -0
  4. data/lib/sso/client/passport.rb +7 -3
  5. data/lib/sso/client/passport_verifier.rb +130 -0
  6. data/lib/sso/client/warden/hooks/after_fetch.rb +32 -81
  7. data/lib/sso/client/warden/strategies/passport.rb +43 -0
  8. data/lib/sso/client.rb +3 -0
  9. data/lib/sso/server/authentications/passport.rb +16 -49
  10. data/lib/sso/server/configuration.rb +36 -6
  11. data/lib/sso/server/doorkeeper/access_token_marker.rb +7 -7
  12. data/lib/sso/server/middleware/passport_destruction.rb +40 -0
  13. data/lib/sso/server/middleware/{passport_creation.rb → passport_exchange.rb} +15 -11
  14. data/lib/sso/server/middleware/passport_verification.rb +2 -2
  15. data/lib/sso/server/passport.rb +43 -15
  16. data/lib/sso/server/passports.rb +59 -31
  17. data/lib/sso/server/warden/hooks/after_authentication.rb +1 -1
  18. data/lib/sso/server/warden/hooks/before_logout.rb +1 -1
  19. data/lib/sso/server/warden/strategies/passport.rb +8 -6
  20. data/lib/sso/server.rb +2 -3
  21. data/spec/dummy/app/controllers/sessions_controller.rb +1 -1
  22. data/spec/dummy/config/application.rb +6 -0
  23. data/spec/dummy/config/initializers/sso.rb +10 -6
  24. data/spec/dummy/config/initializers/warden.rb +3 -11
  25. data/spec/dummy/db/migrate/20150303132931_create_passports_table.rb +29 -15
  26. data/spec/dummy/db/schema.rb +10 -5
  27. data/spec/integration/oauth/authorization_code_spec.rb +80 -10
  28. data/spec/integration/oauth/{password_verification_spec.rb → password_spec.rb} +39 -3
  29. data/spec/lib/sso/client/authentications/passport_spec.rb +92 -0
  30. data/spec/{integration/oauth → lib/sso/client/warden/hooks}/after_fetch_spec.rb +4 -3
  31. data/spec/lib/sso/server/middleware/passport_destruction_spec.rb +33 -0
  32. data/spec/lib/sso/server/passports_spec.rb +104 -0
  33. data/spec/spec_helper.rb +2 -0
  34. data/spec/support/factories/doorkeeper/application.rb +0 -3
  35. data/spec/support/factories/server/passport.rb +5 -1
  36. data/spec/support/factories/server/user.rb +1 -1
  37. 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
- # yield Operations.failure :user_not_encapsulated if passport.user.blank?
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 ||= backend.find_by_id passport_id
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
- debug { "Will update activity of Passport #{passport.id} if neccesary..." }
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 user/Passport is the incoming grant token.
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 is always set because it was set in this very response
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
- generation = ::SSO::Server::Passports.register_access_token passport_id: local_passport_id, access_token: outgoing_access_token
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 generation.success?
59
- warn { 'The passport could not be generated. Destroying warden session.' }
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
- class PassportCreation
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
- creation = ::SSO::Server::Passports.generate owner_id: access_token.resource_owner_id, ip: remote_ip, agent: request.user_agent
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
- error { "Could not find newly generated Passport #{finding.code.inspect} - #{finding.object.inspect}"}
38
- return json_code :access_token_not_attached_to_valid_passport
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
- debug { "Attaching user to passport #{passport.inspect}" }
43
- passport.user = SSO.config.find_user_for_passport.call(passport: passport, ip: remote_ip)
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
@@ -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
- belongs_to :application, class_name: 'Doorkeeper::Application'
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
- result = nil
51
- time = Benchmark.realtime do
52
- result = OpenSSL::HMAC.hexdigest user_state_digest, user_state_key, user_state_base
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
@@ -17,10 +17,20 @@ module SSO
17
17
  Operations.failure :backend_error, object: exception
18
18
  end
19
19
 
20
- def self.generate(owner_id:, ip:, agent:)
21
- debug { "Generating Passport for user ID #{owner_id.inspect} and IP #{ip.inspect} and Agent #{agent.inspect}" }
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 = backend.create owner_id: owner_id, ip: ip, agent: agent, application_id: 0
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
- if record.update_attribute :oauth_access_token_id, access_token.id
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.register_access_token(passport_id:, access_token:)
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
- if record.update_attribute :oauth_access_token_id, access_token.id
63
- debug { "Successfully augmented Passport #{record.id} with Access Token ID #{access_token.id} which is #{access_token.token}" }
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:, provider_passport_id:)
71
- if passport_id.present? || provider_passport_id.present?
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
- count = 0
79
- count += logout_cluster(passport_id) if passport_id.present?
80
- count += logout_cluster(provider_passport_id) if provider_passport_id.present?
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
- Operations.success :passports_revoked, object: count
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 cookie at the SSO server..." }
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'], provider_passport_id: session['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 = nil
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/passport_creation'
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 tou back to #{session[:return_path]}" }
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