maestrano 0.1.0
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 +7 -0
 - data/.gitignore +34 -0
 - data/Gemfile +2 -0
 - data/Gemfile.lock +43 -0
 - data/LICENSE +21 -0
 - data/README.md +4 -0
 - data/Rakefile +32 -0
 - data/bin/maestrano-console +9 -0
 - data/lib/maestrano.rb +114 -0
 - data/lib/maestrano/account/bill.rb +14 -0
 - data/lib/maestrano/api/error/authentication_error.rb +8 -0
 - data/lib/maestrano/api/error/base_error.rb +24 -0
 - data/lib/maestrano/api/error/connection_error.rb +8 -0
 - data/lib/maestrano/api/error/invalid_request_error.rb +14 -0
 - data/lib/maestrano/api/list_object.rb +37 -0
 - data/lib/maestrano/api/object.rb +187 -0
 - data/lib/maestrano/api/operation/base.rb +216 -0
 - data/lib/maestrano/api/operation/create.rb +18 -0
 - data/lib/maestrano/api/operation/delete.rb +13 -0
 - data/lib/maestrano/api/operation/list.rb +18 -0
 - data/lib/maestrano/api/operation/update.rb +59 -0
 - data/lib/maestrano/api/resource.rb +39 -0
 - data/lib/maestrano/api/util.rb +121 -0
 - data/lib/maestrano/saml/attribute_value.rb +15 -0
 - data/lib/maestrano/saml/metadata.rb +64 -0
 - data/lib/maestrano/saml/request.rb +93 -0
 - data/lib/maestrano/saml/response.rb +201 -0
 - data/lib/maestrano/saml/schemas/saml20assertion_schema.xsd +283 -0
 - data/lib/maestrano/saml/schemas/saml20protocol_schema.xsd +302 -0
 - data/lib/maestrano/saml/schemas/xenc_schema.xsd +146 -0
 - data/lib/maestrano/saml/schemas/xmldsig_schema.xsd +318 -0
 - data/lib/maestrano/saml/settings.rb +37 -0
 - data/lib/maestrano/saml/validation_error.rb +7 -0
 - data/lib/maestrano/sso.rb +81 -0
 - data/lib/maestrano/sso/base_group.rb +31 -0
 - data/lib/maestrano/sso/base_user.rb +75 -0
 - data/lib/maestrano/sso/group.rb +24 -0
 - data/lib/maestrano/sso/session.rb +63 -0
 - data/lib/maestrano/sso/user.rb +34 -0
 - data/lib/maestrano/version.rb +3 -0
 - data/lib/maestrano/xml_security/signed_document.rb +170 -0
 - data/maestrano.gemspec +32 -0
 - data/test/helpers/api_helpers.rb +82 -0
 - data/test/helpers/saml_helpers.rb +62 -0
 - data/test/maestrano/account/bill_test.rb +48 -0
 - data/test/maestrano/api/list_object_test.rb +20 -0
 - data/test/maestrano/api/object_test.rb +28 -0
 - data/test/maestrano/api/resource_test.rb +343 -0
 - data/test/maestrano/api/util_test.rb +31 -0
 - data/test/maestrano/maestrano_test.rb +49 -0
 - data/test/maestrano/saml/request_test.rb +168 -0
 - data/test/maestrano/saml/response_test.rb +290 -0
 - data/test/maestrano/saml/settings_test.rb +51 -0
 - data/test/maestrano/sso/base_group_test.rb +54 -0
 - data/test/maestrano/sso/base_user_test.rb +114 -0
 - data/test/maestrano/sso/group_test.rb +47 -0
 - data/test/maestrano/sso/session_test.rb +108 -0
 - data/test/maestrano/sso/user_test.rb +65 -0
 - data/test/maestrano/sso_test.rb +81 -0
 - data/test/maestrano/xml_security/signed_document.rb +163 -0
 - data/test/support/saml/certificates/certificate1 +12 -0
 - data/test/support/saml/certificates/r1_certificate2_base64 +1 -0
 - data/test/support/saml/responses/adfs_response_sha1.xml +46 -0
 - data/test/support/saml/responses/adfs_response_sha256.xml +46 -0
 - data/test/support/saml/responses/adfs_response_sha384.xml +46 -0
 - data/test/support/saml/responses/adfs_response_sha512.xml +46 -0
 - data/test/support/saml/responses/no_signature_ns.xml +48 -0
 - data/test/support/saml/responses/open_saml_response.xml +56 -0
 - data/test/support/saml/responses/r1_response6.xml.base64 +1 -0
 - data/test/support/saml/responses/response1.xml.base64 +1 -0
 - data/test/support/saml/responses/response2.xml.base64 +79 -0
 - data/test/support/saml/responses/response3.xml.base64 +66 -0
 - data/test/support/saml/responses/response4.xml.base64 +93 -0
 - data/test/support/saml/responses/response5.xml.base64 +102 -0
 - data/test/support/saml/responses/response_with_ampersands.xml +139 -0
 - data/test/support/saml/responses/response_with_ampersands.xml.base64 +93 -0
 - data/test/support/saml/responses/response_with_multiple_attribute_values.xml +57 -0
 - data/test/support/saml/responses/simple_saml_php.xml +71 -0
 - data/test/support/saml/responses/starfield_response.xml.base64 +1 -0
 - data/test/support/saml/responses/wrapped_response_2.xml.base64 +150 -0
 - data/test/test_helper.rb +46 -0
 - metadata +305 -0
 
| 
         @@ -0,0 +1,15 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Maestrano
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Saml
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
                # Wrapper for AttributeValue with multiple values
         
     | 
| 
      
 5 
     | 
    
         
            +
                # It is subclass of String to be backwards compatible
         
     | 
| 
      
 6 
     | 
    
         
            +
                # Use AttributeValue#values to get all values as an array
         
     | 
| 
      
 7 
     | 
    
         
            +
                class AttributeValue < String
         
     | 
| 
      
 8 
     | 
    
         
            +
                  attr_accessor :values
         
     | 
| 
      
 9 
     | 
    
         
            +
                  def initialize(str="", values=[])
         
     | 
| 
      
 10 
     | 
    
         
            +
                    @values = values
         
     | 
| 
      
 11 
     | 
    
         
            +
                    super(str)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  end
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
              end
         
     | 
| 
      
 15 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,64 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "rexml/document"
         
     | 
| 
      
 2 
     | 
    
         
            +
            require "rexml/xpath"
         
     | 
| 
      
 3 
     | 
    
         
            +
            require "uri"
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            # Class to return SP metadata based on the settings requested.
         
     | 
| 
      
 6 
     | 
    
         
            +
            # Return this XML in a controller, then give that URL to the the
         
     | 
| 
      
 7 
     | 
    
         
            +
            # IdP administrator.  The IdP will poll the URL and your settings
         
     | 
| 
      
 8 
     | 
    
         
            +
            # will be updated automatically
         
     | 
| 
      
 9 
     | 
    
         
            +
            module Maestrano
         
     | 
| 
      
 10 
     | 
    
         
            +
              module Saml
         
     | 
| 
      
 11 
     | 
    
         
            +
                include REXML
         
     | 
| 
      
 12 
     | 
    
         
            +
                class Metadata
         
     | 
| 
      
 13 
     | 
    
         
            +
                  def generate(settings)
         
     | 
| 
      
 14 
     | 
    
         
            +
                    meta_doc = REXML::Document.new
         
     | 
| 
      
 15 
     | 
    
         
            +
                    root = meta_doc.add_element "md:EntityDescriptor", {
         
     | 
| 
      
 16 
     | 
    
         
            +
                        "xmlns:md" => "urn:oasis:names:tc:SAML:2.0:metadata"
         
     | 
| 
      
 17 
     | 
    
         
            +
                    }
         
     | 
| 
      
 18 
     | 
    
         
            +
                    sp_sso = root.add_element "md:SPSSODescriptor", {
         
     | 
| 
      
 19 
     | 
    
         
            +
                        "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 
     | 
    
         
            +
                        # 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 
     | 
    
         
            +
                    }
         
     | 
| 
      
 25 
     | 
    
         
            +
                    if settings.issuer != nil
         
     | 
| 
      
 26 
     | 
    
         
            +
                      root.attributes["entityID"] = settings.issuer
         
     | 
| 
      
 27 
     | 
    
         
            +
                    end
         
     | 
| 
      
 28 
     | 
    
         
            +
                    if settings.assertion_consumer_logout_service_url != nil
         
     | 
| 
      
 29 
     | 
    
         
            +
                      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",
         
     | 
| 
      
 32 
     | 
    
         
            +
                          "Location" => settings.assertion_consumer_logout_service_url,
         
     | 
| 
      
 33 
     | 
    
         
            +
                          "ResponseLocation" => settings.assertion_consumer_logout_service_url,
         
     | 
| 
      
 34 
     | 
    
         
            +
                          "isDefault" => true,
         
     | 
| 
      
 35 
     | 
    
         
            +
                          "index" => 0
         
     | 
| 
      
 36 
     | 
    
         
            +
                      }
         
     | 
| 
      
 37 
     | 
    
         
            +
                    end
         
     | 
| 
      
 38 
     | 
    
         
            +
                    if settings.name_identifier_format != nil
         
     | 
| 
      
 39 
     | 
    
         
            +
                      name_id = sp_sso.add_element "md:NameIDFormat"
         
     | 
| 
      
 40 
     | 
    
         
            +
                      name_id.text = settings.name_identifier_format
         
     | 
| 
      
 41 
     | 
    
         
            +
                    end
         
     | 
| 
      
 42 
     | 
    
         
            +
                    if settings.assertion_consumer_service_url != nil
         
     | 
| 
      
 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",
         
     | 
| 
      
 46 
     | 
    
         
            +
                          "Location" => settings.assertion_consumer_service_url,
         
     | 
| 
      
 47 
     | 
    
         
            +
                          "isDefault" => true,
         
     | 
| 
      
 48 
     | 
    
         
            +
                          "index" => 0
         
     | 
| 
      
 49 
     | 
    
         
            +
                      }
         
     | 
| 
      
 50 
     | 
    
         
            +
                    end
         
     | 
| 
      
 51 
     | 
    
         
            +
                    # With OpenSSO, it might be required to also include
         
     | 
| 
      
 52 
     | 
    
         
            +
                    #  <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 
     | 
    
         
            +
                    #  <md:XACMLAuthzDecisionQueryDescriptor WantAssertionsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"/>
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                    meta_doc << REXML::XMLDecl.new
         
     | 
| 
      
 56 
     | 
    
         
            +
                    ret = ""
         
     | 
| 
      
 57 
     | 
    
         
            +
                    # pretty print the XML so IdP administrators can easily see what the SP supports
         
     | 
| 
      
 58 
     | 
    
         
            +
                    meta_doc.write(ret, 1)
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                    ret
         
     | 
| 
      
 61 
     | 
    
         
            +
                  end
         
     | 
| 
      
 62 
     | 
    
         
            +
                end
         
     | 
| 
      
 63 
     | 
    
         
            +
              end
         
     | 
| 
      
 64 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,93 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "base64"
         
     | 
| 
      
 2 
     | 
    
         
            +
            require "uuid"
         
     | 
| 
      
 3 
     | 
    
         
            +
            require "zlib"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require "cgi"
         
     | 
| 
      
 5 
     | 
    
         
            +
            require "rexml/document"
         
     | 
| 
      
 6 
     | 
    
         
            +
            require "rexml/xpath"
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            module Maestrano
         
     | 
| 
      
 9 
     | 
    
         
            +
              module Saml
         
     | 
| 
      
 10 
     | 
    
         
            +
              include REXML
         
     | 
| 
      
 11 
     | 
    
         
            +
                class Request
         
     | 
| 
      
 12 
     | 
    
         
            +
                  attr_accessor :settings, :params, :session
         
     | 
| 
      
 13 
     | 
    
         
            +
                  
         
     | 
| 
      
 14 
     | 
    
         
            +
                  def initialize(params = {}, session = {})
         
     | 
| 
      
 15 
     | 
    
         
            +
                    self.settings = Maestrano::SSO.saml_settings
         
     | 
| 
      
 16 
     | 
    
         
            +
                    self.params = params
         
     | 
| 
      
 17 
     | 
    
         
            +
                    self.session = session
         
     | 
| 
      
 18 
     | 
    
         
            +
                  end
         
     | 
| 
      
 19 
     | 
    
         
            +
                  
         
     | 
| 
      
 20 
     | 
    
         
            +
                  def redirect_url
         
     | 
| 
      
 21 
     | 
    
         
            +
                    request_doc = create_authentication_xml_doc(settings)
         
     | 
| 
      
 22 
     | 
    
         
            +
                    request_doc.context[:attribute_quote] = :quote if self.settings.double_quote_xml_attribute_values
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                    request = ""
         
     | 
| 
      
 25 
     | 
    
         
            +
                    request_doc.write(request)
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                    request           = Zlib::Deflate.deflate(request, 9)[2..-5] if self.settings.compress_request
         
     | 
| 
      
 28 
     | 
    
         
            +
                    base64_request    = Base64.encode64(request)
         
     | 
| 
      
 29 
     | 
    
         
            +
                    encoded_request   = CGI.escape(base64_request)
         
     | 
| 
      
 30 
     | 
    
         
            +
                    params_prefix     = (self.settings.idp_sso_target_url =~ /\?/) ? '&' : '?'
         
     | 
| 
      
 31 
     | 
    
         
            +
                    request_params    = "#{params_prefix}SAMLRequest=#{encoded_request}"
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                    self.params.each_pair do |key, value|
         
     | 
| 
      
 34 
     | 
    
         
            +
                      request_params << "&#{key.to_s}=#{CGI.escape(value.to_s)}"
         
     | 
| 
      
 35 
     | 
    
         
            +
                    end
         
     | 
| 
      
 36 
     | 
    
         
            +
                    
         
     | 
| 
      
 37 
     | 
    
         
            +
                    if (request_params !~ /group_id=/) && (group_id = (self.session[:mno_group_uid] || self.session['mno_group_uid']))
         
     | 
| 
      
 38 
     | 
    
         
            +
                      request_params << "&group_id=#{CGI.escape(group_id.to_s)}"
         
     | 
| 
      
 39 
     | 
    
         
            +
                    end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                    self.settings.idp_sso_target_url + request_params
         
     | 
| 
      
 42 
     | 
    
         
            +
                  end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                  def create_authentication_xml_doc(settings)
         
     | 
| 
      
 45 
     | 
    
         
            +
                    uuid = "_" + UUID.new.generate
         
     | 
| 
      
 46 
     | 
    
         
            +
                    time = Time.now.utc.strftime("%Y-%m-%dT%H:%M:%SZ")
         
     | 
| 
      
 47 
     | 
    
         
            +
                    # Create AuthnRequest root element using REXML
         
     | 
| 
      
 48 
     | 
    
         
            +
                    request_doc = REXML::Document.new
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                    root = request_doc.add_element "samlp:AuthnRequest", { "xmlns:samlp" => "urn:oasis:names:tc:SAML:2.0:protocol" }
         
     | 
| 
      
 51 
     | 
    
         
            +
                    root.attributes['ID'] = uuid
         
     | 
| 
      
 52 
     | 
    
         
            +
                    root.attributes['IssueInstant'] = time
         
     | 
| 
      
 53 
     | 
    
         
            +
                    root.attributes['Version'] = "2.0"
         
     | 
| 
      
 54 
     | 
    
         
            +
                    root.attributes['Destination'] = self.settings.idp_sso_target_url unless self.settings.idp_sso_target_url.nil?
         
     | 
| 
      
 55 
     | 
    
         
            +
                    root.attributes['IsPassive'] = self.settings.passive unless self.settings.passive.nil?
         
     | 
| 
      
 56 
     | 
    
         
            +
                    root.attributes['ProtocolBinding'] = self.settings.protocol_binding unless self.settings.protocol_binding.nil?
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                    # Conditionally defined elements based on settings
         
     | 
| 
      
 59 
     | 
    
         
            +
                    if self.settings.assertion_consumer_service_url != nil
         
     | 
| 
      
 60 
     | 
    
         
            +
                      root.attributes["AssertionConsumerServiceURL"] = self.settings.assertion_consumer_service_url
         
     | 
| 
      
 61 
     | 
    
         
            +
                    end
         
     | 
| 
      
 62 
     | 
    
         
            +
                    if self.settings.issuer != nil
         
     | 
| 
      
 63 
     | 
    
         
            +
                      issuer = root.add_element "saml:Issuer", { "xmlns:saml" => "urn:oasis:names:tc:SAML:2.0:assertion" }
         
     | 
| 
      
 64 
     | 
    
         
            +
                      issuer.text = self.settings.issuer
         
     | 
| 
      
 65 
     | 
    
         
            +
                    end
         
     | 
| 
      
 66 
     | 
    
         
            +
                    if self.settings.name_identifier_format != nil
         
     | 
| 
      
 67 
     | 
    
         
            +
                      root.add_element "samlp:NameIDPolicy", {
         
     | 
| 
      
 68 
     | 
    
         
            +
                          "xmlns:samlp" => "urn:oasis:names:tc:SAML:2.0:protocol",
         
     | 
| 
      
 69 
     | 
    
         
            +
                          # Might want to make AllowCreate a setting?
         
     | 
| 
      
 70 
     | 
    
         
            +
                          "AllowCreate" => "true",
         
     | 
| 
      
 71 
     | 
    
         
            +
                          "Format" => self.settings.name_identifier_format
         
     | 
| 
      
 72 
     | 
    
         
            +
                      }
         
     | 
| 
      
 73 
     | 
    
         
            +
                    end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                    # BUG fix here -- if an authn_context is defined, add the tags with an "exact"
         
     | 
| 
      
 76 
     | 
    
         
            +
                    # match required for authentication to succeed.  If this is not defined,
         
     | 
| 
      
 77 
     | 
    
         
            +
                    # the IdP will choose default rules for authentication.  (Shibboleth IdP)
         
     | 
| 
      
 78 
     | 
    
         
            +
                    if self.settings.authn_context != nil
         
     | 
| 
      
 79 
     | 
    
         
            +
                      requested_context = root.add_element "samlp:RequestedAuthnContext", {
         
     | 
| 
      
 80 
     | 
    
         
            +
                        "xmlns:samlp" => "urn:oasis:names:tc:SAML:2.0:protocol",
         
     | 
| 
      
 81 
     | 
    
         
            +
                        "Comparison" => "exact",
         
     | 
| 
      
 82 
     | 
    
         
            +
                      }
         
     | 
| 
      
 83 
     | 
    
         
            +
                      class_ref = requested_context.add_element "saml:AuthnContextClassRef", {
         
     | 
| 
      
 84 
     | 
    
         
            +
                        "xmlns:saml" => "urn:oasis:names:tc:SAML:2.0:assertion",
         
     | 
| 
      
 85 
     | 
    
         
            +
                      }
         
     | 
| 
      
 86 
     | 
    
         
            +
                      class_ref.text = self.settings.authn_context
         
     | 
| 
      
 87 
     | 
    
         
            +
                    end
         
     | 
| 
      
 88 
     | 
    
         
            +
                    request_doc
         
     | 
| 
      
 89 
     | 
    
         
            +
                  end
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                end
         
     | 
| 
      
 92 
     | 
    
         
            +
              end
         
     | 
| 
      
 93 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,201 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "time"
         
     | 
| 
      
 2 
     | 
    
         
            +
            require "nokogiri"
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            # Only supports SAML 2.0
         
     | 
| 
      
 5 
     | 
    
         
            +
            module Maestrano
         
     | 
| 
      
 6 
     | 
    
         
            +
              module Saml
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                class Response
         
     | 
| 
      
 9 
     | 
    
         
            +
                  ASSERTION = "urn:oasis:names:tc:SAML:2.0:assertion"
         
     | 
| 
      
 10 
     | 
    
         
            +
                  PROTOCOL  = "urn:oasis:names:tc:SAML:2.0:protocol"
         
     | 
| 
      
 11 
     | 
    
         
            +
                  DSIG      = "http://www.w3.org/2000/09/xmldsig#"
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                  # TODO: This should probably be ctor initialized too... WDYT?
         
     | 
| 
      
 14 
     | 
    
         
            +
                  attr_accessor :settings
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  attr_reader :options
         
     | 
| 
      
 17 
     | 
    
         
            +
                  attr_reader :response
         
     | 
| 
      
 18 
     | 
    
         
            +
                  attr_reader :document
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                  def initialize(response, options = {})
         
     | 
| 
      
 21 
     | 
    
         
            +
                    raise ArgumentError.new("Response cannot be nil") if response.nil?
         
     | 
| 
      
 22 
     | 
    
         
            +
                    @options  = options
         
     | 
| 
      
 23 
     | 
    
         
            +
                    @response = (response =~ /^</) ? response : Base64.decode64(response)
         
     | 
| 
      
 24 
     | 
    
         
            +
                    @document = Maestrano::XMLSecurity::SignedDocument.new(@response)
         
     | 
| 
      
 25 
     | 
    
         
            +
                    @settings = Maestrano::SSO.saml_settings
         
     | 
| 
      
 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 all the attributes with the response.
         
     | 
| 
      
 52 
     | 
    
         
            +
                  # Multiple values will be returned in the AttributeValue#values array
         
     | 
| 
      
 53 
     | 
    
         
            +
                  # in reverse order, when compared to XML
         
     | 
| 
      
 54 
     | 
    
         
            +
                  def attributes
         
     | 
| 
      
 55 
     | 
    
         
            +
                    @attr_statements ||= begin
         
     | 
| 
      
 56 
     | 
    
         
            +
                      result = {}
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                      stmt_element = xpath_first_from_signed_assertion('/a:AttributeStatement')
         
     | 
| 
      
 59 
     | 
    
         
            +
                      return {} if stmt_element.nil?
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                      stmt_element.elements.each do |attr_element|
         
     | 
| 
      
 62 
     | 
    
         
            +
                        name  = attr_element.attributes["Name"]
         
     | 
| 
      
 63 
     | 
    
         
            +
                        values = attr_element.elements.collect(&:text)
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                        # Set up a string-like wrapper for the values array
         
     | 
| 
      
 66 
     | 
    
         
            +
                        attr_value = AttributeValue.new(values.first, values.reverse)
         
     | 
| 
      
 67 
     | 
    
         
            +
                        # Merge values if the Attribute has already been seen
         
     | 
| 
      
 68 
     | 
    
         
            +
                        if result[name]
         
     | 
| 
      
 69 
     | 
    
         
            +
                          attr_value.values += result[name].values
         
     | 
| 
      
 70 
     | 
    
         
            +
                        end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                        result[name] = attr_value
         
     | 
| 
      
 73 
     | 
    
         
            +
                      end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                      result.keys.each do |key|
         
     | 
| 
      
 76 
     | 
    
         
            +
                        result[key.intern] = result[key]
         
     | 
| 
      
 77 
     | 
    
         
            +
                      end
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                      result
         
     | 
| 
      
 80 
     | 
    
         
            +
                    end
         
     | 
| 
      
 81 
     | 
    
         
            +
                  end
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                  # When this user session should expire at latest
         
     | 
| 
      
 84 
     | 
    
         
            +
                  def session_expires_at
         
     | 
| 
      
 85 
     | 
    
         
            +
                    @expires_at ||= begin
         
     | 
| 
      
 86 
     | 
    
         
            +
                      node = xpath_first_from_signed_assertion('/a:AuthnStatement')
         
     | 
| 
      
 87 
     | 
    
         
            +
                      parse_time(node, "SessionNotOnOrAfter")
         
     | 
| 
      
 88 
     | 
    
         
            +
                    end
         
     | 
| 
      
 89 
     | 
    
         
            +
                  end
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                  # Checks the status of the response for a "Success" code
         
     | 
| 
      
 92 
     | 
    
         
            +
                  def success?
         
     | 
| 
      
 93 
     | 
    
         
            +
                    @status_code ||= begin
         
     | 
| 
      
 94 
     | 
    
         
            +
                      node = REXML::XPath.first(document, "/p:Response/p:Status/p:StatusCode", { "p" => PROTOCOL, "a" => ASSERTION })
         
     | 
| 
      
 95 
     | 
    
         
            +
                      node.attributes["Value"] == "urn:oasis:names:tc:SAML:2.0:status:Success"
         
     | 
| 
      
 96 
     | 
    
         
            +
                    end
         
     | 
| 
      
 97 
     | 
    
         
            +
                  end
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                  # Conditions (if any) for the assertion to run
         
     | 
| 
      
 100 
     | 
    
         
            +
                  def conditions
         
     | 
| 
      
 101 
     | 
    
         
            +
                    @conditions ||= xpath_first_from_signed_assertion('/a:Conditions')
         
     | 
| 
      
 102 
     | 
    
         
            +
                  end
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
                  def not_before
         
     | 
| 
      
 105 
     | 
    
         
            +
                    @not_before ||= parse_time(conditions, "NotBefore")
         
     | 
| 
      
 106 
     | 
    
         
            +
                  end
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
                  def not_on_or_after
         
     | 
| 
      
 109 
     | 
    
         
            +
                    @not_on_or_after ||= parse_time(conditions, "NotOnOrAfter")
         
     | 
| 
      
 110 
     | 
    
         
            +
                  end
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
                  def issuer
         
     | 
| 
      
 113 
     | 
    
         
            +
                    @issuer ||= begin
         
     | 
| 
      
 114 
     | 
    
         
            +
                      node = REXML::XPath.first(document, "/p:Response/a:Issuer", { "p" => PROTOCOL, "a" => ASSERTION })
         
     | 
| 
      
 115 
     | 
    
         
            +
                      node ||= xpath_first_from_signed_assertion('/a:Issuer')
         
     | 
| 
      
 116 
     | 
    
         
            +
                      node.nil? ? nil : node.text
         
     | 
| 
      
 117 
     | 
    
         
            +
                    end
         
     | 
| 
      
 118 
     | 
    
         
            +
                  end
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                  private
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
      
 122 
     | 
    
         
            +
                  def validation_error(message)
         
     | 
| 
      
 123 
     | 
    
         
            +
                    raise ValidationError.new(message)
         
     | 
| 
      
 124 
     | 
    
         
            +
                  end
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
                  def validate(soft = true)
         
     | 
| 
      
 127 
     | 
    
         
            +
                    validate_structure(soft)      &&
         
     | 
| 
      
 128 
     | 
    
         
            +
                    validate_response_state(soft) &&
         
     | 
| 
      
 129 
     | 
    
         
            +
                    validate_conditions(soft)     &&
         
     | 
| 
      
 130 
     | 
    
         
            +
                    document.validate_document(get_fingerprint, soft) &&
         
     | 
| 
      
 131 
     | 
    
         
            +
                    success?
         
     | 
| 
      
 132 
     | 
    
         
            +
                  end
         
     | 
| 
      
 133 
     | 
    
         
            +
             
     | 
| 
      
 134 
     | 
    
         
            +
                  def validate_structure(soft = true)
         
     | 
| 
      
 135 
     | 
    
         
            +
                    Dir.chdir(File.expand_path(File.join(File.dirname(__FILE__), 'schemas'))) do
         
     | 
| 
      
 136 
     | 
    
         
            +
                      @schema = Nokogiri::XML::Schema(IO.read('saml20protocol_schema.xsd'))
         
     | 
| 
      
 137 
     | 
    
         
            +
                      @xml = Nokogiri::XML(self.document.to_s)
         
     | 
| 
      
 138 
     | 
    
         
            +
                    end
         
     | 
| 
      
 139 
     | 
    
         
            +
                    if soft
         
     | 
| 
      
 140 
     | 
    
         
            +
                      @schema.validate(@xml).map{ return false }
         
     | 
| 
      
 141 
     | 
    
         
            +
                    else
         
     | 
| 
      
 142 
     | 
    
         
            +
                      @schema.validate(@xml).map{ |error| validation_error("#{error.message}\n\n#{@xml.to_s}") }
         
     | 
| 
      
 143 
     | 
    
         
            +
                    end
         
     | 
| 
      
 144 
     | 
    
         
            +
                  end
         
     | 
| 
      
 145 
     | 
    
         
            +
             
     | 
| 
      
 146 
     | 
    
         
            +
                  def validate_response_state(soft = true)
         
     | 
| 
      
 147 
     | 
    
         
            +
                    if response.empty?
         
     | 
| 
      
 148 
     | 
    
         
            +
                      return soft ? false : validation_error("Blank response")
         
     | 
| 
      
 149 
     | 
    
         
            +
                    end
         
     | 
| 
      
 150 
     | 
    
         
            +
             
     | 
| 
      
 151 
     | 
    
         
            +
                    if settings.nil?
         
     | 
| 
      
 152 
     | 
    
         
            +
                      return soft ? false : validation_error("No settings on response")
         
     | 
| 
      
 153 
     | 
    
         
            +
                    end
         
     | 
| 
      
 154 
     | 
    
         
            +
             
     | 
| 
      
 155 
     | 
    
         
            +
                    if settings.idp_cert_fingerprint.nil? && settings.idp_cert.nil?
         
     | 
| 
      
 156 
     | 
    
         
            +
                      return soft ? false : validation_error("No fingerprint or certificate on settings")
         
     | 
| 
      
 157 
     | 
    
         
            +
                    end
         
     | 
| 
      
 158 
     | 
    
         
            +
             
     | 
| 
      
 159 
     | 
    
         
            +
                    true
         
     | 
| 
      
 160 
     | 
    
         
            +
                  end
         
     | 
| 
      
 161 
     | 
    
         
            +
             
     | 
| 
      
 162 
     | 
    
         
            +
                  def xpath_first_from_signed_assertion(subelt=nil)
         
     | 
| 
      
 163 
     | 
    
         
            +
                    node = REXML::XPath.first(document, "/p:Response/a:Assertion[@ID='#{document.signed_element_id}']#{subelt}", { "p" => PROTOCOL, "a" => ASSERTION })
         
     | 
| 
      
 164 
     | 
    
         
            +
                    node ||= REXML::XPath.first(document, "/p:Response[@ID='#{document.signed_element_id}']/a:Assertion#{subelt}", { "p" => PROTOCOL, "a" => ASSERTION })
         
     | 
| 
      
 165 
     | 
    
         
            +
                    node
         
     | 
| 
      
 166 
     | 
    
         
            +
                  end
         
     | 
| 
      
 167 
     | 
    
         
            +
             
     | 
| 
      
 168 
     | 
    
         
            +
                  def get_fingerprint
         
     | 
| 
      
 169 
     | 
    
         
            +
                    if settings.idp_cert
         
     | 
| 
      
 170 
     | 
    
         
            +
                      cert = OpenSSL::X509::Certificate.new(settings.idp_cert)
         
     | 
| 
      
 171 
     | 
    
         
            +
                      Digest::SHA1.hexdigest(cert.to_der).upcase.scan(/../).join(":")
         
     | 
| 
      
 172 
     | 
    
         
            +
                    else
         
     | 
| 
      
 173 
     | 
    
         
            +
                      settings.idp_cert_fingerprint
         
     | 
| 
      
 174 
     | 
    
         
            +
                    end
         
     | 
| 
      
 175 
     | 
    
         
            +
                  end
         
     | 
| 
      
 176 
     | 
    
         
            +
             
     | 
| 
      
 177 
     | 
    
         
            +
                  def validate_conditions(soft = true)
         
     | 
| 
      
 178 
     | 
    
         
            +
                    return true if conditions.nil?
         
     | 
| 
      
 179 
     | 
    
         
            +
                    return true if options[:skip_conditions]
         
     | 
| 
      
 180 
     | 
    
         
            +
             
     | 
| 
      
 181 
     | 
    
         
            +
                    now = Time.now.utc
         
     | 
| 
      
 182 
     | 
    
         
            +
             
     | 
| 
      
 183 
     | 
    
         
            +
                    if not_before && (now + (options[:allowed_clock_drift] || 0)) < not_before
         
     | 
| 
      
 184 
     | 
    
         
            +
                      return soft ? false : validation_error("Current time is earlier than NotBefore condition")
         
     | 
| 
      
 185 
     | 
    
         
            +
                    end
         
     | 
| 
      
 186 
     | 
    
         
            +
             
     | 
| 
      
 187 
     | 
    
         
            +
                    if not_on_or_after && now >= not_on_or_after
         
     | 
| 
      
 188 
     | 
    
         
            +
                      return soft ? false : validation_error("Current time is on or after NotOnOrAfter condition")
         
     | 
| 
      
 189 
     | 
    
         
            +
                    end
         
     | 
| 
      
 190 
     | 
    
         
            +
             
     | 
| 
      
 191 
     | 
    
         
            +
                    true
         
     | 
| 
      
 192 
     | 
    
         
            +
                  end
         
     | 
| 
      
 193 
     | 
    
         
            +
             
     | 
| 
      
 194 
     | 
    
         
            +
                  def parse_time(node, attribute)
         
     | 
| 
      
 195 
     | 
    
         
            +
                    if node && node.attributes[attribute]
         
     | 
| 
      
 196 
     | 
    
         
            +
                      Time.parse(node.attributes[attribute])
         
     | 
| 
      
 197 
     | 
    
         
            +
                    end
         
     | 
| 
      
 198 
     | 
    
         
            +
                  end
         
     | 
| 
      
 199 
     | 
    
         
            +
                end
         
     | 
| 
      
 200 
     | 
    
         
            +
              end
         
     | 
| 
      
 201 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -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>
         
     |