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.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  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 -93
  11. data/lib/onelogin/ruby-saml/idp_metadata_parser.rb +87 -0
  12. data/lib/onelogin/ruby-saml/logoutrequest.rb +36 -100
  13. data/lib/onelogin/ruby-saml/logoutresponse.rb +25 -35
  14. data/lib/onelogin/ruby-saml/metadata.rb +46 -16
  15. data/lib/onelogin/ruby-saml/response.rb +63 -373
  16. data/lib/onelogin/ruby-saml/saml_message.rb +78 -0
  17. data/lib/onelogin/ruby-saml/settings.rb +54 -122
  18. data/lib/onelogin/ruby-saml/slo_logoutrequest.rb +25 -71
  19. data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +37 -102
  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 -155
  39. data/test/logoutresponse_test.rb +43 -32
  40. data/test/metadata_test.rb +87 -0
  41. data/test/request_test.rb +102 -99
  42. data/test/response_test.rb +181 -495
  43. data/test/responses/idp_descriptor.xml +3 -0
  44. data/test/responses/logoutresponse_fixtures.rb +7 -8
  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 +40 -50
  50. data/test/slo_logoutresponse_test.rb +86 -185
  51. data/test/test_helper.rb +27 -102
  52. data/test/xml_security_test.rb +114 -337
  53. metadata +30 -81
  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
@@ -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,27 +24,16 @@ 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
 
35
- def response_id
36
- @response_id ||= begin
37
- node = REXML::XPath.first(
38
- document,
39
- "/p:LogoutResponse",
40
- { "p" => PROTOCOL }
41
- )
42
- node.nil? ? nil : node.attributes['ID']
43
- end
44
- end
45
-
46
31
  def validate!
47
32
  validate(false)
48
33
  end
49
34
 
50
35
  def validate(soft = true)
51
- return false unless validate_structure(soft)
36
+ return false unless valid_saml?(document, soft) && valid_state?(soft)
52
37
 
53
38
  valid_in_response_to?(soft) && valid_issuer?(soft) && success?(soft)
54
39
  end
@@ -62,7 +47,7 @@ module OneLogin
62
47
 
63
48
  def in_response_to
64
49
  @in_response_to ||= begin
65
- node = REXML::XPath.first(document, "/p:LogoutResponse", { "p" => PROTOCOL })
50
+ node = REXML::XPath.first(document, "/p:LogoutResponse", { "p" => PROTOCOL, "a" => ASSERTION })
66
51
  node.nil? ? nil : node.attributes['InResponseTo']
67
52
  end
68
53
  end
@@ -70,7 +55,8 @@ module OneLogin
70
55
  def issuer
71
56
  @issuer ||= begin
72
57
  node = REXML::XPath.first(document, "/p:LogoutResponse/a:Issuer", { "p" => PROTOCOL, "a" => ASSERTION })
73
- 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
74
60
  end
75
61
  end
76
62
 
@@ -83,16 +69,24 @@ module OneLogin
83
69
 
84
70
  private
85
71
 
86
- def validate_structure(soft = true)
87
- Dir.chdir(File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'schemas'))) do
88
- @schema = Nokogiri::XML::Schema(IO.read('saml20protocol_schema.xsd'))
89
- @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")
90
79
  end
91
- if soft
92
- @schema.validate(@xml).map{ return false }
93
- else
94
- @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")
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")
95
87
  end
88
+
89
+ true
96
90
  end
97
91
 
98
92
  def valid_in_response_to?(soft = true)
@@ -106,17 +100,13 @@ module OneLogin
106
100
  end
107
101
 
108
102
  def valid_issuer?(soft = true)
109
- 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?
110
104
 
111
- unless OneLogin::RubySaml::Utils.uri_match?(issuer, settings.idp_entity_id)
112
- 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}>")
113
107
  end
114
108
  true
115
109
  end
116
-
117
- def validation_error(message)
118
- raise ValidationError.new(message)
119
- end
120
110
  end
121
111
  end
122
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