ruby-saml-federa 0.0.2

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 (48) hide show
  1. data/.document +5 -0
  2. data/.gitignore +10 -0
  3. data/.travis.yml +5 -0
  4. data/Gemfile +12 -0
  5. data/LICENSE +19 -0
  6. data/README.md +124 -0
  7. data/Rakefile +41 -0
  8. data/lib/federa/ruby-saml/authrequest.rb +181 -0
  9. data/lib/federa/ruby-saml/coding.rb +34 -0
  10. data/lib/federa/ruby-saml/logging.rb +26 -0
  11. data/lib/federa/ruby-saml/logout_request.rb +126 -0
  12. data/lib/federa/ruby-saml/logout_response.rb +132 -0
  13. data/lib/federa/ruby-saml/metadata.rb +266 -0
  14. data/lib/federa/ruby-saml/request.rb +81 -0
  15. data/lib/federa/ruby-saml/response.rb +203 -0
  16. data/lib/federa/ruby-saml/settings.rb +28 -0
  17. data/lib/federa/ruby-saml/validation_error.rb +7 -0
  18. data/lib/federa/ruby-saml/version.rb +5 -0
  19. data/lib/ruby-saml-federa.rb +11 -0
  20. data/lib/schemas/saml20assertion_schema.xsd +283 -0
  21. data/lib/schemas/saml20protocol_schema.xsd +302 -0
  22. data/lib/schemas/xenc_schema.xsd +146 -0
  23. data/lib/schemas/xmldsig_schema.xsd +318 -0
  24. data/lib/xml_security.rb +165 -0
  25. data/ruby-saml-federa.gemspec +21 -0
  26. data/test/certificates/certificate1 +12 -0
  27. data/test/logoutrequest_test.rb +98 -0
  28. data/test/request_test.rb +53 -0
  29. data/test/response_test.rb +219 -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/no_signature_ns.xml +48 -0
  35. data/test/responses/open_saml_response.xml +56 -0
  36. data/test/responses/response1.xml.base64 +1 -0
  37. data/test/responses/response2.xml.base64 +79 -0
  38. data/test/responses/response3.xml.base64 +66 -0
  39. data/test/responses/response4.xml.base64 +93 -0
  40. data/test/responses/response5.xml.base64 +102 -0
  41. data/test/responses/response_with_ampersands.xml +139 -0
  42. data/test/responses/response_with_ampersands.xml.base64 +93 -0
  43. data/test/responses/simple_saml_php.xml +71 -0
  44. data/test/responses/wrapped_response_2.xml.base64 +150 -0
  45. data/test/settings_test.rb +43 -0
  46. data/test/test_helper.rb +66 -0
  47. data/test/xml_security_test.rb +123 -0
  48. metadata +155 -0
@@ -0,0 +1,81 @@
1
+
2
+ # A few helper functions for assembling a SAMLRequest and
3
+ # sending it to the IdP
4
+ module Federa::Saml
5
+ include Coding
6
+ module Request
7
+
8
+ # a few symbols for SAML class names
9
+ HTTP_POST = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
10
+ HTTP_GET = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
11
+ # get the IdP metadata, and select the appropriate SSO binding
12
+ # that we can support. Currently this is HTTP-Redirect and HTTP-POST
13
+ # but more could be added in the future
14
+ def binding_select(service)
15
+ # first check if we're still using the old hard coded method for
16
+ # backwards compatability
17
+ if @settings.idp_metadata == nil && @settings.idp_sso_target_url != nil
18
+ @URL = @settings.idp_sso_target_url
19
+ return "GET", content_get
20
+ end
21
+ # grab the metadata
22
+ metadata = Metadata::new
23
+ meta_doc = metadata.get_idp_metadata(@settings)
24
+
25
+ # first try POST
26
+ sso_element = REXML::XPath.first(meta_doc,
27
+ "/EntityDescriptor/IDPSSODescriptor/#{service}[@Binding='#{HTTP_POST}']")
28
+ if sso_element
29
+ @URL = sso_element.attributes["Location"]
30
+ #Logging.debug "binding_select: POST to #{@URL}"
31
+ return "POST", content_post
32
+ end
33
+
34
+ # next try GET
35
+ sso_element = REXML::XPath.first(meta_doc,
36
+ "/EntityDescriptor/IDPSSODescriptor/#{service}[@Binding='#{HTTP_GET}']")
37
+ if sso_element
38
+ @URL = sso_element.attributes["Location"]
39
+ Logging.debug "binding_select: GET from #{@URL}"
40
+ return "GET", content_get
41
+ end
42
+ # other types we might want to add in the future: SOAP, Artifact
43
+ end
44
+
45
+ # construct the the parameter list on the URL and return
46
+ def content_get
47
+ # compress GET requests to try and stay under that 8KB request limit
48
+ deflated_request = Zlib::Deflate.deflate(@request, 9)[2..-5]
49
+ # strict_encode64() isn't available? sub out the newlines
50
+ @request_params["SAMLRequest"] = Base64.encode64(deflated_request).gsub(/\n/, "")
51
+
52
+ Logging.debug "SAMLRequest=#{@request_params["SAMLRequest"]}"
53
+ uri = Addressable::URI.parse(@URL)
54
+ uri.query_values = @request_params
55
+ url = uri.to_s
56
+ #url = @URL + "?SAMLRequest=" + @request_params["SAMLRequest"]
57
+ Logging.debug "Sending to URL #{url}"
58
+ return url
59
+ end
60
+ # construct an HTML form (POST) and return the content
61
+ def content_post
62
+ # POST requests seem to bomb out when they're deflated
63
+ # and they probably don't need to be compressed anyway
64
+ @request_params["SAMLRequest"] = Base64.encode64(@request).gsub(/\n/, "")
65
+
66
+ #Logging.debug "SAMLRequest=#{@request_params["SAMLRequest"]}"
67
+ # kind of a cheesy method of building an HTML, form since we can't rely on Rails too much,
68
+ # and REXML doesn't work well with quote characters
69
+ str = "<html><body onLoad=\"document.getElementById('form').submit();\">\n"
70
+ str += "<form id='form' name='form' method='POST' action=\"#{@URL}\">\n"
71
+ # we could change this in the future to associate a temp auth session ID
72
+ str += "<input name='RelayState' value='ruby-saml' type='hidden' />\n"
73
+ @request_params.each_pair do |key, value|
74
+ str += "<input name=\"#{key}\" value=\"#{value}\" type='hidden' />\n"
75
+ end
76
+ str += "</form></body></html>\n"
77
+ #Logging.debug "Created form:\n#{str}"
78
+ return str
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,203 @@
1
+ require "xml_security"
2
+ require "time"
3
+ require "nokogiri"
4
+ require 'ruby-debug'
5
+ require "base64"
6
+ require "openssl"
7
+ require "digest/sha1"
8
+
9
+ # Only supports SAML 2.0
10
+ module Federa
11
+ module Saml
12
+
13
+ class Response
14
+ ASSERTION = "urn:oasis:names:tc:SAML:2.0:assertion"
15
+ PROTOCOL = "urn:oasis:names:tc:SAML:2.0:protocol"
16
+ DSIG = "http://www.w3.org/2000/09/xmldsig#"
17
+
18
+ attr_accessor :options, :response, :document, :settings
19
+
20
+ def initialize(response, options = {})
21
+ raise ArgumentError.new("Response cannot be nil") if response.nil?
22
+ self.options = options
23
+ self.response = response
24
+ begin
25
+ self.document = XMLSecurity::SignedDocument.new(Base64.decode64(response))
26
+ rescue REXML::ParseException => e
27
+ if response =~ /</
28
+ self.document = XMLSecurity::SignedDocument.new(response)
29
+ else
30
+ raise e
31
+ end
32
+ end
33
+
34
+ end
35
+
36
+ def is_valid?
37
+ validate
38
+ end
39
+
40
+ def validate!
41
+ validate(false)
42
+ end
43
+
44
+ # The value of the user identifier as designated by the initialization request response
45
+ def name_id
46
+ @name_id ||= begin
47
+ node = REXML::XPath.first(document, "/saml2p:Response/saml2:Assertion[@ID='#{document.signed_element_id}']/saml2:Subject/saml2:NameID")
48
+ node ||= REXML::XPath.first(document, "/saml2p:Response[@ID='#{document.signed_element_id}']/saml2:Assertion/saml2:Subject/saml2:NameID")
49
+ node.nil? ? nil : node.text
50
+ end
51
+ end
52
+
53
+ # A hash of alle the attributes with the response. Assuming there is only one value for each key
54
+ def attributes
55
+ @attr_statements ||= begin
56
+ result = {}
57
+
58
+ stmt_element = REXML::XPath.first(document, "/p:Response/a:Assertion/a:AttributeStatement", { "p" => PROTOCOL, "a" => ASSERTION })
59
+ return {} if stmt_element.nil?
60
+
61
+ stmt_element.elements.each do |attr_element|
62
+ name = attr_element.attributes["Name"]
63
+ value = attr_element.elements.first.text
64
+
65
+ result[name] = value
66
+ end
67
+
68
+ result.keys.each do |key|
69
+ result[key.intern] = result[key]
70
+ end
71
+
72
+ result
73
+ end
74
+ end
75
+
76
+ # When this user session should expire at latest
77
+ def session_expires_at
78
+ @expires_at ||= begin
79
+ node = REXML::XPath.first(document, "/p:Response/a:Assertion/a:AuthnStatement", { "p" => PROTOCOL, "a" => ASSERTION })
80
+ parse_time(node, "SessionNotOnOrAfter")
81
+ end
82
+ end
83
+
84
+ # Checks the status of the response for a "Success" code
85
+ def success?
86
+ @status_code ||= begin
87
+ node = REXML::XPath.first(document, "/p:Response/p:Status/p:StatusCode", { "p" => PROTOCOL, "a" => ASSERTION })
88
+ node.attributes["Value"] == "urn:oasis:names:tc:SAML:2.0:status:Success"
89
+ end
90
+ end
91
+
92
+ # Conditions (if any) for the assertion to run
93
+ def conditions
94
+ @conditions ||= begin
95
+ REXML::XPath.first(document, "/p:Response/a:Assertion[@ID='#{document.signed_element_id}']/a:Conditions", { "p" => PROTOCOL, "a" => ASSERTION })
96
+ end
97
+ end
98
+
99
+ def issuer
100
+ @issuer ||= begin
101
+ node = REXML::XPath.first(document, "/p:Response/a:Issuer", { "p" => PROTOCOL, "a" => ASSERTION })
102
+ node ||= REXML::XPath.first(document, "/p:Response/a:Assertion/a:Issuer", { "p" => PROTOCOL, "a" => ASSERTION })
103
+ node.nil? ? nil : node.text
104
+ end
105
+ end
106
+
107
+ private
108
+
109
+ def validation_error(message)
110
+ raise ValidationError.new(message)
111
+ end
112
+
113
+ def validate(soft = true)
114
+ # prime the IdP metadata before the document validation.
115
+ # The idp_cert needs to be populated before the validate_response_state method
116
+
117
+ if settings
118
+ Federa::Saml::Metadata.new(settings).get_idp_metadata
119
+ end
120
+ return false if validate_structure(soft) == false
121
+ return false if validate_response_state(soft) == false
122
+ return false if validate_conditions(soft) == false
123
+
124
+ # Just in case a user needs to toss out the signature validation,
125
+ # I'm adding in an option for it. (Sometimes canonicalization is a bitch!)
126
+ return true if settings.skip_validation == true
127
+
128
+ # document.validte populates the idp_cert
129
+ return false if document.validate(get_fingerprint, soft) == false
130
+
131
+ # validate response code
132
+ return false if success? == false
133
+
134
+ return true
135
+ end
136
+
137
+
138
+ def validate_structure(soft = true)
139
+ Dir.chdir(File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'schemas'))) do
140
+ @schema = Nokogiri::XML::Schema(IO.read('saml20protocol_schema.xsd'))
141
+ @xml = Nokogiri::XML(self.document.to_s)
142
+ end
143
+ if soft
144
+ @schema.validate(@xml).map{ return false }
145
+ else
146
+ @schema.validate(@xml).map{ |error| raise(Exception.new("#{error.message}\n\n#{@xml.to_s}")) }
147
+ end
148
+ end
149
+
150
+ def validate_response_state(soft = true)
151
+ if response.empty?
152
+ return soft ? false : validation_error("Blank response")
153
+ end
154
+
155
+ if settings.nil?
156
+ return soft ? false : validation_error("No settings on response")
157
+ end
158
+
159
+ if settings.idp_cert_fingerprint.nil? && settings.idp_cert.nil?
160
+ return soft ? false : validation_error("No fingerprint or certificate on settings")
161
+ end
162
+
163
+ true
164
+ end
165
+
166
+ def get_fingerprint
167
+ if settings.idp_cert
168
+ cert_text = Base64.decode64(settings.idp_cert)
169
+ cert = OpenSSL::X509::Certificate.new(cert_text)
170
+ Digest::SHA1.hexdigest(cert.to_der).upcase.scan(/../).join(":")
171
+ else
172
+ settings.idp_cert_fingerprint
173
+ end
174
+
175
+ end
176
+
177
+ def validate_conditions(soft = true)
178
+ return true if conditions.nil?
179
+ return true if options[:skip_conditions]
180
+
181
+ if not_before = parse_time(conditions, "NotBefore")
182
+ if Time.now.utc < not_before
183
+ return soft ? false : validation_error("Current time is earlier than NotBefore condition")
184
+ end
185
+ end
186
+
187
+ if not_on_or_after = parse_time(conditions, "NotOnOrAfter")
188
+ if Time.now.utc >= not_on_or_after
189
+ return soft ? false : validation_error("Current time is on or after NotOnOrAfter condition")
190
+ end
191
+ end
192
+
193
+ true
194
+ end
195
+
196
+ def parse_time(node, attribute)
197
+ if node && node.attributes[attribute]
198
+ Time.parse(node.attributes[attribute])
199
+ end
200
+ end
201
+ end
202
+ end
203
+ end
@@ -0,0 +1,28 @@
1
+ module Federa
2
+ module Saml
3
+ class Settings
4
+
5
+ attr_accessor :sp_name_qualifier, :sp_cert
6
+ attr_accessor :idp_sso_target_url, :idp_cert_fingerprint, :idp_cert, :idp_slo_target_url, :idp_metadata, :idp_metadata_ttl, :idp_name_qualifier
7
+ attr_accessor :assertion_consumer_service_binding, :assertion_consumer_service_url
8
+ attr_accessor :name_identifier_value, :name_identifier_format
9
+ attr_accessor :sessionindex, :issuer, :destination_service_url, :authn_context, :requester_identificator
10
+ attr_accessor :single_logout_service_url, :single_logout_service_binding, :single_logout_destination
11
+ attr_accessor :skip_validation
12
+
13
+ def initialize(config = {})
14
+ config.each do |k,v|
15
+ acc = "#{k.to_s}=".to_sym
16
+ self.send(acc, v) if self.respond_to? acc
17
+ end
18
+
19
+ # Set some sane default values on a few options
20
+ self.assertion_consumer_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
21
+ self.single_logout_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
22
+ # Default cache TTL for metadata is 1 day
23
+ self.idp_metadata_ttl = 86400
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,7 @@
1
+ module Federa
2
+ module Saml
3
+ class ValidationError < Exception
4
+ end
5
+ end
6
+ end
7
+
@@ -0,0 +1,5 @@
1
+ module Federa
2
+ module Saml
3
+ VERSION = '0.6.0'
4
+ end
5
+ end
@@ -0,0 +1,11 @@
1
+ require 'federa/ruby-saml/logging'
2
+ require 'federa/ruby-saml/coding'
3
+ require 'federa/ruby-saml/request'
4
+ require 'federa/ruby-saml/authrequest'
5
+ require 'federa/ruby-saml/logout_request'
6
+ require 'federa/ruby-saml/logout_response'
7
+ require 'federa/ruby-saml/response'
8
+ require 'federa/ruby-saml/settings'
9
+ require 'federa/ruby-saml/validation_error'
10
+ require 'federa/ruby-saml/metadata'
11
+ require 'federa/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>