ruby-saml 1.12.4 → 1.13.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 ruby-saml might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/.github/workflows/test.yml +4 -44
- data/{changelog.md → CHANGELOG.md} +19 -12
- data/README.md +288 -226
- data/UPGRADING.md +149 -0
- data/lib/onelogin/ruby-saml/authrequest.rb +2 -3
- data/lib/onelogin/ruby-saml/idp_metadata_parser.rb +110 -77
- data/lib/onelogin/ruby-saml/logoutrequest.rb +3 -3
- data/lib/onelogin/ruby-saml/logoutresponse.rb +2 -3
- data/lib/onelogin/ruby-saml/metadata.rb +59 -22
- data/lib/onelogin/ruby-saml/response.rb +16 -25
- data/lib/onelogin/ruby-saml/saml_message.rb +12 -24
- data/lib/onelogin/ruby-saml/settings.rb +76 -70
- data/lib/onelogin/ruby-saml/slo_logoutrequest.rb +13 -6
- data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +2 -2
- data/lib/onelogin/ruby-saml/utils.rb +27 -22
- data/lib/onelogin/ruby-saml/version.rb +1 -1
- data/lib/xml_security.rb +25 -72
- data/ruby-saml.gemspec +1 -9
- metadata +23 -36
- data/.travis.yml +0 -48
@@ -21,21 +21,50 @@ module OneLogin
|
|
21
21
|
#
|
22
22
|
def generate(settings, pretty_print=false, valid_until=nil, cache_duration=nil)
|
23
23
|
meta_doc = XMLSecurity::Document.new
|
24
|
+
add_xml_declaration(meta_doc)
|
25
|
+
root = add_root_element(meta_doc, settings, valid_until, cache_duration)
|
26
|
+
sp_sso = add_sp_sso_element(root, settings)
|
27
|
+
add_sp_certificates(sp_sso, settings)
|
28
|
+
add_sp_service_elements(sp_sso, settings)
|
29
|
+
add_extras(root, settings)
|
30
|
+
embed_signature(meta_doc, settings)
|
31
|
+
output_xml(meta_doc, pretty_print)
|
32
|
+
end
|
33
|
+
|
34
|
+
protected
|
35
|
+
|
36
|
+
def add_xml_declaration(meta_doc)
|
37
|
+
meta_doc << REXML::XMLDecl.new('1.0', 'UTF-8')
|
38
|
+
end
|
39
|
+
|
40
|
+
def add_root_element(meta_doc, settings, valid_until, cache_duration)
|
24
41
|
namespaces = {
|
25
42
|
"xmlns:md" => "urn:oasis:names:tc:SAML:2.0:metadata"
|
26
43
|
}
|
44
|
+
|
27
45
|
if settings.attribute_consuming_service.configured?
|
28
46
|
namespaces["xmlns:saml"] = "urn:oasis:names:tc:SAML:2.0:assertion"
|
29
47
|
end
|
30
|
-
|
31
|
-
|
48
|
+
|
49
|
+
root = meta_doc.add_element("md:EntityDescriptor", namespaces)
|
50
|
+
root.attributes["ID"] = OneLogin::RubySaml::Utils.uuid
|
51
|
+
root.attributes["entityID"] = settings.sp_entity_id if settings.sp_entity_id
|
52
|
+
root.attributes["validUntil"] = valid_until.strftime('%Y-%m-%dT%H:%M:%S%z') if valid_until
|
53
|
+
root.attributes["cacheDuration"] = "PT" + cache_duration.to_s + "S" if cache_duration
|
54
|
+
root
|
55
|
+
end
|
56
|
+
|
57
|
+
def add_sp_sso_element(root, settings)
|
58
|
+
root.add_element "md:SPSSODescriptor", {
|
32
59
|
"protocolSupportEnumeration" => "urn:oasis:names:tc:SAML:2.0:protocol",
|
33
60
|
"AuthnRequestsSigned" => settings.security[:authn_requests_signed],
|
34
61
|
"WantAssertionsSigned" => settings.security[:want_assertions_signed],
|
35
62
|
}
|
63
|
+
end
|
36
64
|
|
37
|
-
|
38
|
-
|
65
|
+
# Add KeyDescriptor if messages will be signed / encrypted
|
66
|
+
# with SP certificate, and new SP certificate if any
|
67
|
+
def add_sp_certificates(sp_sso, settings)
|
39
68
|
cert = settings.get_sp_cert
|
40
69
|
cert_new = settings.get_sp_cert_new
|
41
70
|
|
@@ -58,16 +87,10 @@ module OneLogin
|
|
58
87
|
end
|
59
88
|
end
|
60
89
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
if valid_until
|
66
|
-
root.attributes["validUntil"] = valid_until.strftime('%Y-%m-%dT%H:%M:%S%z')
|
67
|
-
end
|
68
|
-
if cache_duration
|
69
|
-
root.attributes["cacheDuration"] = "PT" + cache_duration.to_s + "S"
|
70
|
-
end
|
90
|
+
sp_sso
|
91
|
+
end
|
92
|
+
|
93
|
+
def add_sp_service_elements(sp_sso, settings)
|
71
94
|
if settings.single_logout_service_url
|
72
95
|
sp_sso.add_element "md:SingleLogoutService", {
|
73
96
|
"Binding" => settings.single_logout_service_binding,
|
@@ -75,10 +98,12 @@ module OneLogin
|
|
75
98
|
"ResponseLocation" => settings.single_logout_service_url
|
76
99
|
}
|
77
100
|
end
|
101
|
+
|
78
102
|
if settings.name_identifier_format
|
79
103
|
nameid = sp_sso.add_element "md:NameIDFormat"
|
80
104
|
nameid.text = settings.name_identifier_format
|
81
105
|
end
|
106
|
+
|
82
107
|
if settings.assertion_consumer_service_url
|
83
108
|
sp_sso.add_element "md:AssertionConsumerService", {
|
84
109
|
"Binding" => settings.assertion_consumer_service_binding,
|
@@ -117,15 +142,27 @@ module OneLogin
|
|
117
142
|
# <md:RoleDescriptor xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:query="urn:oasis:names:tc:SAML:metadata:ext:query" xsi:type="query:AttributeQueryDescriptorType" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"/>
|
118
143
|
# <md:XACMLAuthzDecisionQueryDescriptor WantAssertionsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"/>
|
119
144
|
|
120
|
-
|
145
|
+
sp_sso
|
146
|
+
end
|
121
147
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
148
|
+
# can be overridden in subclass
|
149
|
+
def add_extras(root, _settings)
|
150
|
+
root
|
151
|
+
end
|
152
|
+
|
153
|
+
def embed_signature(meta_doc, settings)
|
154
|
+
return unless settings.security[:metadata_signed]
|
155
|
+
|
156
|
+
private_key = settings.get_sp_key
|
157
|
+
cert = settings.get_sp_cert
|
158
|
+
return unless private_key && cert
|
159
|
+
|
160
|
+
meta_doc.sign_document(private_key, cert, settings.security[:signature_method], settings.security[:digest_method])
|
161
|
+
end
|
162
|
+
|
163
|
+
def output_xml(meta_doc, pretty_print)
|
164
|
+
ret = ''
|
127
165
|
|
128
|
-
ret = ""
|
129
166
|
# pretty print the XML so IdP administrators can easily see what the SP supports
|
130
167
|
if pretty_print
|
131
168
|
meta_doc.write(ret, 1)
|
@@ -133,7 +170,7 @@ module OneLogin
|
|
133
170
|
ret = meta_doc.to_s
|
134
171
|
end
|
135
172
|
|
136
|
-
|
173
|
+
ret
|
137
174
|
end
|
138
175
|
end
|
139
176
|
end
|
@@ -63,7 +63,7 @@ module OneLogin
|
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
66
|
-
@response = decode_raw_saml(response)
|
66
|
+
@response = decode_raw_saml(response, settings)
|
67
67
|
@document = XMLSecurity::SignedDocument.new(@response, @errors)
|
68
68
|
|
69
69
|
if assertion_encrypted?
|
@@ -227,11 +227,10 @@ module OneLogin
|
|
227
227
|
statuses = nodes.collect do |inner_node|
|
228
228
|
inner_node.attributes["Value"]
|
229
229
|
end
|
230
|
-
|
231
|
-
|
232
|
-
code = "#{code} | #{extra_code}"
|
233
|
-
end
|
230
|
+
|
231
|
+
code = [code, statuses].flatten.join(" | ")
|
234
232
|
end
|
233
|
+
|
235
234
|
code
|
236
235
|
end
|
237
236
|
end
|
@@ -338,9 +337,9 @@ module OneLogin
|
|
338
337
|
end
|
339
338
|
|
340
339
|
# returns the allowed clock drift on timing validation
|
341
|
-
# @return [
|
340
|
+
# @return [Float]
|
342
341
|
def allowed_clock_drift
|
343
|
-
|
342
|
+
options[:allowed_clock_drift].to_f.abs + Float::EPSILON
|
344
343
|
end
|
345
344
|
|
346
345
|
# Checks if the SAML Response contains or not an EncryptedAssertion element
|
@@ -377,7 +376,6 @@ module OneLogin
|
|
377
376
|
return false unless validate_response_state
|
378
377
|
|
379
378
|
validations = [
|
380
|
-
:validate_response_state,
|
381
379
|
:validate_version,
|
382
380
|
:validate_id,
|
383
381
|
:validate_success_status,
|
@@ -425,13 +423,12 @@ module OneLogin
|
|
425
423
|
#
|
426
424
|
def validate_structure
|
427
425
|
structure_error_msg = "Invalid SAML Response. Not match the saml-schema-protocol-2.0.xsd"
|
428
|
-
|
429
|
-
unless valid_saml?(document, soft, check_malformed_doc)
|
426
|
+
unless valid_saml?(document, soft)
|
430
427
|
return append_error(structure_error_msg)
|
431
428
|
end
|
432
429
|
|
433
430
|
unless decrypted_document.nil?
|
434
|
-
unless valid_saml?(decrypted_document, soft
|
431
|
+
unless valid_saml?(decrypted_document, soft)
|
435
432
|
return append_error(structure_error_msg)
|
436
433
|
end
|
437
434
|
end
|
@@ -695,13 +692,13 @@ module OneLogin
|
|
695
692
|
|
696
693
|
now = Time.now.utc
|
697
694
|
|
698
|
-
if not_before &&
|
699
|
-
error_msg = "Current time is earlier than NotBefore condition (#{
|
695
|
+
if not_before && now < (not_before - allowed_clock_drift)
|
696
|
+
error_msg = "Current time is earlier than NotBefore condition (#{now} < #{not_before}#{" - #{allowed_clock_drift.ceil}s" if allowed_clock_drift > 0})"
|
700
697
|
return append_error(error_msg)
|
701
698
|
end
|
702
699
|
|
703
|
-
if not_on_or_after && now >= (
|
704
|
-
error_msg = "Current time is on or after NotOnOrAfter condition (#{now} >= #{
|
700
|
+
if not_on_or_after && now >= (not_on_or_after + allowed_clock_drift)
|
701
|
+
error_msg = "Current time is on or after NotOnOrAfter condition (#{now} >= #{not_on_or_after}#{" + #{allowed_clock_drift.ceil}s" if allowed_clock_drift > 0})"
|
705
702
|
return append_error(error_msg)
|
706
703
|
end
|
707
704
|
|
@@ -743,7 +740,7 @@ module OneLogin
|
|
743
740
|
return true if session_expires_at.nil?
|
744
741
|
|
745
742
|
now = Time.now.utc
|
746
|
-
unless (session_expires_at + allowed_clock_drift)
|
743
|
+
unless now < (session_expires_at + allowed_clock_drift)
|
747
744
|
error_msg = "The attributes have expired, based on the SessionNotOnOrAfter of the AuthnStatement of this Response"
|
748
745
|
return append_error(error_msg)
|
749
746
|
end
|
@@ -781,8 +778,8 @@ module OneLogin
|
|
781
778
|
|
782
779
|
attrs = confirmation_data_node.attributes
|
783
780
|
next if (attrs.include? "InResponseTo" and attrs['InResponseTo'] != in_response_to) ||
|
784
|
-
(attrs.include? "
|
785
|
-
(attrs.include? "
|
781
|
+
(attrs.include? "NotBefore" and now < (parse_time(confirmation_data_node, "NotBefore") - allowed_clock_drift)) ||
|
782
|
+
(attrs.include? "NotOnOrAfter" and now >= (parse_time(confirmation_data_node, "NotOnOrAfter") + allowed_clock_drift)) ||
|
786
783
|
(attrs.include? "Recipient" and !options[:skip_recipient_check] and settings and attrs['Recipient'] != settings.assertion_consumer_service_url)
|
787
784
|
|
788
785
|
valid_subject_confirmation = true
|
@@ -866,8 +863,6 @@ module OneLogin
|
|
866
863
|
fingerprint = settings.get_fingerprint
|
867
864
|
opts[:cert] = idp_cert
|
868
865
|
|
869
|
-
check_malformed_doc = check_malformed_doc_enabled?
|
870
|
-
opts[:check_malformed_doc] = check_malformed_doc
|
871
866
|
if fingerprint && doc.validate_document(fingerprint, @soft, opts)
|
872
867
|
if settings.security[:check_idp_cert_expiration]
|
873
868
|
if OneLogin::RubySaml::Utils.is_cert_expired(idp_cert)
|
@@ -882,7 +877,7 @@ module OneLogin
|
|
882
877
|
valid = false
|
883
878
|
expired = false
|
884
879
|
idp_certs[:signing].each do |idp_cert|
|
885
|
-
valid = doc.validate_document_with_cert(idp_cert, true
|
880
|
+
valid = doc.validate_document_with_cert(idp_cert, true)
|
886
881
|
if valid
|
887
882
|
if settings.security[:check_idp_cert_expiration]
|
888
883
|
if OneLogin::RubySaml::Utils.is_cert_expired(idp_cert)
|
@@ -1066,10 +1061,6 @@ module OneLogin
|
|
1066
1061
|
Time.parse(node.attributes[attribute])
|
1067
1062
|
end
|
1068
1063
|
end
|
1069
|
-
|
1070
|
-
def check_malformed_doc_enabled?
|
1071
|
-
check_malformed_doc?(settings)
|
1072
|
-
end
|
1073
1064
|
end
|
1074
1065
|
end
|
1075
1066
|
end
|
@@ -16,14 +16,12 @@ module OneLogin
|
|
16
16
|
class SamlMessage
|
17
17
|
include REXML
|
18
18
|
|
19
|
-
ASSERTION = "urn:oasis:names:tc:SAML:2.0:assertion"
|
20
|
-
PROTOCOL = "urn:oasis:names:tc:SAML:2.0:protocol"
|
19
|
+
ASSERTION = "urn:oasis:names:tc:SAML:2.0:assertion".freeze
|
20
|
+
PROTOCOL = "urn:oasis:names:tc:SAML:2.0:protocol".freeze
|
21
21
|
|
22
22
|
BASE64_FORMAT = %r(\A([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?\Z)
|
23
23
|
@@mutex = Mutex.new
|
24
24
|
|
25
|
-
MAX_BYTE_SIZE = 250000
|
26
|
-
|
27
25
|
# @return [Nokogiri::XML::Schema] Gets the schema object of the SAML 2.0 Protocol schema
|
28
26
|
#
|
29
27
|
def self.schema
|
@@ -63,13 +61,14 @@ module OneLogin
|
|
63
61
|
# Validates the SAML Message against the specified schema.
|
64
62
|
# @param document [REXML::Document] The message that will be validated
|
65
63
|
# @param soft [Boolean] soft Enable or Disable the soft mode (In order to raise exceptions when the message is invalid or not)
|
66
|
-
# @param check_malformed_doc [Boolean] check_malformed_doc Enable or Disable the check for malformed XML
|
67
64
|
# @return [Boolean] True if the XML is valid, otherwise False, if soft=True
|
68
65
|
# @raise [ValidationError] if soft == false and validation fails
|
69
66
|
#
|
70
|
-
def valid_saml?(document, soft = true
|
67
|
+
def valid_saml?(document, soft = true)
|
71
68
|
begin
|
72
|
-
xml =
|
69
|
+
xml = Nokogiri::XML(document.to_s) do |config|
|
70
|
+
config.options = XMLSecurity::BaseDocument::NOKOGIRI_OPTIONS
|
71
|
+
end
|
73
72
|
rescue Exception => error
|
74
73
|
return false if soft
|
75
74
|
raise ValidationError.new("XML load failed: #{error.message}")
|
@@ -87,25 +86,20 @@ module OneLogin
|
|
87
86
|
# @param saml [String] The deflated and encoded SAML Message
|
88
87
|
# @return [String] The plain SAML Message
|
89
88
|
#
|
90
|
-
def decode_raw_saml(saml)
|
89
|
+
def decode_raw_saml(saml, settings = nil)
|
91
90
|
return saml unless base64_encoded?(saml)
|
92
91
|
|
93
|
-
|
94
|
-
|
92
|
+
settings = OneLogin::RubySaml::Settings.new if settings.nil?
|
93
|
+
if saml.bytesize > settings.message_max_bytesize
|
94
|
+
raise ValidationError.new("Encoded SAML Message exceeds " + settings.message_max_bytesize.to_s + " bytes, so was rejected")
|
95
95
|
end
|
96
96
|
|
97
97
|
decoded = decode(saml)
|
98
98
|
begin
|
99
|
-
|
99
|
+
inflate(decoded)
|
100
100
|
rescue
|
101
|
-
|
102
|
-
end
|
103
|
-
|
104
|
-
if message.bytesize > MAX_BYTE_SIZE
|
105
|
-
raise ValidationError.new("Encoded SAML Message exceeds " + MAX_BYTE_SIZE.to_s + " bytes, so was rejected")
|
101
|
+
decoded
|
106
102
|
end
|
107
|
-
|
108
|
-
message
|
109
103
|
end
|
110
104
|
|
111
105
|
# Deflate, base64 encode and url-encode a SAML Message (To be used in the HTTP-redirect binding)
|
@@ -162,12 +156,6 @@ module OneLogin
|
|
162
156
|
def deflate(inflated)
|
163
157
|
Zlib::Deflate.deflate(inflated, 9)[2..-5]
|
164
158
|
end
|
165
|
-
|
166
|
-
def check_malformed_doc?(settings)
|
167
|
-
default_value = OneLogin::RubySaml::Settings::DEFAULTS[:check_malformed_doc]
|
168
|
-
|
169
|
-
settings.nil? ? default_value : settings.check_malformed_doc
|
170
|
-
end
|
171
159
|
end
|
172
160
|
end
|
173
161
|
end
|
@@ -31,9 +31,8 @@ module OneLogin
|
|
31
31
|
|
32
32
|
# IdP Data
|
33
33
|
attr_accessor :idp_entity_id
|
34
|
-
|
35
|
-
|
36
|
-
attr_accessor :idp_slo_service_url
|
34
|
+
attr_writer :idp_sso_service_url
|
35
|
+
attr_writer :idp_slo_service_url
|
37
36
|
attr_accessor :idp_slo_response_service_url
|
38
37
|
attr_accessor :idp_cert
|
39
38
|
attr_accessor :idp_cert_fingerprint
|
@@ -43,8 +42,10 @@ module OneLogin
|
|
43
42
|
attr_accessor :idp_name_qualifier
|
44
43
|
attr_accessor :valid_until
|
45
44
|
# SP Data
|
45
|
+
attr_writer :sp_entity_id
|
46
46
|
attr_accessor :assertion_consumer_service_url
|
47
|
-
|
47
|
+
attr_reader :assertion_consumer_service_binding
|
48
|
+
attr_writer :single_logout_service_url
|
48
49
|
attr_accessor :sp_name_qualifier
|
49
50
|
attr_accessor :name_identifier_format
|
50
51
|
attr_accessor :name_identifier_value
|
@@ -53,9 +54,9 @@ module OneLogin
|
|
53
54
|
attr_accessor :compress_request
|
54
55
|
attr_accessor :compress_response
|
55
56
|
attr_accessor :double_quote_xml_attribute_values
|
56
|
-
attr_accessor :
|
57
|
+
attr_accessor :message_max_bytesize
|
57
58
|
attr_accessor :passive
|
58
|
-
|
59
|
+
attr_reader :protocol_binding
|
59
60
|
attr_accessor :attributes_index
|
60
61
|
attr_accessor :force_authn
|
61
62
|
attr_accessor :certificate
|
@@ -68,9 +69,9 @@ module OneLogin
|
|
68
69
|
# Work-flow
|
69
70
|
attr_accessor :security
|
70
71
|
attr_accessor :soft
|
71
|
-
#
|
72
|
+
# Deprecated
|
72
73
|
attr_accessor :assertion_consumer_logout_service_url
|
73
|
-
|
74
|
+
attr_reader :assertion_consumer_logout_service_binding
|
74
75
|
attr_accessor :issuer
|
75
76
|
attr_accessor :idp_sso_target_url
|
76
77
|
attr_accessor :idp_slo_target_url
|
@@ -78,94 +79,89 @@ module OneLogin
|
|
78
79
|
# @return [String] IdP Single Sign On Service URL
|
79
80
|
#
|
80
81
|
def idp_sso_service_url
|
81
|
-
|
82
|
-
if @idp_sso_service_url.nil?
|
83
|
-
if @idp_sso_target_url
|
84
|
-
val = @idp_sso_target_url
|
85
|
-
end
|
86
|
-
else
|
87
|
-
val = @idp_sso_service_url
|
88
|
-
end
|
89
|
-
val
|
82
|
+
@idp_sso_service_url || @idp_sso_target_url
|
90
83
|
end
|
91
84
|
|
92
85
|
# @return [String] IdP Single Logout Service URL
|
93
86
|
#
|
94
87
|
def idp_slo_service_url
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
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)
|
104
115
|
end
|
105
116
|
|
106
117
|
# @return [String] SP Entity ID
|
107
118
|
#
|
108
119
|
def sp_entity_id
|
109
|
-
|
110
|
-
if @sp_entity_id.nil?
|
111
|
-
if @issuer
|
112
|
-
val = @issuer
|
113
|
-
end
|
114
|
-
else
|
115
|
-
val = @sp_entity_id
|
116
|
-
end
|
117
|
-
val
|
120
|
+
@sp_entity_id || @issuer
|
118
121
|
end
|
119
122
|
|
120
|
-
# Setter for SP
|
121
|
-
# @param
|
123
|
+
# Setter for SP Protocol Binding
|
124
|
+
# @param value [String, Symbol].
|
122
125
|
#
|
123
|
-
def
|
124
|
-
@
|
126
|
+
def protocol_binding=(value)
|
127
|
+
@protocol_binding = get_binding(value)
|
125
128
|
end
|
126
129
|
|
127
|
-
#
|
130
|
+
# Setter for SP Assertion Consumer Service Binding
|
131
|
+
# @param value [String, Symbol].
|
128
132
|
#
|
129
|
-
def
|
130
|
-
|
131
|
-
if @single_logout_service_url.nil?
|
132
|
-
if @assertion_consumer_logout_service_url
|
133
|
-
val = @assertion_consumer_logout_service_url
|
134
|
-
end
|
135
|
-
else
|
136
|
-
val = @single_logout_service_url
|
137
|
-
end
|
138
|
-
val
|
133
|
+
def assertion_consumer_service_binding=(value)
|
134
|
+
@assertion_consumer_service_binding = get_binding(value)
|
139
135
|
end
|
140
136
|
|
141
|
-
#
|
142
|
-
# @param url [String].
|
137
|
+
# @return [String] Single Logout Service URL.
|
143
138
|
#
|
144
|
-
def single_logout_service_url
|
145
|
-
@single_logout_service_url
|
139
|
+
def single_logout_service_url
|
140
|
+
@single_logout_service_url || @assertion_consumer_logout_service_url
|
146
141
|
end
|
147
142
|
|
148
143
|
# @return [String] Single Logout Service Binding.
|
149
144
|
#
|
150
145
|
def single_logout_service_binding
|
151
|
-
|
152
|
-
if @single_logout_service_binding.nil?
|
153
|
-
if @assertion_consumer_logout_service_binding
|
154
|
-
val = @assertion_consumer_logout_service_binding
|
155
|
-
end
|
156
|
-
else
|
157
|
-
val = @single_logout_service_binding
|
158
|
-
end
|
159
|
-
val
|
146
|
+
@single_logout_service_binding || @assertion_consumer_logout_service_binding
|
160
147
|
end
|
161
148
|
|
162
149
|
# Setter for Single Logout Service Binding.
|
163
150
|
#
|
164
151
|
# (Currently we only support "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect")
|
165
|
-
# @param
|
152
|
+
# @param value [String, Symbol]
|
166
153
|
#
|
167
|
-
def single_logout_service_binding=(
|
168
|
-
@single_logout_service_binding =
|
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]
|
162
|
+
#
|
163
|
+
def assertion_consumer_logout_service_binding=(value)
|
164
|
+
@assertion_consumer_logout_service_binding = get_binding(value)
|
169
165
|
end
|
170
166
|
|
171
167
|
# Calculates the fingerprint of the IdP x509 certificate.
|
@@ -253,14 +249,24 @@ module OneLogin
|
|
253
249
|
|
254
250
|
private
|
255
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
|
+
|
256
262
|
DEFAULTS = {
|
257
|
-
:assertion_consumer_service_binding =>
|
258
|
-
:single_logout_service_binding =>
|
263
|
+
:assertion_consumer_service_binding => Utils::BINDINGS[:post],
|
264
|
+
:single_logout_service_binding => Utils::BINDINGS[:redirect],
|
259
265
|
:idp_cert_fingerprint_algorithm => XMLSecurity::Document::SHA1,
|
260
266
|
:compress_request => true,
|
261
267
|
:compress_response => true,
|
268
|
+
:message_max_bytesize => 250000,
|
262
269
|
:soft => true,
|
263
|
-
:check_malformed_doc => true,
|
264
270
|
:double_quote_xml_attribute_values => false,
|
265
271
|
:security => {
|
266
272
|
:authn_requests_signed => false,
|
@@ -270,7 +276,7 @@ module OneLogin
|
|
270
276
|
:want_assertions_encrypted => false,
|
271
277
|
:want_name_id => false,
|
272
278
|
:metadata_signed => false,
|
273
|
-
:embed_sign => false,
|
279
|
+
:embed_sign => false, # Deprecated
|
274
280
|
:digest_method => XMLSecurity::Document::SHA1,
|
275
281
|
:signature_method => XMLSecurity::Document::RSA_SHA1,
|
276
282
|
:check_idp_cert_expiration => false,
|
@@ -43,7 +43,7 @@ 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
|
|
@@ -130,6 +130,12 @@ module OneLogin
|
|
130
130
|
|
131
131
|
private
|
132
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
|
+
|
133
139
|
# Hard aux function to validate the Logout Request
|
134
140
|
# @param collect_errors [Boolean] Stop validation when first error appears or keep validating. (if soft=true)
|
135
141
|
# @return [Boolean] TRUE if the Logout Request is valid
|
@@ -180,15 +186,17 @@ module OneLogin
|
|
180
186
|
true
|
181
187
|
end
|
182
188
|
|
183
|
-
# 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)
|
184
191
|
# If fails, the error is added to the errors array
|
185
192
|
# @return [Boolean] True if satisfies the conditions, otherwise False if soft=True
|
186
193
|
# @raise [ValidationError] if soft == false and validation fails
|
187
194
|
#
|
188
195
|
def validate_not_on_or_after
|
189
196
|
now = Time.now.utc
|
190
|
-
|
191
|
-
|
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})")
|
192
200
|
end
|
193
201
|
|
194
202
|
true
|
@@ -199,8 +207,7 @@ module OneLogin
|
|
199
207
|
# @raise [ValidationError] if soft == false and validation fails
|
200
208
|
#
|
201
209
|
def validate_structure
|
202
|
-
|
203
|
-
unless valid_saml?(document, soft, check_malformed_doc)
|
210
|
+
unless valid_saml?(document, soft)
|
204
211
|
return append_error("Invalid SAML Logout Request. Not match the saml-schema-protocol-2.0.xsd")
|
205
212
|
end
|
206
213
|
|
@@ -79,7 +79,7 @@ module OneLogin
|
|
79
79
|
base64_response = encode(response)
|
80
80
|
response_params = {"SAMLResponse" => base64_response}
|
81
81
|
|
82
|
-
if settings.
|
82
|
+
if settings.idp_slo_service_binding == Utils::BINDINGS[:redirect] && settings.security[:logout_responses_signed] && settings.private_key
|
83
83
|
params['SigAlg'] = settings.security[:signature_method]
|
84
84
|
url_string = OneLogin::RubySaml::Utils.build_query(
|
85
85
|
:type => 'SAMLResponse',
|
@@ -150,7 +150,7 @@ module OneLogin
|
|
150
150
|
|
151
151
|
def sign_document(document, settings)
|
152
152
|
# embed signature
|
153
|
-
if settings.
|
153
|
+
if settings.idp_slo_service_binding == Utils::BINDINGS[:post] && settings.private_key && settings.certificate
|
154
154
|
private_key = settings.get_sp_key
|
155
155
|
cert = settings.get_sp_cert
|
156
156
|
document.sign_document(private_key, cert, settings.security[:signature_method], settings.security[:digest_method])
|