ruby-saml 0.8.16 → 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.
Files changed (90) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -0
  3. data/.travis.yml +1 -6
  4. data/Gemfile +2 -12
  5. data/README.md +363 -35
  6. data/Rakefile +14 -0
  7. data/changelog.md +22 -9
  8. data/lib/onelogin/ruby-saml/attribute_service.rb +34 -0
  9. data/lib/onelogin/ruby-saml/attributes.rb +26 -64
  10. data/lib/onelogin/ruby-saml/authrequest.rb +47 -89
  11. data/lib/onelogin/ruby-saml/idp_metadata_parser.rb +87 -0
  12. data/lib/onelogin/ruby-saml/logoutrequest.rb +34 -93
  13. data/lib/onelogin/ruby-saml/logoutresponse.rb +25 -24
  14. data/lib/onelogin/ruby-saml/metadata.rb +46 -16
  15. data/lib/onelogin/ruby-saml/response.rb +62 -322
  16. data/lib/onelogin/ruby-saml/saml_message.rb +78 -0
  17. data/lib/onelogin/ruby-saml/settings.rb +54 -121
  18. data/lib/onelogin/ruby-saml/slo_logoutrequest.rb +26 -61
  19. data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +27 -84
  20. data/lib/onelogin/ruby-saml/utils.rb +32 -199
  21. data/lib/onelogin/ruby-saml/version.rb +1 -1
  22. data/lib/ruby-saml.rb +5 -2
  23. data/lib/schemas/{saml20assertion_schema.xsd → saml-schema-assertion-2.0.xsd} +283 -283
  24. data/lib/schemas/saml-schema-authn-context-2.0.xsd +23 -0
  25. data/lib/schemas/saml-schema-authn-context-types-2.0.xsd +821 -0
  26. data/lib/schemas/saml-schema-metadata-2.0.xsd +339 -0
  27. data/lib/schemas/{saml20protocol_schema.xsd → saml-schema-protocol-2.0.xsd} +302 -302
  28. data/lib/schemas/sstc-metadata-attr.xsd +35 -0
  29. data/lib/schemas/sstc-saml-attribute-ext.xsd +25 -0
  30. data/lib/schemas/sstc-saml-metadata-algsupport-v1.0.xsd +41 -0
  31. data/lib/schemas/sstc-saml-metadata-ui-v1.0.xsd +89 -0
  32. data/lib/schemas/{xenc_schema.xsd → xenc-schema.xsd} +1 -11
  33. data/lib/schemas/xml.xsd +287 -0
  34. data/lib/schemas/{xmldsig_schema.xsd → xmldsig-core-schema.xsd} +0 -9
  35. data/lib/xml_security.rb +83 -235
  36. data/ruby-saml.gemspec +1 -0
  37. data/test/idp_metadata_parser_test.rb +54 -0
  38. data/test/logoutrequest_test.rb +68 -144
  39. data/test/logoutresponse_test.rb +43 -25
  40. data/test/metadata_test.rb +87 -0
  41. data/test/request_test.rb +103 -90
  42. data/test/response_test.rb +181 -471
  43. data/test/responses/idp_descriptor.xml +3 -0
  44. data/test/responses/logoutresponse_fixtures.rb +5 -5
  45. data/test/responses/response_no_cert_and_encrypted_attrs.xml +29 -0
  46. data/test/responses/response_with_multiple_attribute_values.xml +1 -1
  47. data/test/responses/slo_request.xml +4 -0
  48. data/test/settings_test.rb +25 -112
  49. data/test/slo_logoutrequest_test.rb +41 -44
  50. data/test/slo_logoutresponse_test.rb +87 -167
  51. data/test/test_helper.rb +27 -102
  52. data/test/xml_security_test.rb +114 -337
  53. metadata +34 -84
  54. data/lib/onelogin/ruby-saml/setting_error.rb +0 -6
  55. data/test/certificates/certificate.der +0 -0
  56. data/test/certificates/formatted_certificate +0 -14
  57. data/test/certificates/formatted_chained_certificate +0 -42
  58. data/test/certificates/formatted_private_key +0 -12
  59. data/test/certificates/formatted_rsa_private_key +0 -12
  60. data/test/certificates/invalid_certificate1 +0 -1
  61. data/test/certificates/invalid_certificate2 +0 -1
  62. data/test/certificates/invalid_certificate3 +0 -12
  63. data/test/certificates/invalid_chained_certificate1 +0 -1
  64. data/test/certificates/invalid_private_key1 +0 -1
  65. data/test/certificates/invalid_private_key2 +0 -1
  66. data/test/certificates/invalid_private_key3 +0 -10
  67. data/test/certificates/invalid_rsa_private_key1 +0 -1
  68. data/test/certificates/invalid_rsa_private_key2 +0 -1
  69. data/test/certificates/invalid_rsa_private_key3 +0 -10
  70. data/test/certificates/ruby-saml-2.crt +0 -15
  71. data/test/requests/logoutrequest_fixtures.rb +0 -47
  72. data/test/responses/encrypted_new_attack.xml.base64 +0 -1
  73. data/test/responses/invalids/invalid_issuer_assertion.xml.base64 +0 -1
  74. data/test/responses/invalids/invalid_issuer_message.xml.base64 +0 -1
  75. data/test/responses/invalids/multiple_signed.xml.base64 +0 -1
  76. data/test/responses/invalids/no_signature.xml.base64 +0 -1
  77. data/test/responses/invalids/response_with_concealed_signed_assertion.xml +0 -51
  78. data/test/responses/invalids/response_with_doubled_signed_assertion.xml +0 -49
  79. data/test/responses/invalids/signature_wrapping_attack.xml.base64 +0 -1
  80. data/test/responses/response_node_text_attack.xml.base64 +0 -1
  81. data/test/responses/response_with_concealed_signed_assertion.xml +0 -51
  82. data/test/responses/response_with_doubled_signed_assertion.xml +0 -49
  83. data/test/responses/response_with_multiple_attribute_statements.xml +0 -72
  84. data/test/responses/response_with_signed_assertion_3.xml +0 -30
  85. data/test/responses/response_with_signed_message_and_assertion.xml +0 -34
  86. data/test/responses/response_with_undefined_recipient.xml.base64 +0 -1
  87. data/test/responses/response_wrapped.xml.base64 +0 -150
  88. data/test/responses/valid_response.xml.base64 +0 -1
  89. data/test/responses/valid_response_without_x509certificate.xml.base64 +0 -1
  90. data/test/utils_test.rb +0 -231
@@ -0,0 +1,78 @@
1
+ require 'cgi'
2
+ require 'zlib'
3
+ require 'base64'
4
+ require "rexml/document"
5
+ require "rexml/xpath"
6
+
7
+ module OneLogin
8
+ module RubySaml
9
+ class SamlMessage
10
+ include REXML
11
+
12
+ ASSERTION = "urn:oasis:names:tc:SAML:2.0:assertion"
13
+ PROTOCOL = "urn:oasis:names:tc:SAML:2.0:protocol"
14
+
15
+ def valid_saml?(document, soft = true)
16
+ Dir.chdir(File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'schemas'))) do
17
+ @schema = Nokogiri::XML::Schema(IO.read('saml-schema-protocol-2.0.xsd'))
18
+ @xml = Nokogiri::XML(document.to_s)
19
+ end
20
+ if soft
21
+ @schema.validate(@xml).map{ return false }
22
+ else
23
+ @schema.validate(@xml).map{ |error| validation_error("#{error.message}\n\n#{@xml.to_s}") }
24
+ end
25
+ end
26
+
27
+ def validation_error(message)
28
+ raise ValidationError.new(message)
29
+ end
30
+
31
+ private
32
+
33
+ def decode_raw_saml(saml)
34
+ if saml =~ /^</
35
+ return saml
36
+ elsif (decoded = decode(saml)) =~ /^</
37
+ return decoded
38
+ elsif (inflated = inflate(decoded)) =~ /^</
39
+ return inflated
40
+ end
41
+
42
+ return nil
43
+ end
44
+
45
+ def encode_raw_saml(saml, settings)
46
+ saml = Zlib::Deflate.deflate(saml, 9)[2..-5] if settings.compress_request
47
+ base64_saml = Base64.encode64(saml)
48
+ return CGI.escape(base64_saml)
49
+ end
50
+
51
+ def decode(encoded)
52
+ Base64.decode64(encoded)
53
+ end
54
+
55
+ def encode(encoded)
56
+ Base64.encode64(encoded).gsub(/\n/, "")
57
+ end
58
+
59
+ def escape(unescaped)
60
+ CGI.escape(unescaped)
61
+ end
62
+
63
+ def unescape(escaped)
64
+ CGI.unescape(escaped)
65
+ end
66
+
67
+ def inflate(deflated)
68
+ zlib = Zlib::Inflate.new(-Zlib::MAX_WBITS)
69
+ zlib.inflate(deflated)
70
+ end
71
+
72
+ def deflate(inflated)
73
+ Zlib::Deflate.deflate(inflated, 9)[2..-5]
74
+ end
75
+
76
+ end
77
+ end
78
+ end
@@ -1,81 +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 = {}, keep_security_attributes = false)
8
- if keep_security_attributes
9
- security_attributes = overrides.delete(:security) || {}
10
- config = DEFAULTS.merge(overrides)
11
- config[:security] = DEFAULTS[:security].merge(security_attributes)
12
- else
13
- config = DEFAULTS.merge(overrides)
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
- #idp data
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 :idp_entity_id
31
- #sp data
32
- attr_accessor :sp_entity_id
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 :authn_context
22
+ attr_accessor :assertion_consumer_service_binding
35
23
  attr_accessor :sp_name_qualifier
36
24
  attr_accessor :name_identifier_format
37
25
  attr_accessor :name_identifier_value
38
- attr_accessor :name_identifier_value_requested
39
26
  attr_accessor :sessionindex
40
27
  attr_accessor :compress_request
41
28
  attr_accessor :compress_response
42
29
  attr_accessor :double_quote_xml_attribute_values
43
- attr_accessor :force_authn
44
30
  attr_accessor :passive
45
31
  attr_accessor :protocol_binding
32
+ attr_accessor :attributes_index
33
+ attr_accessor :force_authn
34
+ attr_accessor :security
46
35
  attr_accessor :certificate
47
36
  attr_accessor :private_key
48
- # Work-flow
49
- attr_accessor :security
37
+ attr_accessor :authn_context
38
+ attr_accessor :authn_context_comparison
39
+ attr_accessor :authn_context_decl_ref
40
+ attr_reader :attribute_consuming_service
50
41
  # Compability
51
- attr_accessor :issuer
52
42
  attr_accessor :assertion_consumer_logout_service_url
53
43
  attr_accessor :assertion_consumer_logout_service_binding
54
44
 
55
- # @return [String] SP Entity ID
56
- #
57
- def sp_entity_id
58
- val = nil
59
- if @sp_entity_id.nil?
60
- if @issuer
61
- val = @issuer
62
- end
63
- else
64
- val = @sp_entity_id
65
- end
66
- val
67
- end
68
-
69
- # Setter for SP Entity ID.
70
- # @param val [String].
71
- #
72
- def sp_entity_id=(val)
73
- @sp_entity_id = val
74
- end
75
-
76
- # @return [String] Single Logout Service URL.
77
- #
78
- def single_logout_service_url
45
+ def single_logout_service_url()
79
46
  val = nil
80
47
  if @single_logout_service_url.nil?
81
48
  if @assertion_consumer_logout_service_url
@@ -87,16 +54,12 @@ module OneLogin
87
54
  val
88
55
  end
89
56
 
90
- # Setter for the Single Logout Service URL.
91
- # @param url [String].
92
- #
93
- def single_logout_service_url=(url)
94
- @single_logout_service_url = url
57
+ # setter
58
+ def single_logout_service_url=(val)
59
+ @single_logout_service_url = val
95
60
  end
96
61
 
97
- # @return [String] Single Logout Service Binding.
98
- #
99
- def single_logout_service_binding
62
+ def single_logout_service_binding()
100
63
  val = nil
101
64
  if @single_logout_service_binding.nil?
102
65
  if @assertion_consumer_logout_service_binding
@@ -108,76 +71,46 @@ module OneLogin
108
71
  val
109
72
  end
110
73
 
111
- # Setter for Single Logout Service Binding.
112
- #
113
- # (Currently we only support "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect")
114
- # @param url [String]
115
- #
116
- def single_logout_service_binding=(url)
117
- @single_logout_service_binding = url
118
- end
119
-
120
- # Calculates the fingerprint of the IdP x509 certificate.
121
- # @return [String] The fingerprint
122
- #
123
- def get_fingerprint
124
- idp_cert_fingerprint || begin
125
- idp_cert = get_idp_cert
126
- if idp_cert
127
- Digest::SHA1.hexdigest(idp_cert.to_der).upcase.scan(/../).join(":")
128
- end
129
- end
74
+ # setter
75
+ def single_logout_service_binding=(val)
76
+ @single_logout_service_binding = val
130
77
  end
131
78
 
132
- # @return [OpenSSL::X509::Certificate|nil] Build the IdP certificate from the settings (previously format it)
133
- #
134
- def get_idp_cert
135
- return nil if idp_cert.nil?
136
-
137
- if idp_cert.respond_to?(:to_pem)
138
- idp_cert
139
- else
140
- return nil if idp_cert.empty?
141
- formatted_cert = OneLogin::RubySaml::Utils.format_cert(idp_cert)
142
- OpenSSL::X509::Certificate.new(formatted_cert)
143
- end
144
- end
145
-
146
- # @return [OpenSSL::X509::Certificate|nil] Build the SP certificate from the settings (previously format it)
147
- #
148
79
  def get_sp_cert
149
- return nil if certificate.nil? || certificate.empty?
150
-
151
- formatted_cert = OneLogin::RubySaml::Utils.format_cert(certificate)
152
- OpenSSL::X509::Certificate.new(formatted_cert)
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
153
86
  end
154
87
 
155
- # @return [OpenSSL::PKey::RSA] Build the SP private from the settings (previously format it)
156
- #
157
88
  def get_sp_key
158
- return nil if private_key.nil? || private_key.empty?
159
-
160
- formatted_private_key = OneLogin::RubySaml::Utils.format_private_key(private_key)
161
- OpenSSL::PKey::RSA.new(formatted_private_key)
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
162
95
  end
163
96
 
164
97
  private
165
98
 
166
99
  DEFAULTS = {
167
- :compress_request => true,
168
- :compress_response => true,
169
- :double_quote_xml_attribute_values => false,
170
- :assertion_consumer_service_binding => "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST".freeze,
171
- :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,
172
104
  :security => {
173
- :authn_requests_signed => false,
174
- :logout_requests_signed => false,
175
- :logout_responses_signed => false,
176
- :embed_sign => false,
177
- :digest_method => XMLSecurity::Document::SHA1,
178
- :signature_method => XMLSecurity::Document::RSA_SHA1
179
- }.freeze
180
- }.freeze
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
+ }
181
114
  end
182
115
  end
183
116
  end
@@ -1,101 +1,66 @@
1
- require "xml_security"
2
- require "time"
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, settings = nil, options = {})
13
+ def initialize(request, options = {})
21
14
  raise ArgumentError.new("Request cannot be nil") if request.nil?
22
- self.settings = settings
15
+ @options = options
16
+ @request = decode_raw_saml(request)
17
+ @document = REXML::Document.new(@request)
18
+ end
23
19
 
24
- @options = options
25
- @request = OneLogin::RubySaml::Utils.decode_raw_saml(request)
26
- @document = XMLSecurity::SignedDocument.new(@request)
20
+ def is_valid?
21
+ validate
27
22
  end
28
23
 
29
24
  def validate!
30
25
  validate(false)
31
26
  end
32
27
 
33
- def validate(soft = true)
34
- return false unless validate_structure(soft)
35
-
36
- valid_issuer?(soft)
37
- end
38
-
28
+ # The value of the user identifier as designated by the initialization request response
39
29
  def name_id
40
30
  @name_id ||= begin
41
31
  node = REXML::XPath.first(document, "/p:LogoutRequest/a:NameID", { "p" => PROTOCOL, "a" => ASSERTION })
42
- Utils.element_text(node)
32
+ node.nil? ? nil : node.text
43
33
  end
44
34
  end
45
35
 
46
- alias_method :nameid, :name_id
47
-
48
- def name_id_format
49
- @name_id_node ||= REXML::XPath.first(document, "/p:LogoutRequest/a:NameID", { "p" => PROTOCOL, "a" => ASSERTION })
50
- @name_id_format ||=
51
- if @name_id_node && @name_id_node.attribute("Format")
52
- @name_id_node.attribute("Format").value
53
- end
54
- end
55
-
56
- alias_method :nameid_format, :name_id_format
57
-
58
36
  def id
59
- @id ||= begin
60
- node = REXML::XPath.first(document, "/p:LogoutRequest", { "p" => PROTOCOL } )
61
- node.nil? ? nil : node.attributes['ID']
62
- end
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"]
63
42
  end
64
43
 
65
44
  def issuer
66
45
  @issuer ||= begin
67
46
  node = REXML::XPath.first(document, "/p:LogoutRequest/a:Issuer", { "p" => PROTOCOL, "a" => ASSERTION })
68
- Utils.element_text(node)
47
+ node.nil? ? nil : node.text
69
48
  end
70
49
  end
71
50
 
72
51
  private
73
52
 
74
- def validate_structure(soft = true)
75
- Dir.chdir(File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'schemas'))) do
76
- @schema = Nokogiri::XML::Schema(IO.read('saml20protocol_schema.xsd'))
77
- @xml = Nokogiri::XML(self.document.to_s)
78
- end
79
- if soft
80
- @schema.validate(@xml).map{ return false }
81
- else
82
- @schema.validate(@xml).map{ |error| validation_error("#{error.message}\n\n#{@xml.to_s}") }
83
- end
53
+ def validate(soft = true)
54
+ valid_saml?(document, soft) && validate_request_state(soft)
84
55
  end
85
56
 
86
- def valid_issuer?(soft = true)
87
- return true if settings.nil? || settings.idp_entity_id.nil? || issuer.nil?
88
-
89
- unless OneLogin::RubySaml::Utils.uri_match?(issuer, settings.idp_entity_id)
90
- 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")
91
60
  end
92
61
  true
93
62
  end
94
63
 
95
- def validation_error(message)
96
- raise ValidationError.new(message)
97
- end
98
-
99
64
  end
100
65
  end
101
66
  end
@@ -1,33 +1,17 @@
1
- require "base64"
2
- require "zlib"
3
- require "cgi"
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
- # SAML2 Logout Response (SLO SP initiated)
11
- #
12
- class SloLogoutresponse
13
-
14
- # Logout Response ID
15
- attr_reader :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 = OneLogin::RubySaml::Utils.uuid
12
+ @uuid = "_" + UUID.new.generate
22
13
  end
23
14
 
24
- # Creates the Logout Response string.
25
- # @param settings [OneLogin::RubySaml::Settings|nil] Toolkit settings
26
- # @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
- # @param logout_message [String] The Message to be placed as StatusMessage in the logout response
28
- # @param params [Hash] Some extra parameters to be added in the GET for example the RelayState
29
- # @return [String] Logout Request string that includes the SAMLRequest
30
- #
31
15
  def create(settings, request_id = nil, logout_message = nil, params = {})
32
16
  params = create_params(settings, request_id, logout_message, params)
33
17
  params_prefix = (settings.idp_slo_target_url =~ /\?/) ? '&' : '?'
@@ -36,27 +20,12 @@ module OneLogin
36
20
  params.each_pair do |key, value|
37
21
  response_params << "&#{key.to_s}=#{CGI.escape(value.to_s)}"
38
22
  end
39
- raise SettingError.new "Invalid settings, idp_slo_target_url is not set!" if settings.idp_slo_target_url.nil? or settings.idp_slo_target_url.empty?
23
+
40
24
  @logout_url = settings.idp_slo_target_url + response_params
41
25
  end
42
26
 
43
- # Creates the Get parameters for the logout response.
44
- # @param settings [OneLogin::RubySaml::Settings|nil] Toolkit settings
45
- # @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
- # @param logout_message [String] The Message to be placed as StatusMessage in the logout response
47
- # @param params [Hash] Some extra parameters to be added in the GET for example the RelayState
48
- # @return [Hash] Parameters
49
- #
50
27
  def create_params(settings, request_id = nil, logout_message = nil, params = {})
51
- # The method expects :RelayState but sometimes we get 'RelayState' instead.
52
- # Based on the HashWithIndifferentAccess value in Rails we could experience
53
- # conflicts so this line will solve them.
54
- relay_state = params[:RelayState] || params['RelayState']
55
-
56
- if relay_state.nil?
57
- params.delete(:RelayState)
58
- params.delete('RelayState')
59
- end
28
+ params = {} if params.nil?
60
29
 
61
30
  response_doc = create_logout_response_xml_doc(settings, request_id, logout_message)
62
31
  response_doc.context[:attribute_quote] = :quote if settings.double_quote_xml_attribute_values
@@ -66,29 +35,18 @@ module OneLogin
66
35
 
67
36
  Logging.debug "Created SLO Logout Response: #{response}"
68
37
 
69
- response = Zlib::Deflate.deflate(response, 9)[2..-5] if settings.compress_response
70
- if Base64.respond_to?('strict_encode64')
71
- base64_response = Base64.strict_encode64(response)
72
- else
73
- base64_response = Base64.encode64(response).gsub(/\n/, "")
74
- end
38
+ response = deflate(response) if settings.compress_response
39
+ base64_response = encode(response)
75
40
  response_params = {"SAMLResponse" => base64_response}
76
41
 
77
42
  if settings.security[:logout_responses_signed] && !settings.security[:embed_sign] && settings.private_key
78
- params['SigAlg'] = settings.security[:signature_method]
79
- url_string = OneLogin::RubySaml::Utils.build_query(
80
- :type => 'SAMLResponse',
81
- :data => base64_response,
82
- :relay_state => relay_state,
83
- :sig_alg => params['SigAlg']
84
- )
85
- sign_algorithm = XMLSecurity::BaseDocument.new.algorithm(settings.security[:signature_method])
86
- signature = settings.get_sp_key.sign(sign_algorithm.new, url_string)
87
- if Base64.respond_to?('strict_encode64')
88
- params['Signature'] = Base64.strict_encode64(signature)
89
- else
90
- params['Signature'] = Base64.encode64(signature).gsub(/\n/, "")
91
- 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)
92
50
  end
93
51
 
94
52
  params.each_pair do |key, value|
@@ -98,18 +56,7 @@ module OneLogin
98
56
  response_params
99
57
  end
100
58
 
101
- # Creates the SAMLResponse String.
102
- # @param settings [OneLogin::RubySaml::Settings|nil] Toolkit settings
103
- # @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
104
- # @param logout_message [String] The Message to be placed as StatusMessage in the logout response
105
- # @return [String] The SAMLResponse String.
106
- #
107
59
  def create_logout_response_xml_doc(settings, request_id = nil, logout_message = nil)
108
- document = create_xml_document(settings, request_id, logout_message)
109
- sign_document(document, settings)
110
- end
111
-
112
- def create_xml_document(settings, request_id = nil, logout_message = nil)
113
60
  time = Time.now.utc.strftime('%Y-%m-%dT%H:%M:%SZ')
114
61
 
115
62
  response_doc = XMLSecurity::Document.new
@@ -120,12 +67,7 @@ module OneLogin
120
67
  root.attributes['IssueInstant'] = time
121
68
  root.attributes['Version'] = '2.0'
122
69
  root.attributes['InResponseTo'] = request_id unless request_id.nil?
123
- root.attributes['Destination'] = settings.idp_slo_target_url unless settings.idp_slo_target_url.nil? or settings.idp_slo_target_url.empty?
124
-
125
- if settings.sp_entity_id != nil
126
- issuer = root.add_element "saml:Issuer"
127
- issuer.text = settings.sp_entity_id
128
- end
70
+ root.attributes['Destination'] = settings.idp_slo_target_url unless settings.idp_slo_target_url.nil?
129
71
 
130
72
  # add success message
131
73
  status = root.add_element 'samlp:Status'
@@ -139,18 +81,19 @@ module OneLogin
139
81
  status_message = status.add_element 'samlp:StatusMessage'
140
82
  status_message.text = logout_message
141
83
 
142
- response_doc
143
- end
84
+ if settings.issuer != nil
85
+ issuer = root.add_element "saml:Issuer"
86
+ issuer.text = settings.issuer
87
+ end
144
88
 
145
- def sign_document(document, settings)
146
- # embed signature
89
+ # embebed sign
147
90
  if settings.security[:logout_responses_signed] && settings.private_key && settings.certificate && settings.security[:embed_sign]
148
- private_key = settings.get_sp_key
149
- cert = settings.get_sp_cert
150
- document.sign_document(private_key, cert, settings.security[:signature_method], settings.security[:digest_method])
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])
151
94
  end
152
95
 
153
- document
96
+ response_doc
154
97
  end
155
98
 
156
99
  end