ruby-saml 1.11.0 → 1.13.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of ruby-saml might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +25 -0
- data/{changelog.md → CHANGELOG.md} +44 -1
- data/README.md +333 -217
- data/UPGRADING.md +149 -0
- data/lib/onelogin/ruby-saml/attributes.rb +24 -1
- data/lib/onelogin/ruby-saml/authrequest.rb +11 -7
- data/lib/onelogin/ruby-saml/idp_metadata_parser.rb +154 -83
- data/lib/onelogin/ruby-saml/logoutrequest.rb +12 -6
- data/lib/onelogin/ruby-saml/logoutresponse.rb +5 -1
- data/lib/onelogin/ruby-saml/metadata.rb +62 -17
- data/lib/onelogin/ruby-saml/response.rb +51 -31
- data/lib/onelogin/ruby-saml/saml_message.rb +8 -3
- data/lib/onelogin/ruby-saml/setting_error.rb +6 -0
- data/lib/onelogin/ruby-saml/settings.rb +89 -49
- data/lib/onelogin/ruby-saml/slo_logoutrequest.rb +16 -4
- data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +31 -17
- data/lib/onelogin/ruby-saml/utils.rb +63 -2
- data/lib/onelogin/ruby-saml/version.rb +1 -1
- data/lib/xml_security.rb +39 -13
- data/ruby-saml.gemspec +8 -4
- metadata +24 -282
- data/.travis.yml +0 -46
- data/test/certificates/certificate.der +0 -0
- data/test/certificates/certificate1 +0 -12
- data/test/certificates/certificate_without_head_foot +0 -1
- data/test/certificates/formatted_certificate +0 -14
- data/test/certificates/formatted_chained_certificate +0 -42
- data/test/certificates/formatted_private_key +0 -12
- data/test/certificates/formatted_rsa_private_key +0 -12
- data/test/certificates/invalid_certificate1 +0 -1
- data/test/certificates/invalid_certificate2 +0 -1
- data/test/certificates/invalid_certificate3 +0 -12
- data/test/certificates/invalid_chained_certificate1 +0 -1
- data/test/certificates/invalid_private_key1 +0 -1
- data/test/certificates/invalid_private_key2 +0 -1
- data/test/certificates/invalid_private_key3 +0 -10
- data/test/certificates/invalid_rsa_private_key1 +0 -1
- data/test/certificates/invalid_rsa_private_key2 +0 -1
- data/test/certificates/invalid_rsa_private_key3 +0 -10
- data/test/certificates/ruby-saml-2.crt +0 -15
- data/test/certificates/ruby-saml.crt +0 -14
- data/test/certificates/ruby-saml.key +0 -15
- data/test/idp_metadata_parser_test.rb +0 -594
- data/test/logging_test.rb +0 -62
- data/test/logout_requests/invalid_slo_request.xml +0 -6
- data/test/logout_requests/slo_request.xml +0 -4
- data/test/logout_requests/slo_request.xml.base64 +0 -1
- data/test/logout_requests/slo_request_deflated.xml.base64 +0 -1
- data/test/logout_requests/slo_request_with_name_id_format.xml +0 -4
- data/test/logout_requests/slo_request_with_session_index.xml +0 -5
- data/test/logout_responses/logoutresponse_fixtures.rb +0 -86
- data/test/logoutrequest_test.rb +0 -260
- data/test/logoutresponse_test.rb +0 -427
- data/test/metadata/idp_descriptor.xml +0 -26
- data/test/metadata/idp_descriptor_2.xml +0 -56
- data/test/metadata/idp_descriptor_3.xml +0 -14
- data/test/metadata/idp_descriptor_4.xml +0 -72
- data/test/metadata/idp_metadata_different_sign_and_encrypt_cert.xml +0 -72
- data/test/metadata/idp_metadata_multi_certs.xml +0 -75
- data/test/metadata/idp_metadata_multi_signing_certs.xml +0 -52
- data/test/metadata/idp_metadata_same_sign_and_encrypt_cert.xml +0 -71
- data/test/metadata/idp_multiple_descriptors.xml +0 -59
- data/test/metadata/idp_multiple_descriptors_2.xml +0 -59
- data/test/metadata/no_idp_descriptor.xml +0 -21
- data/test/metadata_test.rb +0 -331
- data/test/request_test.rb +0 -340
- data/test/response_test.rb +0 -1629
- data/test/responses/adfs_response_sha1.xml +0 -46
- data/test/responses/adfs_response_sha256.xml +0 -46
- data/test/responses/adfs_response_sha384.xml +0 -46
- data/test/responses/adfs_response_sha512.xml +0 -46
- data/test/responses/adfs_response_xmlns.xml +0 -45
- data/test/responses/attackxee.xml +0 -13
- data/test/responses/invalids/duplicated_attributes.xml.base64 +0 -1
- data/test/responses/invalids/empty_destination.xml.base64 +0 -1
- data/test/responses/invalids/empty_nameid.xml.base64 +0 -1
- data/test/responses/invalids/encrypted_new_attack.xml.base64 +0 -1
- data/test/responses/invalids/invalid_audience.xml.base64 +0 -1
- data/test/responses/invalids/invalid_issuer_assertion.xml.base64 +0 -1
- data/test/responses/invalids/invalid_issuer_message.xml.base64 +0 -1
- data/test/responses/invalids/invalid_signature_position.xml.base64 +0 -1
- data/test/responses/invalids/invalid_subjectconfirmation_inresponse.xml.base64 +0 -1
- data/test/responses/invalids/invalid_subjectconfirmation_nb.xml.base64 +0 -1
- data/test/responses/invalids/invalid_subjectconfirmation_noa.xml.base64 +0 -1
- data/test/responses/invalids/invalid_subjectconfirmation_recipient.xml.base64 +0 -1
- data/test/responses/invalids/multiple_assertions.xml.base64 +0 -2
- data/test/responses/invalids/multiple_signed.xml.base64 +0 -1
- data/test/responses/invalids/no_authnstatement.xml.base64 +0 -1
- data/test/responses/invalids/no_conditions.xml.base64 +0 -1
- data/test/responses/invalids/no_id.xml.base64 +0 -1
- data/test/responses/invalids/no_issuer_assertion.xml.base64 +0 -1
- data/test/responses/invalids/no_issuer_response.xml.base64 +0 -1
- data/test/responses/invalids/no_nameid.xml.base64 +0 -1
- data/test/responses/invalids/no_saml2.xml.base64 +0 -1
- data/test/responses/invalids/no_signature.xml.base64 +0 -1
- data/test/responses/invalids/no_status.xml.base64 +0 -1
- data/test/responses/invalids/no_status_code.xml.base64 +0 -1
- data/test/responses/invalids/no_subjectconfirmation_data.xml.base64 +0 -1
- data/test/responses/invalids/no_subjectconfirmation_method.xml.base64 +0 -1
- data/test/responses/invalids/response_invalid_signed_element.xml.base64 +0 -1
- data/test/responses/invalids/response_with_concealed_signed_assertion.xml +0 -51
- data/test/responses/invalids/response_with_doubled_signed_assertion.xml +0 -49
- data/test/responses/invalids/signature_wrapping_attack.xml.base64 +0 -1
- data/test/responses/invalids/status_code_responder.xml.base64 +0 -1
- data/test/responses/invalids/status_code_responer_and_msg.xml.base64 +0 -1
- data/test/responses/invalids/wrong_spnamequalifier.xml.base64 +0 -1
- data/test/responses/no_signature_ns.xml +0 -48
- data/test/responses/open_saml_response.xml +0 -56
- data/test/responses/response_assertion_wrapped.xml.base64 +0 -93
- data/test/responses/response_audience_self_closed_tag.xml.base64 +0 -1
- data/test/responses/response_double_status_code.xml.base64 +0 -1
- data/test/responses/response_encrypted_attrs.xml.base64 +0 -1
- data/test/responses/response_encrypted_nameid.xml.base64 +0 -1
- data/test/responses/response_eval.xml +0 -7
- data/test/responses/response_no_cert_and_encrypted_attrs.xml +0 -29
- data/test/responses/response_node_text_attack.xml.base64 +0 -1
- data/test/responses/response_node_text_attack2.xml.base64 +0 -1
- data/test/responses/response_node_text_attack3.xml.base64 +0 -1
- data/test/responses/response_unsigned_xml_base64 +0 -1
- data/test/responses/response_with_ampersands.xml +0 -139
- data/test/responses/response_with_ampersands.xml.base64 +0 -93
- data/test/responses/response_with_ds_namespace_at_the_root.xml.base64 +0 -1
- data/test/responses/response_with_multiple_attribute_statements.xml +0 -72
- data/test/responses/response_with_multiple_attribute_values.xml +0 -67
- data/test/responses/response_with_retrieval_method.xml +0 -26
- data/test/responses/response_with_saml2_namespace.xml.base64 +0 -102
- data/test/responses/response_with_signed_assertion.xml.base64 +0 -66
- data/test/responses/response_with_signed_assertion_2.xml.base64 +0 -1
- data/test/responses/response_with_signed_assertion_3.xml +0 -30
- data/test/responses/response_with_signed_message_and_assertion.xml +0 -34
- data/test/responses/response_with_undefined_recipient.xml.base64 +0 -1
- data/test/responses/response_without_attributes.xml.base64 +0 -79
- data/test/responses/response_without_reference_uri.xml.base64 +0 -1
- data/test/responses/response_wrapped.xml.base64 +0 -150
- data/test/responses/signed_message_encrypted_signed_assertion.xml.base64 +0 -1
- data/test/responses/signed_message_encrypted_unsigned_assertion.xml.base64 +0 -1
- data/test/responses/signed_nameid_in_atts.xml +0 -47
- data/test/responses/signed_unqual_nameid_in_atts.xml +0 -47
- data/test/responses/simple_saml_php.xml +0 -71
- data/test/responses/starfield_response.xml.base64 +0 -1
- data/test/responses/test_sign.xml +0 -43
- data/test/responses/unsigned_encrypted_adfs.xml +0 -23
- data/test/responses/unsigned_message_aes128_encrypted_signed_assertion.xml.base64 +0 -1
- data/test/responses/unsigned_message_aes192_encrypted_signed_assertion.xml.base64 +0 -1
- data/test/responses/unsigned_message_aes256_encrypted_signed_assertion.xml.base64 +0 -1
- data/test/responses/unsigned_message_des192_encrypted_signed_assertion.xml.base64 +0 -1
- data/test/responses/unsigned_message_encrypted_assertion_without_saml_namespace.xml.base64 +0 -1
- data/test/responses/unsigned_message_encrypted_signed_assertion.xml.base64 +0 -1
- data/test/responses/unsigned_message_encrypted_unsigned_assertion.xml.base64 +0 -1
- data/test/responses/valid_response.xml.base64 +0 -1
- data/test/responses/valid_response_with_formatted_x509certificate.xml.base64 +0 -1
- data/test/responses/valid_response_without_x509certificate.xml.base64 +0 -1
- data/test/saml_message_test.rb +0 -56
- data/test/settings_test.rb +0 -338
- data/test/slo_logoutrequest_test.rb +0 -467
- data/test/slo_logoutresponse_test.rb +0 -233
- data/test/test_helper.rb +0 -333
- data/test/utils_test.rb +0 -259
- data/test/xml_security_test.rb +0 -421
@@ -31,8 +31,9 @@ module OneLogin
|
|
31
31
|
|
32
32
|
# IdP Data
|
33
33
|
attr_accessor :idp_entity_id
|
34
|
-
|
35
|
-
|
34
|
+
attr_writer :idp_sso_service_url
|
35
|
+
attr_writer :idp_slo_service_url
|
36
|
+
attr_accessor :idp_slo_response_service_url
|
36
37
|
attr_accessor :idp_cert
|
37
38
|
attr_accessor :idp_cert_fingerprint
|
38
39
|
attr_accessor :idp_cert_fingerprint_algorithm
|
@@ -41,8 +42,10 @@ module OneLogin
|
|
41
42
|
attr_accessor :idp_name_qualifier
|
42
43
|
attr_accessor :valid_until
|
43
44
|
# SP Data
|
45
|
+
attr_writer :sp_entity_id
|
44
46
|
attr_accessor :assertion_consumer_service_url
|
45
|
-
|
47
|
+
attr_reader :assertion_consumer_service_binding
|
48
|
+
attr_writer :single_logout_service_url
|
46
49
|
attr_accessor :sp_name_qualifier
|
47
50
|
attr_accessor :name_identifier_format
|
48
51
|
attr_accessor :name_identifier_value
|
@@ -51,8 +54,9 @@ module OneLogin
|
|
51
54
|
attr_accessor :compress_request
|
52
55
|
attr_accessor :compress_response
|
53
56
|
attr_accessor :double_quote_xml_attribute_values
|
57
|
+
attr_accessor :message_max_bytesize
|
54
58
|
attr_accessor :passive
|
55
|
-
|
59
|
+
attr_reader :protocol_binding
|
56
60
|
attr_accessor :attributes_index
|
57
61
|
attr_accessor :force_authn
|
58
62
|
attr_accessor :certificate
|
@@ -65,74 +69,99 @@ module OneLogin
|
|
65
69
|
# Work-flow
|
66
70
|
attr_accessor :security
|
67
71
|
attr_accessor :soft
|
68
|
-
#
|
72
|
+
# Deprecated
|
69
73
|
attr_accessor :assertion_consumer_logout_service_url
|
70
|
-
|
74
|
+
attr_reader :assertion_consumer_logout_service_binding
|
71
75
|
attr_accessor :issuer
|
76
|
+
attr_accessor :idp_sso_target_url
|
77
|
+
attr_accessor :idp_slo_target_url
|
78
|
+
|
79
|
+
# @return [String] IdP Single Sign On Service URL
|
80
|
+
#
|
81
|
+
def idp_sso_service_url
|
82
|
+
@idp_sso_service_url || @idp_sso_target_url
|
83
|
+
end
|
84
|
+
|
85
|
+
# @return [String] IdP Single Logout Service URL
|
86
|
+
#
|
87
|
+
def idp_slo_service_url
|
88
|
+
@idp_slo_service_url || @idp_slo_target_url
|
89
|
+
end
|
90
|
+
|
91
|
+
# @return [String] IdP Single Sign On Service Binding
|
92
|
+
#
|
93
|
+
def idp_sso_service_binding
|
94
|
+
@idp_sso_service_binding || idp_binding_from_embed_sign
|
95
|
+
end
|
96
|
+
|
97
|
+
# Setter for IdP Single Sign On Service Binding
|
98
|
+
# @param value [String, Symbol].
|
99
|
+
#
|
100
|
+
def idp_sso_service_binding=(value)
|
101
|
+
@idp_sso_service_binding = get_binding(value)
|
102
|
+
end
|
103
|
+
|
104
|
+
# @return [String] IdP Single Logout Service Binding
|
105
|
+
#
|
106
|
+
def idp_slo_service_binding
|
107
|
+
@idp_slo_service_binding || idp_binding_from_embed_sign
|
108
|
+
end
|
109
|
+
|
110
|
+
# Setter for IdP Single Logout Service Binding
|
111
|
+
# @param value [String, Symbol].
|
112
|
+
#
|
113
|
+
def idp_slo_service_binding=(value)
|
114
|
+
@idp_slo_service_binding = get_binding(value)
|
115
|
+
end
|
72
116
|
|
73
117
|
# @return [String] SP Entity ID
|
74
118
|
#
|
75
119
|
def sp_entity_id
|
76
|
-
|
77
|
-
if @sp_entity_id.nil?
|
78
|
-
if @issuer
|
79
|
-
val = @issuer
|
80
|
-
end
|
81
|
-
else
|
82
|
-
val = @sp_entity_id
|
83
|
-
end
|
84
|
-
val
|
120
|
+
@sp_entity_id || @issuer
|
85
121
|
end
|
86
122
|
|
87
|
-
# Setter for SP
|
88
|
-
# @param
|
123
|
+
# Setter for SP Protocol Binding
|
124
|
+
# @param value [String, Symbol].
|
89
125
|
#
|
90
|
-
def
|
91
|
-
@
|
126
|
+
def protocol_binding=(value)
|
127
|
+
@protocol_binding = get_binding(value)
|
92
128
|
end
|
93
129
|
|
94
|
-
#
|
130
|
+
# Setter for SP Assertion Consumer Service Binding
|
131
|
+
# @param value [String, Symbol].
|
95
132
|
#
|
96
|
-
def
|
97
|
-
|
98
|
-
if @single_logout_service_url.nil?
|
99
|
-
if @assertion_consumer_logout_service_url
|
100
|
-
val = @assertion_consumer_logout_service_url
|
101
|
-
end
|
102
|
-
else
|
103
|
-
val = @single_logout_service_url
|
104
|
-
end
|
105
|
-
val
|
133
|
+
def assertion_consumer_service_binding=(value)
|
134
|
+
@assertion_consumer_service_binding = get_binding(value)
|
106
135
|
end
|
107
136
|
|
108
|
-
#
|
109
|
-
# @param url [String].
|
137
|
+
# @return [String] Single Logout Service URL.
|
110
138
|
#
|
111
|
-
def single_logout_service_url
|
112
|
-
@single_logout_service_url
|
139
|
+
def single_logout_service_url
|
140
|
+
@single_logout_service_url || @assertion_consumer_logout_service_url
|
113
141
|
end
|
114
142
|
|
115
143
|
# @return [String] Single Logout Service Binding.
|
116
144
|
#
|
117
145
|
def single_logout_service_binding
|
118
|
-
|
119
|
-
if @single_logout_service_binding.nil?
|
120
|
-
if @assertion_consumer_logout_service_binding
|
121
|
-
val = @assertion_consumer_logout_service_binding
|
122
|
-
end
|
123
|
-
else
|
124
|
-
val = @single_logout_service_binding
|
125
|
-
end
|
126
|
-
val
|
146
|
+
@single_logout_service_binding || @assertion_consumer_logout_service_binding
|
127
147
|
end
|
128
148
|
|
129
149
|
# Setter for Single Logout Service Binding.
|
130
150
|
#
|
131
151
|
# (Currently we only support "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect")
|
132
|
-
# @param
|
152
|
+
# @param value [String, Symbol]
|
153
|
+
#
|
154
|
+
def single_logout_service_binding=(value)
|
155
|
+
@single_logout_service_binding = get_binding(value)
|
156
|
+
end
|
157
|
+
|
158
|
+
# @deprecated Setter for legacy Single Logout Service Binding parameter.
|
159
|
+
#
|
160
|
+
# (Currently we only support "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect")
|
161
|
+
# @param value [String, Symbol]
|
133
162
|
#
|
134
|
-
def
|
135
|
-
@
|
163
|
+
def assertion_consumer_logout_service_binding=(value)
|
164
|
+
@assertion_consumer_logout_service_binding = get_binding(value)
|
136
165
|
end
|
137
166
|
|
138
167
|
# Calculates the fingerprint of the IdP x509 certificate.
|
@@ -220,12 +249,23 @@ module OneLogin
|
|
220
249
|
|
221
250
|
private
|
222
251
|
|
252
|
+
def idp_binding_from_embed_sign
|
253
|
+
security[:embed_sign] ? Utils::BINDINGS[:post] : Utils::BINDINGS[:redirect]
|
254
|
+
end
|
255
|
+
|
256
|
+
def get_binding(value)
|
257
|
+
return unless value
|
258
|
+
|
259
|
+
Utils::BINDINGS[value.to_sym] || value
|
260
|
+
end
|
261
|
+
|
223
262
|
DEFAULTS = {
|
224
|
-
:assertion_consumer_service_binding =>
|
225
|
-
:single_logout_service_binding =>
|
263
|
+
:assertion_consumer_service_binding => Utils::BINDINGS[:post],
|
264
|
+
:single_logout_service_binding => Utils::BINDINGS[:redirect],
|
226
265
|
:idp_cert_fingerprint_algorithm => XMLSecurity::Document::SHA1,
|
227
266
|
:compress_request => true,
|
228
267
|
:compress_response => true,
|
268
|
+
:message_max_bytesize => 250000,
|
229
269
|
:soft => true,
|
230
270
|
:double_quote_xml_attribute_values => false,
|
231
271
|
:security => {
|
@@ -236,7 +276,7 @@ module OneLogin
|
|
236
276
|
:want_assertions_encrypted => false,
|
237
277
|
:want_name_id => false,
|
238
278
|
:metadata_signed => false,
|
239
|
-
:embed_sign => false,
|
279
|
+
:embed_sign => false, # Deprecated
|
240
280
|
:digest_method => XMLSecurity::Document::SHA1,
|
241
281
|
:signature_method => XMLSecurity::Document::RSA_SHA1,
|
242
282
|
:check_idp_cert_expiration => false,
|
@@ -43,10 +43,14 @@ module OneLogin
|
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
-
@request = decode_raw_saml(request)
|
46
|
+
@request = decode_raw_saml(request, settings)
|
47
47
|
@document = REXML::Document.new(@request)
|
48
48
|
end
|
49
49
|
|
50
|
+
def request_id
|
51
|
+
id(document)
|
52
|
+
end
|
53
|
+
|
50
54
|
# Validates the Logout Request with the default values (soft = true)
|
51
55
|
# @param collect_errors [Boolean] Stop validation when first error appears or keep validating.
|
52
56
|
# @return [Boolean] TRUE if the Logout Request is valid
|
@@ -126,6 +130,12 @@ module OneLogin
|
|
126
130
|
|
127
131
|
private
|
128
132
|
|
133
|
+
# returns the allowed clock drift on timing validation
|
134
|
+
# @return [Float]
|
135
|
+
def allowed_clock_drift
|
136
|
+
options[:allowed_clock_drift].to_f.abs + Float::EPSILON
|
137
|
+
end
|
138
|
+
|
129
139
|
# Hard aux function to validate the Logout Request
|
130
140
|
# @param collect_errors [Boolean] Stop validation when first error appears or keep validating. (if soft=true)
|
131
141
|
# @return [Boolean] TRUE if the Logout Request is valid
|
@@ -176,15 +186,17 @@ module OneLogin
|
|
176
186
|
true
|
177
187
|
end
|
178
188
|
|
179
|
-
# Validates the time. (If the logout request was initialized with the :allowed_clock_drift
|
189
|
+
# Validates the time. (If the logout request was initialized with the :allowed_clock_drift
|
190
|
+
# option, the timing validations are relaxed by the allowed_clock_drift value)
|
180
191
|
# If fails, the error is added to the errors array
|
181
192
|
# @return [Boolean] True if satisfies the conditions, otherwise False if soft=True
|
182
193
|
# @raise [ValidationError] if soft == false and validation fails
|
183
194
|
#
|
184
195
|
def validate_not_on_or_after
|
185
196
|
now = Time.now.utc
|
186
|
-
|
187
|
-
|
197
|
+
|
198
|
+
if not_on_or_after && now >= (not_on_or_after + allowed_clock_drift)
|
199
|
+
return append_error("Current time is on or after NotOnOrAfter (#{now} >= #{not_on_or_after}#{" + #{allowed_clock_drift.ceil}s" if allowed_clock_drift > 0})")
|
188
200
|
end
|
189
201
|
|
190
202
|
true
|
@@ -2,6 +2,7 @@ require "onelogin/ruby-saml/logging"
|
|
2
2
|
|
3
3
|
require "onelogin/ruby-saml/saml_message"
|
4
4
|
require "onelogin/ruby-saml/utils"
|
5
|
+
require "onelogin/ruby-saml/setting_error"
|
5
6
|
|
6
7
|
# Only supports SAML 2.0
|
7
8
|
module OneLogin
|
@@ -21,23 +22,30 @@ module OneLogin
|
|
21
22
|
@uuid = OneLogin::RubySaml::Utils.uuid
|
22
23
|
end
|
23
24
|
|
25
|
+
def response_id
|
26
|
+
@uuid
|
27
|
+
end
|
28
|
+
|
24
29
|
# Creates the Logout Response string.
|
25
30
|
# @param settings [OneLogin::RubySaml::Settings|nil] Toolkit settings
|
26
31
|
# @param request_id [String] The ID of the LogoutRequest sent by this SP to the IdP. That ID will be placed as the InResponseTo in the logout response
|
27
32
|
# @param logout_message [String] The Message to be placed as StatusMessage in the logout response
|
28
33
|
# @param params [Hash] Some extra parameters to be added in the GET for example the RelayState
|
34
|
+
# @param logout_status_code [String] The StatusCode to be placed as StatusMessage in the logout response
|
29
35
|
# @return [String] Logout Request string that includes the SAMLRequest
|
30
36
|
#
|
31
|
-
def create(settings, request_id = nil, logout_message = nil, params = {})
|
32
|
-
params = create_params(settings, request_id, logout_message, params)
|
33
|
-
params_prefix = (settings.
|
37
|
+
def create(settings, request_id = nil, logout_message = nil, params = {}, logout_status_code = nil)
|
38
|
+
params = create_params(settings, request_id, logout_message, params, logout_status_code)
|
39
|
+
params_prefix = (settings.idp_slo_service_url =~ /\?/) ? '&' : '?'
|
40
|
+
url = settings.idp_slo_response_service_url || settings.idp_slo_service_url
|
34
41
|
saml_response = CGI.escape(params.delete("SAMLResponse"))
|
35
42
|
response_params = "#{params_prefix}SAMLResponse=#{saml_response}"
|
36
43
|
params.each_pair do |key, value|
|
37
44
|
response_params << "&#{key.to_s}=#{CGI.escape(value.to_s)}"
|
38
45
|
end
|
39
46
|
|
40
|
-
|
47
|
+
raise SettingError.new "Invalid settings, idp_slo_service_url is not set!" if url.nil? or url.empty?
|
48
|
+
@logout_url = url + response_params
|
41
49
|
end
|
42
50
|
|
43
51
|
# Creates the Get parameters for the logout response.
|
@@ -45,9 +53,10 @@ module OneLogin
|
|
45
53
|
# @param request_id [String] The ID of the LogoutRequest sent by this SP to the IdP. That ID will be placed as the InResponseTo in the logout response
|
46
54
|
# @param logout_message [String] The Message to be placed as StatusMessage in the logout response
|
47
55
|
# @param params [Hash] Some extra parameters to be added in the GET for example the RelayState
|
56
|
+
# @param logout_status_code [String] The StatusCode to be placed as StatusMessage in the logout response
|
48
57
|
# @return [Hash] Parameters
|
49
58
|
#
|
50
|
-
def create_params(settings, request_id = nil, logout_message = nil, params = {})
|
59
|
+
def create_params(settings, request_id = nil, logout_message = nil, params = {}, logout_status_code = nil)
|
51
60
|
# The method expects :RelayState but sometimes we get 'RelayState' instead.
|
52
61
|
# Based on the HashWithIndifferentAccess value in Rails we could experience
|
53
62
|
# conflicts so this line will solve them.
|
@@ -58,7 +67,7 @@ module OneLogin
|
|
58
67
|
params.delete('RelayState')
|
59
68
|
end
|
60
69
|
|
61
|
-
response_doc = create_logout_response_xml_doc(settings, request_id, logout_message)
|
70
|
+
response_doc = create_logout_response_xml_doc(settings, request_id, logout_message, logout_status_code)
|
62
71
|
response_doc.context[:attribute_quote] = :quote if settings.double_quote_xml_attribute_values
|
63
72
|
|
64
73
|
response = ""
|
@@ -70,7 +79,7 @@ module OneLogin
|
|
70
79
|
base64_response = encode(response)
|
71
80
|
response_params = {"SAMLResponse" => base64_response}
|
72
81
|
|
73
|
-
if settings.
|
82
|
+
if settings.idp_slo_service_binding == Utils::BINDINGS[:redirect] && settings.security[:logout_responses_signed] && settings.private_key
|
74
83
|
params['SigAlg'] = settings.security[:signature_method]
|
75
84
|
url_string = OneLogin::RubySaml::Utils.build_query(
|
76
85
|
:type => 'SAMLResponse',
|
@@ -94,39 +103,44 @@ module OneLogin
|
|
94
103
|
# @param settings [OneLogin::RubySaml::Settings|nil] Toolkit settings
|
95
104
|
# @param request_id [String] The ID of the LogoutRequest sent by this SP to the IdP. That ID will be placed as the InResponseTo in the logout response
|
96
105
|
# @param logout_message [String] The Message to be placed as StatusMessage in the logout response
|
106
|
+
# @param logout_status_code [String] The StatusCode to be placed as StatusMessage in the logout response
|
97
107
|
# @return [String] The SAMLResponse String.
|
98
108
|
#
|
99
|
-
def create_logout_response_xml_doc(settings, request_id = nil, logout_message = nil)
|
100
|
-
document = create_xml_document(settings, request_id, logout_message)
|
109
|
+
def create_logout_response_xml_doc(settings, request_id = nil, logout_message = nil, logout_status_code = nil)
|
110
|
+
document = create_xml_document(settings, request_id, logout_message, logout_status_code)
|
101
111
|
sign_document(document, settings)
|
102
112
|
end
|
103
113
|
|
104
|
-
def create_xml_document(settings, request_id = nil, logout_message = nil)
|
114
|
+
def create_xml_document(settings, request_id = nil, logout_message = nil, status_code = nil)
|
105
115
|
time = Time.now.utc.strftime('%Y-%m-%dT%H:%M:%SZ')
|
106
116
|
|
107
117
|
response_doc = XMLSecurity::Document.new
|
108
118
|
response_doc.uuid = uuid
|
109
119
|
|
120
|
+
destination = settings.idp_slo_response_service_url || settings.idp_slo_service_url
|
121
|
+
|
122
|
+
|
110
123
|
root = response_doc.add_element 'samlp:LogoutResponse', { 'xmlns:samlp' => 'urn:oasis:names:tc:SAML:2.0:protocol', "xmlns:saml" => "urn:oasis:names:tc:SAML:2.0:assertion" }
|
111
124
|
root.attributes['ID'] = uuid
|
112
125
|
root.attributes['IssueInstant'] = time
|
113
126
|
root.attributes['Version'] = '2.0'
|
114
127
|
root.attributes['InResponseTo'] = request_id unless request_id.nil?
|
115
|
-
root.attributes['Destination'] =
|
128
|
+
root.attributes['Destination'] = destination unless destination.nil? or destination.empty?
|
116
129
|
|
117
130
|
if settings.sp_entity_id != nil
|
118
131
|
issuer = root.add_element "saml:Issuer"
|
119
132
|
issuer.text = settings.sp_entity_id
|
120
133
|
end
|
121
134
|
|
122
|
-
# add
|
135
|
+
# add status
|
123
136
|
status = root.add_element 'samlp:Status'
|
124
137
|
|
125
|
-
#
|
126
|
-
status_code
|
127
|
-
|
138
|
+
# status code
|
139
|
+
status_code ||= 'urn:oasis:names:tc:SAML:2.0:status:Success'
|
140
|
+
status_code_elem = status.add_element 'samlp:StatusCode'
|
141
|
+
status_code_elem.attributes['Value'] = status_code
|
128
142
|
|
129
|
-
#
|
143
|
+
# status message
|
130
144
|
logout_message ||= 'Successfully Signed Out'
|
131
145
|
status_message = status.add_element 'samlp:StatusMessage'
|
132
146
|
status_message.text = logout_message
|
@@ -136,7 +150,7 @@ module OneLogin
|
|
136
150
|
|
137
151
|
def sign_document(document, settings)
|
138
152
|
# embed signature
|
139
|
-
if settings.
|
153
|
+
if settings.idp_slo_service_binding == Utils::BINDINGS[:post] && settings.private_key && settings.certificate
|
140
154
|
private_key = settings.get_sp_key
|
141
155
|
cert = settings.get_sp_cert
|
142
156
|
document.sign_document(private_key, cert, settings.security[:signature_method], settings.security[:digest_method])
|
@@ -13,8 +13,25 @@ module OneLogin
|
|
13
13
|
class Utils
|
14
14
|
@@uuid_generator = UUID.new if RUBY_VERSION < '1.9'
|
15
15
|
|
16
|
-
|
17
|
-
|
16
|
+
BINDINGS = { :post => "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST".freeze,
|
17
|
+
:redirect => "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect".freeze }.freeze
|
18
|
+
DSIG = "http://www.w3.org/2000/09/xmldsig#".freeze
|
19
|
+
XENC = "http://www.w3.org/2001/04/xmlenc#".freeze
|
20
|
+
DURATION_FORMAT = %r(^
|
21
|
+
(-?)P # 1: Duration sign
|
22
|
+
(?:
|
23
|
+
(?:(\d+)Y)? # 2: Years
|
24
|
+
(?:(\d+)M)? # 3: Months
|
25
|
+
(?:(\d+)D)? # 4: Days
|
26
|
+
(?:T
|
27
|
+
(?:(\d+)H)? # 5: Hours
|
28
|
+
(?:(\d+)M)? # 6: Minutes
|
29
|
+
(?:(\d+(?:[.,]\d+)?)S)? # 7: Seconds
|
30
|
+
)?
|
31
|
+
|
|
32
|
+
(\d+)W # 8: Weeks
|
33
|
+
)
|
34
|
+
$)x.freeze
|
18
35
|
|
19
36
|
# Checks if the x509 cert provided is expired
|
20
37
|
#
|
@@ -28,6 +45,37 @@ module OneLogin
|
|
28
45
|
return cert.not_after < Time.now
|
29
46
|
end
|
30
47
|
|
48
|
+
# Interprets a ISO8601 duration value relative to a given timestamp.
|
49
|
+
#
|
50
|
+
# @param duration [String] The duration, as a string.
|
51
|
+
# @param timestamp [Integer] The unix timestamp we should apply the
|
52
|
+
# duration to. Optional, default to the
|
53
|
+
# current time.
|
54
|
+
#
|
55
|
+
# @return [Integer] The new timestamp, after the duration is applied.
|
56
|
+
#
|
57
|
+
def self.parse_duration(duration, timestamp=Time.now.utc)
|
58
|
+
return nil if RUBY_VERSION < '1.9' # 1.8.7 not supported
|
59
|
+
|
60
|
+
matches = duration.match(DURATION_FORMAT)
|
61
|
+
|
62
|
+
if matches.nil?
|
63
|
+
raise Exception.new("Invalid ISO 8601 duration")
|
64
|
+
end
|
65
|
+
|
66
|
+
sign = matches[1] == '-' ? -1 : 1
|
67
|
+
|
68
|
+
durYears, durMonths, durDays, durHours, durMinutes, durSeconds, durWeeks =
|
69
|
+
matches[2..8].map { |match| match ? sign * match.tr(',', '.').to_f : 0.0 }
|
70
|
+
|
71
|
+
initial_datetime = Time.at(timestamp).utc.to_datetime
|
72
|
+
final_datetime = initial_datetime.next_year(durYears)
|
73
|
+
final_datetime = final_datetime.next_month(durMonths)
|
74
|
+
final_datetime = final_datetime.next_day((7*durWeeks) + durDays)
|
75
|
+
final_timestamp = final_datetime.to_time.utc.to_i + (durHours * 3600) + (durMinutes * 60) + durSeconds
|
76
|
+
return final_timestamp
|
77
|
+
end
|
78
|
+
|
31
79
|
# Return a properly formatted x509 certificate
|
32
80
|
#
|
33
81
|
# @param cert [String] The original certificate
|
@@ -253,6 +301,9 @@ module OneLogin
|
|
253
301
|
when 'http://www.w3.org/2001/04/xmlenc#aes128-cbc' then cipher = OpenSSL::Cipher.new('AES-128-CBC').decrypt
|
254
302
|
when 'http://www.w3.org/2001/04/xmlenc#aes192-cbc' then cipher = OpenSSL::Cipher.new('AES-192-CBC').decrypt
|
255
303
|
when 'http://www.w3.org/2001/04/xmlenc#aes256-cbc' then cipher = OpenSSL::Cipher.new('AES-256-CBC').decrypt
|
304
|
+
when 'http://www.w3.org/2009/xmlenc11#aes128-gcm' then auth_cipher = OpenSSL::Cipher::AES.new(128, :GCM).decrypt
|
305
|
+
when 'http://www.w3.org/2009/xmlenc11#aes192-gcm' then auth_cipher = OpenSSL::Cipher::AES.new(192, :GCM).decrypt
|
306
|
+
when 'http://www.w3.org/2009/xmlenc11#aes256-gcm' then auth_cipher = OpenSSL::Cipher::AES.new(256, :GCM).decrypt
|
256
307
|
when 'http://www.w3.org/2001/04/xmlenc#rsa-1_5' then rsa = symmetric_key
|
257
308
|
when 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p' then oaep = symmetric_key
|
258
309
|
end
|
@@ -263,6 +314,16 @@ module OneLogin
|
|
263
314
|
cipher.padding, cipher.key, cipher.iv = 0, symmetric_key, cipher_text[0..iv_len-1]
|
264
315
|
assertion_plaintext = cipher.update(data)
|
265
316
|
assertion_plaintext << cipher.final
|
317
|
+
elsif auth_cipher
|
318
|
+
iv_len, text_len, tag_len = auth_cipher.iv_len, cipher_text.length, 16
|
319
|
+
data = cipher_text[iv_len..text_len-1-tag_len]
|
320
|
+
auth_cipher.padding = 0
|
321
|
+
auth_cipher.key = symmetric_key
|
322
|
+
auth_cipher.iv = cipher_text[0..iv_len-1]
|
323
|
+
auth_cipher.auth_data = ''
|
324
|
+
auth_cipher.auth_tag = cipher_text[text_len-tag_len..-1]
|
325
|
+
assertion_plaintext = auth_cipher.update(data)
|
326
|
+
assertion_plaintext << auth_cipher.final
|
266
327
|
elsif rsa
|
267
328
|
rsa.private_decrypt(cipher_text)
|
268
329
|
elsif oaep
|