ruby-saml-nechotech 0.7.21

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/.document +5 -0
  2. data/.gitignore +11 -0
  3. data/.travis.yml +5 -0
  4. data/Gemfile +13 -0
  5. data/LICENSE +19 -0
  6. data/README.md +128 -0
  7. data/Rakefile +41 -0
  8. data/lib/onelogin/ruby-saml/authrequest.rb +84 -0
  9. data/lib/onelogin/ruby-saml/logging.rb +26 -0
  10. data/lib/onelogin/ruby-saml/logoutrequest.rb +82 -0
  11. data/lib/onelogin/ruby-saml/logoutresponse.rb +154 -0
  12. data/lib/onelogin/ruby-saml/metadata.rb +66 -0
  13. data/lib/onelogin/ruby-saml/response.rb +186 -0
  14. data/lib/onelogin/ruby-saml/settings.rb +27 -0
  15. data/lib/onelogin/ruby-saml/validation_error.rb +7 -0
  16. data/lib/onelogin/ruby-saml/version.rb +5 -0
  17. data/lib/ruby-saml.rb +9 -0
  18. data/lib/schemas/saml20assertion_schema.xsd +283 -0
  19. data/lib/schemas/saml20protocol_schema.xsd +302 -0
  20. data/lib/schemas/xenc_schema.xsd +146 -0
  21. data/lib/schemas/xmldsig_schema.xsd +318 -0
  22. data/lib/xml_security.rb +169 -0
  23. data/ruby-saml-nechotech.gemspec +29 -0
  24. data/test/certificates/certificate1 +12 -0
  25. data/test/certificates/r1_certificate2_base64 +1 -0
  26. data/test/logoutrequest_test.rb +111 -0
  27. data/test/logoutresponse_test.rb +116 -0
  28. data/test/request_test.rb +97 -0
  29. data/test/response_test.rb +247 -0
  30. data/test/responses/adfs_response_sha1.xml +46 -0
  31. data/test/responses/adfs_response_sha256.xml +46 -0
  32. data/test/responses/adfs_response_sha384.xml +46 -0
  33. data/test/responses/adfs_response_sha512.xml +46 -0
  34. data/test/responses/logoutresponse_fixtures.rb +67 -0
  35. data/test/responses/no_signature_ns.xml +48 -0
  36. data/test/responses/open_saml_response.xml +56 -0
  37. data/test/responses/r1_response6.xml.base64 +1 -0
  38. data/test/responses/response1.xml.base64 +1 -0
  39. data/test/responses/response2.xml.base64 +79 -0
  40. data/test/responses/response3.xml.base64 +66 -0
  41. data/test/responses/response4.xml.base64 +93 -0
  42. data/test/responses/response5.xml.base64 +102 -0
  43. data/test/responses/response_with_ampersands.xml +139 -0
  44. data/test/responses/response_with_ampersands.xml.base64 +93 -0
  45. data/test/responses/simple_saml_php.xml +71 -0
  46. data/test/responses/starfield_response.xml.base64 +1 -0
  47. data/test/responses/wrapped_response_2.xml.base64 +150 -0
  48. data/test/settings_test.rb +46 -0
  49. data/test/test_helper.rb +75 -0
  50. data/test/xml_security_test.rb +160 -0
  51. metadata +189 -0
@@ -0,0 +1,186 @@
1
+ require "xml_security"
2
+ require "time"
3
+ require "nokogiri"
4
+
5
+ # Only supports SAML 2.0
6
+ module Onelogin
7
+ module Saml
8
+
9
+ class Response
10
+ ASSERTION = "urn:oasis:names:tc:SAML:2.0:assertion"
11
+ PROTOCOL = "urn:oasis:names:tc:SAML:2.0:protocol"
12
+ DSIG = "http://www.w3.org/2000/09/xmldsig#"
13
+
14
+ # TODO: This should probably be ctor initialized too... WDYT?
15
+ attr_accessor :settings
16
+
17
+ attr_reader :options
18
+ attr_reader :response
19
+ attr_reader :document
20
+
21
+ def initialize(response, options = {})
22
+ raise ArgumentError.new("Response cannot be nil") if response.nil?
23
+ @options = options
24
+ @response = (response =~ /^</) ? response : Base64.decode64(response)
25
+ @document = XMLSecurity::SignedDocument.new(@response)
26
+ end
27
+
28
+ def is_valid?
29
+ validate
30
+ end
31
+
32
+ def validate!
33
+ validate(false)
34
+ end
35
+
36
+ # The value of the user identifier as designated by the initialization request response
37
+ def name_id
38
+ @name_id ||= begin
39
+ node = xpath_first_from_signed_assertion('/a:Subject/a:NameID')
40
+ node.nil? ? nil : node.text
41
+ end
42
+ end
43
+
44
+ def sessionindex
45
+ @sessionindex ||= begin
46
+ node = xpath_first_from_signed_assertion('/a:AuthnStatement')
47
+ node.nil? ? nil : node.attributes['SessionIndex']
48
+ end
49
+ end
50
+
51
+ # A hash of alle the attributes with the response. Assuming there is only one value for each key
52
+ def attributes
53
+ @attr_statements ||= begin
54
+ result = {}
55
+
56
+ stmt_element = xpath_first_from_signed_assertion('/a:AttributeStatement')
57
+ return {} if stmt_element.nil?
58
+
59
+ stmt_element.elements.each do |attr_element|
60
+ name = attr_element.attributes["Name"]
61
+ value = attr_element.elements.first.text
62
+
63
+ result[name] = value
64
+ end
65
+
66
+ result.keys.each do |key|
67
+ result[key.intern] = result[key]
68
+ end
69
+
70
+ result
71
+ end
72
+ end
73
+
74
+ # When this user session should expire at latest
75
+ def session_expires_at
76
+ @expires_at ||= begin
77
+ node = xpath_first_from_signed_assertion('/a:AuthnStatement')
78
+ parse_time(node, "SessionNotOnOrAfter")
79
+ end
80
+ end
81
+
82
+ # Checks the status of the response for a "Success" code
83
+ def success?
84
+ @status_code ||= begin
85
+ node = REXML::XPath.first(document, "/p:Response/p:Status/p:StatusCode", { "p" => PROTOCOL, "a" => ASSERTION })
86
+ node.attributes["Value"] == "urn:oasis:names:tc:SAML:2.0:status:Success"
87
+ end
88
+ end
89
+
90
+ # Conditions (if any) for the assertion to run
91
+ def conditions
92
+ @conditions ||= xpath_first_from_signed_assertion('/a:Conditions')
93
+ end
94
+
95
+ def issuer
96
+ @issuer ||= begin
97
+ node = REXML::XPath.first(document, "/p:Response/a:Issuer", { "p" => PROTOCOL, "a" => ASSERTION })
98
+ node ||= xpath_first_from_signed_assertion('/a:Issuer')
99
+ node.nil? ? nil : node.text
100
+ end
101
+ end
102
+
103
+ private
104
+
105
+ def validation_error(message)
106
+ raise ValidationError.new(message)
107
+ end
108
+
109
+ def validate(soft = true)
110
+ validate_structure(soft) &&
111
+ validate_response_state(soft) &&
112
+ validate_conditions(soft) &&
113
+ document.validate(get_fingerprint, soft) &&
114
+ success?
115
+ end
116
+
117
+ def validate_structure(soft = true)
118
+ Dir.chdir(File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'schemas'))) do
119
+ @schema = Nokogiri::XML::Schema(IO.read('saml20protocol_schema.xsd'))
120
+ @xml = Nokogiri::XML(self.document.to_s)
121
+ end
122
+ if soft
123
+ @schema.validate(@xml).map{ return false }
124
+ else
125
+ @schema.validate(@xml).map{ |error| validation_error("#{error.message}\n\n#{@xml.to_s}") }
126
+ end
127
+ end
128
+
129
+ def validate_response_state(soft = true)
130
+ if response.empty?
131
+ return soft ? false : validation_error("Blank response")
132
+ end
133
+
134
+ if settings.nil?
135
+ return soft ? false : validation_error("No settings on response")
136
+ end
137
+
138
+ if settings.idp_cert_fingerprint.nil? && settings.idp_cert.nil?
139
+ return soft ? false : validation_error("No fingerprint or certificate on settings")
140
+ end
141
+
142
+ true
143
+ end
144
+
145
+ def xpath_first_from_signed_assertion(subelt=nil)
146
+ node = REXML::XPath.first(document, "/p:Response/a:Assertion[@ID='#{document.signed_element_id}']#{subelt}", { "p" => PROTOCOL, "a" => ASSERTION })
147
+ node ||= REXML::XPath.first(document, "/p:Response[@ID='#{document.signed_element_id}']/a:Assertion#{subelt}", { "p" => PROTOCOL, "a" => ASSERTION })
148
+ node
149
+ end
150
+
151
+ def get_fingerprint
152
+ if settings.idp_cert
153
+ cert = OpenSSL::X509::Certificate.new(settings.idp_cert)
154
+ Digest::SHA1.hexdigest(cert.to_der).upcase.scan(/../).join(":")
155
+ else
156
+ settings.idp_cert_fingerprint
157
+ end
158
+ end
159
+
160
+ def validate_conditions(soft = true)
161
+ return true if conditions.nil?
162
+ return true if options[:skip_conditions]
163
+
164
+ if (not_before = parse_time(conditions, "NotBefore"))
165
+ if Time.now.utc < not_before
166
+ return soft ? false : validation_error("Current time is earlier than NotBefore condition")
167
+ end
168
+ end
169
+
170
+ if (not_on_or_after = parse_time(conditions, "NotOnOrAfter"))
171
+ if Time.now.utc >= not_on_or_after
172
+ return soft ? false : validation_error("Current time is on or after NotOnOrAfter condition")
173
+ end
174
+ end
175
+
176
+ true
177
+ end
178
+
179
+ def parse_time(node, attribute)
180
+ if node && node.attributes[attribute]
181
+ Time.parse(node.attributes[attribute])
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,27 @@
1
+ module Onelogin
2
+ module Saml
3
+ class Settings
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
+ end
11
+ attr_accessor :assertion_consumer_service_url, :issuer, :sp_name_qualifier
12
+ attr_accessor :idp_sso_target_url, :idp_cert_fingerprint, :idp_cert, :name_identifier_format
13
+ attr_accessor :authn_context
14
+ attr_accessor :idp_slo_target_url
15
+ attr_accessor :name_identifier_value
16
+ attr_accessor :sessionindex
17
+ attr_accessor :assertion_consumer_logout_service_url
18
+ attr_accessor :compress_request
19
+ attr_accessor :double_quote_xml_attribute_values
20
+ attr_accessor :passive
21
+
22
+ private
23
+
24
+ DEFAULTS = {:compress_request => true, :double_quote_xml_attribute_values => false}
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,7 @@
1
+ module Onelogin
2
+ module Saml
3
+ class ValidationError < StandardError
4
+ end
5
+ end
6
+ end
7
+
@@ -0,0 +1,5 @@
1
+ module Onelogin
2
+ module Saml
3
+ VERSION = '0.7.21'
4
+ end
5
+ end
data/lib/ruby-saml.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'onelogin/ruby-saml/logging'
2
+ require 'onelogin/ruby-saml/authrequest'
3
+ require 'onelogin/ruby-saml/logoutrequest'
4
+ require 'onelogin/ruby-saml/logoutresponse'
5
+ require 'onelogin/ruby-saml/response'
6
+ require 'onelogin/ruby-saml/settings'
7
+ require 'onelogin/ruby-saml/validation_error'
8
+ require 'onelogin/ruby-saml/metadata'
9
+ require 'onelogin/ruby-saml/version'
@@ -0,0 +1,283 @@
1
+ <?xml version="1.0" encoding="US-ASCII"?>
2
+ <schema
3
+ targetNamespace="urn:oasis:names:tc:SAML:2.0:assertion"
4
+ xmlns="http://www.w3.org/2001/XMLSchema"
5
+ xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
6
+ xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
7
+ xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"
8
+ elementFormDefault="unqualified"
9
+ attributeFormDefault="unqualified"
10
+ blockDefault="substitution"
11
+ version="2.0">
12
+ <import namespace="http://www.w3.org/2000/09/xmldsig#"
13
+ schemaLocation="xmldsig_schema.xsd"/>
14
+ <import namespace="http://www.w3.org/2001/04/xmlenc#"
15
+ schemaLocation="xenc_schema.xsd"/>
16
+ <annotation>
17
+ <documentation>
18
+ Document identifier: saml-schema-assertion-2.0
19
+ Location: http://docs.oasis-open.org/security/saml/v2.0/
20
+ Revision history:
21
+ V1.0 (November, 2002):
22
+ Initial Standard Schema.
23
+ V1.1 (September, 2003):
24
+ Updates within the same V1.0 namespace.
25
+ V2.0 (March, 2005):
26
+ New assertion schema for SAML V2.0 namespace.
27
+ </documentation>
28
+ </annotation>
29
+ <attributeGroup name="IDNameQualifiers">
30
+ <attribute name="NameQualifier" type="string" use="optional"/>
31
+ <attribute name="SPNameQualifier" type="string" use="optional"/>
32
+ </attributeGroup>
33
+ <element name="BaseID" type="saml:BaseIDAbstractType"/>
34
+ <complexType name="BaseIDAbstractType" abstract="true">
35
+ <attributeGroup ref="saml:IDNameQualifiers"/>
36
+ </complexType>
37
+ <element name="NameID" type="saml:NameIDType"/>
38
+ <complexType name="NameIDType">
39
+ <simpleContent>
40
+ <extension base="string">
41
+ <attributeGroup ref="saml:IDNameQualifiers"/>
42
+ <attribute name="Format" type="anyURI" use="optional"/>
43
+ <attribute name="SPProvidedID" type="string" use="optional"/>
44
+ </extension>
45
+ </simpleContent>
46
+ </complexType>
47
+ <complexType name="EncryptedElementType">
48
+ <sequence>
49
+ <element ref="xenc:EncryptedData"/>
50
+ <element ref="xenc:EncryptedKey" minOccurs="0" maxOccurs="unbounded"/>
51
+ </sequence>
52
+ </complexType>
53
+ <element name="EncryptedID" type="saml:EncryptedElementType"/>
54
+ <element name="Issuer" type="saml:NameIDType"/>
55
+ <element name="AssertionIDRef" type="NCName"/>
56
+ <element name="AssertionURIRef" type="anyURI"/>
57
+ <element name="Assertion" type="saml:AssertionType"/>
58
+ <complexType name="AssertionType">
59
+ <sequence>
60
+ <element ref="saml:Issuer"/>
61
+ <element ref="ds:Signature" minOccurs="0"/>
62
+ <element ref="saml:Subject" minOccurs="0"/>
63
+ <element ref="saml:Conditions" minOccurs="0"/>
64
+ <element ref="saml:Advice" minOccurs="0"/>
65
+ <choice minOccurs="0" maxOccurs="unbounded">
66
+ <element ref="saml:Statement"/>
67
+ <element ref="saml:AuthnStatement"/>
68
+ <element ref="saml:AuthzDecisionStatement"/>
69
+ <element ref="saml:AttributeStatement"/>
70
+ </choice>
71
+ </sequence>
72
+ <attribute name="Version" type="string" use="required"/>
73
+ <attribute name="ID" type="ID" use="required"/>
74
+ <attribute name="IssueInstant" type="dateTime" use="required"/>
75
+ </complexType>
76
+ <element name="Subject" type="saml:SubjectType"/>
77
+ <complexType name="SubjectType">
78
+ <choice>
79
+ <sequence>
80
+ <choice>
81
+ <element ref="saml:BaseID"/>
82
+ <element ref="saml:NameID"/>
83
+ <element ref="saml:EncryptedID"/>
84
+ </choice>
85
+ <element ref="saml:SubjectConfirmation" minOccurs="0" maxOccurs="unbounded"/>
86
+ </sequence>
87
+ <element ref="saml:SubjectConfirmation" maxOccurs="unbounded"/>
88
+ </choice>
89
+ </complexType>
90
+ <element name="SubjectConfirmation" type="saml:SubjectConfirmationType"/>
91
+ <complexType name="SubjectConfirmationType">
92
+ <sequence>
93
+ <choice minOccurs="0">
94
+ <element ref="saml:BaseID"/>
95
+ <element ref="saml:NameID"/>
96
+ <element ref="saml:EncryptedID"/>
97
+ </choice>
98
+ <element ref="saml:SubjectConfirmationData" minOccurs="0"/>
99
+ </sequence>
100
+ <attribute name="Method" type="anyURI" use="required"/>
101
+ </complexType>
102
+ <element name="SubjectConfirmationData" type="saml:SubjectConfirmationDataType"/>
103
+ <complexType name="SubjectConfirmationDataType" mixed="true">
104
+ <complexContent>
105
+ <restriction base="anyType">
106
+ <sequence>
107
+ <any namespace="##any" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
108
+ </sequence>
109
+ <attribute name="NotBefore" type="dateTime" use="optional"/>
110
+ <attribute name="NotOnOrAfter" type="dateTime" use="optional"/>
111
+ <attribute name="Recipient" type="anyURI" use="optional"/>
112
+ <attribute name="InResponseTo" type="NCName" use="optional"/>
113
+ <attribute name="Address" type="string" use="optional"/>
114
+ <anyAttribute namespace="##other" processContents="lax"/>
115
+ </restriction>
116
+ </complexContent>
117
+ </complexType>
118
+ <complexType name="KeyInfoConfirmationDataType" mixed="false">
119
+ <complexContent>
120
+ <restriction base="saml:SubjectConfirmationDataType">
121
+ <sequence>
122
+ <element ref="ds:KeyInfo" maxOccurs="unbounded"/>
123
+ </sequence>
124
+ </restriction>
125
+ </complexContent>
126
+ </complexType>
127
+ <element name="Conditions" type="saml:ConditionsType"/>
128
+ <complexType name="ConditionsType">
129
+ <choice minOccurs="0" maxOccurs="unbounded">
130
+ <element ref="saml:Condition"/>
131
+ <element ref="saml:AudienceRestriction"/>
132
+ <element ref="saml:OneTimeUse"/>
133
+ <element ref="saml:ProxyRestriction"/>
134
+ </choice>
135
+ <attribute name="NotBefore" type="dateTime" use="optional"/>
136
+ <attribute name="NotOnOrAfter" type="dateTime" use="optional"/>
137
+ </complexType>
138
+ <element name="Condition" type="saml:ConditionAbstractType"/>
139
+ <complexType name="ConditionAbstractType" abstract="true"/>
140
+ <element name="AudienceRestriction" type="saml:AudienceRestrictionType"/>
141
+ <complexType name="AudienceRestrictionType">
142
+ <complexContent>
143
+ <extension base="saml:ConditionAbstractType">
144
+ <sequence>
145
+ <element ref="saml:Audience" maxOccurs="unbounded"/>
146
+ </sequence>
147
+ </extension>
148
+ </complexContent>
149
+ </complexType>
150
+ <element name="Audience" type="anyURI"/>
151
+ <element name="OneTimeUse" type="saml:OneTimeUseType" />
152
+ <complexType name="OneTimeUseType">
153
+ <complexContent>
154
+ <extension base="saml:ConditionAbstractType"/>
155
+ </complexContent>
156
+ </complexType>
157
+ <element name="ProxyRestriction" type="saml:ProxyRestrictionType"/>
158
+ <complexType name="ProxyRestrictionType">
159
+ <complexContent>
160
+ <extension base="saml:ConditionAbstractType">
161
+ <sequence>
162
+ <element ref="saml:Audience" minOccurs="0" maxOccurs="unbounded"/>
163
+ </sequence>
164
+ <attribute name="Count" type="nonNegativeInteger" use="optional"/>
165
+ </extension>
166
+ </complexContent>
167
+ </complexType>
168
+ <element name="Advice" type="saml:AdviceType"/>
169
+ <complexType name="AdviceType">
170
+ <choice minOccurs="0" maxOccurs="unbounded">
171
+ <element ref="saml:AssertionIDRef"/>
172
+ <element ref="saml:AssertionURIRef"/>
173
+ <element ref="saml:Assertion"/>
174
+ <element ref="saml:EncryptedAssertion"/>
175
+ <any namespace="##other" processContents="lax"/>
176
+ </choice>
177
+ </complexType>
178
+ <element name="EncryptedAssertion" type="saml:EncryptedElementType"/>
179
+ <element name="Statement" type="saml:StatementAbstractType"/>
180
+ <complexType name="StatementAbstractType" abstract="true"/>
181
+ <element name="AuthnStatement" type="saml:AuthnStatementType"/>
182
+ <complexType name="AuthnStatementType">
183
+ <complexContent>
184
+ <extension base="saml:StatementAbstractType">
185
+ <sequence>
186
+ <element ref="saml:SubjectLocality" minOccurs="0"/>
187
+ <element ref="saml:AuthnContext"/>
188
+ </sequence>
189
+ <attribute name="AuthnInstant" type="dateTime" use="required"/>
190
+ <attribute name="SessionIndex" type="string" use="optional"/>
191
+ <attribute name="SessionNotOnOrAfter" type="dateTime" use="optional"/>
192
+ </extension>
193
+ </complexContent>
194
+ </complexType>
195
+ <element name="SubjectLocality" type="saml:SubjectLocalityType"/>
196
+ <complexType name="SubjectLocalityType">
197
+ <attribute name="Address" type="string" use="optional"/>
198
+ <attribute name="DNSName" type="string" use="optional"/>
199
+ </complexType>
200
+ <element name="AuthnContext" type="saml:AuthnContextType"/>
201
+ <complexType name="AuthnContextType">
202
+ <sequence>
203
+ <choice>
204
+ <sequence>
205
+ <element ref="saml:AuthnContextClassRef"/>
206
+ <choice minOccurs="0">
207
+ <element ref="saml:AuthnContextDecl"/>
208
+ <element ref="saml:AuthnContextDeclRef"/>
209
+ </choice>
210
+ </sequence>
211
+ <choice>
212
+ <element ref="saml:AuthnContextDecl"/>
213
+ <element ref="saml:AuthnContextDeclRef"/>
214
+ </choice>
215
+ </choice>
216
+ <element ref="saml:AuthenticatingAuthority" minOccurs="0" maxOccurs="unbounded"/>
217
+ </sequence>
218
+ </complexType>
219
+ <element name="AuthnContextClassRef" type="anyURI"/>
220
+ <element name="AuthnContextDeclRef" type="anyURI"/>
221
+ <element name="AuthnContextDecl" type="anyType"/>
222
+ <element name="AuthenticatingAuthority" type="anyURI"/>
223
+ <element name="AuthzDecisionStatement" type="saml:AuthzDecisionStatementType"/>
224
+ <complexType name="AuthzDecisionStatementType">
225
+ <complexContent>
226
+ <extension base="saml:StatementAbstractType">
227
+ <sequence>
228
+ <element ref="saml:Action" maxOccurs="unbounded"/>
229
+ <element ref="saml:Evidence" minOccurs="0"/>
230
+ </sequence>
231
+ <attribute name="Resource" type="anyURI" use="required"/>
232
+ <attribute name="Decision" type="saml:DecisionType" use="required"/>
233
+ </extension>
234
+ </complexContent>
235
+ </complexType>
236
+ <simpleType name="DecisionType">
237
+ <restriction base="string">
238
+ <enumeration value="Permit"/>
239
+ <enumeration value="Deny"/>
240
+ <enumeration value="Indeterminate"/>
241
+ </restriction>
242
+ </simpleType>
243
+ <element name="Action" type="saml:ActionType"/>
244
+ <complexType name="ActionType">
245
+ <simpleContent>
246
+ <extension base="string">
247
+ <attribute name="Namespace" type="anyURI" use="required"/>
248
+ </extension>
249
+ </simpleContent>
250
+ </complexType>
251
+ <element name="Evidence" type="saml:EvidenceType"/>
252
+ <complexType name="EvidenceType">
253
+ <choice maxOccurs="unbounded">
254
+ <element ref="saml:AssertionIDRef"/>
255
+ <element ref="saml:AssertionURIRef"/>
256
+ <element ref="saml:Assertion"/>
257
+ <element ref="saml:EncryptedAssertion"/>
258
+ </choice>
259
+ </complexType>
260
+ <element name="AttributeStatement" type="saml:AttributeStatementType"/>
261
+ <complexType name="AttributeStatementType">
262
+ <complexContent>
263
+ <extension base="saml:StatementAbstractType">
264
+ <choice maxOccurs="unbounded">
265
+ <element ref="saml:Attribute"/>
266
+ <element ref="saml:EncryptedAttribute"/>
267
+ </choice>
268
+ </extension>
269
+ </complexContent>
270
+ </complexType>
271
+ <element name="Attribute" type="saml:AttributeType"/>
272
+ <complexType name="AttributeType">
273
+ <sequence>
274
+ <element ref="saml:AttributeValue" minOccurs="0" maxOccurs="unbounded"/>
275
+ </sequence>
276
+ <attribute name="Name" type="string" use="required"/>
277
+ <attribute name="NameFormat" type="anyURI" use="optional"/>
278
+ <attribute name="FriendlyName" type="string" use="optional"/>
279
+ <anyAttribute namespace="##other" processContents="lax"/>
280
+ </complexType>
281
+ <element name="AttributeValue" type="anyType" nillable="true"/>
282
+ <element name="EncryptedAttribute" type="saml:EncryptedElementType"/>
283
+ </schema>