omniauth-saml 2.2.3 → 2.2.5
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/CHANGELOG.md +19 -1
- data/README.md +19 -12
- data/lib/omniauth/strategies/saml.rb +76 -27
- data/lib/omniauth-saml/version.rb +1 -1
- data/spec/omniauth/strategies/saml_spec.rb +271 -100
- metadata +3 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7d346f41f4069110547ddd73e4d9f7b23196d8519871da44f0eb42f2176c5fe6
|
|
4
|
+
data.tar.gz: 7451c766a513d52948fc07e0ac971e6e5d1b4392d822991444db38d72a66c835
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f93c043e99ccd8521877c45d2627cc87a150c42fb086b394172be4a12d02c28b816fec390810f934bdf10b07f68049ee222d15dc2a1e50e623e155b90951fca1
|
|
7
|
+
data.tar.gz: ae4440bfcb758760cd1f15b797d2f9b0b339a4ecfebd16e80834359c247cb335c15459fd509acf71364971ed5ef9d8d7009224f2996c3fbfd1ad54a92b26282d
|
data/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,28 @@
|
|
|
1
|
+
<a name="v2.2.5"></a>
|
|
2
|
+
### v2.2.5 (2023-07-25)
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
#### Features
|
|
6
|
+
|
|
7
|
+
* Support RelayState binding by default during SSO ([a508436](/../../commit/a508436))
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
<a name="v2.2.4"></a>
|
|
11
|
+
### v2.2.4 (2025-05-14)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
#### Bug Fixes
|
|
15
|
+
|
|
16
|
+
* remove :idp_cert_fingerprint_validator ([c573690](/../../commit/c573690))
|
|
17
|
+
* Fix GHSA-cgp2-2cmh-pf7x
|
|
18
|
+
|
|
1
19
|
<a name="v2.2.3"></a>
|
|
2
20
|
### v2.2.3 (2025-03-12)
|
|
3
21
|
|
|
4
22
|
|
|
5
23
|
#### Features
|
|
6
24
|
|
|
7
|
-
* new release 2.2.3 ([
|
|
25
|
+
* new release 2.2.3 ([34eb354](/../../commit/34eb354))
|
|
8
26
|
|
|
9
27
|
|
|
10
28
|
#### Bug Fixes
|
data/README.md
CHANGED
|
@@ -39,7 +39,6 @@ use OmniAuth::Strategies::SAML,
|
|
|
39
39
|
:encryption => []
|
|
40
40
|
},
|
|
41
41
|
:idp_cert_fingerprint => "E7:91:B2:E1:...",
|
|
42
|
-
:idp_cert_fingerprint_validator => lambda { |fingerprint| fingerprint },
|
|
43
42
|
:name_identifier_format => "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
|
|
44
43
|
```
|
|
45
44
|
|
|
@@ -66,7 +65,6 @@ Rails.application.config.middleware.use OmniAuth::Builder do
|
|
|
66
65
|
:encryption => []
|
|
67
66
|
},
|
|
68
67
|
:idp_cert_fingerprint => "E7:91:B2:E1:...",
|
|
69
|
-
:idp_cert_fingerprint_validator => lambda { |fingerprint| fingerprint },
|
|
70
68
|
:name_identifier_format => "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
|
|
71
69
|
end
|
|
72
70
|
```
|
|
@@ -103,7 +101,18 @@ Note that when [integrating with Devise](#devise-integration), the URL path will
|
|
|
103
101
|
* `:slo_default_relay_state` - The value to use as default `RelayState` for single log outs. The
|
|
104
102
|
value can be a string, or a `Proc` (or other object responding to `call`). The `request`
|
|
105
103
|
instance will be passed to this callable if it has an arity of 1. If the value is a string,
|
|
106
|
-
the string will be returned, when the `RelayState` is called.
|
|
104
|
+
the string will be returned, when the `RelayState` is called.
|
|
105
|
+
The value is assumed to be safe and is not validated by `:slo_relay_state_validator`.
|
|
106
|
+
Optional.
|
|
107
|
+
|
|
108
|
+
* `:slo_enabled` - Enables or disables Single Logout (SLO). Set to `false` to disable SLO. Defaults to `true`. Optional.
|
|
109
|
+
|
|
110
|
+
* `:slo_relay_state_validator` - A callable used to validate any `RelayState` before performing the redirect
|
|
111
|
+
in Single Logout flows. The callable receives the RelayState value and the current Rack request.
|
|
112
|
+
If unset, the default validator is used. The default validator allows only relative paths beginning
|
|
113
|
+
with `/` and rejects absolute URLs, invalid URIs, protocol-relative URLs, and other schemes.
|
|
114
|
+
If the given `RelayState` is considered invalid then the `slo_default_relay_state` value is used for the SLO redirect.
|
|
115
|
+
Optional.
|
|
107
116
|
|
|
108
117
|
* `:idp_sso_service_url_runtime_params` - A dynamic mapping of request params that exist
|
|
109
118
|
during the request phase of OmniAuth that should to be sent to the IdP after a specific
|
|
@@ -112,20 +121,16 @@ Note that when [integrating with Devise](#devise-integration), the URL path will
|
|
|
112
121
|
`original_param_value`. Optional.
|
|
113
122
|
|
|
114
123
|
* `:idp_cert` - The identity provider's certificate in PEM format. Takes precedence
|
|
115
|
-
over the fingerprint option below. This option or `:idp_cert_multi` or `:idp_cert_fingerprint`
|
|
124
|
+
over the fingerprint option below. This option or `:idp_cert_multi` or `:idp_cert_fingerprint` must
|
|
116
125
|
be present.
|
|
117
|
-
|
|
126
|
+
|
|
118
127
|
* `:idp_cert_multi` - Multiple identity provider certificates in PEM format. Takes precedence
|
|
119
|
-
over the fingerprint option below. This option `:idp_cert` or `:idp_cert_fingerprint`
|
|
128
|
+
over the fingerprint option below. This option `:idp_cert` or `:idp_cert_fingerprint` must
|
|
120
129
|
be present.
|
|
121
130
|
|
|
122
131
|
* `:idp_cert_fingerprint` - The SHA1 fingerprint of the certificate, e.g.
|
|
123
132
|
"90:CC:16:F0:8D:...". This is provided from the identity provider when setting up
|
|
124
|
-
the relationship. This option or `:idp_cert` or `:idp_cert_multi`
|
|
125
|
-
|
|
126
|
-
* `:idp_cert_fingerprint_validator` - A lambda that MUST accept one parameter
|
|
127
|
-
(the fingerprint), verify if it is valid and return it if successful. This option
|
|
128
|
-
or `:idp_cert` or `:idp_cert_multi` or `:idp_cert_fingerprint` MUST be present.
|
|
133
|
+
the relationship. This option or `:idp_cert` or `:idp_cert_multi` MUST be present.
|
|
129
134
|
|
|
130
135
|
* `:name_identifier_format` - Used during SP-initiated SSO. Describes the format of
|
|
131
136
|
the username required by this application. If you need the email address, use
|
|
@@ -198,7 +203,9 @@ Single Logout can be Service Provider initiated or Identity Provider initiated.
|
|
|
198
203
|
For SP initiated logout, the `idp_slo_service_url` option must be set to the logout url on the IdP,
|
|
199
204
|
and users directed to `user_saml_omniauth_authorize_path + '/spslo'` after logging out locally. For
|
|
200
205
|
IdP initiated logout, logout requests from the IdP should go to `/auth/saml/slo` (this can be
|
|
201
|
-
advertised in metadata by setting the `single_logout_service_url` config option).
|
|
206
|
+
advertised in metadata by setting the `single_logout_service_url` config option). If you wish to
|
|
207
|
+
disable Single Logout entirely (both SP and IdP initiated), set `:slo_enabled => false`; the `/auth/saml/slo`
|
|
208
|
+
and `/auth/saml/spslo` endpoints will then respond with HTTP 501 Not Implemented.
|
|
202
209
|
|
|
203
210
|
When using Devise as an authentication solution, the SP initiated flow can be integrated
|
|
204
211
|
in the `SessionsController#destroy` action.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require 'omniauth'
|
|
2
2
|
require 'ruby-saml'
|
|
3
|
+
require 'uri'
|
|
3
4
|
|
|
4
5
|
module OmniAuth
|
|
5
6
|
module Strategies
|
|
@@ -13,7 +14,7 @@ module OmniAuth
|
|
|
13
14
|
RUBYSAML_RESPONSE_OPTIONS = OneLogin::RubySaml::Response::AVAILABLE_OPTIONS
|
|
14
15
|
|
|
15
16
|
option :name_identifier_format, nil
|
|
16
|
-
option :idp_sso_service_url_runtime_params, {}
|
|
17
|
+
option :idp_sso_service_url_runtime_params, { RelayState: 'RelayState' }
|
|
17
18
|
option :request_attributes, [
|
|
18
19
|
{ :name => 'email', :name_format => 'urn:oasis:names:tc:SAML:2.0:attrname-format:basic', :friendly_name => 'Email address' },
|
|
19
20
|
{ :name => 'name', :name_format => 'urn:oasis:names:tc:SAML:2.0:attrname-format:basic', :friendly_name => 'Full name' },
|
|
@@ -27,7 +28,26 @@ module OmniAuth
|
|
|
27
28
|
first_name: ["first_name", "firstname", "firstName"],
|
|
28
29
|
last_name: ["last_name", "lastname", "lastName"]
|
|
29
30
|
}
|
|
31
|
+
DEFAULT_SLO_RELAY_STATE_VALIDATOR = lambda do |relay_state, _request|
|
|
32
|
+
return true if relay_state.nil? || relay_state == ""
|
|
33
|
+
|
|
34
|
+
return false if relay_state.start_with?("//")
|
|
35
|
+
|
|
36
|
+
begin
|
|
37
|
+
uri = URI.parse(relay_state)
|
|
38
|
+
rescue URI::Error
|
|
39
|
+
return false
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
return false unless uri.relative?
|
|
43
|
+
|
|
44
|
+
path = uri.path
|
|
45
|
+
path && path.start_with?("/")
|
|
46
|
+
end
|
|
47
|
+
|
|
30
48
|
option :slo_default_relay_state
|
|
49
|
+
option :slo_enabled, true
|
|
50
|
+
option :slo_relay_state_validator, DEFAULT_SLO_RELAY_STATE_VALIDATOR
|
|
31
51
|
option :uid_attribute
|
|
32
52
|
option :idp_slo_session_destroy, proc { |_env, session| session.clear }
|
|
33
53
|
|
|
@@ -43,9 +63,6 @@ module OmniAuth
|
|
|
43
63
|
raise OmniAuth::Strategies::SAML::ValidationError.new("SAML response missing") unless request.params["SAMLResponse"]
|
|
44
64
|
|
|
45
65
|
with_settings do |settings|
|
|
46
|
-
# Call a fingerprint validation method if there's one
|
|
47
|
-
validate_fingerprint(settings) if options.idp_cert_fingerprint_validator
|
|
48
|
-
|
|
49
66
|
handle_response(request.params["SAMLResponse"], options_for_response_object, settings) do
|
|
50
67
|
super
|
|
51
68
|
end
|
|
@@ -76,8 +93,12 @@ module OmniAuth
|
|
|
76
93
|
if on_subpath?(:metadata)
|
|
77
94
|
other_phase_for_metadata
|
|
78
95
|
elsif on_subpath?(:slo)
|
|
96
|
+
return slo_disabled_response unless slo_enabled?
|
|
97
|
+
|
|
79
98
|
other_phase_for_slo
|
|
80
99
|
elsif on_subpath?(:spslo)
|
|
100
|
+
return slo_disabled_response unless slo_enabled?
|
|
101
|
+
|
|
81
102
|
other_phase_for_spslo
|
|
82
103
|
else
|
|
83
104
|
call_app!
|
|
@@ -118,6 +139,22 @@ module OmniAuth
|
|
|
118
139
|
nil
|
|
119
140
|
end
|
|
120
141
|
|
|
142
|
+
def mock_request_call
|
|
143
|
+
# Per SAML 2.0, if a RelayState param is passed, IDPs "MUST place the exact RelayState
|
|
144
|
+
# data it received with the request into the corresponding RelayState parameter in the response."
|
|
145
|
+
#
|
|
146
|
+
# By default, the "mock" `OmniAuth::Strategy` implementation will forward along any URL params,
|
|
147
|
+
# so we can in turn take any POSTed RelayState params and put them in the GET query string:
|
|
148
|
+
query_hash = request.GET.merge!(additional_params_for_authn_request.slice('RelayState'))
|
|
149
|
+
query_string = Rack::Utils.build_query(query_hash)
|
|
150
|
+
|
|
151
|
+
request.set_header(Rack::QUERY_STRING, query_string)
|
|
152
|
+
request.set_header(Rack::RACK_REQUEST_QUERY_STRING, query_string)
|
|
153
|
+
request.set_header(Rack::RACK_REQUEST_QUERY_HASH, query_hash)
|
|
154
|
+
|
|
155
|
+
super
|
|
156
|
+
end
|
|
157
|
+
|
|
121
158
|
private
|
|
122
159
|
|
|
123
160
|
def request_path_pattern
|
|
@@ -146,19 +183,34 @@ module OmniAuth
|
|
|
146
183
|
|
|
147
184
|
def slo_relay_state
|
|
148
185
|
if request.params.has_key?("RelayState") && request.params["RelayState"] != ""
|
|
149
|
-
request.params["RelayState"]
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
if slo_default_relay_state.respond_to?(:call)
|
|
153
|
-
if slo_default_relay_state.arity == 1
|
|
154
|
-
slo_default_relay_state.call(request)
|
|
155
|
-
else
|
|
156
|
-
slo_default_relay_state.call
|
|
157
|
-
end
|
|
158
|
-
else
|
|
159
|
-
slo_default_relay_state
|
|
160
|
-
end
|
|
186
|
+
relay_state = request.params["RelayState"]
|
|
187
|
+
|
|
188
|
+
return relay_state if valid_slo_relay_state?(relay_state)
|
|
161
189
|
end
|
|
190
|
+
|
|
191
|
+
default_slo_relay_state
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def valid_slo_relay_state?(relay_state)
|
|
195
|
+
validator = options.slo_relay_state_validator
|
|
196
|
+
|
|
197
|
+
return !!call_slo_relay_state_validator(validator, relay_state) if validator.respond_to?(:call)
|
|
198
|
+
|
|
199
|
+
!!validator
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def call_slo_relay_state_validator(validator, relay_state)
|
|
203
|
+
return validator.call if validator.arity.zero?
|
|
204
|
+
return validator.call(relay_state) if validator.arity == 1
|
|
205
|
+
validator.call(relay_state, request)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def default_slo_relay_state
|
|
209
|
+
slo_default_relay_state = options.slo_default_relay_state
|
|
210
|
+
|
|
211
|
+
return slo_default_relay_state unless slo_default_relay_state.respond_to?(:call)
|
|
212
|
+
return slo_default_relay_state.call if slo_default_relay_state.arity.zero?
|
|
213
|
+
slo_default_relay_state.call(request)
|
|
162
214
|
end
|
|
163
215
|
|
|
164
216
|
def handle_logout_response(raw_response, settings)
|
|
@@ -218,17 +270,6 @@ module OmniAuth
|
|
|
218
270
|
yield OneLogin::RubySaml::Settings.new(options)
|
|
219
271
|
end
|
|
220
272
|
|
|
221
|
-
def validate_fingerprint(settings)
|
|
222
|
-
fingerprint_exists = options.idp_cert_fingerprint_validator[response_fingerprint]
|
|
223
|
-
|
|
224
|
-
unless fingerprint_exists
|
|
225
|
-
raise OmniAuth::Strategies::SAML::ValidationError.new("Non-existent fingerprint")
|
|
226
|
-
end
|
|
227
|
-
|
|
228
|
-
# id_cert_fingerprint becomes the given fingerprint if it exists
|
|
229
|
-
settings.idp_cert_fingerprint = fingerprint_exists
|
|
230
|
-
end
|
|
231
|
-
|
|
232
273
|
def options_for_response_object
|
|
233
274
|
# filter options to select only extra parameters
|
|
234
275
|
opts = options.select {|k,_| RUBYSAML_RESPONSE_OPTIONS.include?(k.to_sym)}
|
|
@@ -273,6 +314,14 @@ module OmniAuth
|
|
|
273
314
|
end
|
|
274
315
|
end
|
|
275
316
|
|
|
317
|
+
def slo_enabled?
|
|
318
|
+
!!options[:slo_enabled]
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
def slo_disabled_response
|
|
322
|
+
Rack::Response.new("Not Implemented", 501, { "Content-Type" => "text/html" }).finish
|
|
323
|
+
end
|
|
324
|
+
|
|
276
325
|
def add_request_attributes_to(settings)
|
|
277
326
|
settings.attribute_consuming_service.service_name options.attribute_service_name
|
|
278
327
|
settings.sp_entity_id = options.sp_entity_id
|
|
@@ -6,10 +6,6 @@ RSpec::Matchers.define :fail_with do |message|
|
|
|
6
6
|
end
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
-
def post_xml(xml = :example_response, opts = {})
|
|
10
|
-
post "/auth/saml/callback", opts.merge({'SAMLResponse' => load_xml(xml)})
|
|
11
|
-
end
|
|
12
|
-
|
|
13
9
|
describe OmniAuth::Strategies::SAML, :type => :strategy do
|
|
14
10
|
include OmniAuth::Test::StrategyTestCase
|
|
15
11
|
|
|
@@ -34,6 +30,55 @@ describe OmniAuth::Strategies::SAML, :type => :strategy do
|
|
|
34
30
|
end
|
|
35
31
|
let(:strategy) { [OmniAuth::Strategies::SAML, saml_options] }
|
|
36
32
|
|
|
33
|
+
shared_examples 'validating RelayState param' do
|
|
34
|
+
context 'when slo_relay_state_validator is not defined and default' do
|
|
35
|
+
[
|
|
36
|
+
['/signed-out', '//attacker.test', '%2Fsigned-out'],
|
|
37
|
+
['/signed-out', 'javascript:alert(1)', '%2Fsigned-out'],
|
|
38
|
+
['/signed-out', 'https://example.com/logout', '%2Fsigned-out'],
|
|
39
|
+
['/signed-out', 'https://example.com/logout?param=1&two=two', '%2Fsigned-out'],
|
|
40
|
+
['/signed-out', '/', '%2F'],
|
|
41
|
+
['', '//attacker.test', ''],
|
|
42
|
+
['', '/team/logout', '%2Fteam%2Flogout'],
|
|
43
|
+
].each do |slo_default_relay_state, relay_state_param, expected_relay_state|
|
|
44
|
+
context "when slo_default_relay_state: #{slo_default_relay_state.inspect}, relay_state_param: #{relay_state_param.inspect}" do
|
|
45
|
+
let(:saml_options) { super().merge(slo_default_relay_state: slo_default_relay_state) }
|
|
46
|
+
let(:params) { super().merge('RelayState' => relay_state_param) }
|
|
47
|
+
|
|
48
|
+
it { is_expected.to be_redirect.and have_attributes(location: a_string_including("RelayState=#{expected_relay_state}")) }
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
context 'when slo_relay_state_validator is overridden' do
|
|
54
|
+
[
|
|
55
|
+
['/signed-out', proc { |state| state.start_with?('https://trusted.example.com') }, 'https://trusted.example.com/logout', 'https%3A%2F%2Ftrusted.example.com%2Flogout'],
|
|
56
|
+
['/signed-out', proc { |state| state.start_with?('https://trusted.example.com') }, 'https://attacker.test/logout', '%2Fsigned-out'],
|
|
57
|
+
['/signed-out', proc { |state| state.start_with?('https://trusted.example.com') }, '/safe/path', '%2Fsigned-out'],
|
|
58
|
+
['/signed-out', proc { |state, req| state == req.params['RelayState'] }, '/team/logout', '%2Fteam%2Flogout'],
|
|
59
|
+
['/signed-out', nil, '//attacker.test', '%2Fsigned-out'],
|
|
60
|
+
['/signed-out', false, '//attacker.test', '%2Fsigned-out'],
|
|
61
|
+
['/signed-out', proc { |_| false }, '//attacker.test', '%2Fsigned-out'],
|
|
62
|
+
['/signed-out', proc { |_| true }, 'javascript:alert(1)', 'javascript%3Aalert%281%29'],
|
|
63
|
+
[nil, true, 'https://example.com/logout', 'https%3A%2F%2Fexample.com%2Flogout'],
|
|
64
|
+
[nil, true, 'javascript:alert(1)', 'javascript%3Aalert%281%29'],
|
|
65
|
+
[nil, true, '/', '%2F'],
|
|
66
|
+
].each do |slo_default_relay_state, slo_relay_state_validator, relay_state_param, expected_relay_state|
|
|
67
|
+
context "when slo_default_relay_state: #{slo_default_relay_state.inspect}, slo_relay_state_validator: #{slo_relay_state_validator.inspect}, relay_state_param: #{relay_state_param.inspect}" do
|
|
68
|
+
let(:saml_options) do
|
|
69
|
+
super().merge(
|
|
70
|
+
slo_default_relay_state: slo_default_relay_state,
|
|
71
|
+
slo_relay_state_validator: slo_relay_state_validator,
|
|
72
|
+
)
|
|
73
|
+
end
|
|
74
|
+
let(:params) { super().merge('RelayState' => relay_state_param) }
|
|
75
|
+
|
|
76
|
+
it { is_expected.to be_redirect.and have_attributes(location: a_string_including("RelayState=#{expected_relay_state}")) }
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
37
82
|
describe 'POST /auth/saml' do
|
|
38
83
|
context 'without idp runtime params present' do
|
|
39
84
|
before do
|
|
@@ -63,6 +108,33 @@ describe OmniAuth::Strategies::SAML, :type => :strategy do
|
|
|
63
108
|
end
|
|
64
109
|
end
|
|
65
110
|
|
|
111
|
+
context 'with RelayState param' do
|
|
112
|
+
before do
|
|
113
|
+
post '/auth/saml', 'RelayState' => 'RELAY_STATE_VALUE'
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
it 'should get authentication page' do
|
|
117
|
+
expect(last_response).to be_redirect
|
|
118
|
+
expect(last_response.location).to match(
|
|
119
|
+
/\Ahttps:\/\/idp.sso.example.com\/signon\/29490\?SAMLRequest=.*&RelayState=RELAY_STATE_VALUE\z/,
|
|
120
|
+
)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
context 'when test_mode is enabled' do
|
|
124
|
+
around do |example|
|
|
125
|
+
OmniAuth.config.test_mode = true
|
|
126
|
+
example.run
|
|
127
|
+
ensure
|
|
128
|
+
OmniAuth.config.test_mode = false
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
it 'should redirect to local saml callback page' do
|
|
132
|
+
expect(last_response).to be_redirect
|
|
133
|
+
expect(last_response.location).to eq('http://example.org/auth/saml/callback?RelayState=RELAY_STATE_VALUE')
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
66
138
|
context "when the assertion_consumer_service_url is the default" do
|
|
67
139
|
before :each do
|
|
68
140
|
saml_options[:compress_request] = false
|
|
@@ -118,24 +190,27 @@ describe OmniAuth::Strategies::SAML, :type => :strategy do
|
|
|
118
190
|
end
|
|
119
191
|
|
|
120
192
|
describe 'POST /auth/saml/callback' do
|
|
121
|
-
subject { last_response }
|
|
122
|
-
|
|
123
193
|
let(:xml) { :example_response }
|
|
194
|
+
let(:params) { { 'SAMLResponse' => load_xml(xml) } }
|
|
195
|
+
|
|
196
|
+
subject(:post_callback_response) do
|
|
197
|
+
post "/auth/saml/callback", params
|
|
198
|
+
end
|
|
124
199
|
|
|
125
200
|
before :each do
|
|
126
201
|
allow(Time).to receive(:now).and_return(Time.utc(2012, 11, 8, 20, 40, 00))
|
|
127
202
|
end
|
|
128
203
|
|
|
129
204
|
context "when the response is valid" do
|
|
130
|
-
before :each do
|
|
131
|
-
post_xml
|
|
132
|
-
end
|
|
133
|
-
|
|
134
205
|
it "should set the uid to the nameID in the SAML response" do
|
|
206
|
+
post_callback_response
|
|
207
|
+
|
|
135
208
|
expect(auth_hash['uid']).to eq '_1f6fcf6be5e13b08b1e3610e7ff59f205fbd814f23'
|
|
136
209
|
end
|
|
137
210
|
|
|
138
211
|
it "should set the raw info to all attributes" do
|
|
212
|
+
post_callback_response
|
|
213
|
+
|
|
139
214
|
expect(auth_hash['extra']['raw_info'].all.to_hash).to eq(
|
|
140
215
|
'first_name' => ['Rajiv'],
|
|
141
216
|
'last_name' => ['Manglani'],
|
|
@@ -146,42 +221,9 @@ describe OmniAuth::Strategies::SAML, :type => :strategy do
|
|
|
146
221
|
end
|
|
147
222
|
|
|
148
223
|
it "should set the response_object to the response object from ruby_saml response" do
|
|
149
|
-
|
|
150
|
-
end
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
context "when fingerprint is empty and there's a fingerprint validator" do
|
|
154
|
-
before :each do
|
|
155
|
-
saml_options.delete(:idp_cert_fingerprint)
|
|
156
|
-
saml_options[:idp_cert_fingerprint_validator] = fingerprint_validator
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
let(:fingerprint_validator) { lambda { |_| "C1:59:74:2B:E8:0C:6C:A9:41:0F:6E:83:F6:D1:52:25:45:58:89:FB" } }
|
|
160
|
-
|
|
161
|
-
context "when the fingerprint validator returns a truthy value" do
|
|
162
|
-
before { post_xml }
|
|
163
|
-
|
|
164
|
-
it "should set the uid to the nameID in the SAML response" do
|
|
165
|
-
expect(auth_hash['uid']).to eq '_1f6fcf6be5e13b08b1e3610e7ff59f205fbd814f23'
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
it "should set the raw info to all attributes" do
|
|
169
|
-
expect(auth_hash['extra']['raw_info'].all.to_hash).to eq(
|
|
170
|
-
'first_name' => ['Rajiv'],
|
|
171
|
-
'last_name' => ['Manglani'],
|
|
172
|
-
'email' => ['user@example.com'],
|
|
173
|
-
'company_name' => ['Example Company'],
|
|
174
|
-
'fingerprint' => 'C1:59:74:2B:E8:0C:6C:A9:41:0F:6E:83:F6:D1:52:25:45:58:89:FB'
|
|
175
|
-
)
|
|
176
|
-
end
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
context "when the fingerprint validator returns false" do
|
|
180
|
-
let(:fingerprint_validator) { lambda { |_| false } }
|
|
181
|
-
|
|
182
|
-
before { post_xml }
|
|
224
|
+
post_callback_response
|
|
183
225
|
|
|
184
|
-
|
|
226
|
+
expect(auth_hash['extra']['response_object']).to be_kind_of(OneLogin::RubySaml::Response)
|
|
185
227
|
end
|
|
186
228
|
end
|
|
187
229
|
|
|
@@ -189,24 +231,22 @@ describe OmniAuth::Strategies::SAML, :type => :strategy do
|
|
|
189
231
|
before :each do
|
|
190
232
|
saml_options.delete(:assertion_consumer_service_url)
|
|
191
233
|
OmniAuth.config.full_host = 'http://localhost:9080'
|
|
192
|
-
post_xml
|
|
193
234
|
end
|
|
194
235
|
|
|
195
236
|
it { is_expected.not_to fail_with(:invalid_ticket) }
|
|
196
237
|
end
|
|
197
238
|
|
|
198
239
|
context "when there is no SAMLResponse parameter" do
|
|
199
|
-
|
|
200
|
-
post '/auth/saml/callback'
|
|
201
|
-
end
|
|
240
|
+
let(:params) { {} }
|
|
202
241
|
|
|
203
242
|
it { is_expected.to fail_with(:invalid_ticket) }
|
|
204
243
|
end
|
|
205
244
|
|
|
206
245
|
context "when there is no name id in the XML" do
|
|
246
|
+
let(:xml) { :no_name_id }
|
|
247
|
+
|
|
207
248
|
before :each do
|
|
208
249
|
allow(Time).to receive(:now).and_return(Time.utc(2012, 11, 8, 23, 55, 00))
|
|
209
|
-
post_xml :no_name_id
|
|
210
250
|
end
|
|
211
251
|
|
|
212
252
|
it { is_expected.to fail_with(:invalid_ticket) }
|
|
@@ -215,43 +255,37 @@ describe OmniAuth::Strategies::SAML, :type => :strategy do
|
|
|
215
255
|
context "when the fingerprint is invalid" do
|
|
216
256
|
before :each do
|
|
217
257
|
saml_options[:idp_cert_fingerprint] = "00:00:00:00:00:0C:6C:A9:41:0F:6E:83:F6:D1:52:25:45:58:89:FB"
|
|
218
|
-
post_xml
|
|
219
258
|
end
|
|
220
259
|
|
|
221
260
|
it { is_expected.to fail_with(:invalid_ticket) }
|
|
222
261
|
end
|
|
223
262
|
|
|
224
263
|
context "when the digest is invalid" do
|
|
225
|
-
|
|
226
|
-
post_xml :digest_mismatch
|
|
227
|
-
end
|
|
264
|
+
let(:xml) { :digest_mismatch }
|
|
228
265
|
|
|
229
266
|
it { is_expected.to fail_with(:invalid_ticket) }
|
|
230
267
|
end
|
|
231
268
|
|
|
232
269
|
context "when the signature is invalid" do
|
|
233
|
-
|
|
234
|
-
post_xml :invalid_signature
|
|
235
|
-
end
|
|
270
|
+
let(:xml) { :invalid_signature }
|
|
236
271
|
|
|
237
272
|
it { is_expected.to fail_with(:invalid_ticket) }
|
|
238
273
|
end
|
|
239
274
|
|
|
240
275
|
context "when the response is stale" do
|
|
276
|
+
let(:xml) { :example_response }
|
|
277
|
+
|
|
241
278
|
before :each do
|
|
242
279
|
allow(Time).to receive(:now).and_return(Time.utc(2012, 11, 8, 20, 45, 00))
|
|
243
280
|
end
|
|
244
281
|
|
|
245
282
|
context "without :allowed_clock_drift option" do
|
|
246
|
-
before { post_xml :example_response }
|
|
247
|
-
|
|
248
283
|
it { is_expected.to fail_with(:invalid_ticket) }
|
|
249
284
|
end
|
|
250
285
|
|
|
251
286
|
context "with :allowed_clock_drift option" do
|
|
252
287
|
before :each do
|
|
253
288
|
saml_options[:allowed_clock_drift] = 60
|
|
254
|
-
post_xml :example_response
|
|
255
289
|
end
|
|
256
290
|
|
|
257
291
|
it { is_expected.to_not fail_with(:invalid_ticket) }
|
|
@@ -259,14 +293,16 @@ describe OmniAuth::Strategies::SAML, :type => :strategy do
|
|
|
259
293
|
end
|
|
260
294
|
|
|
261
295
|
context "when response has custom attributes" do
|
|
296
|
+
let(:xml) { :custom_attributes }
|
|
297
|
+
|
|
262
298
|
before :each do
|
|
263
|
-
saml_options[:idp_cert_fingerprint] = "3B:82:F1:F5:54:FC:A8:FF:12:B8:4B:B8:16:61:1D:E4:8E:9B:E2:3C"
|
|
264
299
|
saml_options[:attribute_statements] = {
|
|
265
300
|
email: ["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"],
|
|
266
301
|
first_name: ["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"],
|
|
267
302
|
last_name: ["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"]
|
|
268
303
|
}
|
|
269
|
-
|
|
304
|
+
|
|
305
|
+
post_callback_response
|
|
270
306
|
end
|
|
271
307
|
|
|
272
308
|
it "should obey attribute statements mapping" do
|
|
@@ -280,10 +316,12 @@ describe OmniAuth::Strategies::SAML, :type => :strategy do
|
|
|
280
316
|
end
|
|
281
317
|
|
|
282
318
|
context "when using custom user id attribute" do
|
|
319
|
+
let(:xml) { :custom_attributes }
|
|
320
|
+
|
|
283
321
|
before :each do
|
|
284
|
-
saml_options[:idp_cert_fingerprint] = "3B:82:F1:F5:54:FC:A8:FF:12:B8:4B:B8:16:61:1D:E4:8E:9B:E2:3C"
|
|
285
322
|
saml_options[:uid_attribute] = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"
|
|
286
|
-
|
|
323
|
+
|
|
324
|
+
post_callback_response
|
|
287
325
|
end
|
|
288
326
|
|
|
289
327
|
it "should return user id attribute" do
|
|
@@ -294,55 +332,146 @@ describe OmniAuth::Strategies::SAML, :type => :strategy do
|
|
|
294
332
|
context "when using custom user id attribute, but it is missing" do
|
|
295
333
|
before :each do
|
|
296
334
|
saml_options[:uid_attribute] = "missing_attribute"
|
|
297
|
-
post_xml
|
|
298
335
|
end
|
|
299
336
|
|
|
300
337
|
it "should fail to authenticate" do
|
|
301
|
-
|
|
338
|
+
expect(post_callback_response).to fail_with(:invalid_ticket)
|
|
302
339
|
expect(last_request.env['omniauth.error']).to be_instance_of(OmniAuth::Strategies::SAML::ValidationError)
|
|
303
340
|
expect(last_request.env['omniauth.error'].message).to eq("SAML response missing 'missing_attribute' attribute")
|
|
304
341
|
end
|
|
305
342
|
end
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
describe 'POST /auth/saml/slo' do
|
|
346
|
+
before do
|
|
347
|
+
saml_options[:sp_entity_id] = "https://idp.sso.example.com/metadata/29490"
|
|
348
|
+
end
|
|
306
349
|
|
|
307
350
|
context "when response is a logout response" do
|
|
308
|
-
|
|
309
|
-
|
|
351
|
+
let(:opts) do
|
|
352
|
+
{ "rack.session" => { "saml_transaction_id" => "_3fef1069-d0c6-418a-b68d-6f008a4787e9" } }
|
|
353
|
+
end
|
|
310
354
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
355
|
+
let(:params) { { SAMLResponse: load_xml(:example_logout_response) } }
|
|
356
|
+
|
|
357
|
+
subject(:post_slo_response) { post "/auth/saml/slo", params, opts }
|
|
358
|
+
|
|
359
|
+
context "when relay state is relative" do
|
|
360
|
+
let(:params) { super().merge(RelayState: "/signed-out") }
|
|
361
|
+
|
|
362
|
+
it "redirects to the relaystate" do
|
|
363
|
+
post_slo_response
|
|
364
|
+
|
|
365
|
+
expect(last_response).to be_redirect
|
|
366
|
+
expect(last_response.location).to eq "/signed-out"
|
|
367
|
+
end
|
|
315
368
|
end
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
369
|
+
|
|
370
|
+
context "when relay state is an absolute https URL" do
|
|
371
|
+
let(:params) { super().merge(RelayState: "https://example.com/") }
|
|
372
|
+
|
|
373
|
+
it "redirects without a location header" do
|
|
374
|
+
post_slo_response
|
|
375
|
+
|
|
376
|
+
expect(last_response).to be_redirect
|
|
377
|
+
expect(last_response.headers.fetch("Location")).to be_nil
|
|
378
|
+
end
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
context 'when slo_default_relay_state is present' do
|
|
382
|
+
let(:saml_options) { super().merge(slo_default_relay_state: '/signed-out') }
|
|
383
|
+
|
|
384
|
+
context "when response relay state is valid" do
|
|
385
|
+
let(:params) { super().merge(RelayState: "/safe/logout") }
|
|
386
|
+
|
|
387
|
+
it { is_expected.to be_redirect.and have_attributes(location: '/safe/logout') }
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
context "when response relay state is invalid" do
|
|
391
|
+
let(:params) { super().merge(RelayState: "javascript:alert(1)") }
|
|
392
|
+
|
|
393
|
+
it { is_expected.to be_redirect.and have_attributes(location: '/signed-out') }
|
|
394
|
+
end
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
context 'when slo_default_relay_state is blank' do
|
|
398
|
+
let(:saml_options) { super().merge(slo_default_relay_state: nil) }
|
|
399
|
+
|
|
400
|
+
context "when response relay state is valid" do
|
|
401
|
+
let(:params) { super().merge(RelayState: "/safe/logout") }
|
|
402
|
+
|
|
403
|
+
it { is_expected.to be_redirect.and have_attributes(location: '/safe/logout') }
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
context "when response relay state is invalid" do
|
|
407
|
+
let(:params) { super().merge(RelayState: "javascript:alert(1)") }
|
|
408
|
+
|
|
409
|
+
it { is_expected.to be_redirect.and have_attributes(location: nil) }
|
|
410
|
+
end
|
|
319
411
|
end
|
|
320
412
|
end
|
|
321
413
|
|
|
322
414
|
context "when request is a logout request" do
|
|
323
415
|
subject { post "/auth/saml/slo", params, "rack.session" => { "saml_uid" => "username@example.com" } }
|
|
324
416
|
|
|
325
|
-
|
|
326
|
-
saml_options[:sp_entity_id] = "https://idp.sso.example.com/metadata/29490"
|
|
327
|
-
end
|
|
417
|
+
let(:relay_state) { "https://example.com/" }
|
|
328
418
|
|
|
329
419
|
let(:params) do
|
|
330
420
|
{
|
|
331
421
|
"SAMLRequest" => load_xml(:example_logout_request),
|
|
332
|
-
"RelayState" =>
|
|
422
|
+
"RelayState" => relay_state,
|
|
333
423
|
}
|
|
334
424
|
end
|
|
335
425
|
|
|
336
426
|
context "when logout request is valid" do
|
|
427
|
+
let(:relay_state) { "/logout" }
|
|
428
|
+
|
|
337
429
|
before { subject }
|
|
338
430
|
|
|
339
431
|
it "should redirect to logout response" do
|
|
340
432
|
expect(last_response).to be_redirect
|
|
341
433
|
expect(last_response.location).to match /https:\/\/idp.sso.example.com\/signoff\/29490/
|
|
342
|
-
expect(last_response.location).to match /RelayState
|
|
434
|
+
expect(last_response.location).to match /RelayState=%2Flogout/
|
|
435
|
+
end
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
it_behaves_like 'validating RelayState param'
|
|
439
|
+
|
|
440
|
+
context 'when slo_default_relay_state is blank' do
|
|
441
|
+
let(:saml_options) { super().merge(slo_default_relay_state: nil) }
|
|
442
|
+
|
|
443
|
+
context "when request relay state is invalid" do
|
|
444
|
+
let(:params) do
|
|
445
|
+
{
|
|
446
|
+
"SAMLRequest" => load_xml(:example_logout_request),
|
|
447
|
+
"RelayState" => "javascript:alert(1)",
|
|
448
|
+
}
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
it "redirects without including a RelayState parameter" do
|
|
452
|
+
subject
|
|
453
|
+
|
|
454
|
+
expect(last_response).to be_redirect
|
|
455
|
+
expect(last_response.location).to match %r{https://idp\.sso\.example\.com/signoff/29490}
|
|
456
|
+
expect(last_response.location).not_to match(/RelayState=/)
|
|
457
|
+
end
|
|
343
458
|
end
|
|
344
459
|
end
|
|
345
460
|
|
|
461
|
+
context "with a custom relay state validator" do
|
|
462
|
+
let(:saml_options) do
|
|
463
|
+
super().merge(
|
|
464
|
+
slo_relay_state_validator: proc do |relay_state, rack_request|
|
|
465
|
+
expect(rack_request).to respond_to(:params)
|
|
466
|
+
relay_state == "custom-state"
|
|
467
|
+
end,
|
|
468
|
+
)
|
|
469
|
+
end
|
|
470
|
+
let(:params) { super().merge("RelayState" => "custom-state") }
|
|
471
|
+
|
|
472
|
+
it { is_expected.to be_redirect.and have_attributes(location: a_string_matching(/RelayState=custom-state/)) }
|
|
473
|
+
end
|
|
474
|
+
|
|
346
475
|
context "when request is an invalid logout request" do
|
|
347
476
|
before :each do
|
|
348
477
|
allow_any_instance_of(OneLogin::RubySaml::SloLogoutrequest).to receive(:is_valid?).and_return(false)
|
|
@@ -367,38 +496,80 @@ describe OmniAuth::Strategies::SAML, :type => :strategy do
|
|
|
367
496
|
end
|
|
368
497
|
end
|
|
369
498
|
|
|
370
|
-
context "when
|
|
371
|
-
|
|
372
|
-
saml_options[
|
|
373
|
-
post "/auth/saml/
|
|
499
|
+
context "when SLO is disabled" do
|
|
500
|
+
before do
|
|
501
|
+
saml_options[:slo_enabled] = false
|
|
502
|
+
post "/auth/saml/slo"
|
|
503
|
+
end
|
|
374
504
|
|
|
375
|
-
|
|
376
|
-
expect(last_response.
|
|
377
|
-
expect(last_response.
|
|
505
|
+
it "should return not implemented" do
|
|
506
|
+
expect(last_response.status).to eq 501
|
|
507
|
+
expect(last_response.body).to eq "Not Implemented"
|
|
378
508
|
end
|
|
509
|
+
end
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
describe 'POST /auth/saml/spslo' do
|
|
513
|
+
let(:params) { {} }
|
|
514
|
+
subject { post "/auth/saml/spslo", params }
|
|
379
515
|
|
|
380
|
-
|
|
381
|
-
|
|
516
|
+
def test_default_relay_state(static_default_relay_state = nil, &block_default_relay_state)
|
|
517
|
+
saml_options["slo_default_relay_state"] = static_default_relay_state || block_default_relay_state
|
|
518
|
+
post "/auth/saml/spslo"
|
|
519
|
+
|
|
520
|
+
expect(last_response).to be_redirect
|
|
521
|
+
expect(last_response.location).to match /https:\/\/idp.sso.example.com\/signoff\/29490/
|
|
522
|
+
expect(last_response.location).to match /RelayState=https%3A%2F%2Fexample.com%2F/
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
it "should redirect to logout request" do
|
|
526
|
+
test_default_relay_state("https://example.com/")
|
|
527
|
+
end
|
|
528
|
+
|
|
529
|
+
it "should redirect to logout request with a block" do
|
|
530
|
+
test_default_relay_state do
|
|
531
|
+
"https://example.com/"
|
|
382
532
|
end
|
|
533
|
+
end
|
|
383
534
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
end
|
|
535
|
+
it "should redirect to logout request with a block with a request parameter" do
|
|
536
|
+
test_default_relay_state do |request|
|
|
537
|
+
"https://example.com/"
|
|
388
538
|
end
|
|
539
|
+
end
|
|
389
540
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
541
|
+
it_behaves_like 'validating RelayState param'
|
|
542
|
+
|
|
543
|
+
context 'when slo_default_relay_state is blank' do
|
|
544
|
+
let(:saml_options) { super().merge(slo_default_relay_state: nil) }
|
|
545
|
+
let(:params) { { RelayState: "//example.com" } }
|
|
546
|
+
|
|
547
|
+
it "redirects without including a RelayState parameter" do
|
|
548
|
+
subject
|
|
549
|
+
|
|
550
|
+
expect(last_response).to be_redirect
|
|
551
|
+
expect(last_response.location).to match %r{https://idp\.sso\.example\.com/signoff/29490}
|
|
552
|
+
expect(last_response.location).not_to match(/RelayState=/)
|
|
394
553
|
end
|
|
554
|
+
end
|
|
555
|
+
|
|
556
|
+
it "should give not implemented without an idp_slo_service_url" do
|
|
557
|
+
saml_options.delete(:idp_slo_service_url)
|
|
558
|
+
post "/auth/saml/spslo"
|
|
395
559
|
|
|
396
|
-
|
|
397
|
-
|
|
560
|
+
expect(last_response.status).to eq 501
|
|
561
|
+
expect(last_response.body).to match /Not Implemented/
|
|
562
|
+
end
|
|
563
|
+
|
|
564
|
+
context "when SLO is disabled" do
|
|
565
|
+
before do
|
|
566
|
+
saml_options[:slo_enabled] = false
|
|
398
567
|
post "/auth/saml/spslo"
|
|
568
|
+
end
|
|
399
569
|
|
|
570
|
+
it "should return not implemented" do
|
|
400
571
|
expect(last_response.status).to eq 501
|
|
401
|
-
expect(last_response.body).to
|
|
572
|
+
expect(last_response.body).to eq "Not Implemented"
|
|
402
573
|
end
|
|
403
574
|
end
|
|
404
575
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: omniauth-saml
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.2.
|
|
4
|
+
version: 2.2.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Raecoo Cao
|
|
@@ -11,10 +11,9 @@ authors:
|
|
|
11
11
|
- Nikos Dimitrakopoulos
|
|
12
12
|
- Rudolf Vriend
|
|
13
13
|
- Bruno Pedro
|
|
14
|
-
autorequire:
|
|
15
14
|
bindir: bin
|
|
16
15
|
cert_chain: []
|
|
17
|
-
date:
|
|
16
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
18
17
|
dependencies:
|
|
19
18
|
- !ruby/object:Gem::Dependency
|
|
20
19
|
name: omniauth
|
|
@@ -129,7 +128,6 @@ dependencies:
|
|
|
129
128
|
- !ruby/object:Gem::Version
|
|
130
129
|
version: '0.8'
|
|
131
130
|
description: A generic SAML strategy for OmniAuth.
|
|
132
|
-
email:
|
|
133
131
|
executables: []
|
|
134
132
|
extensions: []
|
|
135
133
|
extra_rdoc_files: []
|
|
@@ -147,7 +145,6 @@ homepage: https://github.com/omniauth/omniauth-saml
|
|
|
147
145
|
licenses:
|
|
148
146
|
- MIT
|
|
149
147
|
metadata: {}
|
|
150
|
-
post_install_message:
|
|
151
148
|
rdoc_options: []
|
|
152
149
|
require_paths:
|
|
153
150
|
- lib
|
|
@@ -162,8 +159,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
162
159
|
- !ruby/object:Gem::Version
|
|
163
160
|
version: '0'
|
|
164
161
|
requirements: []
|
|
165
|
-
rubygems_version: 3.
|
|
166
|
-
signing_key:
|
|
162
|
+
rubygems_version: 3.6.9
|
|
167
163
|
specification_version: 4
|
|
168
164
|
summary: A generic SAML strategy for OmniAuth.
|
|
169
165
|
test_files:
|