spid-es 0.0.1
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 +15 -0
- data/.document +5 -0
- data/.travis.yml +5 -0
- data/Gemfile +12 -0
- data/LICENSE +19 -0
- data/README.md +124 -0
- data/Rakefile +41 -0
- data/lib/schemas/saml20assertion_schema.xsd +283 -0
- data/lib/schemas/saml20protocol_schema.xsd +302 -0
- data/lib/schemas/xenc_schema.xsd +146 -0
- data/lib/schemas/xmldsig_schema.xsd +318 -0
- data/lib/spid/ruby-saml/authrequest.rb +196 -0
- data/lib/spid/ruby-saml/coding.rb +34 -0
- data/lib/spid/ruby-saml/logging.rb +26 -0
- data/lib/spid/ruby-saml/logout_request.rb +126 -0
- data/lib/spid/ruby-saml/logout_response.rb +132 -0
- data/lib/spid/ruby-saml/metadata.rb +353 -0
- data/lib/spid/ruby-saml/request.rb +81 -0
- data/lib/spid/ruby-saml/response.rb +202 -0
- data/lib/spid/ruby-saml/settings.rb +72 -0
- data/lib/spid/ruby-saml/validation_error.rb +7 -0
- data/lib/spid/ruby-saml/version.rb +5 -0
- data/lib/spid-es.rb +14 -0
- data/lib/xml_security.rb +165 -0
- data/spid-es.gemspec +23 -0
- data/test/certificates/certificate1 +12 -0
- data/test/logoutrequest_test.rb +98 -0
- data/test/request_test.rb +53 -0
- data/test/response_test.rb +219 -0
- data/test/responses/adfs_response_sha1.xml +46 -0
- data/test/responses/adfs_response_sha256.xml +46 -0
- data/test/responses/adfs_response_sha384.xml +46 -0
- data/test/responses/adfs_response_sha512.xml +46 -0
- data/test/responses/no_signature_ns.xml +48 -0
- data/test/responses/open_saml_response.xml +56 -0
- data/test/responses/response1.xml.base64 +1 -0
- data/test/responses/response2.xml.base64 +79 -0
- data/test/responses/response3.xml.base64 +66 -0
- data/test/responses/response4.xml.base64 +93 -0
- data/test/responses/response5.xml.base64 +102 -0
- data/test/responses/response_with_ampersands.xml +139 -0
- data/test/responses/response_with_ampersands.xml.base64 +93 -0
- data/test/responses/simple_saml_php.xml +71 -0
- data/test/responses/wrapped_response_2.xml.base64 +150 -0
- data/test/settings_test.rb +43 -0
- data/test/test_helper.rb +65 -0
- data/test/xml_security_test.rb +123 -0
- metadata +158 -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,196 @@
|
|
1
|
+
require "base64"
|
2
|
+
require "uuid"
|
3
|
+
require "zlib"
|
4
|
+
require "cgi"
|
5
|
+
require "rexml/document"
|
6
|
+
require "rexml/xpath"
|
7
|
+
require "rubygems"
|
8
|
+
require "addressable/uri"
|
9
|
+
|
10
|
+
module Spid::Saml
|
11
|
+
include REXML
|
12
|
+
class Authrequest
|
13
|
+
# a few symbols for SAML class names
|
14
|
+
HTTP_POST = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
|
15
|
+
HTTP_GET = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
|
16
|
+
|
17
|
+
attr_accessor :uuid, :request
|
18
|
+
|
19
|
+
def initialize( settings )
|
20
|
+
@settings = settings
|
21
|
+
@request_params = Hash.new
|
22
|
+
end
|
23
|
+
|
24
|
+
def create(params = {})
|
25
|
+
uuid = "_" + UUID.new.generate
|
26
|
+
self.uuid = uuid
|
27
|
+
time = Time.now.utc.strftime("%Y-%m-%dT%H:%M:%SZ")
|
28
|
+
# Create AuthnRequest root element using REXML
|
29
|
+
request_doc = REXML::Document.new
|
30
|
+
request_doc.context[:attribute_quote] = :quote
|
31
|
+
root = request_doc.add_element "saml2p:AuthnRequest", { "xmlns:saml2p" => "urn:oasis:names:tc:SAML:2.0:protocol" }
|
32
|
+
root.attributes['ID'] = uuid
|
33
|
+
root.attributes['IssueInstant'] = time
|
34
|
+
root.attributes['Version'] = "2.0"
|
35
|
+
root.attributes['ProtocolBinding'] = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
|
36
|
+
root.attributes['AttributeConsumingServiceIndex'] = "1"
|
37
|
+
root.attributes['ForceAuthn'] = "false"
|
38
|
+
#root.attributes['IsPassive'] = "false"
|
39
|
+
#usato AssertionConsumerServiceURL e ProtocolBinding in alternativa, pag 8 regole tecniche
|
40
|
+
#root.attributes['AssertionConsumerServiceIndex'] = 1
|
41
|
+
|
42
|
+
# Conditionally defined elements based on settings
|
43
|
+
if @settings.assertion_consumer_service_url != nil
|
44
|
+
root.attributes["AssertionConsumerServiceURL"] = @settings.assertion_consumer_service_url
|
45
|
+
end
|
46
|
+
|
47
|
+
if @settings.destination_service_url != nil
|
48
|
+
root.attributes["Destination"] = @settings.destination_service_url
|
49
|
+
end
|
50
|
+
|
51
|
+
if @settings.issuer != nil
|
52
|
+
issuer = root.add_element "saml2:Issuer", { "xmlns:saml2" => "urn:oasis:names:tc:SAML:2.0:assertion" }
|
53
|
+
issuer.attributes['NameQualifier'] = @settings.issuer
|
54
|
+
issuer.attributes['Format'] = "urn:oasis:names:tc:SAML:2.0:nameid-format:entity"
|
55
|
+
issuer.text = @settings.issuer
|
56
|
+
end
|
57
|
+
|
58
|
+
#opzionale
|
59
|
+
if @settings.sp_name_qualifier != nil
|
60
|
+
subject = root.add_element "saml2:Subject", { "xmlns:saml2" => "urn:oasis:names:tc:SAML:2.0:assertion" }
|
61
|
+
name_id = subject.add_element "saml2:NameID", { "xmlns:saml2" => "urn:oasis:names:tc:SAML:2.0:assertion" }
|
62
|
+
name_id.attributes['Format'] = "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
|
63
|
+
name_id.attributes['NameQualifier'] = @settings.sp_name_qualifier
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
|
68
|
+
if @settings.name_identifier_format != nil
|
69
|
+
root.add_element "saml2p:NameIDPolicy", {
|
70
|
+
# Might want to make AllowCreate a setting?
|
71
|
+
#{}"AllowCreate" => "true",
|
72
|
+
"Format" => @settings.name_identifier_format[1],
|
73
|
+
"SPNameQualifier" => @settings.sp_name_qualifier
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
# BUG fix here -- if an authn_context is defined, add the tags with an "exact"
|
78
|
+
# match required for authentication to succeed. If this is not defined,
|
79
|
+
# the IdP will choose default rules for authentication. (Shibboleth IdP)
|
80
|
+
if @settings.authn_context != nil
|
81
|
+
requested_context = root.add_element "saml2p:RequestedAuthnContext", {
|
82
|
+
"Comparison" => "exact"
|
83
|
+
}
|
84
|
+
context_class = []
|
85
|
+
@settings.authn_context.each_with_index{ |context, index|
|
86
|
+
context_class[index] = requested_context.add_element "saml2:AuthnContextClassRef", {
|
87
|
+
"xmlns:saml2" => "urn:oasis:names:tc:SAML:2.0:assertion"
|
88
|
+
}
|
89
|
+
context_class[index].text = context
|
90
|
+
}
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
if @settings.requester_identificator != nil
|
95
|
+
requester_identificator = root.add_element "saml2p:Scoping", {
|
96
|
+
"ProxyCount" => "0"
|
97
|
+
}
|
98
|
+
identificators = []
|
99
|
+
@settings.requester_identificator.each_with_index{ |requester, index|
|
100
|
+
identificators[index] = requester_identificator.add_element "saml2p:RequesterID"
|
101
|
+
identificators[index].text = requester
|
102
|
+
}
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
request_doc << REXML::XMLDecl.new(version='1.0', encoding='UTF-8')
|
107
|
+
ret = ""
|
108
|
+
# pretty print the XML so IdP administrators can easily see what the SP supports
|
109
|
+
request_doc.write(ret, 1)
|
110
|
+
|
111
|
+
@request = ""
|
112
|
+
request_doc.write(@request)
|
113
|
+
|
114
|
+
#Logging.debug "Created AuthnRequest: #{@request}"
|
115
|
+
|
116
|
+
return self
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
# get the IdP metadata, and select the appropriate SSO binding
|
121
|
+
# that we can support. Currently this is HTTP-Redirect and HTTP-POST
|
122
|
+
# but more could be added in the future
|
123
|
+
def binding_select
|
124
|
+
# first check if we're still using the old hard coded method for
|
125
|
+
# backwards compatability
|
126
|
+
if @settings.idp_metadata == nil && @settings.idp_sso_target_url != nil
|
127
|
+
@URL = @settings.idp_sso_target_url
|
128
|
+
return "GET", content_get
|
129
|
+
end
|
130
|
+
# grab the metadata
|
131
|
+
metadata = Metadata::new
|
132
|
+
meta_doc = metadata.get_idp_metadata(@settings)
|
133
|
+
|
134
|
+
# first try POST
|
135
|
+
sso_element = REXML::XPath.first(meta_doc,
|
136
|
+
"/EntityDescriptor/IDPSSODescriptor/SingleSignOnService[@Binding='#{HTTP_POST}']")
|
137
|
+
if sso_element
|
138
|
+
@URL = sso_element.attributes["Location"]
|
139
|
+
#Logging.debug "binding_select: POST to #{@URL}"
|
140
|
+
return "POST", content_post
|
141
|
+
end
|
142
|
+
|
143
|
+
# next try GET
|
144
|
+
sso_element = REXML::XPath.first(meta_doc,
|
145
|
+
"/EntityDescriptor/IDPSSODescriptor/SingleSignOnService[@Binding='#{HTTP_GET}']")
|
146
|
+
if sso_element
|
147
|
+
@URL = sso_element.attributes["Location"]
|
148
|
+
Logging.debug "binding_select: GET from #{@URL}"
|
149
|
+
return "GET", content_get
|
150
|
+
end
|
151
|
+
# other types we might want to add in the future: SOAP, Artifact
|
152
|
+
end
|
153
|
+
|
154
|
+
# construct the the parameter list on the URL and return
|
155
|
+
def content_get
|
156
|
+
# compress GET requests to try and stay under that 8KB request limit
|
157
|
+
deflated_request = Zlib::Deflate.deflate(@request, 9)[2..-5]
|
158
|
+
# strict_encode64() isn't available? sub out the newlines
|
159
|
+
@request_params["SAMLRequest"] = Base64.encode64(deflated_request).gsub(/\n/, "")
|
160
|
+
|
161
|
+
Logging.debug "SAMLRequest=#{@request_params["SAMLRequest"]}"
|
162
|
+
uri = Addressable::URI.parse(@URL)
|
163
|
+
if uri.query_values == nil
|
164
|
+
uri.query_values = @request_params
|
165
|
+
else
|
166
|
+
# solution to stevenwilkin's parameter merge
|
167
|
+
uri.query_values = @request_params.merge(uri.query_values)
|
168
|
+
end
|
169
|
+
url = uri.to_s
|
170
|
+
#Logging.debug "Sending to URL #{url}"
|
171
|
+
return url
|
172
|
+
end
|
173
|
+
# construct an HTML form (POST) and return the content
|
174
|
+
def content_post
|
175
|
+
# POST requests seem to bomb out when they're deflated
|
176
|
+
# and they probably don't need to be compressed anyway
|
177
|
+
@request_params["SAMLRequest"] = Base64.encode64(@request).gsub(/\n/, "")
|
178
|
+
|
179
|
+
#Logging.debug "SAMLRequest=#{@request_params["SAMLRequest"]}"
|
180
|
+
# kind of a cheesy method of building an HTML, form since we can't rely on Rails too much,
|
181
|
+
# and REXML doesn't work well with quote characters
|
182
|
+
str = "<html><body onLoad=\"document.getElementById('form').submit();\">\n"
|
183
|
+
str += "<form id='form' name='form' method='POST' action=\"#{@URL}\">\n"
|
184
|
+
# we could change this in the future to associate a temp auth session ID
|
185
|
+
str += "<input name='RelayState' value='ruby-saml' type='hidden' />\n"
|
186
|
+
@request_params.each_pair do |key, value|
|
187
|
+
str += "<input name=\"#{key}\" value=\"#{value}\" type='hidden' />\n"
|
188
|
+
#str += "<input name=\"#{key}\" value=\"#{CGI.escape(value)}\" type='hidden' />\n"
|
189
|
+
end
|
190
|
+
str += "</form></body></html>\n"
|
191
|
+
|
192
|
+
#Logging.debug "Created form:\n#{str}"
|
193
|
+
return str
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require "cgi"
|
2
|
+
require 'zlib'
|
3
|
+
|
4
|
+
module Spid
|
5
|
+
module Saml
|
6
|
+
module Coding
|
7
|
+
def decode(encoded)
|
8
|
+
Base64.decode64(encoded)
|
9
|
+
end
|
10
|
+
|
11
|
+
def encode(encoded)
|
12
|
+
Base64.encode64(encoded).gsub(/\n/, "")
|
13
|
+
end
|
14
|
+
|
15
|
+
def escape(unescaped)
|
16
|
+
CGI.escape(unescaped)
|
17
|
+
end
|
18
|
+
|
19
|
+
def unescape(escaped)
|
20
|
+
CGI.unescape(escaped)
|
21
|
+
end
|
22
|
+
|
23
|
+
def inflate(deflated)
|
24
|
+
zlib = Zlib::Inflate.new(-Zlib::MAX_WBITS)
|
25
|
+
zlib.inflate(deflated)
|
26
|
+
end
|
27
|
+
|
28
|
+
def deflate(inflated)
|
29
|
+
Zlib::Deflate.deflate(inflated, 9)[2..-5]
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# Simplistic log class when we're running in Rails
|
2
|
+
module Spid
|
3
|
+
module Saml
|
4
|
+
class Logging
|
5
|
+
def self.debug(message)
|
6
|
+
return if !!ENV["ruby-saml/testing"]
|
7
|
+
|
8
|
+
if defined? Rails
|
9
|
+
Rails.logger.debug message
|
10
|
+
else
|
11
|
+
puts message
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.info(message)
|
16
|
+
return if !!ENV["ruby-saml/testing"]
|
17
|
+
|
18
|
+
if defined? Rails
|
19
|
+
Rails.logger.info message
|
20
|
+
else
|
21
|
+
puts message
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|