samlr 2.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of samlr might be problematic. Click here for more details.

Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +3 -0
  3. data/.travis.yml +5 -0
  4. data/Gemfile +8 -0
  5. data/LICENSE +176 -0
  6. data/README.md +182 -0
  7. data/Rakefile +12 -0
  8. data/bin/samlr +46 -0
  9. data/config/schemas/XMLSchema.xsd +2534 -0
  10. data/config/schemas/saml-schema-assertion-2.0.xsd +283 -0
  11. data/config/schemas/saml-schema-metadata-2.0.xsd +337 -0
  12. data/config/schemas/saml-schema-protocol-2.0.xsd +302 -0
  13. data/config/schemas/xenc-schema.xsd +146 -0
  14. data/config/schemas/xml.xsd +287 -0
  15. data/config/schemas/xmldsig-core-schema.xsd +318 -0
  16. data/lib/samlr.rb +52 -0
  17. data/lib/samlr/assertion.rb +91 -0
  18. data/lib/samlr/certificate.rb +23 -0
  19. data/lib/samlr/command.rb +41 -0
  20. data/lib/samlr/condition.rb +31 -0
  21. data/lib/samlr/errors.rb +22 -0
  22. data/lib/samlr/fingerprint.rb +44 -0
  23. data/lib/samlr/logout_request.rb +7 -0
  24. data/lib/samlr/reference.rb +32 -0
  25. data/lib/samlr/request.rb +37 -0
  26. data/lib/samlr/response.rb +68 -0
  27. data/lib/samlr/signature.rb +129 -0
  28. data/lib/samlr/tools.rb +108 -0
  29. data/lib/samlr/tools/certificate_builder.rb +74 -0
  30. data/lib/samlr/tools/logout_request_builder.rb +27 -0
  31. data/lib/samlr/tools/metadata_builder.rb +41 -0
  32. data/lib/samlr/tools/request_builder.rb +44 -0
  33. data/lib/samlr/tools/response_builder.rb +157 -0
  34. data/lib/samlr/tools/timestamp.rb +26 -0
  35. data/samlr.gemspec +19 -0
  36. data/test/fixtures/default_samlr_certificate.pem +11 -0
  37. data/test/fixtures/default_samlr_private_key.pem +9 -0
  38. data/test/fixtures/no_cert_response.xml +2 -0
  39. data/test/fixtures/sample_metadata.xml +7 -0
  40. data/test/fixtures/sample_response.xml +2 -0
  41. data/test/test_helper.rb +55 -0
  42. data/test/unit/test_assertion.rb +54 -0
  43. data/test/unit/test_condition.rb +71 -0
  44. data/test/unit/test_fingerprint.rb +45 -0
  45. data/test/unit/test_logout_request.rb +39 -0
  46. data/test/unit/test_reference.rb +32 -0
  47. data/test/unit/test_request.rb +34 -0
  48. data/test/unit/test_response.rb +94 -0
  49. data/test/unit/test_response_scenarios.rb +111 -0
  50. data/test/unit/test_signature.rb +54 -0
  51. data/test/unit/test_timestamp.rb +58 -0
  52. data/test/unit/test_tools.rb +100 -0
  53. data/test/unit/tools/test_certificate_builder.rb +41 -0
  54. data/test/unit/tools/test_logout_request_builder.rb +26 -0
  55. data/test/unit/tools/test_metadata_builder.rb +26 -0
  56. data/test/unit/tools/test_request_builder.rb +35 -0
  57. data/test/unit/tools/test_response_builder.rb +19 -0
  58. metadata +184 -0
@@ -0,0 +1,318 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!DOCTYPE schema
3
+ PUBLIC "-//W3C//DTD XMLSchema 200102//EN" "http://www.w3.org/2001/XMLSchema.dtd"
4
+ [
5
+ <!ATTLIST schema
6
+ xmlns:ds CDATA #FIXED "http://www.w3.org/2000/09/xmldsig#">
7
+ <!ENTITY dsig 'http://www.w3.org/2000/09/xmldsig#'>
8
+ <!ENTITY % p ''>
9
+ <!ENTITY % s ''>
10
+ ]>
11
+
12
+ <!-- Schema for XML Signatures
13
+ http://www.w3.org/2000/09/xmldsig#
14
+ $Revision: 1.1 $ on $Date: 2002/02/08 20:32:26 $ by $Author: reagle $
15
+
16
+ Copyright 2001 The Internet Society and W3C (Massachusetts Institute
17
+ of Technology, Institut National de Recherche en Informatique et en
18
+ Automatique, Keio University). All Rights Reserved.
19
+ http://www.w3.org/Consortium/Legal/
20
+
21
+ This document is governed by the W3C Software License [1] as described
22
+ in the FAQ [2].
23
+
24
+ [1] http://www.w3.org/Consortium/Legal/copyright-software-19980720
25
+ [2] http://www.w3.org/Consortium/Legal/IPR-FAQ-20000620.html#DTD
26
+ -->
27
+
28
+
29
+ <schema xmlns="http://www.w3.org/2001/XMLSchema"
30
+ xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
31
+ targetNamespace="http://www.w3.org/2000/09/xmldsig#"
32
+ version="0.1" elementFormDefault="qualified">
33
+
34
+ <!-- Basic Types Defined for Signatures -->
35
+
36
+ <simpleType name="CryptoBinary">
37
+ <restriction base="base64Binary">
38
+ </restriction>
39
+ </simpleType>
40
+
41
+ <!-- Start Signature -->
42
+
43
+ <element name="Signature" type="ds:SignatureType"/>
44
+ <complexType name="SignatureType">
45
+ <sequence>
46
+ <element ref="ds:SignedInfo"/>
47
+ <element ref="ds:SignatureValue"/>
48
+ <element ref="ds:KeyInfo" minOccurs="0"/>
49
+ <element ref="ds:Object" minOccurs="0" maxOccurs="unbounded"/>
50
+ </sequence>
51
+ <attribute name="Id" type="ID" use="optional"/>
52
+ </complexType>
53
+
54
+ <element name="SignatureValue" type="ds:SignatureValueType"/>
55
+ <complexType name="SignatureValueType">
56
+ <simpleContent>
57
+ <extension base="base64Binary">
58
+ <attribute name="Id" type="ID" use="optional"/>
59
+ </extension>
60
+ </simpleContent>
61
+ </complexType>
62
+
63
+ <!-- Start SignedInfo -->
64
+
65
+ <element name="SignedInfo" type="ds:SignedInfoType"/>
66
+ <complexType name="SignedInfoType">
67
+ <sequence>
68
+ <element ref="ds:CanonicalizationMethod"/>
69
+ <element ref="ds:SignatureMethod"/>
70
+ <element ref="ds:Reference" maxOccurs="unbounded"/>
71
+ </sequence>
72
+ <attribute name="Id" type="ID" use="optional"/>
73
+ </complexType>
74
+
75
+ <element name="CanonicalizationMethod" type="ds:CanonicalizationMethodType"/>
76
+ <complexType name="CanonicalizationMethodType" mixed="true">
77
+ <sequence>
78
+ <any namespace="##any" minOccurs="0" maxOccurs="unbounded"/>
79
+ <!-- (0,unbounded) elements from (1,1) namespace -->
80
+ </sequence>
81
+ <attribute name="Algorithm" type="anyURI" use="required"/>
82
+ </complexType>
83
+
84
+ <element name="SignatureMethod" type="ds:SignatureMethodType"/>
85
+ <complexType name="SignatureMethodType" mixed="true">
86
+ <sequence>
87
+ <element name="HMACOutputLength" minOccurs="0" type="ds:HMACOutputLengthType"/>
88
+ <any namespace="##other" minOccurs="0" maxOccurs="unbounded"/>
89
+ <!-- (0,unbounded) elements from (1,1) external namespace -->
90
+ </sequence>
91
+ <attribute name="Algorithm" type="anyURI" use="required"/>
92
+ </complexType>
93
+
94
+ <!-- Start Reference -->
95
+
96
+ <element name="Reference" type="ds:ReferenceType"/>
97
+ <complexType name="ReferenceType">
98
+ <sequence>
99
+ <element ref="ds:Transforms" minOccurs="0"/>
100
+ <element ref="ds:DigestMethod"/>
101
+ <element ref="ds:DigestValue"/>
102
+ </sequence>
103
+ <attribute name="Id" type="ID" use="optional"/>
104
+ <attribute name="URI" type="anyURI" use="optional"/>
105
+ <attribute name="Type" type="anyURI" use="optional"/>
106
+ </complexType>
107
+
108
+ <element name="Transforms" type="ds:TransformsType"/>
109
+ <complexType name="TransformsType">
110
+ <sequence>
111
+ <element ref="ds:Transform" maxOccurs="unbounded"/>
112
+ </sequence>
113
+ </complexType>
114
+
115
+ <element name="Transform" type="ds:TransformType"/>
116
+ <complexType name="TransformType" mixed="true">
117
+ <choice minOccurs="0" maxOccurs="unbounded">
118
+ <any namespace="##other" processContents="lax"/>
119
+ <!-- (1,1) elements from (0,unbounded) namespaces -->
120
+ <element name="XPath" type="string"/>
121
+ </choice>
122
+ <attribute name="Algorithm" type="anyURI" use="required"/>
123
+ </complexType>
124
+
125
+ <!-- End Reference -->
126
+
127
+ <element name="DigestMethod" type="ds:DigestMethodType"/>
128
+ <complexType name="DigestMethodType" mixed="true">
129
+ <sequence>
130
+ <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
131
+ </sequence>
132
+ <attribute name="Algorithm" type="anyURI" use="required"/>
133
+ </complexType>
134
+
135
+ <element name="DigestValue" type="ds:DigestValueType"/>
136
+ <simpleType name="DigestValueType">
137
+ <restriction base="base64Binary"/>
138
+ </simpleType>
139
+
140
+ <!-- End SignedInfo -->
141
+
142
+ <!-- Start KeyInfo -->
143
+
144
+ <element name="KeyInfo" type="ds:KeyInfoType"/>
145
+ <complexType name="KeyInfoType" mixed="true">
146
+ <choice maxOccurs="unbounded">
147
+ <element ref="ds:KeyName"/>
148
+ <element ref="ds:KeyValue"/>
149
+ <element ref="ds:RetrievalMethod"/>
150
+ <element ref="ds:X509Data"/>
151
+ <element ref="ds:PGPData"/>
152
+ <element ref="ds:SPKIData"/>
153
+ <element ref="ds:MgmtData"/>
154
+ <any processContents="lax" namespace="##other"/>
155
+ <!-- (1,1) elements from (0,unbounded) namespaces -->
156
+ </choice>
157
+ <attribute name="Id" type="ID" use="optional"/>
158
+ </complexType>
159
+
160
+ <element name="KeyName" type="string"/>
161
+ <element name="MgmtData" type="string"/>
162
+
163
+ <element name="KeyValue" type="ds:KeyValueType"/>
164
+ <complexType name="KeyValueType" mixed="true">
165
+ <choice>
166
+ <element ref="ds:DSAKeyValue"/>
167
+ <element ref="ds:RSAKeyValue"/>
168
+ <any namespace="##other" processContents="lax"/>
169
+ </choice>
170
+ </complexType>
171
+
172
+ <element name="RetrievalMethod" type="ds:RetrievalMethodType"/>
173
+ <complexType name="RetrievalMethodType">
174
+ <sequence>
175
+ <element ref="ds:Transforms" minOccurs="0"/>
176
+ </sequence>
177
+ <attribute name="URI" type="anyURI"/>
178
+ <attribute name="Type" type="anyURI" use="optional"/>
179
+ </complexType>
180
+
181
+ <!-- Start X509Data -->
182
+
183
+ <element name="X509Data" type="ds:X509DataType"/>
184
+ <complexType name="X509DataType">
185
+ <sequence maxOccurs="unbounded">
186
+ <choice>
187
+ <element name="X509IssuerSerial" type="ds:X509IssuerSerialType"/>
188
+ <element name="X509SKI" type="base64Binary"/>
189
+ <element name="X509SubjectName" type="string"/>
190
+ <element name="X509Certificate" type="base64Binary"/>
191
+ <element name="X509CRL" type="base64Binary"/>
192
+ <any namespace="##other" processContents="lax"/>
193
+ </choice>
194
+ </sequence>
195
+ </complexType>
196
+
197
+ <complexType name="X509IssuerSerialType">
198
+ <sequence>
199
+ <element name="X509IssuerName" type="string"/>
200
+ <element name="X509SerialNumber" type="integer"/>
201
+ </sequence>
202
+ </complexType>
203
+
204
+ <!-- End X509Data -->
205
+
206
+ <!-- Begin PGPData -->
207
+
208
+ <element name="PGPData" type="ds:PGPDataType"/>
209
+ <complexType name="PGPDataType">
210
+ <choice>
211
+ <sequence>
212
+ <element name="PGPKeyID" type="base64Binary"/>
213
+ <element name="PGPKeyPacket" type="base64Binary" minOccurs="0"/>
214
+ <any namespace="##other" processContents="lax" minOccurs="0"
215
+ maxOccurs="unbounded"/>
216
+ </sequence>
217
+ <sequence>
218
+ <element name="PGPKeyPacket" type="base64Binary"/>
219
+ <any namespace="##other" processContents="lax" minOccurs="0"
220
+ maxOccurs="unbounded"/>
221
+ </sequence>
222
+ </choice>
223
+ </complexType>
224
+
225
+ <!-- End PGPData -->
226
+
227
+ <!-- Begin SPKIData -->
228
+
229
+ <element name="SPKIData" type="ds:SPKIDataType"/>
230
+ <complexType name="SPKIDataType">
231
+ <sequence maxOccurs="unbounded">
232
+ <element name="SPKISexp" type="base64Binary"/>
233
+ <any namespace="##other" processContents="lax" minOccurs="0"/>
234
+ </sequence>
235
+ </complexType>
236
+
237
+ <!-- End SPKIData -->
238
+
239
+ <!-- End KeyInfo -->
240
+
241
+ <!-- Start Object (Manifest, SignatureProperty) -->
242
+
243
+ <element name="Object" type="ds:ObjectType"/>
244
+ <complexType name="ObjectType" mixed="true">
245
+ <sequence minOccurs="0" maxOccurs="unbounded">
246
+ <any namespace="##any" processContents="lax"/>
247
+ </sequence>
248
+ <attribute name="Id" type="ID" use="optional"/>
249
+ <attribute name="MimeType" type="string" use="optional"/> <!-- add a grep facet -->
250
+ <attribute name="Encoding" type="anyURI" use="optional"/>
251
+ </complexType>
252
+
253
+ <element name="Manifest" type="ds:ManifestType"/>
254
+ <complexType name="ManifestType">
255
+ <sequence>
256
+ <element ref="ds:Reference" maxOccurs="unbounded"/>
257
+ </sequence>
258
+ <attribute name="Id" type="ID" use="optional"/>
259
+ </complexType>
260
+
261
+ <element name="SignatureProperties" type="ds:SignaturePropertiesType"/>
262
+ <complexType name="SignaturePropertiesType">
263
+ <sequence>
264
+ <element ref="ds:SignatureProperty" maxOccurs="unbounded"/>
265
+ </sequence>
266
+ <attribute name="Id" type="ID" use="optional"/>
267
+ </complexType>
268
+
269
+ <element name="SignatureProperty" type="ds:SignaturePropertyType"/>
270
+ <complexType name="SignaturePropertyType" mixed="true">
271
+ <choice maxOccurs="unbounded">
272
+ <any namespace="##other" processContents="lax"/>
273
+ <!-- (1,1) elements from (1,unbounded) namespaces -->
274
+ </choice>
275
+ <attribute name="Target" type="anyURI" use="required"/>
276
+ <attribute name="Id" type="ID" use="optional"/>
277
+ </complexType>
278
+
279
+ <!-- End Object (Manifest, SignatureProperty) -->
280
+
281
+ <!-- Start Algorithm Parameters -->
282
+
283
+ <simpleType name="HMACOutputLengthType">
284
+ <restriction base="integer"/>
285
+ </simpleType>
286
+
287
+ <!-- Start KeyValue Element-types -->
288
+
289
+ <element name="DSAKeyValue" type="ds:DSAKeyValueType"/>
290
+ <complexType name="DSAKeyValueType">
291
+ <sequence>
292
+ <sequence minOccurs="0">
293
+ <element name="P" type="ds:CryptoBinary"/>
294
+ <element name="Q" type="ds:CryptoBinary"/>
295
+ </sequence>
296
+ <element name="G" type="ds:CryptoBinary" minOccurs="0"/>
297
+ <element name="Y" type="ds:CryptoBinary"/>
298
+ <element name="J" type="ds:CryptoBinary" minOccurs="0"/>
299
+ <sequence minOccurs="0">
300
+ <element name="Seed" type="ds:CryptoBinary"/>
301
+ <element name="PgenCounter" type="ds:CryptoBinary"/>
302
+ </sequence>
303
+ </sequence>
304
+ </complexType>
305
+
306
+ <element name="RSAKeyValue" type="ds:RSAKeyValueType"/>
307
+ <complexType name="RSAKeyValueType">
308
+ <sequence>
309
+ <element name="Modulus" type="ds:CryptoBinary"/>
310
+ <element name="Exponent" type="ds:CryptoBinary"/>
311
+ </sequence>
312
+ </complexType>
313
+
314
+ <!-- End KeyValue Element-types -->
315
+
316
+ <!-- End Signature -->
317
+
318
+ </schema>
@@ -0,0 +1,52 @@
1
+ require "nokogiri"
2
+ require "logger"
3
+
4
+ module Samlr
5
+ C14N = Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0
6
+ COMPACT = { :indent => 0, :save_with => Nokogiri::XML::Node::SaveOptions::AS_XML }
7
+
8
+ NS_MAP = {
9
+ "c14n" => "http://www.w3.org/2001/10/xml-exc-c14n#",
10
+ "ds" => "http://www.w3.org/2000/09/xmldsig#",
11
+ "saml" => "urn:oasis:names:tc:SAML:2.0:assertion",
12
+ "samlp" => "urn:oasis:names:tc:SAML:2.0:protocol",
13
+ "md" => "urn:oasis:names:tc:SAML:2.0:metadata",
14
+ "xsi" => "http://www.w3.org/2001/XMLSchema-instance",
15
+ "xs" => "http://www.w3.org/2001/XMLSchema"
16
+ }
17
+
18
+ EMAIL_FORMAT = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
19
+ SAML_SCHEMA = "saml-schema-protocol-2.0.xsd"
20
+ META_SCHEMA = "saml-schema-metadata-2.0.xsd"
21
+
22
+ class << self
23
+ attr_accessor :schema_location
24
+ attr_accessor :validation_mode
25
+ attr_accessor :jitter
26
+ attr_accessor :logger
27
+ end
28
+
29
+ self.schema_location = File.join(File.dirname(__FILE__), "..", "config", "schemas")
30
+ self.validation_mode = :reject
31
+ self.jitter = 0
32
+ self.logger = Logger.new(STDERR)
33
+ self.logger.level = Logger::UNKNOWN
34
+ end
35
+
36
+ unless Object.new.respond_to?(:try)
37
+ class Object
38
+ def try(method)
39
+ send(method) if respond_to?(method)
40
+ end
41
+ end
42
+ end
43
+
44
+ require "samlr/errors"
45
+ require "samlr/tools"
46
+ require "samlr/condition"
47
+ require "samlr/assertion"
48
+ require "samlr/fingerprint"
49
+ require "samlr/signature"
50
+ require "samlr/response"
51
+ require "samlr/request"
52
+ require "samlr/logout_request"
@@ -0,0 +1,91 @@
1
+ module Samlr
2
+ class Assertion
3
+ DEFAULT_LOCATION = "/samlp:Response/saml:Assertion"
4
+ attr_reader :document, :options
5
+
6
+ def initialize(document, options)
7
+ @document = document
8
+ @options = options
9
+ end
10
+
11
+ def verify!
12
+ verify_signature!
13
+ verify_conditions! unless skip_conditions?
14
+
15
+ true
16
+ end
17
+
18
+ def location
19
+ @location ||= if !signature.missing?
20
+ verify_signature!
21
+ "//saml:Assertion[@ID='#{signature.references.first.uri}']"
22
+ else
23
+ DEFAULT_LOCATION
24
+ end
25
+ end
26
+
27
+ def signature
28
+ @signature ||= Samlr::Signature.new(document, DEFAULT_LOCATION, options)
29
+ end
30
+
31
+ def attributes
32
+ @attributes ||= {}.tap do |attrs|
33
+ assertion.xpath("./saml:AttributeStatement/saml:Attribute", NS_MAP).each do |statement|
34
+ name = statement["Name"]
35
+ values = statement.xpath("./saml:AttributeValue", NS_MAP)
36
+
37
+ if values.size == 0
38
+ next
39
+ elsif values.size == 1
40
+ value = values.first.text
41
+ else
42
+ value = values.map { |value| value.text }
43
+ end
44
+
45
+ attrs[name] = attrs[name.to_sym] = value
46
+ end
47
+ end
48
+ end
49
+
50
+ def name_id
51
+ @name_id ||= assertion.at("./saml:Subject/saml:NameID", NS_MAP).text
52
+ end
53
+
54
+ private
55
+
56
+ def assertion
57
+ @assertion ||= document.at(location, NS_MAP)
58
+ end
59
+
60
+ def verify_signature!
61
+ verify_assertion!
62
+ signature.verify! unless signature.missing?
63
+
64
+ true
65
+ end
66
+
67
+ def skip_conditions?
68
+ !!options[:skip_conditions]
69
+ end
70
+
71
+ def conditions
72
+ @conditions ||= Condition.new(assertion.at("./saml:Conditions", NS_MAP))
73
+ end
74
+
75
+ def verify_conditions!
76
+ conditions.verify!
77
+ end
78
+
79
+ def verify_assertion!
80
+ assertion_count = document.xpath("//saml:Assertion", NS_MAP).size
81
+
82
+ if assertion_count == 0
83
+ raise Samlr::FormatError.new("Invalid SAML response: assertion missing")
84
+ elsif assertion_count != 1
85
+ raise Samlr::FormatError.new("Invalid SAML response: unexpected number of assertions")
86
+ end
87
+
88
+ true
89
+ end
90
+ end
91
+ end