omniauth-saml 1.6.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

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: []