omniauth-saml 1.6.0 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of omniauth-saml might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: dbc50594dcc8687f230341b090cf59c01c4398de
4
- data.tar.gz: 7950589848f80e1016bf3651f45b53f7a22a5867
3
+ metadata.gz: f00284ea5fdac55a5ea5cd48980d48e31534f07d
4
+ data.tar.gz: 2b0e57bbc925087f1bc0f2e50d95123b10767536
5
5
  SHA512:
6
- metadata.gz: 223bf2b718cd9bbede71929f931b4cb2b1ab4c3996ccfd6fea4e898d3f91a061f0e7f8453d0f1a97000901d727861055790461a3902211bcb26519b93c0cb1df
7
- data.tar.gz: 735fb8c25ab720ea7a7d8cf53074bfde7aee7ed3554df78e523d964d714b03bde5209a399c28ad409cd204fe0a62f02b29bec5d8a065e6678d624d751cc5020e
6
+ metadata.gz: e9b49c5336256abad932cd797e884a221424574eb4bdeda60fabef9d9fd97cfde9b4910feb823bfd3f2a89b95cc05a3bc6f8eb19591e32f1b155dfac0c0a474b
7
+ data.tar.gz: 4cc832476d2f55e103cf138770f14175d9a193b06e9e58816f3ca5ea31e001acb02dfcc5246df07aad291efb67cf8b910cce601666ed258b353b523ba5f1c32b
@@ -1,14 +1,25 @@
1
- # OmniAuth SAML Version History
1
+ <a name="v1.7.0"></a>
2
+ ### v1.7.0 (2016-09-18)
2
3
 
3
- A generic SAML strategy for OmniAuth.
4
4
 
5
- https://github.com/omniauth/omniauth-saml
5
+ #### Features
6
6
 
7
- ## 1.6.0 (2016-06-27)
7
+ * Support for Single Logout ([cd3fc43](/../../commit/cd3fc43))
8
+ * Add issuer information to the metadata endpoint, to allow IdPs to properly configure themselves. ([7bbbb67](/../../commit/7bbbb67))
9
+ * Added the response object to the extra['response_object'], so we can use the raw response object if we want to. ([76ed3d6](/../../commit/76ed3d6))
10
+
11
+ #### Chores
12
+
13
+ * Update `ruby-saml` to 1.4.0 to address security fixes. ([638212](/../../commit/638212))
14
+
15
+
16
+ <a name="v1.6.0"></a>
17
+ ### v1.6.0 (2016-06-27)
8
18
  * Ensure that subclasses of `OmniAuth::Stategies::SAML` are registered with OmniAuth as strategies (https://github.com/omniauth/omniauth-saml/pull/95)
9
19
  * Update ruby-saml to 1.3 to address [CVE-2016-5697](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-5697) (Signature wrapping attacks)
10
20
 
11
- ## 1.5.0 (2016-02-25)
21
+ <a name="v1.5.0"></a>
22
+ ### v1.5.0 (2016-02-25)
12
23
 
13
24
  * Initialize OneLogin::RubySaml::Response instance with settings
14
25
  * Adding "settings" to Response Class at initialization to handle signing verification
@@ -18,56 +29,67 @@ https://github.com/omniauth/omniauth-saml
18
29
  * Call validation earlier to get real error instead of 'response missing name_id'
19
30
  * Avoid mutation of the options hash during requests and callbacks
20
31
 
21
- ## 1.4.2 (2016-02-09)
32
+ <a name="v1.4.2"></a>
33
+ ### v1.4.2 (2016-02-09)
22
34
 
23
35
  * update ruby-saml to 1.1
24
36
 
25
- ## 1.4.1 (2015-08-09)
37
+ <a name="v1.4.1"></a>
38
+ ### v1.4.1 (2015-08-09)
26
39
 
27
40
  * Configurable attribute_consuming_service
28
41
 
29
- ## 1.4.0 (2015-07-23)
42
+ <a name="v1.4.0"></a>
43
+ ### v1.4.0 (2015-07-23)
30
44
 
31
45
  * update ruby-saml to 1.0.0
32
46
 
33
- ## 1.3.1 (2015-02-26)
47
+ <a name="v1.3.1"></a>
48
+ ### v1.3.1 (2015-02-26)
34
49
 
35
50
  * Added missing fingerprint key check
36
51
  * Expose fingerprint on the auth_hash
37
52
 
38
- ## 1.3.0 (2015-01-23)
53
+ <a name="v1.3.0"></a>
54
+ ### v1.3.0 (2015-01-23)
39
55
 
40
56
  * add `idp_cert_fingerprint_validator` option
41
57
 
42
- ## 1.2.0 (2014-03-19)
58
+ <a name="v1.2.0"></a>
59
+ ### v1.2.0 (2014-03-19)
43
60
 
44
61
  * provide SP metadata at `/auth/saml/metadata`
45
62
 
46
- ## 1.1.0 (2013-11-07)
63
+ <a name="v1.1.0"></a>
64
+ ### v1.1.0 (2013-11-07)
47
65
 
48
66
  * no longer set a default `name_identifier_format`
49
67
  * pass strategy options to the underlying ruby-saml library
50
68
  * fallback to omniauth callback url if `assertion_consumer_service_url` is not set
51
69
  * add `idp_sso_target_url_runtime_params` option
52
70
 
53
- ## 1.0.0 (2012-11-12)
71
+ <a name="v1.0.0"></a>
72
+ ### v1.0.0 (2012-11-12)
54
73
 
55
74
  * remove SAML code and port to ruby-saml gem
56
75
  * fix incompatibility with OmniAuth 1.1
57
76
 
58
- ## 0.9.2 (2012-03-30)
77
+ <a name="v0.9.2"></a>
78
+ ### v0.9.2 (2012-03-30)
59
79
 
60
80
  * validate the SAML response
61
81
  * 100% test coverage
62
82
  * now requires ruby 1.9.2+
63
83
 
64
- ## 0.9.1 (2012-02-23)
84
+ <a name="v0.9.1"></a>
85
+ ### v0.9.1 (2012-02-23)
65
86
 
66
87
  * return first and last name in the info hash
67
88
  * no longer use LDAP OIDs for name and email selection
68
89
  * return SAML attributes as the omniauth raw_info hash
69
90
 
70
- ## 0.9.0 (2012-02-14)
91
+ <a name="v0.9.0"></a>
92
+ ### v0.9.0 (2012-02-14)
71
93
 
72
94
  * initial release
73
95
  * extracts commits from omniauth 0-3-stable branch
data/README.md CHANGED
@@ -68,6 +68,8 @@ end
68
68
 
69
69
  For IdP-initiated SSO, users should directly access the IdP SSO target URL. Set the `href` of your application's login link to the value of `idp_sso_target_url`. For SP-initiated SSO, link to `/auth/saml`.
70
70
 
71
+ A `OneLogin::RubySaml::Response` object is added to the `env['omniauth.auth']` extra attribute, so we can use it in the controller via `env['omniauth.auth'].extra.response_object`
72
+
71
73
  ## Metadata
72
74
 
73
75
  The service provider metadata used to ease configuration of the SAML SP in the IdP can be retrieved from `http://example.com/auth/saml/metadata`. Send this URL to the administrator of the IdP.
@@ -84,6 +86,14 @@ The service provider metadata used to ease configuration of the SAML SP in the I
84
86
  * `:idp_sso_target_url` - The URL to which the authentication request should be sent.
85
87
  This would be on the identity provider. **Required**.
86
88
 
89
+ * `:idp_slo_target_url` - The URL to which the single logout request and response should
90
+ be sent. This would be on the identity provider. Optional.
91
+
92
+ * `:slo_default_relay_state` - The value to use as default `RelayState` for single log outs. The
93
+ value can be a string, or a `Proc` (or other object responding to `call`). The `request`
94
+ instance will be passed to this callable if it has an arity of 1. If the value is a string,
95
+ the string will be returned, when the `RelayState` is called. Optional.
96
+
87
97
  * `:idp_sso_target_url_runtime_params` - A dynamic mapping of request params that exist
88
98
  during the request phase of OmniAuth that should to be sent to the IdP after a specific
89
99
  mapping. So for example, a param `original_request_param` with value `original_param_value`,
@@ -143,6 +153,35 @@ end
143
153
 
144
154
  Then follow Devise's general [OmniAuth tutorial](https://github.com/plataformatec/devise/wiki/OmniAuth:-Overview), replacing references to `facebook` with `saml`.
145
155
 
156
+ ## Single Logout
157
+
158
+ Single Logout can be Service Provider initiated or Identity Provider initiated.
159
+ When using Devise as an authentication solution, the SP initiated flow can be integrated
160
+ in the `SessionsController#destroy` action.
161
+
162
+ For this to work it is important to preserve the `saml_uid` value before Devise
163
+ clears the session and redirect to the `/spslo` sub-path to initiate the single logout.
164
+
165
+ Example `destroy` action in `sessions_controller.rb`:
166
+
167
+ ```ruby
168
+ class SessionsController < Devise::SessionsController
169
+ # ...
170
+
171
+ def destroy
172
+ # Preserve the saml_uid in the session
173
+ saml_uid = session["saml_uid"]
174
+ super do
175
+ session["saml_uid"] = saml_uid
176
+ if SAML_SETTINGS.idp_slo_target_url
177
+ spslo_url = user_omniauth_authorize_url(:saml) + "/spslo"
178
+ redirect_to(spslo_url)
179
+ end
180
+ end
181
+ end
182
+ end
183
+ ```
184
+
146
185
  ## Authors
147
186
 
148
187
  Authored by [Rajiv Aaron Manglani](http://www.rajivmanglani.com/), Raecoo Cao, Todd W Saxton, Ryan Wilcox, Steven Anderson, Nikos Dimitrakopoulos, Rudolf Vriend and [Bruno Pedro](http://brunopedro.com/).
@@ -1,5 +1,5 @@
1
1
  module OmniAuth
2
2
  module SAML
3
- VERSION = '1.6.0'
3
+ VERSION = '1.7.0'
4
4
  end
5
5
  end
@@ -27,15 +27,19 @@ module OmniAuth
27
27
  first_name: ["first_name", "firstname", "firstName"],
28
28
  last_name: ["last_name", "lastname", "lastName"]
29
29
  }
30
+ option :slo_default_relay_state
30
31
 
31
32
  def request_phase
32
33
  options[:assertion_consumer_service_url] ||= callback_url
33
34
  runtime_request_parameters = options.delete(:idp_sso_target_url_runtime_params)
34
35
 
35
36
  additional_params = {}
36
- runtime_request_parameters.each_pair do |request_param_key, mapped_param_key|
37
- additional_params[mapped_param_key] = request.params[request_param_key.to_s] if request.params.has_key?(request_param_key.to_s)
38
- end if runtime_request_parameters
37
+
38
+ if runtime_request_parameters
39
+ runtime_request_parameters.each_pair do |request_param_key, mapped_param_key|
40
+ additional_params[mapped_param_key] = request.params[request_param_key.to_s] if request.params.has_key?(request_param_key.to_s)
41
+ end
42
+ end
39
43
 
40
44
  authn_request = OneLogin::RubySaml::Authrequest.new
41
45
  settings = OneLogin::RubySaml::Settings.new(options)
@@ -44,9 +48,7 @@ module OmniAuth
44
48
  end
45
49
 
46
50
  def callback_phase
47
- unless request.params['SAMLResponse']
48
- raise OmniAuth::Strategies::SAML::ValidationError.new("SAML response missing")
49
- end
51
+ raise OmniAuth::Strategies::SAML::ValidationError.new("SAML response missing") unless request.params["SAMLResponse"]
50
52
 
51
53
  # Call a fingerprint validation method if there's one
52
54
  if options.idp_cert_fingerprint_validator
@@ -59,29 +61,21 @@ module OmniAuth
59
61
  end
60
62
 
61
63
  settings = OneLogin::RubySaml::Settings.new(options)
64
+
62
65
  # filter options to select only extra parameters
63
66
  opts = options.select {|k,_| OTHER_REQUEST_OPTIONS.include?(k.to_sym)}
67
+
64
68
  # symbolize keys without activeSupport/symbolize_keys (ruby-saml use symbols)
65
69
  opts =
66
70
  opts.inject({}) do |new_hash, (key, value)|
67
71
  new_hash[key.to_sym] = value
68
72
  new_hash
69
73
  end
70
- response = OneLogin::RubySaml::Response.new(request.params['SAMLResponse'], opts.merge(settings: settings))
71
- response.attributes['fingerprint'] = options.idp_cert_fingerprint
72
-
73
- # will raise an error since we are not in soft mode
74
- response.soft = false
75
- response.is_valid?
76
-
77
- @name_id = response.name_id
78
- @attributes = response.attributes
79
74
 
80
- if @name_id.nil? || @name_id.empty?
81
- raise OmniAuth::Strategies::SAML::ValidationError.new("SAML response missing 'name_id'")
75
+ handle_response(request.params["SAMLResponse"], opts, settings) do
76
+ super
82
77
  end
83
78
 
84
- super
85
79
  rescue OmniAuth::Strategies::SAML::ValidationError
86
80
  fail!(:invalid_ticket, $!)
87
81
  rescue OneLogin::RubySaml::ValidationError
@@ -90,7 +84,7 @@ module OmniAuth
90
84
 
91
85
  # Obtain an idp certificate fingerprint from the response.
92
86
  def response_fingerprint
93
- response = request.params['SAMLResponse']
87
+ response = request.params["SAMLResponse"]
94
88
  response = (response =~ /^</) ? response : Base64.decode64(response)
95
89
  document = XMLSecurity::SignedDocument::new(response)
96
90
  cert_element = REXML::XPath.first(document, "//ds:X509Certificate", { "ds"=> 'http://www.w3.org/2000/09/xmldsig#' })
@@ -100,25 +94,43 @@ module OmniAuth
100
94
  Digest::SHA1.hexdigest(cert.to_der).upcase.scan(/../).join(':')
101
95
  end
102
96
 
103
- def on_metadata_path?
104
- on_path?("#{request_path}/metadata")
105
- end
106
-
107
97
  def other_phase
108
- if on_metadata_path?
109
- # omniauth does not set the strategy on the other_phase
98
+ if current_path.start_with?(request_path)
110
99
  @env['omniauth.strategy'] ||= self
111
100
  setup_phase
112
-
113
- response = OneLogin::RubySaml::Metadata.new
114
101
  settings = OneLogin::RubySaml::Settings.new(options)
115
- if options.request_attributes.length > 0
116
- settings.attribute_consuming_service.service_name options.attribute_service_name
117
- options.request_attributes.each do |attribute|
118
- settings.attribute_consuming_service.add_attribute attribute
102
+
103
+ if on_subpath?(:metadata)
104
+ # omniauth does not set the strategy on the other_phase
105
+ response = OneLogin::RubySaml::Metadata.new
106
+
107
+ if options.request_attributes.length > 0
108
+ settings.attribute_consuming_service.service_name options.attribute_service_name
109
+ settings.issuer = options.issuer
110
+
111
+ options.request_attributes.each do |attribute|
112
+ settings.attribute_consuming_service.add_attribute attribute
113
+ end
119
114
  end
115
+
116
+ Rack::Response.new(response.generate(settings), 200, { "Content-Type" => "application/xml" }).finish
117
+ elsif on_subpath?(:slo)
118
+ if request.params["SAMLResponse"]
119
+ handle_logout_response(request.params["SAMLResponse"], settings)
120
+ elsif request.params["SAMLRequest"]
121
+ handle_logout_request(request.params["SAMLRequest"], settings)
122
+ else
123
+ raise OmniAuth::Strategies::SAML::ValidationError.new("SAML logout response/request missing")
124
+ end
125
+ elsif on_subpath?(:spslo)
126
+ if options.idp_slo_target_url
127
+ redirect(generate_logout_request(settings))
128
+ else
129
+ Rack::Response.new("Not Implemented", 501, { "Content-Type" => "text/html" }).finish
130
+ end
131
+ else
132
+ call_app!
120
133
  end
121
- Rack::Response.new(response.generate(settings), 200, { "Content-Type" => "application/xml" }).finish
122
134
  else
123
135
  call_app!
124
136
  end
@@ -135,7 +147,7 @@ module OmniAuth
135
147
  Hash[found_attributes]
136
148
  end
137
149
 
138
- extra { { :raw_info => @attributes } }
150
+ extra { { :raw_info => @attributes, :response_object => @response_object } }
139
151
 
140
152
  def find_attribute_by(keys)
141
153
  keys.each do |key|
@@ -144,6 +156,94 @@ module OmniAuth
144
156
 
145
157
  nil
146
158
  end
159
+
160
+ private
161
+
162
+ def on_subpath?(subpath)
163
+ on_path?("#{request_path}/#{subpath}")
164
+ end
165
+
166
+ def handle_response(raw_response, opts, settings)
167
+ response = OneLogin::RubySaml::Response.new(raw_response, opts.merge(settings: settings))
168
+ response.attributes["fingerprint"] = options.idp_cert_fingerprint
169
+ response.soft = false
170
+
171
+ response.is_valid?
172
+ @name_id = response.name_id
173
+ @attributes = response.attributes
174
+ @response_object = response
175
+
176
+ if @name_id.nil? || @name_id.empty?
177
+ raise OmniAuth::Strategies::SAML::ValidationError.new("SAML response missing 'name_id'")
178
+ end
179
+
180
+ session["saml_uid"] = @name_id
181
+ yield
182
+ end
183
+
184
+ def slo_relay_state
185
+ if request.params.has_key?("RelayState") && request.params["RelayState"] != ""
186
+ request.params["RelayState"]
187
+ else
188
+ slo_default_relay_state = options.slo_default_relay_state
189
+ if slo_default_relay_state.respond_to?(:call)
190
+ if slo_default_relay_state.arity == 1
191
+ slo_default_relay_state.call(request)
192
+ else
193
+ slo_default_relay_state.call
194
+ end
195
+ else
196
+ slo_default_relay_state
197
+ end
198
+ end
199
+ end
200
+
201
+ def handle_logout_response(raw_response, settings)
202
+ # After sending an SP initiated LogoutRequest to the IdP, we need to accept
203
+ # the LogoutResponse, verify it, then actually delete our session.
204
+
205
+ logout_response = OneLogin::RubySaml::Logoutresponse.new(raw_response, settings, :matches_request_id => session["saml_transaction_id"])
206
+ logout_response.soft = false
207
+ logout_response.validate
208
+
209
+ session.delete("saml_uid")
210
+ session.delete("saml_transaction_id")
211
+
212
+ redirect(slo_relay_state)
213
+ end
214
+
215
+ def handle_logout_request(raw_request, settings)
216
+ logout_request = OneLogin::RubySaml::SloLogoutrequest.new(raw_request)
217
+
218
+ if logout_request.is_valid? &&
219
+ logout_request.name_id == session["saml_uid"]
220
+
221
+ # Actually log out this session
222
+ session.clear
223
+
224
+ # Generate a response to the IdP.
225
+ logout_request_id = logout_request.id
226
+ logout_response = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, logout_request_id, nil, RelayState: slo_relay_state)
227
+ redirect(logout_response)
228
+ else
229
+ raise OmniAuth::Strategies::SAML::ValidationError.new("SAML failed to process LogoutRequest")
230
+ end
231
+ end
232
+
233
+ # Create a SP initiated SLO: https://github.com/onelogin/ruby-saml#single-log-out
234
+ def generate_logout_request(settings)
235
+ logout_request = OneLogin::RubySaml::Logoutrequest.new()
236
+
237
+ # Since we created a new SAML request, save the transaction_id
238
+ # to compare it with the response we get back
239
+ session["saml_transaction_id"] = logout_request.uuid
240
+
241
+ if settings.name_identifier_value.nil?
242
+ settings.name_identifier_value = session["saml_uid"]
243
+ end
244
+
245
+ logout_request.create(settings, RelayState: slo_relay_state)
246
+ end
147
247
  end
148
248
  end
149
249
  end
@@ -6,8 +6,8 @@ RSpec::Matchers.define :fail_with do |message|
6
6
  end
7
7
  end
8
8
 
9
- def post_xml(xml=:example_response)
10
- post "/auth/saml/callback", {'SAMLResponse' => load_xml(xml)}
9
+ def post_xml(xml=:example_response, opts = {})
10
+ post "/auth/saml/callback", opts.merge({'SAMLResponse' => load_xml(xml)})
11
11
  end
12
12
 
13
13
  describe OmniAuth::Strategies::SAML, :type => :strategy do
@@ -17,7 +17,9 @@ describe OmniAuth::Strategies::SAML, :type => :strategy do
17
17
  let(:saml_options) do
18
18
  {
19
19
  :assertion_consumer_service_url => "http://localhost:9080/auth/saml/callback",
20
+ :single_logout_service_url => "http://localhost:9080/auth/saml/slo",
20
21
  :idp_sso_target_url => "https://idp.sso.example.com/signon/29490",
22
+ :idp_slo_target_url => "https://idp.sso.example.com/signoff/29490",
21
23
  :idp_cert_fingerprint => "C1:59:74:2B:E8:0C:6C:A9:41:0F:6E:83:F6:D1:52:25:45:58:89:FB",
22
24
  :idp_sso_target_url_runtime_params => {:original_param_key => :mapped_param_key},
23
25
  :name_identifier_format => "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
@@ -39,11 +41,11 @@ describe OmniAuth::Strategies::SAML, :type => :strategy do
39
41
  end
40
42
 
41
43
  it 'should get authentication page' do
42
- last_response.should be_redirect
43
- last_response.location.should match /https:\/\/idp.sso.example.com\/signon\/29490/
44
- last_response.location.should match /\?SAMLRequest=/
45
- last_response.location.should_not match /mapped_param_key/
46
- last_response.location.should_not match /original_param_key/
44
+ expect(last_response).to be_redirect
45
+ expect(last_response.location).to match /https:\/\/idp.sso.example.com\/signon\/29490/
46
+ expect(last_response.location).to match /\?SAMLRequest=/
47
+ expect(last_response.location).not_to match /mapped_param_key/
48
+ expect(last_response.location).not_to match /original_param_key/
47
49
  end
48
50
  end
49
51
 
@@ -53,11 +55,11 @@ describe OmniAuth::Strategies::SAML, :type => :strategy do
53
55
  end
54
56
 
55
57
  it 'should get authentication page' do
56
- last_response.should be_redirect
57
- last_response.location.should match /https:\/\/idp.sso.example.com\/signon\/29490/
58
- last_response.location.should match /\?SAMLRequest=/
59
- last_response.location.should match /\&mapped_param_key=original_param_value/
60
- last_response.location.should_not match /original_param_key/
58
+ expect(last_response).to be_redirect
59
+ expect(last_response.location).to match /https:\/\/idp.sso.example.com\/signon\/29490/
60
+ expect(last_response.location).to match /\?SAMLRequest=/
61
+ expect(last_response.location).to match /\&mapped_param_key=original_param_value/
62
+ expect(last_response.location).not_to match /original_param_key/
61
63
  end
62
64
  end
63
65
 
@@ -71,17 +73,17 @@ describe OmniAuth::Strategies::SAML, :type => :strategy do
71
73
  %w(foo.example.com bar.example.com).each do |host|
72
74
  get "https://#{host}/auth/saml"
73
75
 
74
- last_response.should be_redirect
76
+ expect(last_response).to be_redirect
75
77
 
76
78
  location = URI.parse(last_response.location)
77
79
  query = Rack::Utils.parse_query location.query
78
- query.should have_key('SAMLRequest')
80
+ expect(query).to have_key('SAMLRequest')
79
81
 
80
82
  request = REXML::Document.new(Base64.decode64(query['SAMLRequest']))
81
- request.root.should_not be_nil
83
+ expect(request.root).not_to be_nil
82
84
 
83
85
  acs = request.root.attributes.get_attribute('AssertionConsumerServiceURL')
84
- acs.to_s.should == "https://#{host}/auth/saml/callback"
86
+ expect(acs.to_s).to eq "https://#{host}/auth/saml/callback"
85
87
  end
86
88
  end
87
89
  end
@@ -93,7 +95,7 @@ describe OmniAuth::Strategies::SAML, :type => :strategy do
93
95
  let(:xml) { :example_response }
94
96
 
95
97
  before :each do
96
- Time.stub(:now).and_return(Time.utc(2012, 11, 8, 20, 40, 00))
98
+ allow(Time).to receive(:now).and_return(Time.utc(2012, 11, 8, 20, 40, 00))
97
99
  end
98
100
 
99
101
  context "when the response is valid" do
@@ -102,17 +104,21 @@ describe OmniAuth::Strategies::SAML, :type => :strategy do
102
104
  end
103
105
 
104
106
  it "should set the uid to the nameID in the SAML response" do
105
- auth_hash['uid'].should == '_1f6fcf6be5e13b08b1e3610e7ff59f205fbd814f23'
107
+ expect(auth_hash['uid']).to eq '_1f6fcf6be5e13b08b1e3610e7ff59f205fbd814f23'
106
108
  end
107
109
 
108
110
  it "should set the raw info to all attributes" do
109
- auth_hash['extra']['raw_info'].all.to_hash.should == {
111
+ expect(auth_hash['extra']['raw_info'].all.to_hash).to eq(
110
112
  'first_name' => ['Rajiv'],
111
113
  'last_name' => ['Manglani'],
112
114
  'email' => ['user@example.com'],
113
115
  'company_name' => ['Example Company'],
114
116
  'fingerprint' => saml_options[:idp_cert_fingerprint]
115
- }
117
+ )
118
+ end
119
+
120
+ it "should set the response_object to the response object from ruby_saml response" do
121
+ expect(auth_hash['extra']['response_object']).to be_kind_of(OneLogin::RubySaml::Response)
116
122
  end
117
123
  end
118
124
 
@@ -124,17 +130,17 @@ describe OmniAuth::Strategies::SAML, :type => :strategy do
124
130
  end
125
131
 
126
132
  it "should set the uid to the nameID in the SAML response" do
127
- auth_hash['uid'].should == '_1f6fcf6be5e13b08b1e3610e7ff59f205fbd814f23'
133
+ expect(auth_hash['uid']).to eq '_1f6fcf6be5e13b08b1e3610e7ff59f205fbd814f23'
128
134
  end
129
135
 
130
136
  it "should set the raw info to all attributes" do
131
- auth_hash['extra']['raw_info'].all.to_hash.should == {
137
+ expect(auth_hash['extra']['raw_info'].all.to_hash).to eq(
132
138
  'first_name' => ['Rajiv'],
133
139
  'last_name' => ['Manglani'],
134
140
  'email' => ['user@example.com'],
135
141
  'company_name' => ['Example Company'],
136
142
  'fingerprint' => 'C1:59:74:2B:E8:0C:6C:A9:41:0F:6E:83:F6:D1:52:25:45:58:89:FB'
137
- }
143
+ )
138
144
  end
139
145
  end
140
146
 
@@ -148,6 +154,7 @@ describe OmniAuth::Strategies::SAML, :type => :strategy do
148
154
 
149
155
  context "when there is no name id in the XML" do
150
156
  before :each do
157
+ allow(Time).to receive(:now).and_return(Time.utc(2012, 11, 8, 23, 55, 00))
151
158
  post_xml :no_name_id
152
159
  end
153
160
 
@@ -191,38 +198,103 @@ describe OmniAuth::Strategies::SAML, :type => :strategy do
191
198
  end
192
199
 
193
200
  it "should obey attribute statements mapping" do
194
- auth_hash[:info].should == {
201
+ expect(auth_hash[:info]).to eq(
195
202
  'first_name' => 'Rajiv',
196
203
  'last_name' => 'Manglani',
197
204
  'email' => 'user@example.com',
198
205
  'name' => nil
199
- }
206
+ )
207
+ end
208
+ end
209
+
210
+ context "when response is a logout response" do
211
+ before :each do
212
+ saml_options[:issuer] = "https://idp.sso.example.com/metadata/29490"
213
+
214
+ post "/auth/saml/slo", {
215
+ SAMLResponse: load_xml(:example_logout_response),
216
+ RelayState: "https://example.com/",
217
+ }, "rack.session" => {"saml_transaction_id" => "_3fef1069-d0c6-418a-b68d-6f008a4787e9"}
218
+ end
219
+ it "should redirect to relaystate" do
220
+ expect(last_response).to be_redirect
221
+ expect(last_response.location).to match /https:\/\/example.com\//
222
+ end
223
+ end
224
+
225
+ context "when request is a logout request" do
226
+ before :each do
227
+ saml_options[:issuer] = "https://idp.sso.example.com/metadata/29490"
228
+ post "/auth/saml/slo", {
229
+ "SAMLRequest" => load_xml(:example_logout_request),
230
+ "RelayState" => "https://example.com/",
231
+ }, "rack.session" => {"saml_uid" => "username@example.com"}
232
+ end
233
+
234
+ it "should redirect to logout response" do
235
+ expect(last_response).to be_redirect
236
+ expect(last_response.location).to match /https:\/\/idp.sso.example.com\/signoff\/29490/
237
+ expect(last_response.location).to match /RelayState=https%3A%2F%2Fexample.com%2F/
238
+ end
239
+ end
240
+
241
+ context "when sp initiated SLO" do
242
+ def test_default_relay_state(static_default_relay_state = nil, &block_default_relay_state)
243
+ saml_options["slo_default_relay_state"] = static_default_relay_state || block_default_relay_state
244
+ post "/auth/saml/spslo"
245
+
246
+ expect(last_response).to be_redirect
247
+ expect(last_response.location).to match /https:\/\/idp.sso.example.com\/signoff\/29490/
248
+ expect(last_response.location).to match /RelayState=https%3A%2F%2Fexample.com%2F/
249
+ end
250
+
251
+ it "should redirect to logout request" do
252
+ test_default_relay_state("https://example.com/")
253
+ end
254
+
255
+ it "should redirect to logout request with a block" do
256
+ test_default_relay_state do
257
+ "https://example.com/"
258
+ end
259
+ end
260
+
261
+ it "should redirect to logout request with a block with a request parameter" do
262
+ test_default_relay_state do |request|
263
+ "https://example.com/"
264
+ end
265
+ end
266
+
267
+ it "should give not implemented without an idp_slo_target_url" do
268
+ saml_options.delete(:idp_slo_target_url)
269
+ post "/auth/saml/spslo"
270
+
271
+ expect(last_response.status).to eq 501
272
+ expect(last_response.body).to match /Not Implemented/
200
273
  end
201
274
  end
202
275
  end
203
276
 
204
277
  describe 'GET /auth/saml/metadata' do
205
278
  before do
279
+ saml_options[:issuer] = 'http://example.com/SAML'
206
280
  get '/auth/saml/metadata'
207
281
  end
208
282
 
209
283
  it 'should get SP metadata page' do
210
- last_response.status.should == 200
211
- last_response.header["Content-Type"].should == "application/xml"
284
+ expect(last_response.status).to eq 200
285
+ expect(last_response.header["Content-Type"]).to eq "application/xml"
212
286
  end
213
287
 
214
288
  it 'should configure attributes consuming service' do
215
- last_response.body.should match /AttributeConsumingService/
216
- last_response.body.should match /first_name/
217
- last_response.body.should match /last_name/
218
- last_response.body.should match /Required attributes/
289
+ expect(last_response.body).to match /AttributeConsumingService/
290
+ expect(last_response.body).to match /first_name/
291
+ expect(last_response.body).to match /last_name/
292
+ expect(last_response.body).to match /Required attributes/
293
+ expect(last_response.body).to match /entityID/
294
+ expect(last_response.body).to match /http:\/\/example.com\/SAML/
219
295
  end
220
296
  end
221
297
 
222
- it 'implements #on_metadata_path?' do
223
- expect(described_class.new(nil)).to respond_to(:on_metadata_path?)
224
- end
225
-
226
298
  describe 'subclass behavior' do
227
299
  it 'registers subclasses in OmniAuth.strategies' do
228
300
  subclass = Class.new(described_class)
@@ -15,6 +15,10 @@ require 'rexml/document'
15
15
  require 'rexml/xpath'
16
16
  require 'base64'
17
17
 
18
+ TEST_LOGGER = Logger.new(StringIO.new)
19
+ OneLogin::RubySaml::Logging.logger = TEST_LOGGER
20
+ OmniAuth.config.logger = TEST_LOGGER
21
+
18
22
  RSpec.configure do |config|
19
23
  config.include Rack::Test::Methods
20
24
  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: 1.6.0
4
+ version: 1.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Raecoo Cao
@@ -14,7 +14,7 @@ authors:
14
14
  autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
- date: 2016-06-27 00:00:00.000000000 Z
17
+ date: 2016-10-19 00:00:00.000000000 Z
18
18
  dependencies:
19
19
  - !ruby/object:Gem::Dependency
20
20
  name: omniauth
@@ -36,14 +36,34 @@ dependencies:
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: '1.3'
39
+ version: '1.4'
40
40
  type: :runtime
41
41
  prerelease: false
42
42
  version_requirements: !ruby/object:Gem::Requirement
43
43
  requirements:
44
44
  - - "~>"
45
45
  - !ruby/object:Gem::Version
46
- version: '1.3'
46
+ version: '1.4'
47
+ - !ruby/object:Gem::Dependency
48
+ name: rake
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '10'
54
+ - - "<"
55
+ - !ruby/object:Gem::Version
56
+ version: '12'
57
+ type: :development
58
+ prerelease: false
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: '10'
64
+ - - "<"
65
+ - !ruby/object:Gem::Version
66
+ version: '12'
47
67
  - !ruby/object:Gem::Dependency
48
68
  name: rspec
49
69
  requirement: !ruby/object:Gem::Requirement
@@ -92,6 +112,20 @@ dependencies:
92
112
  - - ">="
93
113
  - !ruby/object:Gem::Version
94
114
  version: 0.6.3
115
+ - !ruby/object:Gem::Dependency
116
+ name: conventional-changelog
117
+ requirement: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - "~>"
120
+ - !ruby/object:Gem::Version
121
+ version: '1.2'
122
+ type: :development
123
+ prerelease: false
124
+ version_requirements: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - "~>"
127
+ - !ruby/object:Gem::Version
128
+ version: '1.2'
95
129
  description: A generic SAML strategy for OmniAuth.
96
130
  email: rajiv@alum.mit.edu
97
131
  executables: []