ruby-saml 0.8.18 → 0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +1 -6
- data/Gemfile +2 -12
- data/README.md +363 -35
- data/Rakefile +14 -0
- data/changelog.md +22 -9
- data/lib/onelogin/ruby-saml/attribute_service.rb +34 -0
- data/lib/onelogin/ruby-saml/attributes.rb +26 -64
- data/lib/onelogin/ruby-saml/authrequest.rb +47 -93
- data/lib/onelogin/ruby-saml/idp_metadata_parser.rb +87 -0
- data/lib/onelogin/ruby-saml/logoutrequest.rb +36 -100
- data/lib/onelogin/ruby-saml/logoutresponse.rb +25 -35
- data/lib/onelogin/ruby-saml/metadata.rb +46 -16
- data/lib/onelogin/ruby-saml/response.rb +63 -373
- data/lib/onelogin/ruby-saml/saml_message.rb +78 -0
- data/lib/onelogin/ruby-saml/settings.rb +54 -122
- data/lib/onelogin/ruby-saml/slo_logoutrequest.rb +25 -71
- data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +37 -102
- data/lib/onelogin/ruby-saml/utils.rb +32 -199
- data/lib/onelogin/ruby-saml/version.rb +1 -1
- data/lib/ruby-saml.rb +5 -2
- data/lib/schemas/{saml20assertion_schema.xsd → saml-schema-assertion-2.0.xsd} +283 -283
- data/lib/schemas/saml-schema-authn-context-2.0.xsd +23 -0
- data/lib/schemas/saml-schema-authn-context-types-2.0.xsd +821 -0
- data/lib/schemas/saml-schema-metadata-2.0.xsd +339 -0
- data/lib/schemas/{saml20protocol_schema.xsd → saml-schema-protocol-2.0.xsd} +302 -302
- data/lib/schemas/sstc-metadata-attr.xsd +35 -0
- data/lib/schemas/sstc-saml-attribute-ext.xsd +25 -0
- data/lib/schemas/sstc-saml-metadata-algsupport-v1.0.xsd +41 -0
- data/lib/schemas/sstc-saml-metadata-ui-v1.0.xsd +89 -0
- data/lib/schemas/{xenc_schema.xsd → xenc-schema.xsd} +1 -11
- data/lib/schemas/xml.xsd +287 -0
- data/lib/schemas/{xmldsig_schema.xsd → xmldsig-core-schema.xsd} +0 -9
- data/lib/xml_security.rb +83 -235
- data/ruby-saml.gemspec +1 -0
- data/test/idp_metadata_parser_test.rb +54 -0
- data/test/logoutrequest_test.rb +68 -155
- data/test/logoutresponse_test.rb +43 -32
- data/test/metadata_test.rb +87 -0
- data/test/request_test.rb +102 -99
- data/test/response_test.rb +181 -495
- data/test/responses/idp_descriptor.xml +3 -0
- data/test/responses/logoutresponse_fixtures.rb +7 -8
- data/test/responses/response_no_cert_and_encrypted_attrs.xml +29 -0
- data/test/responses/response_with_multiple_attribute_values.xml +1 -1
- data/test/responses/slo_request.xml +4 -0
- data/test/settings_test.rb +25 -112
- data/test/slo_logoutrequest_test.rb +40 -50
- data/test/slo_logoutresponse_test.rb +86 -185
- data/test/test_helper.rb +27 -102
- data/test/xml_security_test.rb +114 -337
- metadata +30 -81
- data/lib/onelogin/ruby-saml/setting_error.rb +0 -6
- data/test/certificates/certificate.der +0 -0
- 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/requests/logoutrequest_fixtures.rb +0 -47
- data/test/responses/encrypted_new_attack.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/multiple_signed.xml.base64 +0 -1
- data/test/responses/invalids/no_signature.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/response_node_text_attack.xml.base64 +0 -1
- data/test/responses/response_with_concealed_signed_assertion.xml +0 -51
- data/test/responses/response_with_doubled_signed_assertion.xml +0 -49
- data/test/responses/response_with_multiple_attribute_statements.xml +0 -72
- 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_wrapped.xml.base64 +0 -150
- data/test/responses/valid_response.xml.base64 +0 -1
- data/test/responses/valid_response_without_x509certificate.xml.base64 +0 -1
- data/test/utils_test.rb +0 -231
|
@@ -1,82 +1,48 @@
|
|
|
1
|
-
require "xml_security"
|
|
2
|
-
require "onelogin/ruby-saml/utils"
|
|
3
|
-
|
|
4
1
|
module OneLogin
|
|
5
2
|
module RubySaml
|
|
6
3
|
class Settings
|
|
7
|
-
def initialize(overrides = {}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
config.each do |k,v|
|
|
17
|
-
acc = "#{k.to_s}=".to_sym
|
|
18
|
-
if respond_to? acc
|
|
19
|
-
value = v.is_a?(Hash) ? v.dup : v
|
|
20
|
-
send(acc, value)
|
|
21
|
-
end
|
|
22
|
-
end
|
|
4
|
+
def initialize(overrides = {})
|
|
5
|
+
config = DEFAULTS.merge(overrides)
|
|
6
|
+
config.each do |k,v|
|
|
7
|
+
acc = "#{k.to_s}=".to_sym
|
|
8
|
+
self.send(acc, v) if self.respond_to? acc
|
|
9
|
+
end
|
|
10
|
+
@attribute_consuming_service = AttributeService.new
|
|
23
11
|
end
|
|
24
12
|
|
|
25
|
-
#
|
|
13
|
+
# IdP Data
|
|
14
|
+
attr_accessor :idp_entity_id
|
|
26
15
|
attr_accessor :idp_sso_target_url
|
|
27
|
-
attr_accessor :idp_cert_fingerprint
|
|
28
|
-
attr_accessor :idp_cert
|
|
29
16
|
attr_accessor :idp_slo_target_url
|
|
30
|
-
attr_accessor :
|
|
31
|
-
|
|
32
|
-
|
|
17
|
+
attr_accessor :idp_cert
|
|
18
|
+
attr_accessor :idp_cert_fingerprint
|
|
19
|
+
# SP Data
|
|
20
|
+
attr_accessor :issuer
|
|
33
21
|
attr_accessor :assertion_consumer_service_url
|
|
34
|
-
attr_accessor :
|
|
22
|
+
attr_accessor :assertion_consumer_service_binding
|
|
35
23
|
attr_accessor :sp_name_qualifier
|
|
36
|
-
attr_accessor :idp_name_qualifier
|
|
37
24
|
attr_accessor :name_identifier_format
|
|
38
25
|
attr_accessor :name_identifier_value
|
|
39
|
-
attr_accessor :name_identifier_value_requested
|
|
40
26
|
attr_accessor :sessionindex
|
|
41
27
|
attr_accessor :compress_request
|
|
42
28
|
attr_accessor :compress_response
|
|
43
29
|
attr_accessor :double_quote_xml_attribute_values
|
|
44
|
-
attr_accessor :force_authn
|
|
45
30
|
attr_accessor :passive
|
|
46
31
|
attr_accessor :protocol_binding
|
|
32
|
+
attr_accessor :attributes_index
|
|
33
|
+
attr_accessor :force_authn
|
|
34
|
+
attr_accessor :security
|
|
47
35
|
attr_accessor :certificate
|
|
48
36
|
attr_accessor :private_key
|
|
49
|
-
|
|
50
|
-
attr_accessor :
|
|
37
|
+
attr_accessor :authn_context
|
|
38
|
+
attr_accessor :authn_context_comparison
|
|
39
|
+
attr_accessor :authn_context_decl_ref
|
|
40
|
+
attr_reader :attribute_consuming_service
|
|
51
41
|
# Compability
|
|
52
|
-
attr_accessor :issuer
|
|
53
42
|
attr_accessor :assertion_consumer_logout_service_url
|
|
54
43
|
attr_accessor :assertion_consumer_logout_service_binding
|
|
55
44
|
|
|
56
|
-
|
|
57
|
-
#
|
|
58
|
-
def sp_entity_id
|
|
59
|
-
val = nil
|
|
60
|
-
if @sp_entity_id.nil?
|
|
61
|
-
if @issuer
|
|
62
|
-
val = @issuer
|
|
63
|
-
end
|
|
64
|
-
else
|
|
65
|
-
val = @sp_entity_id
|
|
66
|
-
end
|
|
67
|
-
val
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
# Setter for SP Entity ID.
|
|
71
|
-
# @param val [String].
|
|
72
|
-
#
|
|
73
|
-
def sp_entity_id=(val)
|
|
74
|
-
@sp_entity_id = val
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
# @return [String] Single Logout Service URL.
|
|
78
|
-
#
|
|
79
|
-
def single_logout_service_url
|
|
45
|
+
def single_logout_service_url()
|
|
80
46
|
val = nil
|
|
81
47
|
if @single_logout_service_url.nil?
|
|
82
48
|
if @assertion_consumer_logout_service_url
|
|
@@ -88,16 +54,12 @@ module OneLogin
|
|
|
88
54
|
val
|
|
89
55
|
end
|
|
90
56
|
|
|
91
|
-
#
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
def single_logout_service_url=(url)
|
|
95
|
-
@single_logout_service_url = url
|
|
57
|
+
# setter
|
|
58
|
+
def single_logout_service_url=(val)
|
|
59
|
+
@single_logout_service_url = val
|
|
96
60
|
end
|
|
97
61
|
|
|
98
|
-
|
|
99
|
-
#
|
|
100
|
-
def single_logout_service_binding
|
|
62
|
+
def single_logout_service_binding()
|
|
101
63
|
val = nil
|
|
102
64
|
if @single_logout_service_binding.nil?
|
|
103
65
|
if @assertion_consumer_logout_service_binding
|
|
@@ -109,76 +71,46 @@ module OneLogin
|
|
|
109
71
|
val
|
|
110
72
|
end
|
|
111
73
|
|
|
112
|
-
#
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
# @param url [String]
|
|
116
|
-
#
|
|
117
|
-
def single_logout_service_binding=(url)
|
|
118
|
-
@single_logout_service_binding = url
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
# Calculates the fingerprint of the IdP x509 certificate.
|
|
122
|
-
# @return [String] The fingerprint
|
|
123
|
-
#
|
|
124
|
-
def get_fingerprint
|
|
125
|
-
idp_cert_fingerprint || begin
|
|
126
|
-
idp_cert = get_idp_cert
|
|
127
|
-
if idp_cert
|
|
128
|
-
Digest::SHA1.hexdigest(idp_cert.to_der).upcase.scan(/../).join(":")
|
|
129
|
-
end
|
|
130
|
-
end
|
|
74
|
+
# setter
|
|
75
|
+
def single_logout_service_binding=(val)
|
|
76
|
+
@single_logout_service_binding = val
|
|
131
77
|
end
|
|
132
78
|
|
|
133
|
-
# @return [OpenSSL::X509::Certificate|nil] Build the IdP certificate from the settings (previously format it)
|
|
134
|
-
#
|
|
135
|
-
def get_idp_cert
|
|
136
|
-
return nil if idp_cert.nil?
|
|
137
|
-
|
|
138
|
-
if idp_cert.respond_to?(:to_pem)
|
|
139
|
-
idp_cert
|
|
140
|
-
else
|
|
141
|
-
return nil if idp_cert.empty?
|
|
142
|
-
formatted_cert = OneLogin::RubySaml::Utils.format_cert(idp_cert)
|
|
143
|
-
OpenSSL::X509::Certificate.new(formatted_cert)
|
|
144
|
-
end
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
# @return [OpenSSL::X509::Certificate|nil] Build the SP certificate from the settings (previously format it)
|
|
148
|
-
#
|
|
149
79
|
def get_sp_cert
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
80
|
+
cert = nil
|
|
81
|
+
if self.certificate
|
|
82
|
+
formated_cert = OneLogin::RubySaml::Utils.format_cert(self.certificate)
|
|
83
|
+
cert = OpenSSL::X509::Certificate.new(formated_cert)
|
|
84
|
+
end
|
|
85
|
+
cert
|
|
154
86
|
end
|
|
155
87
|
|
|
156
|
-
# @return [OpenSSL::PKey::RSA] Build the SP private from the settings (previously format it)
|
|
157
|
-
#
|
|
158
88
|
def get_sp_key
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
89
|
+
private_key = nil
|
|
90
|
+
if self.private_key
|
|
91
|
+
formated_private_key = OneLogin::RubySaml::Utils.format_private_key(self.private_key)
|
|
92
|
+
private_key = OpenSSL::PKey::RSA.new(formated_private_key)
|
|
93
|
+
end
|
|
94
|
+
private_key
|
|
163
95
|
end
|
|
164
96
|
|
|
165
97
|
private
|
|
166
98
|
|
|
167
99
|
DEFAULTS = {
|
|
168
|
-
:
|
|
169
|
-
:
|
|
170
|
-
:
|
|
171
|
-
:
|
|
172
|
-
:single_logout_service_binding => "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect".freeze,
|
|
100
|
+
:assertion_consumer_service_binding => "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
|
|
101
|
+
:single_logout_service_binding => "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect",
|
|
102
|
+
:compress_request => true,
|
|
103
|
+
:compress_response => true,
|
|
173
104
|
:security => {
|
|
174
|
-
:authn_requests_signed
|
|
175
|
-
:logout_requests_signed
|
|
176
|
-
:logout_responses_signed
|
|
177
|
-
:embed_sign
|
|
178
|
-
:digest_method
|
|
179
|
-
:signature_method
|
|
180
|
-
}
|
|
181
|
-
|
|
105
|
+
:authn_requests_signed => false,
|
|
106
|
+
:logout_requests_signed => false,
|
|
107
|
+
:logout_responses_signed => false,
|
|
108
|
+
:embed_sign => false,
|
|
109
|
+
:digest_method => XMLSecurity::Document::SHA1,
|
|
110
|
+
:signature_method => XMLSecurity::Document::SHA1
|
|
111
|
+
},
|
|
112
|
+
:double_quote_xml_attribute_values => false,
|
|
113
|
+
}
|
|
182
114
|
end
|
|
183
115
|
end
|
|
184
116
|
end
|
|
@@ -1,112 +1,66 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
1
|
+
require 'zlib'
|
|
2
|
+
require 'time'
|
|
3
|
+
require 'nokogiri'
|
|
3
4
|
|
|
4
5
|
# Only supports SAML 2.0
|
|
5
|
-
# SAML2 Logout Request (SLO IdP initiated, Parser)
|
|
6
6
|
module OneLogin
|
|
7
7
|
module RubySaml
|
|
8
|
-
class SloLogoutrequest
|
|
9
|
-
|
|
10
|
-
ASSERTION = "urn:oasis:names:tc:SAML:2.0:assertion"
|
|
11
|
-
PROTOCOL = "urn:oasis:names:tc:SAML:2.0:protocol"
|
|
12
|
-
|
|
13
|
-
# OneLogin::RubySaml::Settings Toolkit settings
|
|
14
|
-
attr_accessor :settings
|
|
15
|
-
|
|
16
|
-
attr_reader :document
|
|
17
|
-
attr_reader :request
|
|
8
|
+
class SloLogoutrequest < SamlMessage
|
|
18
9
|
attr_reader :options
|
|
10
|
+
attr_reader :request
|
|
11
|
+
attr_reader :document
|
|
19
12
|
|
|
20
|
-
def initialize(request,
|
|
13
|
+
def initialize(request, options = {})
|
|
21
14
|
raise ArgumentError.new("Request cannot be nil") if request.nil?
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
@
|
|
25
|
-
@request = OneLogin::RubySaml::Utils.decode_raw_saml(request)
|
|
26
|
-
@document = XMLSecurity::SignedDocument.new(@request)
|
|
15
|
+
@options = options
|
|
16
|
+
@request = decode_raw_saml(request)
|
|
17
|
+
@document = REXML::Document.new(@request)
|
|
27
18
|
end
|
|
28
19
|
|
|
29
|
-
def
|
|
30
|
-
|
|
31
|
-
node = REXML::XPath.first(
|
|
32
|
-
document,
|
|
33
|
-
"/p:LogoutRequest",
|
|
34
|
-
{ "p" => PROTOCOL }
|
|
35
|
-
)
|
|
36
|
-
node.nil? ? nil : node.attributes['ID']
|
|
37
|
-
end
|
|
20
|
+
def is_valid?
|
|
21
|
+
validate
|
|
38
22
|
end
|
|
39
23
|
|
|
40
24
|
def validate!
|
|
41
25
|
validate(false)
|
|
42
26
|
end
|
|
43
27
|
|
|
44
|
-
|
|
45
|
-
return false unless validate_structure(soft)
|
|
46
|
-
|
|
47
|
-
valid_issuer?(soft)
|
|
48
|
-
end
|
|
49
|
-
|
|
28
|
+
# The value of the user identifier as designated by the initialization request response
|
|
50
29
|
def name_id
|
|
51
30
|
@name_id ||= begin
|
|
52
31
|
node = REXML::XPath.first(document, "/p:LogoutRequest/a:NameID", { "p" => PROTOCOL, "a" => ASSERTION })
|
|
53
|
-
|
|
32
|
+
node.nil? ? nil : node.text
|
|
54
33
|
end
|
|
55
34
|
end
|
|
56
35
|
|
|
57
|
-
alias_method :nameid, :name_id
|
|
58
|
-
|
|
59
|
-
def name_id_format
|
|
60
|
-
@name_id_node ||= REXML::XPath.first(document, "/p:LogoutRequest/a:NameID", { "p" => PROTOCOL, "a" => ASSERTION })
|
|
61
|
-
@name_id_format ||=
|
|
62
|
-
if @name_id_node && @name_id_node.attribute("Format")
|
|
63
|
-
@name_id_node.attribute("Format").value
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
alias_method :nameid_format, :name_id_format
|
|
68
|
-
|
|
69
36
|
def id
|
|
70
|
-
@id
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
37
|
+
return @id if @id
|
|
38
|
+
element = REXML::XPath.first(document, "/p:LogoutRequest", {
|
|
39
|
+
"p" => PROTOCOL} )
|
|
40
|
+
return nil if element.nil?
|
|
41
|
+
return element.attributes["ID"]
|
|
74
42
|
end
|
|
75
43
|
|
|
76
44
|
def issuer
|
|
77
45
|
@issuer ||= begin
|
|
78
46
|
node = REXML::XPath.first(document, "/p:LogoutRequest/a:Issuer", { "p" => PROTOCOL, "a" => ASSERTION })
|
|
79
|
-
|
|
47
|
+
node.nil? ? nil : node.text
|
|
80
48
|
end
|
|
81
49
|
end
|
|
82
50
|
|
|
83
51
|
private
|
|
84
52
|
|
|
85
|
-
def
|
|
86
|
-
|
|
87
|
-
@schema = Nokogiri::XML::Schema(IO.read('saml20protocol_schema.xsd'))
|
|
88
|
-
@xml = Nokogiri::XML(self.document.to_s)
|
|
89
|
-
end
|
|
90
|
-
if soft
|
|
91
|
-
@schema.validate(@xml).map{ return false }
|
|
92
|
-
else
|
|
93
|
-
@schema.validate(@xml).map{ |error| validation_error("#{error.message}\n\n#{@xml.to_s}") }
|
|
94
|
-
end
|
|
53
|
+
def validate(soft = true)
|
|
54
|
+
valid_saml?(document, soft) && validate_request_state(soft)
|
|
95
55
|
end
|
|
96
56
|
|
|
97
|
-
def
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
unless OneLogin::RubySaml::Utils.uri_match?(issuer, settings.idp_entity_id)
|
|
101
|
-
return soft ? false : validation_error("Doesn't match the issuer, expected: <#{self.settings.idp_entity_id}>, but was: <#{issuer}>")
|
|
57
|
+
def validate_request_state(soft = true)
|
|
58
|
+
if request.empty?
|
|
59
|
+
return soft ? false : validation_error("Blank request")
|
|
102
60
|
end
|
|
103
61
|
true
|
|
104
62
|
end
|
|
105
63
|
|
|
106
|
-
def validation_error(message)
|
|
107
|
-
raise ValidationError.new(message)
|
|
108
|
-
end
|
|
109
|
-
|
|
110
64
|
end
|
|
111
65
|
end
|
|
112
66
|
end
|
|
@@ -1,70 +1,33 @@
|
|
|
1
|
-
require "
|
|
2
|
-
|
|
3
|
-
require "
|
|
4
|
-
require "onelogin/ruby-saml/utils"
|
|
5
|
-
require "onelogin/ruby-saml/setting_error"
|
|
1
|
+
require "uuid"
|
|
2
|
+
|
|
3
|
+
require "onelogin/ruby-saml/logging"
|
|
6
4
|
|
|
7
5
|
module OneLogin
|
|
8
6
|
module RubySaml
|
|
7
|
+
class SloLogoutresponse < SamlMessage
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
#
|
|
12
|
-
class SloLogoutresponse
|
|
13
|
-
|
|
14
|
-
# Logout Response ID
|
|
15
|
-
attr_accessor :uuid
|
|
9
|
+
attr_reader :uuid # Can be obtained if neccessary
|
|
16
10
|
|
|
17
|
-
# Initializes the Logout Response. A SloLogoutresponse Object.
|
|
18
|
-
# Asigns an ID, a random uuid.
|
|
19
|
-
#
|
|
20
11
|
def initialize
|
|
21
|
-
@uuid =
|
|
12
|
+
@uuid = "_" + UUID.new.generate
|
|
22
13
|
end
|
|
23
14
|
|
|
24
|
-
def
|
|
25
|
-
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
# Creates the Logout Response string.
|
|
29
|
-
# @param settings [OneLogin::RubySaml::Settings|nil] Toolkit settings
|
|
30
|
-
# @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
|
|
31
|
-
# @param logout_message [String] The Message to be placed as StatusMessage in the logout response
|
|
32
|
-
# @param params [Hash] Some extra parameters to be added in the GET for example the RelayState
|
|
33
|
-
# @param logout_status_code [String] The StatusCode to be placed as StatusMessage in the logout response
|
|
34
|
-
# @return [String] Logout Request string that includes the SAMLRequest
|
|
35
|
-
#
|
|
36
|
-
def create(settings, request_id = nil, logout_message = nil, params = {}, logout_status_code = nil)
|
|
37
|
-
params = create_params(settings, request_id, logout_message, params, logout_status_code)
|
|
15
|
+
def create(settings, request_id = nil, logout_message = nil, params = {})
|
|
16
|
+
params = create_params(settings, request_id, logout_message, params)
|
|
38
17
|
params_prefix = (settings.idp_slo_target_url =~ /\?/) ? '&' : '?'
|
|
39
18
|
saml_response = CGI.escape(params.delete("SAMLResponse"))
|
|
40
19
|
response_params = "#{params_prefix}SAMLResponse=#{saml_response}"
|
|
41
20
|
params.each_pair do |key, value|
|
|
42
21
|
response_params << "&#{key.to_s}=#{CGI.escape(value.to_s)}"
|
|
43
22
|
end
|
|
44
|
-
|
|
23
|
+
|
|
45
24
|
@logout_url = settings.idp_slo_target_url + response_params
|
|
46
25
|
end
|
|
47
26
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
# @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
|
|
51
|
-
# @param logout_message [String] The Message to be placed as StatusMessage in the logout response
|
|
52
|
-
# @param params [Hash] Some extra parameters to be added in the GET for example the RelayState
|
|
53
|
-
# @param logout_status_code [String] The StatusCode to be placed as StatusMessage in the logout response
|
|
54
|
-
# @return [Hash] Parameters
|
|
55
|
-
#
|
|
56
|
-
def create_params(settings, request_id = nil, logout_message = nil, params = {}, logout_status_code = nil)
|
|
57
|
-
# The method expects :RelayState but sometimes we get 'RelayState' instead.
|
|
58
|
-
# Based on the HashWithIndifferentAccess value in Rails we could experience
|
|
59
|
-
# conflicts so this line will solve them.
|
|
60
|
-
relay_state = params[:RelayState] || params['RelayState']
|
|
61
|
-
|
|
62
|
-
if relay_state.nil?
|
|
63
|
-
params.delete(:RelayState)
|
|
64
|
-
params.delete('RelayState')
|
|
65
|
-
end
|
|
27
|
+
def create_params(settings, request_id = nil, logout_message = nil, params = {})
|
|
28
|
+
params = {} if params.nil?
|
|
66
29
|
|
|
67
|
-
response_doc = create_logout_response_xml_doc(settings, request_id, logout_message
|
|
30
|
+
response_doc = create_logout_response_xml_doc(settings, request_id, logout_message)
|
|
68
31
|
response_doc.context[:attribute_quote] = :quote if settings.double_quote_xml_attribute_values
|
|
69
32
|
|
|
70
33
|
response = ""
|
|
@@ -72,29 +35,18 @@ module OneLogin
|
|
|
72
35
|
|
|
73
36
|
Logging.debug "Created SLO Logout Response: #{response}"
|
|
74
37
|
|
|
75
|
-
response =
|
|
76
|
-
|
|
77
|
-
base64_response = Base64.strict_encode64(response)
|
|
78
|
-
else
|
|
79
|
-
base64_response = Base64.encode64(response).gsub(/\n/, "")
|
|
80
|
-
end
|
|
38
|
+
response = deflate(response) if settings.compress_response
|
|
39
|
+
base64_response = encode(response)
|
|
81
40
|
response_params = {"SAMLResponse" => base64_response}
|
|
82
41
|
|
|
83
42
|
if settings.security[:logout_responses_signed] && !settings.security[:embed_sign] && settings.private_key
|
|
84
|
-
params['SigAlg'] =
|
|
85
|
-
url_string
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
)
|
|
91
|
-
sign_algorithm = XMLSecurity::BaseDocument.new.algorithm(settings.security[:signature_method])
|
|
92
|
-
signature = settings.get_sp_key.sign(sign_algorithm.new, url_string)
|
|
93
|
-
if Base64.respond_to?('strict_encode64')
|
|
94
|
-
params['Signature'] = Base64.strict_encode64(signature)
|
|
95
|
-
else
|
|
96
|
-
params['Signature'] = Base64.encode64(signature).gsub(/\n/, "")
|
|
97
|
-
end
|
|
43
|
+
params['SigAlg'] = XMLSecurity::Document::SHA1
|
|
44
|
+
url_string = "SAMLResponse=#{CGI.escape(base64_response)}"
|
|
45
|
+
url_string += "&RelayState=#{CGI.escape(params['RelayState'])}" if params['RelayState']
|
|
46
|
+
url_string += "&SigAlg=#{CGI.escape(params['SigAlg'])}"
|
|
47
|
+
private_key = settings.get_sp_key()
|
|
48
|
+
signature = private_key.sign(XMLSecurity::BaseDocument.new.algorithm(settings.security[:signature_method]).new, url_string)
|
|
49
|
+
params['Signature'] = encode(signature)
|
|
98
50
|
end
|
|
99
51
|
|
|
100
52
|
params.each_pair do |key, value|
|
|
@@ -104,19 +56,7 @@ module OneLogin
|
|
|
104
56
|
response_params
|
|
105
57
|
end
|
|
106
58
|
|
|
107
|
-
|
|
108
|
-
# @param settings [OneLogin::RubySaml::Settings|nil] Toolkit settings
|
|
109
|
-
# @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
|
|
110
|
-
# @param logout_message [String] The Message to be placed as StatusMessage in the logout response
|
|
111
|
-
# @param logout_status_code [String] The StatusCode to be placed as StatusMessage in the logout response
|
|
112
|
-
# @return [String] The SAMLResponse String.
|
|
113
|
-
#
|
|
114
|
-
def create_logout_response_xml_doc(settings, request_id = nil, logout_message = nil, logout_status_code = nil)
|
|
115
|
-
document = create_xml_document(settings, request_id, logout_message, logout_status_code)
|
|
116
|
-
sign_document(document, settings)
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
def create_xml_document(settings, request_id = nil, logout_message = nil, status_code = nil)
|
|
59
|
+
def create_logout_response_xml_doc(settings, request_id = nil, logout_message = nil)
|
|
120
60
|
time = Time.now.utc.strftime('%Y-%m-%dT%H:%M:%SZ')
|
|
121
61
|
|
|
122
62
|
response_doc = XMLSecurity::Document.new
|
|
@@ -127,38 +67,33 @@ module OneLogin
|
|
|
127
67
|
root.attributes['IssueInstant'] = time
|
|
128
68
|
root.attributes['Version'] = '2.0'
|
|
129
69
|
root.attributes['InResponseTo'] = request_id unless request_id.nil?
|
|
130
|
-
root.attributes['Destination'] = settings.idp_slo_target_url unless settings.idp_slo_target_url.nil?
|
|
70
|
+
root.attributes['Destination'] = settings.idp_slo_target_url unless settings.idp_slo_target_url.nil?
|
|
131
71
|
|
|
132
|
-
|
|
133
|
-
issuer = root.add_element "saml:Issuer"
|
|
134
|
-
issuer.text = settings.sp_entity_id
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
# add status
|
|
72
|
+
# add success message
|
|
138
73
|
status = root.add_element 'samlp:Status'
|
|
139
74
|
|
|
140
|
-
# status code
|
|
141
|
-
status_code
|
|
142
|
-
|
|
143
|
-
status_code_elem.attributes['Value'] = status_code
|
|
75
|
+
# success status code
|
|
76
|
+
status_code = status.add_element 'samlp:StatusCode'
|
|
77
|
+
status_code.attributes['Value'] = 'urn:oasis:names:tc:SAML:2.0:status:Success'
|
|
144
78
|
|
|
145
|
-
# status message
|
|
79
|
+
# success status message
|
|
146
80
|
logout_message ||= 'Successfully Signed Out'
|
|
147
81
|
status_message = status.add_element 'samlp:StatusMessage'
|
|
148
82
|
status_message.text = logout_message
|
|
149
83
|
|
|
150
|
-
|
|
151
|
-
|
|
84
|
+
if settings.issuer != nil
|
|
85
|
+
issuer = root.add_element "saml:Issuer"
|
|
86
|
+
issuer.text = settings.issuer
|
|
87
|
+
end
|
|
152
88
|
|
|
153
|
-
|
|
154
|
-
# embed signature
|
|
89
|
+
# embebed sign
|
|
155
90
|
if settings.security[:logout_responses_signed] && settings.private_key && settings.certificate && settings.security[:embed_sign]
|
|
156
|
-
private_key = settings.get_sp_key
|
|
157
|
-
cert = settings.get_sp_cert
|
|
158
|
-
|
|
91
|
+
private_key = settings.get_sp_key()
|
|
92
|
+
cert = settings.get_sp_cert()
|
|
93
|
+
response_doc.sign_document(private_key, cert, settings.security[:signature_method], settings.security[:digest_method])
|
|
159
94
|
end
|
|
160
95
|
|
|
161
|
-
|
|
96
|
+
response_doc
|
|
162
97
|
end
|
|
163
98
|
|
|
164
99
|
end
|