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,87 @@
1
+ require "base64"
2
+ require "uuid"
3
+ require "zlib"
4
+ require "cgi"
5
+ require "rexml/document"
6
+ require "rexml/xpath"
7
+
8
+ module OneLogin
9
+ module RubySaml
10
+ include REXML
11
+
12
+ class IdpMetadataParser
13
+
14
+ METADATA = "urn:oasis:names:tc:SAML:2.0:metadata"
15
+ DSIG = "http://www.w3.org/2000/09/xmldsig#"
16
+
17
+ attr_reader :document
18
+
19
+ def parse_remote(url, validate_cert = true)
20
+ idp_metadata = get_idp_metadata(url, validate_cert)
21
+ parse(idp_metadata)
22
+ end
23
+
24
+ def parse(idp_metadata)
25
+ @document = REXML::Document.new(idp_metadata)
26
+
27
+ OneLogin::RubySaml::Settings.new.tap do |settings|
28
+
29
+ settings.idp_sso_target_url = single_signon_service_url
30
+ settings.idp_slo_target_url = single_logout_service_url
31
+ settings.idp_cert_fingerprint = fingerprint
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ # Retrieve the remote IdP metadata from the URL or a cached copy
38
+ # # returns a REXML document of the metadata
39
+ def get_idp_metadata(url, validate_cert)
40
+ uri = URI.parse(url)
41
+ if uri.scheme == "http"
42
+ response = Net::HTTP.get_response(uri)
43
+ meta_text = response.body
44
+ elsif uri.scheme == "https"
45
+ http = Net::HTTP.new(uri.host, uri.port)
46
+ http.use_ssl = true
47
+ # Most IdPs will probably use self signed certs
48
+ if validate_cert
49
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
50
+ else
51
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
52
+ end
53
+ get = Net::HTTP::Get.new(uri.request_uri)
54
+ response = http.request(get)
55
+ meta_text = response.body
56
+ end
57
+ meta_text
58
+ end
59
+
60
+ def single_signon_service_url
61
+ node = REXML::XPath.first(document, "/md:EntityDescriptor/md:IDPSSODescriptor/md:SingleSignOnService/@Location", { "md" => METADATA })
62
+ node.value if node
63
+ end
64
+
65
+ def single_logout_service_url
66
+ node = REXML::XPath.first(document, "/md:EntityDescriptor/md:IDPSSODescriptor/md:SingleLogoutService/@Location", { "md" => METADATA })
67
+ node.value if node
68
+ end
69
+
70
+ def certificate
71
+ @certificate ||= begin
72
+ node = REXML::XPath.first(document, "/md:EntityDescriptor/md:IDPSSODescriptor/md:KeyDescriptor[@use='signing']/ds:KeyInfo/ds:X509Data/ds:X509Certificate", { "md" => METADATA, "ds" => DSIG })
73
+ Base64.decode64(node.text) if node
74
+ end
75
+ end
76
+
77
+ def fingerprint
78
+ @fingerprint ||= begin
79
+ if certificate
80
+ cert = OpenSSL::X509::Certificate.new(certificate)
81
+ Digest::SHA1.hexdigest(cert.to_der).upcase.scan(/../).join(":")
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -1,19 +1,15 @@
1
- require "base64"
2
- require "zlib"
3
- require "cgi"
4
- require 'rexml/document'
5
- require "onelogin/ruby-saml/utils"
6
- require "onelogin/ruby-saml/setting_error"
1
+ require "uuid"
2
+
3
+ require "onelogin/ruby-saml/logging"
7
4
 
8
5
  module OneLogin
9
6
  module RubySaml
10
-
11
- class Logoutrequest
7
+ class Logoutrequest < SamlMessage
12
8
 
13
9
  attr_reader :uuid # Can be obtained if neccessary
14
10
 
15
11
  def initialize
16
- @uuid = OneLogin::RubySaml::Utils.uuid
12
+ @uuid = "_" + UUID.new.generate
17
13
  end
18
14
 
19
15
  def create(settings, params={})
@@ -24,25 +20,11 @@ module OneLogin
24
20
  params.each_pair do |key, value|
25
21
  request_params << "&#{key.to_s}=#{CGI.escape(value.to_s)}"
26
22
  end
27
- 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?
28
23
  @logout_url = settings.idp_slo_target_url + request_params
29
24
  end
30
25
 
31
- # Creates the Get parameters for the logout request.
32
- # @param settings [OneLogin::RubySaml::Settings|nil] Toolkit settings
33
- # @param params [Hash] Some extra parameters to be added in the GET for example the RelayState
34
- # @return [Hash] Parameters
35
- #
36
26
  def create_params(settings, params={})
37
- # The method expects :RelayState but sometimes we get 'RelayState' instead.
38
- # Based on the HashWithIndifferentAccess value in Rails we could experience
39
- # conflicts so this line will solve them.
40
- relay_state = params[:RelayState] || params['RelayState']
41
-
42
- if relay_state.nil?
43
- params.delete(:RelayState)
44
- params.delete('RelayState')
45
- end
27
+ params = {} if params.nil?
46
28
 
47
29
  request_doc = create_logout_request_xml_doc(settings)
48
30
  request_doc.context[:attribute_quote] = :quote if settings.double_quote_xml_attribute_values
@@ -52,29 +34,18 @@ module OneLogin
52
34
 
53
35
  Logging.debug "Created SLO Logout Request: #{request}"
54
36
 
55
- request = Zlib::Deflate.deflate(request, 9)[2..-5] if settings.compress_request
56
- if Base64.respond_to?('strict_encode64')
57
- base64_request = Base64.strict_encode64(request)
58
- else
59
- base64_request = Base64.encode64(request).gsub(/\n/, "")
60
- end
37
+ request = deflate(request) if settings.compress_request
38
+ base64_request = encode(request)
61
39
  request_params = {"SAMLRequest" => base64_request}
62
40
 
63
41
  if settings.security[:logout_requests_signed] && !settings.security[:embed_sign] && settings.private_key
64
- params['SigAlg'] = settings.security[:signature_method]
65
- url_string = OneLogin::RubySaml::Utils.build_query(
66
- :type => 'SAMLRequest',
67
- :data => base64_request,
68
- :relay_state => relay_state,
69
- :sig_alg => params['SigAlg']
70
- )
71
- sign_algorithm = XMLSecurity::BaseDocument.new.algorithm(settings.security[:signature_method])
72
- signature = settings.get_sp_key.sign(sign_algorithm.new, url_string)
73
- if Base64.respond_to?('strict_encode64')
74
- params['Signature'] = Base64.strict_encode64(signature)
75
- else
76
- params['Signature'] = Base64.encode64(signature).gsub(/\n/, "")
77
- end
42
+ params['SigAlg'] = XMLSecurity::Document::SHA1
43
+ url_string = "SAMLRequest=#{CGI.escape(base64_request)}"
44
+ url_string += "&RelayState=#{CGI.escape(params['RelayState'])}" if params['RelayState']
45
+ url_string += "&SigAlg=#{CGI.escape(params['SigAlg'])}"
46
+ private_key = settings.get_sp_key()
47
+ signature = private_key.sign(XMLSecurity::BaseDocument.new.algorithm(settings.security[:signature_method]).new, url_string)
48
+ params['Signature'] = encode(signature)
78
49
  end
79
50
 
80
51
  params.each_pair do |key, value|
@@ -84,77 +55,47 @@ module OneLogin
84
55
  request_params
85
56
  end
86
57
 
87
- # Creates the SAMLRequest String.
88
- # @param settings [OneLogin::RubySaml::Settings|nil] Toolkit settings
89
- # @return [String] The SAMLRequest String.
90
- #
91
58
  def create_logout_request_xml_doc(settings)
92
- document = create_xml_document(settings)
93
- sign_document(document, settings)
94
- end
59
+ time = Time.now.utc.strftime("%Y-%m-%dT%H:%M:%SZ")
95
60
 
96
- def create_xml_document(settings, request_doc=nil)
97
- time = Time.now.utc.strftime('%Y-%m-%dT%H:%M:%SZ')
98
-
99
- if request_doc.nil?
100
- request_doc = XMLSecurity::Document.new
101
- request_doc.uuid = uuid
102
- end
61
+ request_doc = XMLSecurity::Document.new
62
+ request_doc.uuid = uuid
103
63
 
104
- root = request_doc.add_element "samlp:LogoutRequest", { "xmlns:samlp" => "urn:oasis:names:tc:SAML:2.0:protocol" }
64
+ root = request_doc.add_element "samlp:LogoutRequest", { "xmlns:samlp" => "urn:oasis:names:tc:SAML:2.0:protocol", "xmlns:saml" => "urn:oasis:names:tc:SAML:2.0:assertion" }
105
65
  root.attributes['ID'] = uuid
106
66
  root.attributes['IssueInstant'] = time
107
67
  root.attributes['Version'] = "2.0"
108
- root.attributes['Destination'] = settings.idp_slo_target_url unless settings.idp_slo_target_url.nil? or settings.idp_slo_target_url.empty?
68
+ root.attributes['Destination'] = settings.idp_slo_target_url unless settings.idp_slo_target_url.nil?
109
69
 
110
- if settings.sp_entity_id
111
- issuer = root.add_element "saml:Issuer", { "xmlns:saml" => "urn:oasis:names:tc:SAML:2.0:assertion" }
112
- issuer.text = settings.sp_entity_id
70
+ if settings.issuer
71
+ issuer = root.add_element "saml:Issuer"
72
+ issuer.text = settings.issuer
113
73
  end
114
74
 
75
+ name_id = root.add_element "saml:NameID"
115
76
  if settings.name_identifier_value
116
- name_id = root.add_element "saml:NameID", { "xmlns:saml" => "urn:oasis:names:tc:SAML:2.0:assertion" }
117
77
  name_id.attributes['NameQualifier'] = settings.sp_name_qualifier if settings.sp_name_qualifier
118
78
  name_id.attributes['Format'] = settings.name_identifier_format if settings.name_identifier_format
119
79
  name_id.text = settings.name_identifier_value
80
+ else
81
+ # If no NameID is present in the settings we generate one
82
+ name_id.text = "_" + UUID.new.generate
83
+ name_id.attributes['Format'] = 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient'
120
84
  end
121
85
 
122
86
  if settings.sessionindex
123
- sessionindex = root.add_element "samlp:SessionIndex", { "xmlns:samlp" => "urn:oasis:names:tc:SAML:2.0:protocol" }
87
+ sessionindex = root.add_element "samlp:SessionIndex"
124
88
  sessionindex.text = settings.sessionindex
125
89
  end
126
90
 
127
- # BUG fix here -- if an authn_context is defined, add the tags with an "exact"
128
- # match required for authentication to succeed. If this is not defined,
129
- # the IdP will choose default rules for authentication. (Shibboleth IdP)
130
- if settings.authn_context != nil
131
- requested_context = root.add_element "samlp:RequestedAuthnContext", {
132
- "xmlns:samlp" => "urn:oasis:names:tc:SAML:2.0:protocol",
133
- "Comparison" => "exact",
134
- }
135
- class_ref = requested_context.add_element "saml:AuthnContextClassRef", {
136
- "xmlns:saml" => "urn:oasis:names:tc:SAML:2.0:assertion",
137
- }
138
- class_ref.text = settings.authn_context
139
- end
140
- request_doc
141
- end
142
-
143
- def sign_document(document, settings)
144
- # embed signature
91
+ # embebed sign
145
92
  if settings.security[:logout_requests_signed] && settings.private_key && settings.certificate && settings.security[:embed_sign]
146
- private_key = settings.get_sp_key
147
- cert = settings.get_sp_cert
148
- document.sign_document(private_key, cert, settings.security[:signature_method], settings.security[:digest_method])
93
+ private_key = settings.get_sp_key()
94
+ cert = settings.get_sp_cert()
95
+ request_doc.sign_document(private_key, cert, settings.security[:signature_method], settings.security[:digest_method])
149
96
  end
150
97
 
151
- document
152
- end
153
-
154
- # Leave due compatibility
155
- def create_unauth_xml_doc(settings, params)
156
- request_doc = ReXML::Document.new
157
- create_xml_document(settings, request_doc)
98
+ request_doc
158
99
  end
159
100
  end
160
101
  end
@@ -3,11 +3,7 @@ require "time"
3
3
 
4
4
  module OneLogin
5
5
  module RubySaml
6
- class Logoutresponse
7
-
8
- ASSERTION = "urn:oasis:names:tc:SAML:2.0:assertion"
9
- PROTOCOL = "urn:oasis:names:tc:SAML:2.0:protocol"
10
-
6
+ class Logoutresponse < SamlMessage
11
7
  # For API compability, this is mutable.
12
8
  attr_accessor :settings
13
9
 
@@ -28,7 +24,7 @@ module OneLogin
28
24
  self.settings = settings
29
25
 
30
26
  @options = options
31
- @response = OneLogin::RubySaml::Utils.decode_raw_saml(response)
27
+ @response = decode_raw_saml(response)
32
28
  @document = XMLSecurity::SignedDocument.new(@response)
33
29
  end
34
30
 
@@ -37,7 +33,7 @@ module OneLogin
37
33
  end
38
34
 
39
35
  def validate(soft = true)
40
- return false unless validate_structure(soft)
36
+ return false unless valid_saml?(document, soft) && valid_state?(soft)
41
37
 
42
38
  valid_in_response_to?(soft) && valid_issuer?(soft) && success?(soft)
43
39
  end
@@ -51,7 +47,7 @@ module OneLogin
51
47
 
52
48
  def in_response_to
53
49
  @in_response_to ||= begin
54
- node = REXML::XPath.first(document, "/p:LogoutResponse", { "p" => PROTOCOL })
50
+ node = REXML::XPath.first(document, "/p:LogoutResponse", { "p" => PROTOCOL, "a" => ASSERTION })
55
51
  node.nil? ? nil : node.attributes['InResponseTo']
56
52
  end
57
53
  end
@@ -59,7 +55,8 @@ module OneLogin
59
55
  def issuer
60
56
  @issuer ||= begin
61
57
  node = REXML::XPath.first(document, "/p:LogoutResponse/a:Issuer", { "p" => PROTOCOL, "a" => ASSERTION })
62
- Utils.element_text(node)
58
+ node ||= REXML::XPath.first(document, "/p:LogoutResponse/a:Assertion/a:Issuer", { "p" => PROTOCOL, "a" => ASSERTION })
59
+ node.nil? ? nil : node.text
63
60
  end
64
61
  end
65
62
 
@@ -72,16 +69,24 @@ module OneLogin
72
69
 
73
70
  private
74
71
 
75
- def validate_structure(soft = true)
76
- Dir.chdir(File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'schemas'))) do
77
- @schema = Nokogiri::XML::Schema(IO.read('saml20protocol_schema.xsd'))
78
- @xml = Nokogiri::XML(self.document.to_s)
72
+ def valid_state?(soft = true)
73
+ if response.empty?
74
+ return soft ? false : validation_error("Blank response")
75
+ end
76
+
77
+ if settings.nil?
78
+ return soft ? false : validation_error("No settings on response")
79
79
  end
80
- if soft
81
- @schema.validate(@xml).map{ return false }
82
- else
83
- @schema.validate(@xml).map{ |error| validation_error("#{error.message}\n\n#{@xml.to_s}") }
80
+
81
+ if settings.issuer.nil?
82
+ return soft ? false : validation_error("No issuer in settings")
84
83
  end
84
+
85
+ if settings.idp_cert_fingerprint.nil? && settings.idp_cert.nil?
86
+ return soft ? false : validation_error("No fingerprint or certificate on settings")
87
+ end
88
+
89
+ true
85
90
  end
86
91
 
87
92
  def valid_in_response_to?(soft = true)
@@ -95,17 +100,13 @@ module OneLogin
95
100
  end
96
101
 
97
102
  def valid_issuer?(soft = true)
98
- return true if settings.nil? || settings.idp_entity_id.nil? || issuer.nil?
103
+ return true if self.settings.idp_entity_id.nil? or self.issuer.nil?
99
104
 
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}>")
105
+ unless URI.parse(self.issuer) == URI.parse(self.settings.idp_entity_id)
106
+ return soft ? false : validation_error("Doesn't match the issuer, expected: <#{self.settings.issuer}>, but was: <#{issuer}>")
102
107
  end
103
108
  true
104
109
  end
105
-
106
- def validation_error(message)
107
- raise ValidationError.new(message)
108
- end
109
110
  end
110
111
  end
111
112
  end
@@ -2,6 +2,8 @@ require "rexml/document"
2
2
  require "rexml/xpath"
3
3
  require "uri"
4
4
 
5
+ require "onelogin/ruby-saml/logging"
6
+
5
7
  # Class to return SP metadata based on the settings requested.
6
8
  # Return this XML in a controller, then give that URL to the the
7
9
  # IdP administrator. The IdP will poll the URL and your settings
@@ -17,49 +19,77 @@ module OneLogin
17
19
  }
18
20
  sp_sso = root.add_element "md:SPSSODescriptor", {
19
21
  "protocolSupportEnumeration" => "urn:oasis:names:tc:SAML:2.0:protocol",
20
- # Metadata request need not be signed (as we don't publish our cert)
21
- "AuthnRequestsSigned" => false,
22
+ "AuthnRequestsSigned" => settings.security[:authn_requests_signed],
22
23
  # However we would like assertions signed if idp_cert_fingerprint or idp_cert is set
23
- "WantAssertionsSigned" => (!settings.idp_cert_fingerprint.nil? || !settings.idp_cert.nil?)
24
+ "WantAssertionsSigned" => !!(settings.idp_cert_fingerprint || settings.idp_cert)
24
25
  }
25
- if settings.sp_entity_id != nil
26
- root.attributes["entityID"] = settings.sp_entity_id
26
+ if settings.issuer
27
+ root.attributes["entityID"] = settings.issuer
27
28
  end
28
- if settings.single_logout_service_url != nil
29
+ if settings.single_logout_service_url
29
30
  sp_sso.add_element "md:SingleLogoutService", {
30
- # Add this as a setting to create different bindings?
31
- "Binding" => "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect",
31
+ "Binding" => settings.single_logout_service_binding,
32
32
  "Location" => settings.single_logout_service_url,
33
33
  "ResponseLocation" => settings.single_logout_service_url,
34
34
  "isDefault" => true,
35
35
  "index" => 0
36
36
  }
37
37
  end
38
- if settings.name_identifier_format != nil
38
+ if settings.name_identifier_format
39
39
  name_id = sp_sso.add_element "md:NameIDFormat"
40
40
  name_id.text = settings.name_identifier_format
41
41
  end
42
- if settings.assertion_consumer_service_url != nil
42
+ if settings.assertion_consumer_service_url
43
43
  sp_sso.add_element "md:AssertionConsumerService", {
44
- # Add this as a setting to create different bindings?
45
- "Binding" => "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
44
+ "Binding" => settings.assertion_consumer_service_binding,
46
45
  "Location" => settings.assertion_consumer_service_url,
47
46
  "isDefault" => true,
48
47
  "index" => 0
49
48
  }
50
49
  end
50
+
51
+ # Add KeyDescriptor if messages will be signed
52
+ cert = settings.get_sp_cert()
53
+ if cert
54
+ kd = sp_sso.add_element "md:KeyDescriptor", { "use" => "signing" }
55
+ ki = kd.add_element "ds:KeyInfo", {"xmlns:ds" => "http://www.w3.org/2000/09/xmldsig#"}
56
+ xd = ki.add_element "ds:X509Data"
57
+ xc = xd.add_element "ds:X509Certificate"
58
+ xc.text = Base64.encode64(cert.to_der).gsub("\n", '')
59
+ end
60
+
61
+ if settings.attribute_consuming_service.configured?
62
+ sp_acs = sp_sso.add_element "md:AttributeConsumingService", {
63
+ "isDefault" => "true",
64
+ "index" => settings.attribute_consuming_service.index
65
+ }
66
+ srv_name = sp_acs.add_element "md:ServiceName", {
67
+ "xml:lang" => "en"
68
+ }
69
+ srv_name.text = settings.attribute_consuming_service.name
70
+ settings.attribute_consuming_service.attributes.each do |attribute|
71
+ sp_req_attr = sp_acs.add_element "md:RequestedAttribute", {
72
+ "NameFormat" => attribute[:name_format],
73
+ "Name" => attribute[:name],
74
+ "FriendlyName" => attribute[:friendly_name]
75
+ }
76
+ unless attribute[:attribute_value].nil?
77
+ sp_attr_val = sp_req_attr.add_element "md:AttributeValue"
78
+ sp_attr_val.text = attribute[:attribute_value]
79
+ end
80
+ end
81
+ end
82
+
51
83
  # With OpenSSO, it might be required to also include
52
84
  # <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"/>
53
85
  # <md:XACMLAuthzDecisionQueryDescriptor WantAssertionsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"/>
54
86
 
55
- meta_doc << REXML::XMLDecl.new
87
+ meta_doc << REXML::XMLDecl.new("1.0", "UTF-8")
56
88
  ret = ""
57
89
  # pretty print the XML so IdP administrators can easily see what the SP supports
58
90
  meta_doc.write(ret, 1)
59
91
 
60
- Logging.debug "Generated metadata:\n#{ret}"
61
-
62
- ret
92
+ return ret
63
93
  end
64
94
  end
65
95
  end