From 7b427fe284007574285f8a3076b58c2c8f78e668 Mon Sep 17 00:00:00 2001 From: davideZanin Date: Tue, 23 Apr 2019 11:41:56 +0200 Subject: [PATCH 001/205] demo-tornado initial commit --- demo-tornado/Docs/DEVELOPING.md | 82 +++++++++ demo-tornado/README.txt | 7 + demo-tornado/Settings.py | 6 + demo-tornado/requirements.txt | 13 ++ .../saml/advanced_settings (copia).json | 33 ++++ demo-tornado/saml/advanced_settings.json | 36 ++++ demo-tornado/saml/certs/README | 13 ++ demo-tornado/saml/settings (copia).json | 30 ++++ .../saml/settings (seconda copia).json | 30 ++++ demo-tornado/saml/settings.json | 30 ++++ demo-tornado/templates/attrs.html | 35 ++++ demo-tornado/templates/base.html | 26 +++ demo-tornado/templates/index.html | 66 +++++++ demo-tornado/views.py | 167 ++++++++++++++++++ 14 files changed, 574 insertions(+) create mode 100644 demo-tornado/Docs/DEVELOPING.md create mode 100644 demo-tornado/README.txt create mode 100644 demo-tornado/Settings.py create mode 100644 demo-tornado/requirements.txt create mode 100644 demo-tornado/saml/advanced_settings (copia).json create mode 100644 demo-tornado/saml/advanced_settings.json create mode 100644 demo-tornado/saml/certs/README create mode 100644 demo-tornado/saml/settings (copia).json create mode 100644 demo-tornado/saml/settings (seconda copia).json create mode 100644 demo-tornado/saml/settings.json create mode 100644 demo-tornado/templates/attrs.html create mode 100644 demo-tornado/templates/base.html create mode 100644 demo-tornado/templates/index.html create mode 100644 demo-tornado/views.py diff --git a/demo-tornado/Docs/DEVELOPING.md b/demo-tornado/Docs/DEVELOPING.md new file mode 100644 index 00000000..27ba1243 --- /dev/null +++ b/demo-tornado/Docs/DEVELOPING.md @@ -0,0 +1,82 @@ +# OneLogin's SAML Python Toolkit (compatible with Python3) + +Installation +------------ + +### Dependencies ### + + * python 3.6 + * apt-get install libxml2-dev libxmlsec1-dev libxmlsec1-openssl + * pip install xmlsec + * pip install isodate + * pip install defusedxml + * pip install python3-saml + * pip install tornado + + +***Virtualenv*** + +The use of virtualenv/virtualenvwrapper is highly recommended. + +### Create certificates ### + +in saml/cert run : + * openssl req -new -x509 -days 3652 -nodes -out sp.crt -keyout sp.key + * openssl req -new -x509 -days 3652 -nodes -out metadata.crt -keyout metadata.key + +### Useful extesion for SAML messages ### +* [SAML Chrome Panel 1.8.9](https://chrome.google.com/webstore/detail/saml-chrome-panel/paijfdbeoenhembfhkhllainmocckace/related) + + + +# Test with keycloack idp + +Installation +------------ + +### Install Docker ### +* sudo apt-get remove docker docker-engine docker.io containerd runc + +* sudo apt-get update + +* sudo apt-get install \ + apt-transport-https \ + ca-certificates \ + curl \ + gnupg-agent \ + software-properties-common +* curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - + +* sudo add-apt-repository \ + "deb [arch=amd64] https://download.docker.com/linux/ubuntu \ + $(lsb_release -cs) \ + stable" + +* sudo apt-get update + +* sudo apt-get install docker-ce docker-ce-cli containerd.io + +* sudo docker run hello-world + + +### Keycloack starting ### +First run only: +* docker run --name keycloackContainer -d -p 8080:8080 -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=admin -e DB_VENDOR=H2 jboss/keycloak + +After first run: +* sudo docker start keycloackContainer + +Remember to stop keycloack after usage: +* sudo docker stop keycloackContainer + + +### Keycloack useful urls ### +* master: http://localhost:8080/auth/admin +* users: http://localhost:8080/auth/realms/idp_dacd/account/ +* saml request: http://localhost:8080/auth/realms/idp_dacd/protocol/saml +* metadata: http://localhost:8080/auth/realms/idp_dacd/protocol/saml/descriptor + + + + + diff --git a/demo-tornado/README.txt b/demo-tornado/README.txt new file mode 100644 index 00000000..dd4418ca --- /dev/null +++ b/demo-tornado/README.txt @@ -0,0 +1,7 @@ +Fully-working tornado-demo. + +ABOUT ISSSUE +This is only a demo, some issues about session still remain. + +PRODUCTION +Remember also to disable debugging in production. diff --git a/demo-tornado/Settings.py b/demo-tornado/Settings.py new file mode 100644 index 00000000..4b80459a --- /dev/null +++ b/demo-tornado/Settings.py @@ -0,0 +1,6 @@ +import os + +BASE_DIR = os.path.dirname(__file__) + +SAML_PATH = os.path.join(BASE_DIR, 'saml') +TEMPLATE_PATH = os.path.join(BASE_DIR, 'templates') diff --git a/demo-tornado/requirements.txt b/demo-tornado/requirements.txt new file mode 100644 index 00000000..99192d20 --- /dev/null +++ b/demo-tornado/requirements.txt @@ -0,0 +1,13 @@ +Click==7.0 +defusedxml==0.5.0 +isodate==0.6.0 +itsdangerous==1.1.0 +Jinja2==2.10.1 +lxml==4.3.3 +MarkupSafe==1.1.1 +pkgconfig==1.5.1 +python3-saml==1.6.0 +six==1.12.0 +tornado==6.0.2 +Werkzeug==0.15.2 +xmlsec==1.3.3 diff --git a/demo-tornado/saml/advanced_settings (copia).json b/demo-tornado/saml/advanced_settings (copia).json new file mode 100644 index 00000000..3115e17e --- /dev/null +++ b/demo-tornado/saml/advanced_settings (copia).json @@ -0,0 +1,33 @@ +{ + "security": { + "nameIdEncrypted": false, + "authnRequestsSigned": false, + "logoutRequestSigned": false, + "logoutResponseSigned": false, + "signMetadata": false, + "wantMessagesSigned": false, + "wantAssertionsSigned": false, + "wantNameId" : true, + "wantNameIdEncrypted": false, + "wantAssertionsEncrypted": false, + "signatureAlgorithm": "http://www.w3.org/2000/09/xmldsig#rsa-sha1", + "digestAlgorithm": "http://www.w3.org/2000/09/xmldsig#sha1" + }, + "contactPerson": { + "technical": { + "givenName": "technical_name", + "emailAddress": "technical@example.com" + }, + "support": { + "givenName": "support_name", + "emailAddress": "support@example.com" + } + }, + "organization": { + "en-US": { + "name": "sp_test", + "displayname": "SP test", + "url": "http://sp.example.com" + } + } +} \ No newline at end of file diff --git a/demo-tornado/saml/advanced_settings.json b/demo-tornado/saml/advanced_settings.json new file mode 100644 index 00000000..e14e1170 --- /dev/null +++ b/demo-tornado/saml/advanced_settings.json @@ -0,0 +1,36 @@ +{ + "security": { + "nameIdEncrypted": false, + "authnRequestsSigned": true, + "logoutRequestSigned": true, + "logoutResponseSigned": true, + "signMetadata": { + "keyFileName": "metadata.key", + "certFileName": "metadata.crt" + }, + "wantMessagesSigned": false, + "wantAssertionsSigned": true, + "wantNameId" : true, + "wantNameIdEncrypted": false, + "wantAssertionsEncrypted": false, + "signatureAlgorithm": "http://www.w3.org/2000/09/xmldsig#rsa-sha1", + "digestAlgorithm": "http://www.w3.org/2000/09/xmldsig#sha1" + }, + "contactPerson": { + "technical": { + "givenName": "technical_name", + "emailAddress": "technical@example.com" + }, + "support": { + "givenName": "support_name", + "emailAddress": "support@example.com" + } + }, + "organization": { + "en-US": { + "name": "sp_test", + "displayname": "SP test", + "url": "http://sp.example.com" + } + } +} diff --git a/demo-tornado/saml/certs/README b/demo-tornado/saml/certs/README new file mode 100644 index 00000000..7e837fb9 --- /dev/null +++ b/demo-tornado/saml/certs/README @@ -0,0 +1,13 @@ +Take care of this folder that could contain private key. Be sure that this folder never is published. + +Onelogin Python Toolkit expects that certs for the SP could be stored in this folder as: + + * sp.key Private Key + * sp.crt Public cert + * sp_new.crt Future Public cert + + +Also you can use other cert to sign the metadata of the SP using the: + + * metadata.key + * metadata.crt diff --git a/demo-tornado/saml/settings (copia).json b/demo-tornado/saml/settings (copia).json new file mode 100644 index 00000000..ec40b674 --- /dev/null +++ b/demo-tornado/saml/settings (copia).json @@ -0,0 +1,30 @@ +{ + "strict": true, + "debug": true, + "sp": { + "entityId": "https:///metadata/", + "assertionConsumerService": { + "url": "https:///?acs", + "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" + }, + "singleLogoutService": { + "url": "https:///?sls", + "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" + }, + "NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", + "x509cert": "", + "privateKey": "" + }, + "idp": { + "entityId": "https://app.onelogin.com/saml/metadata/", + "singleSignOnService": { + "url": "https://app.onelogin.com/trust/saml2/http-post/sso/", + "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" + }, + "singleLogoutService": { + "url": "https://app.onelogin.com/trust/saml2/http-redirect/slo/", + "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" + }, + "x509cert": "" + } +} diff --git a/demo-tornado/saml/settings (seconda copia).json b/demo-tornado/saml/settings (seconda copia).json new file mode 100644 index 00000000..49a626b6 --- /dev/null +++ b/demo-tornado/saml/settings (seconda copia).json @@ -0,0 +1,30 @@ +{ + "strict": true, + "debug": true, + "sp": { + "entityId": "http://0.0.0.0:8000/metadata/", + "assertionConsumerService": { + "url": "http://0.0.0.0:8000/?acs", + "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" + }, + "singleLogoutService": { + "url": "http://0.0.0.0:8000/?sls", + "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" + }, + "NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", + "x509cert": "", + "privateKey": "" + }, + "idp": { + "entityId": "http://localhost:8080/auth/realms/idp_dacd", + "singleSignOnService": { + "url": "http://localhost:8080/auth/realms/idp_dacd/protocol/saml", + "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" + }, + "singleLogoutService": { + "url": "http://localhost:8080/auth/realms/idp_dacd/protocol/saml", + "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" + }, + "x509cert": "MIICnzCCAYcCBgFqC3f1FDANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDDAhpZHBfZGFjZDAeFw0xOTA0MTEwODE0MzJaFw0yOTA0MTEwODE2MTJaMBMxETAPBgNVBAMMCGlkcF9kYWNkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuhRERlbbwUxabS4lnGFOpxBKACpcwQwOSvyCKD3bLDvzfQIRh1x3RDq+6xxBcdP86PKRYjwZc/64HYFUbe9FCDLf1SDQz7XTtpftydRrTUydVaj+3FHhyCLcLkJldMVneVMZWIXSzUISvcURL3sr6HVJ74Uy0KPPOo3vQQ6RRaNaB3RCdZaNbzxjbG5eBgSvWHGE+vkozHpS7y6LHXDP1wXenH65RRrat8PQ/Qp4WVi2U5rVfA2SFbcwohjaJ+vOYH+oARnk5a2IosAMHBtONorPnPrcV7MqUXX7q19hnlFRZA34YVcF6kkWWsS1V/mRm2kK6EE/mABvX4mrefWg+QIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQARKkt9TKg5jLLc7HZSYxHD0MGqDQ8jMAXjpYYaEoIGyYnE31WOEG6HlzHFrkp5ioBT8vTIfejpGGkcL0qspSjQRBYH1eHOEToFo2vhmr0yDUtePNVcQtRN0ImAwNvKU/PciSi4dX0Y8Z/VfeRn8k979o0X82xZSYIxLhKlFp9toOHz0pNTU0Ie4h1zxcPd0L7KAn5eQPHoJwXsdWkM9aoZtCQCjbiexpfOdEOfnUn8QisinAWqcC/gKl9XG7XU4P5cevTeqZgnK0W+ilJVkkJX2zZWlAji+0PKLGr3BgJCfkwUcsm1C8U2ysTGkW1mZHfKVzK2NPA0bSlGgZwbvki+" + } +} diff --git a/demo-tornado/saml/settings.json b/demo-tornado/saml/settings.json new file mode 100644 index 00000000..c91e5cc9 --- /dev/null +++ b/demo-tornado/saml/settings.json @@ -0,0 +1,30 @@ +{ + "strict": true, + "debug": true, + "sp": { + "entityId": "http://localhost:8000/metadata/", + "assertionConsumerService": { + "url": "http://localhost:8000/?acs", + "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" + }, + "singleLogoutService": { + "url": "http://localhost:8000/?sls", + "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" + }, + "NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", + "x509cert": "", + "privateKey": "" + }, + "idp": { + "entityId": "http://localhost:8080/auth/realms/idp_dacd", + "singleSignOnService": { + "url": "http://localhost:8080/auth/realms/idp_dacd/protocol/saml", + "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" + }, + "singleLogoutService": { + "url": "http://localhost:8080/auth/realms/idp_dacd/protocol/saml", + "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" + }, + "x509cert": "MIICnzCCAYcCBgFqC3f1FDANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDDAhpZHBfZGFjZDAeFw0xOTA0MTEwODE0MzJaFw0yOTA0MTEwODE2MTJaMBMxETAPBgNVBAMMCGlkcF9kYWNkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuhRERlbbwUxabS4lnGFOpxBKACpcwQwOSvyCKD3bLDvzfQIRh1x3RDq+6xxBcdP86PKRYjwZc/64HYFUbe9FCDLf1SDQz7XTtpftydRrTUydVaj+3FHhyCLcLkJldMVneVMZWIXSzUISvcURL3sr6HVJ74Uy0KPPOo3vQQ6RRaNaB3RCdZaNbzxjbG5eBgSvWHGE+vkozHpS7y6LHXDP1wXenH65RRrat8PQ/Qp4WVi2U5rVfA2SFbcwohjaJ+vOYH+oARnk5a2IosAMHBtONorPnPrcV7MqUXX7q19hnlFRZA34YVcF6kkWWsS1V/mRm2kK6EE/mABvX4mrefWg+QIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQARKkt9TKg5jLLc7HZSYxHD0MGqDQ8jMAXjpYYaEoIGyYnE31WOEG6HlzHFrkp5ioBT8vTIfejpGGkcL0qspSjQRBYH1eHOEToFo2vhmr0yDUtePNVcQtRN0ImAwNvKU/PciSi4dX0Y8Z/VfeRn8k979o0X82xZSYIxLhKlFp9toOHz0pNTU0Ie4h1zxcPd0L7KAn5eQPHoJwXsdWkM9aoZtCQCjbiexpfOdEOfnUn8QisinAWqcC/gKl9XG7XU4P5cevTeqZgnK0W+ilJVkkJX2zZWlAji+0PKLGr3BgJCfkwUcsm1C8U2ysTGkW1mZHfKVzK2NPA0bSlGgZwbvki+" + } +} diff --git a/demo-tornado/templates/attrs.html b/demo-tornado/templates/attrs.html new file mode 100644 index 00000000..e2429739 --- /dev/null +++ b/demo-tornado/templates/attrs.html @@ -0,0 +1,35 @@ +{% extends "base.html" %} + +{% block content %} + +{% if paint_logout %} + {% if attributes %} +

You have the following attributes:

+ + + + + + {% for attr, i in attributes %} + {% if i == 0 %} + + + {% end %} + +
NameValues
{{ attr }}
    + {% end %} + {% if i == 1 %} + {% for val in attr %} +
  • {{ val }}
  • + {% end %} + {% end %} +
+ {% else %} + + {% end %} + Logout +{% else %} + Login and access again to this page +{% end %} + +{% end %} diff --git a/demo-tornado/templates/base.html b/demo-tornado/templates/base.html new file mode 100644 index 00000000..e403a8ef --- /dev/null +++ b/demo-tornado/templates/base.html @@ -0,0 +1,26 @@ + + + + + + + + A Python SAML Toolkit by OneLogin demo + + + + + + + + +
+

A Python SAML Toolkit by OneLogin demo

+ + {% block content %}{% end %} +
+ + diff --git a/demo-tornado/templates/index.html b/demo-tornado/templates/index.html new file mode 100644 index 00000000..52bee968 --- /dev/null +++ b/demo-tornado/templates/index.html @@ -0,0 +1,66 @@ +{% extends "base.html" %} + +{% block content %} + +{% if errors %} + +{% end %} + +{% if not_auth_warn %} + +{% end %} + +{% if success_slo %} + +{% end %} + +{% if paint_logout %} + {% if attributes %} + + + + + + {% for attr in attributes %} + + + + + {% end %} + + + + +
NameValues
{{ attr[0] }}
    + + {% for elem in attr[1] %} +
  • {{ elem }}
  • + {% end %} +
+ {% else %} + + {% end %} + Logout +{% else %} + Login Login and access to attrs page +{% end %} + +{% end %} diff --git a/demo-tornado/views.py b/demo-tornado/views.py new file mode 100644 index 00000000..5ac3e03a --- /dev/null +++ b/demo-tornado/views.py @@ -0,0 +1,167 @@ +import tornado.ioloop +import tornado.web +import Settings +import tornado.httpserver +import tornado.httputil + +from onelogin.saml2.auth import OneLogin_Saml2_Auth +from onelogin.saml2.settings import OneLogin_Saml2_Settings +from onelogin.saml2.utils import OneLogin_Saml2_Utils + +##Global session info +session = {} + +class Application(tornado.web.Application): + def __init__(self): + handlers = [ + (r"/", IndexHandler), + (r"/attrs", AttrsHandler), + (r"/metadata",MetadataHandler), + ] + settings = { + "template_path": Settings.TEMPLATE_PATH, + "saml_path": Settings.SAML_PATH, + "autorealod": True, + "debug": True + } + tornado.web.Application.__init__(self, handlers, **settings) + + +class IndexHandler(tornado.web.RequestHandler): + def post(self): + req = prepare_tornado_request(self.request) + auth = init_saml_auth(req) + attributes = False + paint_logout = False + + auth.process_response() + errors = auth.get_errors() + not_auth_warn = not auth.is_authenticated() + + if len(errors) == 0: + session['samlUserdata'] = auth.get_attributes() + session['samlNameId'] = auth.get_nameid() + session['samlSessionIndex'] = auth.get_session_index() + self_url = OneLogin_Saml2_Utils.get_self_url(req) + if 'RelayState' in self.request.arguments and self_url != self.request.arguments['RelayState'][0].decode('utf-8'): + return self.redirect(self.request.arguments['RelayState'][0].decode('utf-8')) + + if 'samlUserdata' in session: + paint_logout = True + if len(session['samlUserdata']) > 0: + attributes = session['samlUserdata'].items() + + self.render('index.html',errors=errors,not_auth_warn=not_auth_warn,attributes=attributes,paint_logout=paint_logout) + + def get(self): + req = prepare_tornado_request(self.request) + auth = init_saml_auth(req) + errors = [] + not_auth_warn = False + success_slo = False + attributes = False + paint_logout = False + + if 'sso' in req['get_data']: + print('-sso-') + return self.redirect(auth.login()) + elif 'sso2' in req['get_data']: + print('-sso2-') + return_to = '%s/attrs' % self.request.host + return self.redirect(auth.login(return_to)) + elif 'slo' in req['get_data']: + print('-slo-') + name_id = None + session_index = None + if 'samlNameId' in session: + name_id = session['samlNameId'] + if 'samlSessionIndex' in session: + session_index = session['samlSessionIndex'] + return self.redirect(auth.logout(name_id=name_id, session_index=session_index)) + elif 'acs' in req['get_data']: + print('-acs-') + auth.process_response() + errors = auth.get_errors() + not_auth_warn = not auth.is_authenticated() + if len(errors) == 0: + session['samlUserdata'] = auth.get_attributes() + session['samlNameId'] = auth.get_nameid() + session['samlSessionIndex'] = auth.get_session_index() + self_url = OneLogin_Saml2_Utils.get_self_url(req) + if 'RelayState' in self.request.arguments and self_url != self.request.arguments['RelayState'][0].decode('utf-8'): + return self.redirect(auth.redirect_to(self.request.arguments['RelayState'][0].decode('utf-8'))) + elif 'sls' in req['get_data']: + print('-sls-') + dscb = lambda: session.clear() ## clear out the session + url = auth.process_slo(delete_session_cb=dscb) + errors = auth.get_errors() + if len(errors) == 0: + if url is not None: + return self.redirect(url) + else: + success_slo = True + + if 'samlUserdata' in session: + print('-samlUserdata-') + paint_logout = True + if len(session['samlUserdata']) > 0: + attributes = session['samlUserdata'].items() + print("ATTRIBUTES", attributes) + self.render('index.html',errors=errors,not_auth_warn=not_auth_warn,success_slo=success_slo,attributes=attributes,paint_logout=paint_logout) + +class AttrsHandler(tornado.web.RequestHandler): + def get(self): + paint_logout = False + attributes = False + + if 'samlUserdata' in session: + paint_logout = True + if len(session['samlUserdata']) > 0: + attributes = session['samlUserdata'].items() + + self.render('attrs.html',paint_logout=paint_logout,attributes=attributes) + +class MetadataHandler(tornado.web.RequestHandler): + def get(self): + req = prepare_tornado_request(self.request) + auth = init_saml_auth(req) + saml_settings = auth.get_settings() + #saml_settings = OneLogin_Saml2_Settings(settings=None, custom_base_path=settings.SAML_FOLDER, sp_validation_only=True) + metadata = saml_settings.get_sp_metadata() + errors = saml_settings.validate_metadata(metadata) + + if len(errors) == 0: + #resp = HttpResponse(content=metadata, content_type='text/xml') + self.set_header('Content-Type','text/xml') + self.write(metadata) + else: + #resp = HttpResponseServerError(content=', '.join(errors)) + self.write(', '.join(errors)) + #return resp + +def prepare_tornado_request(request): + + dataDict = {} + for key in request.arguments: + dataDict[key] = request.arguments[key][0].decode('utf-8') + + result = { + 'https': 'on' if request == 'https' else 'off', + 'http_host': tornado.httputil.split_host_and_port(request.host)[0], + 'script_name': request.path, + 'server_port': tornado.httputil.split_host_and_port(request.host)[1], + 'get_data': dataDict, + 'post_data': dataDict, + 'query_string': request.query + } + return result + +def init_saml_auth(req): + auth = OneLogin_Saml2_Auth(req, custom_base_path=Settings.SAML_PATH) + return auth + +if __name__ == "__main__": + app = Application() + http_server = tornado.httpserver.HTTPServer(app) + http_server.listen(8000) + tornado.ioloop.IOLoop.instance().start() From e46e3353c6e55c71cd000fc84170de50b17dcefc Mon Sep 17 00:00:00 2001 From: davideZanin Date: Tue, 23 Apr 2019 14:32:18 +0200 Subject: [PATCH 002/205] docs updated --- README.md | 105 +++++++++++++++++- demo-tornado/Docs/DEVELOPING.md | 82 -------------- demo-tornado/requirements.txt | 5 - .../saml/advanced_settings (copia).json | 33 ------ demo-tornado/saml/settings (copia).json | 30 ----- .../saml/settings (seconda copia).json | 30 ----- 6 files changed, 104 insertions(+), 181 deletions(-) delete mode 100644 demo-tornado/Docs/DEVELOPING.md delete mode 100644 demo-tornado/saml/advanced_settings (copia).json delete mode 100644 demo-tornado/saml/settings (copia).json delete mode 100644 demo-tornado/saml/settings (seconda copia).json diff --git a/README.md b/README.md index 404712e2..993105c9 100644 --- a/README.md +++ b/README.md @@ -172,6 +172,9 @@ This folder contains a Flask project that will be used as demo to show how to ad This folder contains a Pyramid project that will be used as demo to show how to add SAML support to the [Pyramid Web Framework](http://docs.pylonsproject.org/projects/pyramid/en/latest/). ``\_\_init__.py`` is the main file that configures the app and its routes, ``views.py`` is where all the logic and SAML handling takes place, and the templates are stored in the ``templates`` folder. The ``saml`` folder is the same as in the other two demos. +#### demo-tornado #### + +This folder contains a Tornado project that will be used as demo to show how to add SAML support to the Tornado Framework. ``views.py`` (with its ``settings.py``) is the main Flask file that has all the code, this file uses the templates stored at the ``templates`` folder. In the ``saml`` folder we found the ``certs`` folder to store the X.509 public and private key, and the SAML toolkit settings (``settings.json`` and ``advanced_settings.json``). #### setup.py #### @@ -1085,7 +1088,7 @@ For more info, look at the source code. Each method is documented and details ab Demos included in the toolkit ----------------------------- -The toolkit includes 2 demos to teach how use the toolkit (A Django and a Flask project), take a look on it. +The toolkit includes 3 demos to teach how use the toolkit (A Django, Flask and a Tornado project), take a look on it. Demos require that SP and IdP are well configured before test it, so edit the settings files. Notice that each python framework has it own way to handle routes/urls and process request, so focus on @@ -1165,6 +1168,106 @@ First we need to edit the ``saml/settings.json`` file, configure the SP part and Once the SP is configured, the metadata of the SP is published at the ``/metadata`` url. Based on that info, configure the IdP. +#### How it works #### + + 1. First time you access to the main view (http://localhost:8000), you can select to login and return to the same view or login and be redirected to ``/?attrs`` (attrs view). + + 2. When you click: + + 2.1 in the first link, we access to ``/?sso`` (index view). An ``AuthNRequest`` is sent to the IdP, we authenticate at the IdP and then a Response is sent through the user's client to the SP, specifically the Assertion Consumer Service view: ``/?acs``. Notice that a ``RelayState`` parameter is set to the url that initiated the process, the index view. + + 2.2 in the second link we access to ``/?attrs`` (attrs view), we will expetience have the same process described at 2.1 with the diference that as ``RelayState`` is set the ``attrs`` url. + + 3. The SAML Response is processed in the ACS ``/?acs``, if the Response is not valid, the process stops here and a message is shown. Otherwise we are redirected to the ``RelayState`` view. a) / or b) ``/?attrs`` + + 4. We are logged in the app and the user attributes are showed. At this point, we can test the single log out functionality. + + The single log out functionality could be tested by 2 ways. + + 5.1 SLO Initiated by SP. Click on the ``logout`` link at the SP, after that a Logout Request is sent to the IdP, the session at the IdP is closed and replies through the client to the SP with a Logout Response (sent to the Single Logout Service endpoint). The SLS endpoint ``/?sls`` of the SP process the Logout Response and if is valid, close the user session of the local app. Notice that the SLO Workflow starts and ends at the SP. + + 5.2 SLO Initiated by IdP. In this case, the action takes place on the IdP side, the logout process is initiated at the IdP, sends a Logout Request to the SP (SLS endpoint, ``/?sls``). The SLS endpoint of the SP process the Logout Request and if is valid, close the session of the user at the local app and send a Logout Response to the IdP (to the SLS endpoint of the IdP). The IdP receives the Logout Response, process it and close the session at of the IdP. Notice that the SLO Workflow starts and ends at the IdP. + +Notice that all the SAML Requests and Responses are handled at a unique view (index) and how GET parameters are used to know the action that must be done. + +### Demo Tornado ### + +You'll need a virtualenv with the toolkit installed on it. + +First of all you need some packages, execute: +``` +apt-get install libxml2-dev libxmlsec1-dev libxmlsec1-openssl +``` + +To run the demo you need to install the requirements first. Load your +virtualenv and execute: +``` + pip install -r demo-tornado/requirements.txt +``` + + +This will install tornado and its dependencies. Once it has finished, you have to complete the configuration +of the toolkit. You'll find it at `demo-tornado/saml/settings.json` + +Now, with the virtualenv loaded, you can run the demo like this: +``` + cd demo-tornado + python views.py +``` + +You'll have the demo running at http://localhost:8000 + +#### Content #### + +The tornado project contains: + +* ***views.py*** Is the main flask file, where or the SAML handle take place. + +* ***settings.py*** Contains the base path and the path where is located the ``saml`` folder and the ``template`` folder + +* ***templates***. Is the folder where tornado stores the templates of the project. It was implemented a base.html template that is extended by index.html and attrs.html, the templates of our simple demo that shows messages, user attributes when available and login and logout links. + +* ***saml*** Is a folder that contains the 'certs' folder that could be used to store the X.509 public and private key, and the saml toolkit settings (settings.json and advanced_settings.json). + +#### SP setup #### + +The Onelogin's Python Toolkit allows you to provide the settings info in 2 ways: Settings files or define a setting dict. In the ``demo-tornado``, it uses the first method. + +In the ``settings.py`` file we define the ``SAML_PATH``, that will target to the ``saml`` folder. We require it in order to load the settings files. + +First we need to edit the ``saml/settings.json`` file, configure the SP part and review the metadata of the IdP and complete the IdP info. Later edit the ``saml/advanced_settings.json`` files and configure the how the toolkit will work. Check the settings section of this document if you have any doubt. + +#### IdP setup #### + +Once the SP is configured, the metadata of the SP is published at the ``/metadata`` url. Based on that info, configure the IdP. + +#####Test with keycloack##### + +You can test your SP with every compatible IdP, for example Keycloack by Red Hat (Check if you need also authorization and not only authentication ) + +###### Install Docker ###### + +Install docker as suggested by [docker guide](https://docs.docker.com/install/linux/docker-ce/ubuntu/) + +###### Keycloack starting ###### + +First run: +* docker run --name keycloackContainer -d -p 8080:8080 -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=admin -e DB_VENDOR=H2 jboss/keycloak + +After first run: +* sudo docker start keycloackContainer + +Remember to stop keycloack after usage: +* sudo docker stop keycloackContainer + + +###### Keycloack useful urls ###### + +* master: http://localhost:8080/auth/admin +* users: http://localhost:8080/auth/realms/idp_dacd/account/ +* saml request: http://localhost:8080/auth/realms/idp_dacd/protocol/saml +* metadata: http://localhost:8080/auth/realms/idp_dacd/protocol/saml/descriptor + #### How it works #### 1. First time you access to the main view (http://localhost:8000), you can select to login and return to the same view or login and be redirected to ``/?attrs`` (attrs view). diff --git a/demo-tornado/Docs/DEVELOPING.md b/demo-tornado/Docs/DEVELOPING.md deleted file mode 100644 index 27ba1243..00000000 --- a/demo-tornado/Docs/DEVELOPING.md +++ /dev/null @@ -1,82 +0,0 @@ -# OneLogin's SAML Python Toolkit (compatible with Python3) - -Installation ------------- - -### Dependencies ### - - * python 3.6 - * apt-get install libxml2-dev libxmlsec1-dev libxmlsec1-openssl - * pip install xmlsec - * pip install isodate - * pip install defusedxml - * pip install python3-saml - * pip install tornado - - -***Virtualenv*** - -The use of virtualenv/virtualenvwrapper is highly recommended. - -### Create certificates ### - -in saml/cert run : - * openssl req -new -x509 -days 3652 -nodes -out sp.crt -keyout sp.key - * openssl req -new -x509 -days 3652 -nodes -out metadata.crt -keyout metadata.key - -### Useful extesion for SAML messages ### -* [SAML Chrome Panel 1.8.9](https://chrome.google.com/webstore/detail/saml-chrome-panel/paijfdbeoenhembfhkhllainmocckace/related) - - - -# Test with keycloack idp - -Installation ------------- - -### Install Docker ### -* sudo apt-get remove docker docker-engine docker.io containerd runc - -* sudo apt-get update - -* sudo apt-get install \ - apt-transport-https \ - ca-certificates \ - curl \ - gnupg-agent \ - software-properties-common -* curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - - -* sudo add-apt-repository \ - "deb [arch=amd64] https://download.docker.com/linux/ubuntu \ - $(lsb_release -cs) \ - stable" - -* sudo apt-get update - -* sudo apt-get install docker-ce docker-ce-cli containerd.io - -* sudo docker run hello-world - - -### Keycloack starting ### -First run only: -* docker run --name keycloackContainer -d -p 8080:8080 -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=admin -e DB_VENDOR=H2 jboss/keycloak - -After first run: -* sudo docker start keycloackContainer - -Remember to stop keycloack after usage: -* sudo docker stop keycloackContainer - - -### Keycloack useful urls ### -* master: http://localhost:8080/auth/admin -* users: http://localhost:8080/auth/realms/idp_dacd/account/ -* saml request: http://localhost:8080/auth/realms/idp_dacd/protocol/saml -* metadata: http://localhost:8080/auth/realms/idp_dacd/protocol/saml/descriptor - - - - - diff --git a/demo-tornado/requirements.txt b/demo-tornado/requirements.txt index 99192d20..66787013 100644 --- a/demo-tornado/requirements.txt +++ b/demo-tornado/requirements.txt @@ -1,13 +1,8 @@ -Click==7.0 defusedxml==0.5.0 isodate==0.6.0 -itsdangerous==1.1.0 -Jinja2==2.10.1 lxml==4.3.3 -MarkupSafe==1.1.1 pkgconfig==1.5.1 python3-saml==1.6.0 six==1.12.0 tornado==6.0.2 -Werkzeug==0.15.2 xmlsec==1.3.3 diff --git a/demo-tornado/saml/advanced_settings (copia).json b/demo-tornado/saml/advanced_settings (copia).json deleted file mode 100644 index 3115e17e..00000000 --- a/demo-tornado/saml/advanced_settings (copia).json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "security": { - "nameIdEncrypted": false, - "authnRequestsSigned": false, - "logoutRequestSigned": false, - "logoutResponseSigned": false, - "signMetadata": false, - "wantMessagesSigned": false, - "wantAssertionsSigned": false, - "wantNameId" : true, - "wantNameIdEncrypted": false, - "wantAssertionsEncrypted": false, - "signatureAlgorithm": "http://www.w3.org/2000/09/xmldsig#rsa-sha1", - "digestAlgorithm": "http://www.w3.org/2000/09/xmldsig#sha1" - }, - "contactPerson": { - "technical": { - "givenName": "technical_name", - "emailAddress": "technical@example.com" - }, - "support": { - "givenName": "support_name", - "emailAddress": "support@example.com" - } - }, - "organization": { - "en-US": { - "name": "sp_test", - "displayname": "SP test", - "url": "http://sp.example.com" - } - } -} \ No newline at end of file diff --git a/demo-tornado/saml/settings (copia).json b/demo-tornado/saml/settings (copia).json deleted file mode 100644 index ec40b674..00000000 --- a/demo-tornado/saml/settings (copia).json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "strict": true, - "debug": true, - "sp": { - "entityId": "https:///metadata/", - "assertionConsumerService": { - "url": "https:///?acs", - "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" - }, - "singleLogoutService": { - "url": "https:///?sls", - "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" - }, - "NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", - "x509cert": "", - "privateKey": "" - }, - "idp": { - "entityId": "https://app.onelogin.com/saml/metadata/", - "singleSignOnService": { - "url": "https://app.onelogin.com/trust/saml2/http-post/sso/", - "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" - }, - "singleLogoutService": { - "url": "https://app.onelogin.com/trust/saml2/http-redirect/slo/", - "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" - }, - "x509cert": "" - } -} diff --git a/demo-tornado/saml/settings (seconda copia).json b/demo-tornado/saml/settings (seconda copia).json deleted file mode 100644 index 49a626b6..00000000 --- a/demo-tornado/saml/settings (seconda copia).json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "strict": true, - "debug": true, - "sp": { - "entityId": "http://0.0.0.0:8000/metadata/", - "assertionConsumerService": { - "url": "http://0.0.0.0:8000/?acs", - "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" - }, - "singleLogoutService": { - "url": "http://0.0.0.0:8000/?sls", - "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" - }, - "NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", - "x509cert": "", - "privateKey": "" - }, - "idp": { - "entityId": "http://localhost:8080/auth/realms/idp_dacd", - "singleSignOnService": { - "url": "http://localhost:8080/auth/realms/idp_dacd/protocol/saml", - "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" - }, - "singleLogoutService": { - "url": "http://localhost:8080/auth/realms/idp_dacd/protocol/saml", - "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" - }, - "x509cert": "MIICnzCCAYcCBgFqC3f1FDANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDDAhpZHBfZGFjZDAeFw0xOTA0MTEwODE0MzJaFw0yOTA0MTEwODE2MTJaMBMxETAPBgNVBAMMCGlkcF9kYWNkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuhRERlbbwUxabS4lnGFOpxBKACpcwQwOSvyCKD3bLDvzfQIRh1x3RDq+6xxBcdP86PKRYjwZc/64HYFUbe9FCDLf1SDQz7XTtpftydRrTUydVaj+3FHhyCLcLkJldMVneVMZWIXSzUISvcURL3sr6HVJ74Uy0KPPOo3vQQ6RRaNaB3RCdZaNbzxjbG5eBgSvWHGE+vkozHpS7y6LHXDP1wXenH65RRrat8PQ/Qp4WVi2U5rVfA2SFbcwohjaJ+vOYH+oARnk5a2IosAMHBtONorPnPrcV7MqUXX7q19hnlFRZA34YVcF6kkWWsS1V/mRm2kK6EE/mABvX4mrefWg+QIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQARKkt9TKg5jLLc7HZSYxHD0MGqDQ8jMAXjpYYaEoIGyYnE31WOEG6HlzHFrkp5ioBT8vTIfejpGGkcL0qspSjQRBYH1eHOEToFo2vhmr0yDUtePNVcQtRN0ImAwNvKU/PciSi4dX0Y8Z/VfeRn8k979o0X82xZSYIxLhKlFp9toOHz0pNTU0Ie4h1zxcPd0L7KAn5eQPHoJwXsdWkM9aoZtCQCjbiexpfOdEOfnUn8QisinAWqcC/gKl9XG7XU4P5cevTeqZgnK0W+ilJVkkJX2zZWlAji+0PKLGr3BgJCfkwUcsm1C8U2ysTGkW1mZHfKVzK2NPA0bSlGgZwbvki+" - } -} From 43118e7c41b8f4a34232780cdf5c1700a164b8a8 Mon Sep 17 00:00:00 2001 From: davideZanin Date: Tue, 23 Apr 2019 14:42:16 +0200 Subject: [PATCH 003/205] docs updated --- demo-tornado/README.md | 9 +++++++++ demo-tornado/README.txt | 7 ------- 2 files changed, 9 insertions(+), 7 deletions(-) create mode 100644 demo-tornado/README.md delete mode 100644 demo-tornado/README.txt diff --git a/demo-tornado/README.md b/demo-tornado/README.md new file mode 100644 index 00000000..aab7ef19 --- /dev/null +++ b/demo-tornado/README.md @@ -0,0 +1,9 @@ +#Tornado Demo# +Fully-working tornado-demo. + +###ABOUT ISSUE### +This is only a demo, some issues about session still remain. +Actually the session is global. + +###PRODUCTION### +Remember to disable debugging in production. diff --git a/demo-tornado/README.txt b/demo-tornado/README.txt deleted file mode 100644 index dd4418ca..00000000 --- a/demo-tornado/README.txt +++ /dev/null @@ -1,7 +0,0 @@ -Fully-working tornado-demo. - -ABOUT ISSSUE -This is only a demo, some issues about session still remain. - -PRODUCTION -Remember also to disable debugging in production. From 47ba1cad2dd44ab10f1962e67ba4d46c85c29e94 Mon Sep 17 00:00:00 2001 From: davideZanin Date: Tue, 23 Apr 2019 14:52:46 +0200 Subject: [PATCH 004/205] solved formatting error --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 993105c9..6b8a7449 100644 --- a/README.md +++ b/README.md @@ -1241,7 +1241,7 @@ First we need to edit the ``saml/settings.json`` file, configure the SP part and Once the SP is configured, the metadata of the SP is published at the ``/metadata`` url. Based on that info, configure the IdP. -#####Test with keycloack##### +##### Test with keycloack ##### You can test your SP with every compatible IdP, for example Keycloack by Red Hat (Check if you need also authorization and not only authentication ) From a46fddb33cbb0f2254fa7de09803b432a5fbd6f9 Mon Sep 17 00:00:00 2001 From: davideZanin Date: Tue, 23 Apr 2019 15:35:25 +0200 Subject: [PATCH 005/205] ready to pull req --- demo-tornado/README.md | 6 +++--- demo-tornado/saml/advanced_settings.json | 15 ++++++--------- demo-tornado/saml/settings.json | 16 ++++++++-------- 3 files changed, 17 insertions(+), 20 deletions(-) diff --git a/demo-tornado/README.md b/demo-tornado/README.md index aab7ef19..428d192c 100644 --- a/demo-tornado/README.md +++ b/demo-tornado/README.md @@ -1,9 +1,9 @@ -#Tornado Demo# +# Tornado Demo # Fully-working tornado-demo. -###ABOUT ISSUE### +### About issues ### This is only a demo, some issues about session still remain. Actually the session is global. -###PRODUCTION### +### Production ### Remember to disable debugging in production. diff --git a/demo-tornado/saml/advanced_settings.json b/demo-tornado/saml/advanced_settings.json index e14e1170..3115e17e 100644 --- a/demo-tornado/saml/advanced_settings.json +++ b/demo-tornado/saml/advanced_settings.json @@ -1,15 +1,12 @@ { "security": { "nameIdEncrypted": false, - "authnRequestsSigned": true, - "logoutRequestSigned": true, - "logoutResponseSigned": true, - "signMetadata": { - "keyFileName": "metadata.key", - "certFileName": "metadata.crt" - }, + "authnRequestsSigned": false, + "logoutRequestSigned": false, + "logoutResponseSigned": false, + "signMetadata": false, "wantMessagesSigned": false, - "wantAssertionsSigned": true, + "wantAssertionsSigned": false, "wantNameId" : true, "wantNameIdEncrypted": false, "wantAssertionsEncrypted": false, @@ -33,4 +30,4 @@ "url": "http://sp.example.com" } } -} +} \ No newline at end of file diff --git a/demo-tornado/saml/settings.json b/demo-tornado/saml/settings.json index c91e5cc9..391b91c1 100644 --- a/demo-tornado/saml/settings.json +++ b/demo-tornado/saml/settings.json @@ -2,13 +2,13 @@ "strict": true, "debug": true, "sp": { - "entityId": "http://localhost:8000/metadata/", + "entityId": "https:///metadata/", "assertionConsumerService": { - "url": "http://localhost:8000/?acs", + "url": "https:///?acs", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" }, "singleLogoutService": { - "url": "http://localhost:8000/?sls", + "url": "https:///?sls", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" }, "NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", @@ -16,15 +16,15 @@ "privateKey": "" }, "idp": { - "entityId": "http://localhost:8080/auth/realms/idp_dacd", + "entityId": "https://app.onelogin.com/saml/metadata/", "singleSignOnService": { - "url": "http://localhost:8080/auth/realms/idp_dacd/protocol/saml", + "url": "https://app.onelogin.com/trust/saml2/http-post/sso/", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" }, "singleLogoutService": { - "url": "http://localhost:8080/auth/realms/idp_dacd/protocol/saml", + "url": "https://app.onelogin.com/trust/saml2/http-redirect/slo/", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" }, - "x509cert": "MIICnzCCAYcCBgFqC3f1FDANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDDAhpZHBfZGFjZDAeFw0xOTA0MTEwODE0MzJaFw0yOTA0MTEwODE2MTJaMBMxETAPBgNVBAMMCGlkcF9kYWNkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuhRERlbbwUxabS4lnGFOpxBKACpcwQwOSvyCKD3bLDvzfQIRh1x3RDq+6xxBcdP86PKRYjwZc/64HYFUbe9FCDLf1SDQz7XTtpftydRrTUydVaj+3FHhyCLcLkJldMVneVMZWIXSzUISvcURL3sr6HVJ74Uy0KPPOo3vQQ6RRaNaB3RCdZaNbzxjbG5eBgSvWHGE+vkozHpS7y6LHXDP1wXenH65RRrat8PQ/Qp4WVi2U5rVfA2SFbcwohjaJ+vOYH+oARnk5a2IosAMHBtONorPnPrcV7MqUXX7q19hnlFRZA34YVcF6kkWWsS1V/mRm2kK6EE/mABvX4mrefWg+QIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQARKkt9TKg5jLLc7HZSYxHD0MGqDQ8jMAXjpYYaEoIGyYnE31WOEG6HlzHFrkp5ioBT8vTIfejpGGkcL0qspSjQRBYH1eHOEToFo2vhmr0yDUtePNVcQtRN0ImAwNvKU/PciSi4dX0Y8Z/VfeRn8k979o0X82xZSYIxLhKlFp9toOHz0pNTU0Ie4h1zxcPd0L7KAn5eQPHoJwXsdWkM9aoZtCQCjbiexpfOdEOfnUn8QisinAWqcC/gKl9XG7XU4P5cevTeqZgnK0W+ilJVkkJX2zZWlAji+0PKLGr3BgJCfkwUcsm1C8U2ysTGkW1mZHfKVzK2NPA0bSlGgZwbvki+" + "x509cert": "" } -} +} \ No newline at end of file From c77f75f645451032769decd4ae8cba5df30e72bb Mon Sep 17 00:00:00 2001 From: "Bernhard M. Wiedemann" Date: Thu, 1 Aug 2019 13:19:47 +0200 Subject: [PATCH 006/205] tests: make tests pass in 2020 For this we update 2020 to 2999 diff decoded is a/tests/data/responses/unsigned_response.xml b/tests/data/responses/unsigned_response.xml @@ -9,15 +9,15 @@ someone@example.com - + - + http://stuff.com/endpoints/metadata.php - + urn:oasis:names:tc:SAML:2.0:ac:classes:Password --- .../invalids/invalid_subjectconfirmation_nb.xml.base64 | 2 +- tests/data/responses/unsigned_response.xml.base64 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/data/responses/invalids/invalid_subjectconfirmation_nb.xml.base64 b/tests/data/responses/invalids/invalid_subjectconfirmation_nb.xml.base64 index ff6d371c..5d1f8bc1 100644 --- a/tests/data/responses/invalids/invalid_subjectconfirmation_nb.xml.base64 +++ b/tests/data/responses/invalids/invalid_subjectconfirmation_nb.xml.base64 @@ -1 +1 @@ -PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9InBmeGMzMmFlZDY3LTgyMGYtNDI5Ni0wYzIwLTIwNWExMGRkNTc4NyIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIiBEZXN0aW5hdGlvbj0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCI+DQogIDxzYW1sOklzc3Vlcj5odHRwOi8vaWRwLmV4YW1wbGUuY29tLzwvc2FtbDpJc3N1ZXI+DQogIDxzYW1scDpTdGF0dXM+DQogICAgPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPg0KICA8L3NhbWxwOlN0YXR1cz4NCiAgPHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9InBmeDc4NDE5OTFjLWM3M2YtNDAzNS1lMmVlLWMxNzBjMGUxZDNlNCIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIj4NCiAgICA8c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS88L3NhbWw6SXNzdWVyPiAgICANCiAgICA8c2FtbDpTdWJqZWN0Pg0KICAgICAgPHNhbWw6TmFtZUlEIFNQTmFtZVF1YWxpZmllcj0iaGVsbG8uY29tIiBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+c29tZW9uZUBleGFtcGxlLmNvbTwvc2FtbDpOYW1lSUQ+DQogICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+DQogICAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdEJlZm9yZT0iMjAyMC0wNi0xN1QxNDo1OToxNFoiIFJlY2lwaWVudD0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCIvPg0KICAgICAgPC9zYW1sOlN1YmplY3RDb25maXJtYXRpb24+DQogICAgPC9zYW1sOlN1YmplY3Q+DQogICAgPHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTAtMDYtMTdUMTQ6NTM6NDRaIiBOb3RPbk9yQWZ0ZXI9IjIwMjEtMDYtMTdUMTQ6NTk6MTRaIj4NCiAgICAgIDxzYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+DQogICAgICAgIDxzYW1sOkF1ZGllbmNlPmh0dHA6Ly9zdHVmZi5jb20vZW5kcG9pbnRzL21ldGFkYXRhLnBocDwvc2FtbDpBdWRpZW5jZT4NCiAgICAgIDwvc2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPg0KICAgIDwvc2FtbDpDb25kaXRpb25zPg0KICAgIDxzYW1sOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAxMS0wNi0xN1QxNDo1NDowN1oiIFNlc3Npb25Ob3RPbk9yQWZ0ZXI9IjIwMjEtMDYtMTdUMjI6NTQ6MTRaIiBTZXNzaW9uSW5kZXg9Il81MWJlMzc5NjVmZWI1NTc5ZDgwMzE0MTA3NjkzNmRjMmU5ZDFkOThlYmYiPg0KICAgICAgPHNhbWw6QXV0aG5Db250ZXh0Pg0KICAgICAgICA8c2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZDwvc2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj4NCiAgICAgIDwvc2FtbDpBdXRobkNvbnRleHQ+DQogICAgPC9zYW1sOkF1dGhuU3RhdGVtZW50Pg0KICAgIDxzYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4NCiAgICAgIDxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJtYWlsIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj4NCiAgICAgICAgPHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+c29tZW9uZUBleGFtcGxlLmNvbTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT4NCiAgICAgIDwvc2FtbDpBdHRyaWJ1dGU+DQogICAgPC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4NCiAgPC9zYW1sOkFzc2VydGlvbj4NCjwvc2FtbHA6UmVzcG9uc2U+ +PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9InBmeGMzMmFlZDY3LTgyMGYtNDI5Ni0wYzIwLTIwNWExMGRkNTc4NyIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIiBEZXN0aW5hdGlvbj0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCI+DQogIDxzYW1sOklzc3Vlcj5odHRwOi8vaWRwLmV4YW1wbGUuY29tLzwvc2FtbDpJc3N1ZXI+DQogIDxzYW1scDpTdGF0dXM+DQogICAgPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPg0KICA8L3NhbWxwOlN0YXR1cz4NCiAgPHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9InBmeDc4NDE5OTFjLWM3M2YtNDAzNS1lMmVlLWMxNzBjMGUxZDNlNCIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIj4NCiAgICA8c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS88L3NhbWw6SXNzdWVyPiAgICANCiAgICA8c2FtbDpTdWJqZWN0Pg0KICAgICAgPHNhbWw6TmFtZUlEIFNQTmFtZVF1YWxpZmllcj0iaGVsbG8uY29tIiBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+c29tZW9uZUBleGFtcGxlLmNvbTwvc2FtbDpOYW1lSUQ+DQogICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+DQogICAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdEJlZm9yZT0iMjk5OS0wNi0xN1QxNDo1OToxNFoiIFJlY2lwaWVudD0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCIvPg0KICAgICAgPC9zYW1sOlN1YmplY3RDb25maXJtYXRpb24+DQogICAgPC9zYW1sOlN1YmplY3Q+DQogICAgPHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTAtMDYtMTdUMTQ6NTM6NDRaIiBOb3RPbk9yQWZ0ZXI9IjI5OTktMDYtMTdUMTQ6NTk6MTRaIj4NCiAgICAgIDxzYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+DQogICAgICAgIDxzYW1sOkF1ZGllbmNlPmh0dHA6Ly9zdHVmZi5jb20vZW5kcG9pbnRzL21ldGFkYXRhLnBocDwvc2FtbDpBdWRpZW5jZT4NCiAgICAgIDwvc2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPg0KICAgIDwvc2FtbDpDb25kaXRpb25zPg0KICAgIDxzYW1sOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAxMS0wNi0xN1QxNDo1NDowN1oiIFNlc3Npb25Ob3RPbk9yQWZ0ZXI9IjI5OTktMDYtMTdUMjI6NTQ6MTRaIiBTZXNzaW9uSW5kZXg9Il81MWJlMzc5NjVmZWI1NTc5ZDgwMzE0MTA3NjkzNmRjMmU5ZDFkOThlYmYiPg0KICAgICAgPHNhbWw6QXV0aG5Db250ZXh0Pg0KICAgICAgICA8c2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZDwvc2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj4NCiAgICAgIDwvc2FtbDpBdXRobkNvbnRleHQ+DQogICAgPC9zYW1sOkF1dGhuU3RhdGVtZW50Pg0KICAgIDxzYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4NCiAgICAgIDxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJtYWlsIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj4NCiAgICAgICAgPHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+c29tZW9uZUBleGFtcGxlLmNvbTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT4NCiAgICAgIDwvc2FtbDpBdHRyaWJ1dGU+DQogICAgPC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4NCiAgPC9zYW1sOkFzc2VydGlvbj4NCjwvc2FtbHA6UmVzcG9uc2U+ diff --git a/tests/data/responses/unsigned_response.xml.base64 b/tests/data/responses/unsigned_response.xml.base64 index 66892b53..18abcf23 100644 --- a/tests/data/responses/unsigned_response.xml.base64 +++ b/tests/data/responses/unsigned_response.xml.base64 @@ -1 +1 @@ -PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9InBmeGMzMmFlZDY3LTgyMGYtNDI5Ni0wYzIwLTIwNWExMGRkNTc4NyIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIiBEZXN0aW5hdGlvbj0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCI+DQogIDxzYW1sOklzc3Vlcj5odHRwOi8vaWRwLmV4YW1wbGUuY29tLzwvc2FtbDpJc3N1ZXI+DQogIDxzYW1scDpTdGF0dXM+DQogICAgPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPg0KICA8L3NhbWxwOlN0YXR1cz4NCiAgPHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9InBmeDc4NDE5OTFjLWM3M2YtNDAzNS1lMmVlLWMxNzBjMGUxZDNlNCIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIj4NCiAgICA8c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS88L3NhbWw6SXNzdWVyPiAgICANCiAgICA8c2FtbDpTdWJqZWN0Pg0KICAgICAgPHNhbWw6TmFtZUlEIFNQTmFtZVF1YWxpZmllcj0iaGVsbG8uY29tIiBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+c29tZW9uZUBleGFtcGxlLmNvbTwvc2FtbDpOYW1lSUQ+DQogICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+DQogICAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjAyMC0wNi0xN1QxNDo1OToxNFoiIFJlY2lwaWVudD0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCIvPg0KICAgICAgPC9zYW1sOlN1YmplY3RDb25maXJtYXRpb24+DQogICAgPC9zYW1sOlN1YmplY3Q+DQogICAgPHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTAtMDYtMTdUMTQ6NTM6NDRaIiBOb3RPbk9yQWZ0ZXI9IjIwMjEtMDYtMTdUMTQ6NTk6MTRaIj4NCiAgICAgIDxzYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+DQogICAgICAgIDxzYW1sOkF1ZGllbmNlPmh0dHA6Ly9zdHVmZi5jb20vZW5kcG9pbnRzL21ldGFkYXRhLnBocDwvc2FtbDpBdWRpZW5jZT4NCiAgICAgIDwvc2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPg0KICAgIDwvc2FtbDpDb25kaXRpb25zPg0KICAgIDxzYW1sOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAxMS0wNi0xN1QxNDo1NDowN1oiIFNlc3Npb25Ob3RPbk9yQWZ0ZXI9IjIwMjEtMDYtMTdUMjI6NTQ6MTRaIiBTZXNzaW9uSW5kZXg9Il81MWJlMzc5NjVmZWI1NTc5ZDgwMzE0MTA3NjkzNmRjMmU5ZDFkOThlYmYiPg0KICAgICAgPHNhbWw6QXV0aG5Db250ZXh0Pg0KICAgICAgICA8c2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZDwvc2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj4NCiAgICAgIDwvc2FtbDpBdXRobkNvbnRleHQ+DQogICAgPC9zYW1sOkF1dGhuU3RhdGVtZW50Pg0KICAgIDxzYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4NCiAgICAgIDxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJtYWlsIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj4NCiAgICAgICAgPHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+c29tZW9uZUBleGFtcGxlLmNvbTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT4NCiAgICAgIDwvc2FtbDpBdHRyaWJ1dGU+DQogICAgPC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4NCiAgPC9zYW1sOkFzc2VydGlvbj4NCjwvc2FtbHA6UmVzcG9uc2U+ +PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9InBmeGMzMmFlZDY3LTgyMGYtNDI5Ni0wYzIwLTIwNWExMGRkNTc4NyIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIiBEZXN0aW5hdGlvbj0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCI+DQogIDxzYW1sOklzc3Vlcj5odHRwOi8vaWRwLmV4YW1wbGUuY29tLzwvc2FtbDpJc3N1ZXI+DQogIDxzYW1scDpTdGF0dXM+DQogICAgPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPg0KICA8L3NhbWxwOlN0YXR1cz4NCiAgPHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9InBmeDc4NDE5OTFjLWM3M2YtNDAzNS1lMmVlLWMxNzBjMGUxZDNlNCIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIj4NCiAgICA8c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS88L3NhbWw6SXNzdWVyPiAgICANCiAgICA8c2FtbDpTdWJqZWN0Pg0KICAgICAgPHNhbWw6TmFtZUlEIFNQTmFtZVF1YWxpZmllcj0iaGVsbG8uY29tIiBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+c29tZW9uZUBleGFtcGxlLmNvbTwvc2FtbDpOYW1lSUQ+DQogICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+DQogICAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjk5OS0wNi0xN1QxNDo1OToxNFoiIFJlY2lwaWVudD0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCIvPg0KICAgICAgPC9zYW1sOlN1YmplY3RDb25maXJtYXRpb24+DQogICAgPC9zYW1sOlN1YmplY3Q+DQogICAgPHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTAtMDYtMTdUMTQ6NTM6NDRaIiBOb3RPbk9yQWZ0ZXI9IjI5OTktMDYtMTdUMTQ6NTk6MTRaIj4NCiAgICAgIDxzYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+DQogICAgICAgIDxzYW1sOkF1ZGllbmNlPmh0dHA6Ly9zdHVmZi5jb20vZW5kcG9pbnRzL21ldGFkYXRhLnBocDwvc2FtbDpBdWRpZW5jZT4NCiAgICAgIDwvc2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPg0KICAgIDwvc2FtbDpDb25kaXRpb25zPg0KICAgIDxzYW1sOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAxMS0wNi0xN1QxNDo1NDowN1oiIFNlc3Npb25Ob3RPbk9yQWZ0ZXI9IjI5OTktMDYtMTdUMjI6NTQ6MTRaIiBTZXNzaW9uSW5kZXg9Il81MWJlMzc5NjVmZWI1NTc5ZDgwMzE0MTA3NjkzNmRjMmU5ZDFkOThlYmYiPg0KICAgICAgPHNhbWw6QXV0aG5Db250ZXh0Pg0KICAgICAgICA8c2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZDwvc2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj4NCiAgICAgIDwvc2FtbDpBdXRobkNvbnRleHQ+DQogICAgPC9zYW1sOkF1dGhuU3RhdGVtZW50Pg0KICAgIDxzYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4NCiAgICAgIDxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJtYWlsIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj4NCiAgICAgICAgPHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+c29tZW9uZUBleGFtcGxlLmNvbTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT4NCiAgICAgIDwvc2FtbDpBdHRyaWJ1dGU+DQogICAgPC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4NCiAgPC9zYW1sOkFzc2VydGlvbj4NCjwvc2FtbHA6UmVzcG9uc2U+ From 86645a8e2538ad09df38bddead37c090181d1350 Mon Sep 17 00:00:00 2001 From: "Bernhard M. Wiedemann" Date: Wed, 21 Aug 2019 10:12:01 +0200 Subject: [PATCH 007/205] Drop broken python-3.4 tests They failed with `This lxml version requires Python 2.7, 3.5 or later.` --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 8023585e..43d4b210 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,9 @@ language: python python: - '2.7' - - '3.4' - '3.5' - '3.6' + - '3.7' matrix: include: From b7ae95132f35a5b649c546bb79442422aff3073a Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Wed, 11 Sep 2019 16:35:40 +0200 Subject: [PATCH 008/205] Set true as the default value for strict setting --- src/onelogin/saml2/settings.py | 4 ++-- tests/src/OneLogin/saml2_tests/settings_test.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/onelogin/saml2/settings.py b/src/onelogin/saml2/settings.py index 003aa0ad..5057a5ab 100644 --- a/src/onelogin/saml2/settings.py +++ b/src/onelogin/saml2/settings.py @@ -83,7 +83,7 @@ def __init__(self, settings=None, custom_base_path=None, sp_validation_only=Fals """ self.__sp_validation_only = sp_validation_only self.__paths = {} - self.__strict = False + self.__strict = True self.__debug = False self.__sp = {} self.__idp = {} @@ -214,7 +214,7 @@ def __load_settings_from_dict(self, settings): self.__errors = [] self.__sp = settings['sp'] self.__idp = settings.get('idp', {}) - self.__strict = settings.get('strict', False) + self.__strict = settings.get('strict', True) self.__debug = settings.get('debug', False) self.__security = settings.get('security', {}) self.__contacts = settings.get('contactPerson', {}) diff --git a/tests/src/OneLogin/saml2_tests/settings_test.py b/tests/src/OneLogin/saml2_tests/settings_test.py index 75031009..942fdd96 100644 --- a/tests/src/OneLogin/saml2_tests/settings_test.py +++ b/tests/src/OneLogin/saml2_tests/settings_test.py @@ -755,7 +755,7 @@ def testIsStrict(self): del settings_info['strict'] settings = OneLogin_Saml2_Settings(settings_info) - self.assertFalse(settings.is_strict()) + self.assertTrue(settings.is_strict()) settings_info['strict'] = False settings_2 = OneLogin_Saml2_Settings(settings_info) From 1694935585554699d474fcf10f8b31a6aeb52cc7 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Wed, 11 Sep 2019 16:42:36 +0200 Subject: [PATCH 009/205] Release 1.8.0 --- README.md | 2 ++ changelog.md | 6 ++++-- setup.py | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e652786e..106e93c2 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,8 @@ This version supports Python3. There is a separate version that only support Pyt #### Warning #### +Version 1.8.0 sets strict mode active by default + Update ``python3-saml`` to ``1.5.0``, this version includes security improvements for preventing XEE and Xpath Injections. Update ``python3-saml`` to ``1.4.0``, this version includes a fix for the [CVE-2017-11427](https://www.cvedetails.com/cve/CVE-2017-11427/) vulnerability. diff --git a/changelog.md b/changelog.md index 9d7e4384..f0e28708 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,8 @@ # python3-saml changelog -### 1.7.1 (unrelease) -* Drop python3.4 support +### 1.8.0 (Sep 11, 2019) +* Set true as the default value for strict setting +* [#152](https://github.com/onelogin/python3-saml/pull/152/files) Don't clean xsd and xsi namespaces +* Drop python3.4 support due lxml. See lxml 4.4.0 (2019-07-27) ### 1.7.0 (Jul 02, 2019) * Adjusted acs endpoint to extract NameQualifier and SPNameQualifier from SAMLResponse. Adjusted single logout service to provide NameQualifier and SPNameQualifier to logout method. Add getNameIdNameQualifier to Auth and SamlResponse. Extend logout method from Auth and LogoutRequest constructor to support SPNameQualifier parameter. Align LogoutRequest constructor with SAML specs diff --git a/setup.py b/setup.py index 3a248144..cb483f2d 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ setup( name='python3-saml', - version='1.7.0', + version='1.8.0', description='Onelogin Python Toolkit. Add SAML support to your Python software using this library', classifiers=[ 'Development Status :: 5 - Production/Stable', From f900996dd53c2c96eeec25cc2bedf086847dbbbe Mon Sep 17 00:00:00 2001 From: Mika Lehtinen Date: Fri, 18 Oct 2019 11:56:22 +0300 Subject: [PATCH 010/205] Fix parameter type annotations in merge_settings --- src/onelogin/saml2/idp_metadata_parser.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/onelogin/saml2/idp_metadata_parser.py b/src/onelogin/saml2/idp_metadata_parser.py index 036028a2..88411f45 100644 --- a/src/onelogin/saml2/idp_metadata_parser.py +++ b/src/onelogin/saml2/idp_metadata_parser.py @@ -219,9 +219,9 @@ def merge_settings(settings, new_metadata_settings): """ Will update the settings with the provided new settings data extracted from the IdP metadata :param settings: Current settings dict data - :type settings: string + :type settings: dict :param new_metadata_settings: Settings to be merged (extracted from IdP metadata after parsing) - :type new_metadata_settings: string + :type new_metadata_settings: dict :returns: merged settings :rtype: dict """ From 99567790002a317630be417c84b53c892b81682d Mon Sep 17 00:00:00 2001 From: Florent PIGOUT Date: Wed, 14 Aug 2019 17:11:36 +0200 Subject: [PATCH 011/205] Do not touch signed message --- src/onelogin/saml2/utils.py | 8 ++++---- ...ponse_without_assertion_reference_uri.xml.base64 | 1 + .../response_without_reference_uri.xml.base64 | 2 +- tests/src/OneLogin/saml2_tests/response_test.py | 13 +++++++++++-- 4 files changed, 17 insertions(+), 7 deletions(-) create mode 100644 tests/data/responses/response_without_assertion_reference_uri.xml.base64 diff --git a/src/onelogin/saml2/utils.py b/src/onelogin/saml2/utils.py index 4647c230..ebaa9c6d 100644 --- a/src/onelogin/saml2/utils.py +++ b/src/onelogin/saml2/utils.py @@ -956,10 +956,10 @@ def validate_node_sign(signature_node, elem, cert=None, fingerprint=None, finger ) # Check if Reference URI is empty - reference_elem = OneLogin_Saml2_XML.query(signature_node, '//ds:Reference') - if len(reference_elem) > 0: - if reference_elem[0].get('URI') == '': - reference_elem[0].set('URI', '#%s' % signature_node.getparent().get('ID')) + # reference_elem = OneLogin_Saml2_XML.query(signature_node, '//ds:Reference') + # if len(reference_elem) > 0: + # if reference_elem[0].get('URI') == '': + # reference_elem[0].set('URI', '#%s' % signature_node.getparent().get('ID')) if validatecert: manager = xmlsec.KeysManager() diff --git a/tests/data/responses/response_without_assertion_reference_uri.xml.base64 b/tests/data/responses/response_without_assertion_reference_uri.xml.base64 new file mode 100644 index 00000000..8c4dab5d --- /dev/null +++ b/tests/data/responses/response_without_assertion_reference_uri.xml.base64 @@ -0,0 +1 @@ +PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1scDpSZXNwb25zZSB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiBJRD0icGZ4ZDU5NDM0N2QtNDk1Zi1iOGQxLTBlZTItNDFjZmRhMTRkZDM1IiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAxNS0wMS0wMlQyMjo0ODo0OFoiIERlc3RpbmF0aW9uPSJodHRwOi8vbG9jYWxob3N0OjkwMDEvdjEvdXNlcnMvYXV0aG9yaXplL3NhbWwiIENvbnNlbnQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjb25zZW50OnVuc3BlY2lmaWVkIiBJblJlc3BvbnNlVG89Il9lZDkxNWE0MC03NGZiLTAxMzItNWIxNi00OGUwZWIxNGExYzciPgogIDxJc3N1ZXIgeG1sbnM9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPmh0dHA6Ly9leGFtcGxlLmNvbTwvSXNzdWVyPgogIDxzYW1scDpTdGF0dXM+CiAgICA8c2FtbHA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+CiAgPC9zYW1scDpTdGF0dXM+CgogIDxBc3NlcnRpb24geG1sbnM9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJfNzAwYWMzMjAtNzRmZi0wMTMyLTViMTQtNDhlMGViMTRhMWM3IiBJc3N1ZUluc3RhbnQ9IjIwMTUtMDEtMDJUMjI6NDg6NDhaIiBWZXJzaW9uPSIyLjAiPgogICAgPElzc3Vlcj5odHRwOi8vZXhhbXBsZS5jb208L0lzc3Vlcj4KICAgIDxkczpTaWduYXR1cmUgeG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPgogIDxkczpTaWduZWRJbmZvPgogICAgPGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz4KICAgIDxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjcnNhLXNoYTEiLz4KICAgIDxkczpSZWZlcmVuY2UgVVJJPSIiPgogICAgICA8ZHM6VHJhbnNmb3Jtcz4KICAgICAgICA8ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz4KICAgICAgICA8ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+CiAgICAgIDwvZHM6VHJhbnNmb3Jtcz4KICAgICAgPGRzOkRpZ2VzdE1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNzaGExIi8+CiAgICAgIDxkczpEaWdlc3RWYWx1ZT5qQ2dlWENQREZsd2pUZ3FnUHAwbVUyVHF3OWc9PC9kczpEaWdlc3RWYWx1ZT4KICAgIDwvZHM6UmVmZXJlbmNlPgogIDwvZHM6U2lnbmVkSW5mbz4KICA8ZHM6U2lnbmF0dXJlVmFsdWU+bG9SN21DRmlNSURIUHBLeVgzRUd2dzJYeTZycEtFZWZVMDhYS1lWRXJ6MXB3a1BUUFFlYU5iK2RGMHZLai9rNQoyUmJ2Z3ZFUFN2ZGI3RDJOMTY5QjJMTGVmbXpaWTBDY0RKcThkK3lNbnZSNER3YitSUFl6bWJoS29XQ1ZyY3VPCnNvbEUxQTg3WFZjenNpd2JYRWllM2p4RHdDSk5vWi9GRFJRZy80RHRQVmc9PC9kczpTaWduYXR1cmVWYWx1ZT4KPGRzOktleUluZm8+CiAgPGRzOlg1MDlEYXRhPgogICAgPGRzOlg1MDlDZXJ0aWZpY2F0ZT5NSUlDVnpDQ0FjQUNDUURJVkhhTlNCWUw2VEFOQmdrcWhraUc5dzBCQVFzRkFEQndNUXN3Q1FZRFZRUUdFd0pHVWpFT01Bd0dBMVVFQ0F3RlVHRnlhWE14RGpBTUJnTlZCQWNNQlZCaGNtbHpNUll3RkFZRFZRUUtEQTFPYjNaaGNHOXpkQ0JVUlZOVU1Ta3dKd1lKS29aSWh2Y05BUWtCRmhwbWJHOXlaVzUwTG5CcFoyOTFkRUJ1YjNaaGNHOXpkQzVtY2pBZUZ3MHhOREF5TVRNeE16VXpOREJhRncweE5UQXlNVE14TXpVek5EQmFNSEF4Q3pBSkJnTlZCQVlUQWtaU01RNHdEQVlEVlFRSURBVlFZWEpwY3pFT01Bd0dBMVVFQnd3RlVHRnlhWE14RmpBVUJnTlZCQW9NRFU1dmRtRndiM04wSUZSRlUxUXhLVEFuQmdrcWhraUc5dzBCQ1FFV0dtWnNiM0psYm5RdWNHbG5iM1YwUUc1dmRtRndiM04wTG1aeU1JR2ZNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0R05BRENCaVFLQmdRQ2hMRkhuM0xuTjRKUS83V0NkWXVweGtVZ2NOT1FuUEYreWxsKy9EUHB1eDlucGZZMDU5UElVYXRCOFg3a0NuNWk4dFJ3SXkvaWtISlI2TXI4K01QdmM2Vk9aRHhQTmRadk1vLzhsaHhyYk4zSmRydzN3aFptVS9LUFI5RjNCZEZkdStTTHpyTWwxVERVWmxQdFk5WHpVRlhjcU44SVhjeThUSnpDQmVOZXkzUUlEQVFBQk1BMEdDU3FHU0liM0RRRUJDd1VBQTRHQkFDdEo4ZmVHemUxTkhCNVZ3MThqTVVQdkhvN0gzR3dtajZaREFYUWxhaUFYTXVOQnhOWFZXVndpZmw2VituVzN3OVFhN0Zlby9uWi9PNFRVT0gxbnorYWRrbGNDRDRRcFphRUlibUFicmlQV0pLZ2I0TFdHaHFRcnV3WVI3SXRUUjFNTlg5Z0xiUDB6MHp2REVRbm50L1ZVV0ZFQkxTSnE0WjROcmU4TEZtUzI8L2RzOlg1MDlDZXJ0aWZpY2F0ZT4KICA8L2RzOlg1MDlEYXRhPgo8L2RzOktleUluZm8+PC9kczpTaWduYXR1cmU+PFN1YmplY3Q+CiAgICAgIDxOYW1lSUQgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjE6bmFtZWlkLWZvcm1hdDplbWFpbEFkZHJlc3MiPnNhbWxAdXNlci5jb208L05hbWVJRD4KICAgICAgPFN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj4KICAgICAgICA8U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgSW5SZXNwb25zZVRvPSJfZWQ5MTVhNDAtNzRmYi0wMTMyLTViMTYtNDhlMGViMTRhMWM3IiBOb3RPbk9yQWZ0ZXI9IjIwMzgtMDEtMDJUMjI6NTE6NDhaIiBSZWNpcGllbnQ9Imh0dHA6Ly9sb2NhbGhvc3Q6OTAwMS92MS91c2Vycy9hdXRob3JpemUvc2FtbCIvPgogICAgICA8L1N1YmplY3RDb25maXJtYXRpb24+CiAgICA8L1N1YmplY3Q+CiAgICA8Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTUtMDEtMDJUMjI6NDg6NDNaIiBOb3RPbk9yQWZ0ZXI9IjIwMzgtMDEtMDJUMjM6NDg6NDhaIj4KICAgICAgPEF1ZGllbmNlUmVzdHJpY3Rpb24+CiAgICAgICAgPEF1ZGllbmNlPmh0dHA6Ly9sb2NhbGhvc3Q6OTAwMS88L0F1ZGllbmNlPgogICAgICAgIDxBdWRpZW5jZT5mbGF0X3dvcmxkPC9BdWRpZW5jZT4KICAgICAgPC9BdWRpZW5jZVJlc3RyaWN0aW9uPgogICAgPC9Db25kaXRpb25zPgogICAgPEF0dHJpYnV0ZVN0YXRlbWVudD4KICAgICAgPEF0dHJpYnV0ZSBOYW1lPSJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9lbWFpbGFkZHJlc3MiPgogICAgICAgIDxBdHRyaWJ1dGVWYWx1ZT5zYW1sQHVzZXIuY29tPC9BdHRyaWJ1dGVWYWx1ZT4KICAgICAgPC9BdHRyaWJ1dGU+CiAgICA8L0F0dHJpYnV0ZVN0YXRlbWVudD4KICAgIDxBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTUtMDEtMDJUMjI6NDg6NDhaIiBTZXNzaW9uSW5kZXg9Il83MDBhYzMyMC03NGZmLTAxMzItNWIxNC00OGUwZWIxNGExYzciPgogICAgICA8QXV0aG5Db250ZXh0PgogICAgICAgIDxBdXRobkNvbnRleHRDbGFzc1JlZj51cm46ZmVkZXJhdGlvbjphdXRoZW50aWNhdGlvbjp3aW5kb3dzPC9BdXRobkNvbnRleHRDbGFzc1JlZj4KICAgICAgPC9BdXRobkNvbnRleHQ+CiAgICA8L0F1dGhuU3RhdGVtZW50PgogIDwvQXNzZXJ0aW9uPgo8L3NhbWxwOlJlc3BvbnNlPgo= diff --git a/tests/data/responses/response_without_reference_uri.xml.base64 b/tests/data/responses/response_without_reference_uri.xml.base64 index 7ceecf01..d830db01 100644 --- a/tests/data/responses/response_without_reference_uri.xml.base64 +++ b/tests/data/responses/response_without_reference_uri.xml.base64 @@ -1 +1 @@ -PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgSUQ9InBmeGQ1OTQzNDdkLTQ5NWYtYjhkMS0wZWUyLTQxY2ZkYTE0ZGQzNSIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTUtMDEtMDJUMjI6NDg6NDhaIiBEZXN0aW5hdGlvbj0iaHR0cDovL2xvY2FsaG9zdDo5MDAxL3YxL3VzZXJzL2F1dGhvcml6ZS9zYW1sIiBDb25zZW50PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y29uc2VudDp1bnNwZWNpZmllZCIgSW5SZXNwb25zZVRvPSJfZWQ5MTVhNDAtNzRmYi0wMTMyLTViMTYtNDhlMGViMTRhMWM3Ij4NCiAgPElzc3VlciB4bWxucz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiI+aHR0cDovL2V4YW1wbGUuY29tPC9Jc3N1ZXI+PGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+DQogIDxkczpTaWduZWRJbmZvPjxkczpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+DQogICAgPGRzOlNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNyc2Etc2hhMSIvPg0KICA8ZHM6UmVmZXJlbmNlIFVSST0iIj48ZHM6VHJhbnNmb3Jtcz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PC9kczpUcmFuc2Zvcm1zPjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjc2hhMSIvPjxkczpEaWdlc3RWYWx1ZT5qQ2dlWENQREZsd2pUZ3FnUHAwbVUyVHF3OWc9PC9kczpEaWdlc3RWYWx1ZT48L2RzOlJlZmVyZW5jZT48L2RzOlNpZ25lZEluZm8+PGRzOlNpZ25hdHVyZVZhbHVlPkRmdXByMTh3UityRGFndENQRWZRbFNHSHp3NE5kZlBIWjRIc3pGZTFKUENKWGpmYnlFTTFmZytqemdHYk1NdDZYemdDWGNLSk03RS9DUFNURGt2TWUzRFVKbEh1NERodURPQXovRHN5b0J3V3VWK1JmM1dpTmNGNFhDYzl3QlF6dm4vYXREN3pXNnh3TzdOL2hrQVpKcWZ2SmRkbnBNTUhLR1hxRy9aSFpBdz08L2RzOlNpZ25hdHVyZVZhbHVlPg0KPGRzOktleUluZm8+PGRzOlg1MDlEYXRhPjxkczpYNTA5Q2VydGlmaWNhdGU+TUlJQ3FEQ0NBaEdnQXdJQkFnSUJBREFOQmdrcWhraUc5dzBCQVEwRkFEQnhNUXN3Q1FZRFZRUUdFd0oxY3pFVE1CRUdBMVVFQ0F3S1YyRnphR2x1WjNSdmJqRWlNQ0FHQTFVRUNnd1pSbXhoZENCWGIzSnNaQ0JMYm05M2JHVmtaMlVzSUVsdVl6RWNNQm9HQTFVRUF3d1RiR1ZoY200dVpteGhkSGR2Y214a0xtTnZiVEVMTUFrR0ExVUVCd3dDUkVNd0hoY05NVFV3TnpBNE1EazFPVEF6V2hjTk1qVXdOekExTURrMU9UQXpXakJ4TVFzd0NRWURWUVFHRXdKMWN6RVRNQkVHQTFVRUNBd0tWMkZ6YUdsdVozUnZiakVpTUNBR0ExVUVDZ3daUm14aGRDQlhiM0pzWkNCTGJtOTNiR1ZrWjJVc0lFbHVZekVjTUJvR0ExVUVBd3dUYkdWaGNtNHVabXhoZEhkdmNteGtMbU52YlRFTE1Ba0dBMVVFQnd3Q1JFTXdnWjh3RFFZSktvWklodmNOQVFFQkJRQURnWTBBTUlHSkFvR0JBTVBEd3NsNW82eDJRb3VOaTEvRTdJVXFSWWoyWW9jSlJGc3VFR1RldnlVKzJhRkNhQk5WL3R0NnNBYk05V1N1dEx1cWpFL2hmYm5sRWNaMDMrZ24wQ29MbDZZbXdiS0tlUnBrSXplVmhveUoxWVlNUUVBVmhMcmR5OFBvd3U4VUNaMFBiQXorbjlka2lSek01cENDTzc3K2d5Y0ZUQkZLSEFBOXFJcFVaWmtQQWdNQkFBR2pVREJPTUIwR0ExVWREZ1FXQkJRSFU1OGl1R3hGbFp1ckJVSndvbGFsSnIrRlJ6QWZCZ05WSFNNRUdEQVdnQlFIVTU4aXVHeEZsWnVyQlVKd29sYWxKcitGUnpBTUJnTlZIUk1FQlRBREFRSC9NQTBHQ1NxR1NJYjNEUUVCRFFVQUE0R0JBQzZpSGZNbWQraE1TUnpma29zaTNDK3d2cUhDTEVVc2czSEZwa1ptNWp4bVREbEY1cU8rQnQwbjB4bWZvcVdCekJNbE5DOFRzR3JhZmhKM3p1OEdORjBMZW8xMXJmYzFHTUdCdnI1SG9aM1dBQXltbkJFREFBb3N4TjZXWlJtajF4YWdhMTMrNnBXZkdCKysyblB3Y1pXUC84ZGtQY1JvZ2V2VjB4MHA1Njg2PC9kczpYNTA5Q2VydGlmaWNhdGU+PC9kczpYNTA5RGF0YT48L2RzOktleUluZm8+PC9kczpTaWduYXR1cmU+DQogIDxzYW1scDpTdGF0dXM+DQogICAgPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPg0KICA8L3NhbWxwOlN0YXR1cz4NCg0KICA8QXNzZXJ0aW9uIHhtbG5zPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiBJRD0iXzcwMGFjMzIwLTc0ZmYtMDEzMi01YjE0LTQ4ZTBlYjE0YTFjNyIgSXNzdWVJbnN0YW50PSIyMDE1LTAxLTAyVDIyOjQ4OjQ4WiIgVmVyc2lvbj0iMi4wIj4NCiAgICA8SXNzdWVyPmh0dHA6Ly9leGFtcGxlLmNvbTwvSXNzdWVyPg0KICAgIDxTdWJqZWN0Pg0KICAgICAgPE5hbWVJRCBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+c2FtbEB1c2VyLmNvbTwvTmFtZUlEPg0KICAgICAgPFN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj4NCiAgICAgICAgPFN1YmplY3RDb25maXJtYXRpb25EYXRhIEluUmVzcG9uc2VUbz0iX2VkOTE1YTQwLTc0ZmItMDEzMi01YjE2LTQ4ZTBlYjE0YTFjNyIgTm90T25PckFmdGVyPSIyMDM4LTAxLTAyVDIyOjUxOjQ4WiIgUmVjaXBpZW50PSJodHRwOi8vbG9jYWxob3N0OjkwMDEvdjEvdXNlcnMvYXV0aG9yaXplL3NhbWwiLz4NCiAgICAgIDwvU3ViamVjdENvbmZpcm1hdGlvbj4NCiAgICA8L1N1YmplY3Q+DQogICAgPENvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDE1LTAxLTAyVDIyOjQ4OjQzWiIgTm90T25PckFmdGVyPSIyMDM4LTAxLTAyVDIzOjQ4OjQ4WiI+DQogICAgICA8QXVkaWVuY2VSZXN0cmljdGlvbj4NCiAgICAgICAgPEF1ZGllbmNlPmh0dHA6Ly9sb2NhbGhvc3Q6OTAwMS88L0F1ZGllbmNlPg0KICAgICAgICA8QXVkaWVuY2U+ZmxhdF93b3JsZDwvQXVkaWVuY2U+DQogICAgICA8L0F1ZGllbmNlUmVzdHJpY3Rpb24+DQogICAgPC9Db25kaXRpb25zPg0KICAgIDxBdHRyaWJ1dGVTdGF0ZW1lbnQ+DQogICAgICA8QXR0cmlidXRlIE5hbWU9Imh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL2VtYWlsYWRkcmVzcyI+DQogICAgICAgIDxBdHRyaWJ1dGVWYWx1ZT5zYW1sQHVzZXIuY29tPC9BdHRyaWJ1dGVWYWx1ZT4NCiAgICAgIDwvQXR0cmlidXRlPg0KICAgIDwvQXR0cmlidXRlU3RhdGVtZW50Pg0KICAgIDxBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTUtMDEtMDJUMjI6NDg6NDhaIiBTZXNzaW9uSW5kZXg9Il83MDBhYzMyMC03NGZmLTAxMzItNWIxNC00OGUwZWIxNGExYzciPg0KICAgICAgPEF1dGhuQ29udGV4dD4NCiAgICAgICAgPEF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpmZWRlcmF0aW9uOmF1dGhlbnRpY2F0aW9uOndpbmRvd3M8L0F1dGhuQ29udGV4dENsYXNzUmVmPg0KICAgICAgPC9BdXRobkNvbnRleHQ+DQogICAgPC9BdXRoblN0YXRlbWVudD4NCiAgPC9Bc3NlcnRpb24+DQo8L3NhbWxwOlJlc3BvbnNlPg== \ No newline at end of file +PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1scDpSZXNwb25zZSB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiBJRD0icGZ4ZDU5NDM0N2QtNDk1Zi1iOGQxLTBlZTItNDFjZmRhMTRkZDM1IiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAxNS0wMS0wMlQyMjo0ODo0OFoiIERlc3RpbmF0aW9uPSJodHRwOi8vbG9jYWxob3N0OjkwMDEvdjEvdXNlcnMvYXV0aG9yaXplL3NhbWwiIENvbnNlbnQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjb25zZW50OnVuc3BlY2lmaWVkIiBJblJlc3BvbnNlVG89Il9lZDkxNWE0MC03NGZiLTAxMzItNWIxNi00OGUwZWIxNGExYzciPgogIDxJc3N1ZXIgeG1sbnM9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPmh0dHA6Ly9leGFtcGxlLmNvbTwvSXNzdWVyPgogIDxkczpTaWduYXR1cmUgeG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPgogIDxkczpTaWduZWRJbmZvPgogICAgPGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz4KICAgIDxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjcnNhLXNoYTEiLz4KICAgIDxkczpSZWZlcmVuY2UgVVJJPSIiPgogICAgICA8ZHM6VHJhbnNmb3Jtcz4KICAgICAgICA8ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz4KICAgICAgICA8ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+CiAgICAgIDwvZHM6VHJhbnNmb3Jtcz4KICAgICAgPGRzOkRpZ2VzdE1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNzaGExIi8+CiAgICAgIDxkczpEaWdlc3RWYWx1ZT5qQ2dlWENQREZsd2pUZ3FnUHAwbVUyVHF3OWc9PC9kczpEaWdlc3RWYWx1ZT4KICAgIDwvZHM6UmVmZXJlbmNlPgogIDwvZHM6U2lnbmVkSW5mbz4KICA8ZHM6U2lnbmF0dXJlVmFsdWU+bG9SN21DRmlNSURIUHBLeVgzRUd2dzJYeTZycEtFZWZVMDhYS1lWRXJ6MXB3a1BUUFFlYU5iK2RGMHZLai9rNQoyUmJ2Z3ZFUFN2ZGI3RDJOMTY5QjJMTGVmbXpaWTBDY0RKcThkK3lNbnZSNER3YitSUFl6bWJoS29XQ1ZyY3VPCnNvbEUxQTg3WFZjenNpd2JYRWllM2p4RHdDSk5vWi9GRFJRZy80RHRQVmc9PC9kczpTaWduYXR1cmVWYWx1ZT4KPGRzOktleUluZm8+CiAgPGRzOlg1MDlEYXRhPgogICAgPGRzOlg1MDlDZXJ0aWZpY2F0ZT5NSUlDVnpDQ0FjQUNDUURJVkhhTlNCWUw2VEFOQmdrcWhraUc5dzBCQVFzRkFEQndNUXN3Q1FZRFZRUUdFd0pHVWpFT01Bd0dBMVVFQ0F3RlVHRnlhWE14RGpBTUJnTlZCQWNNQlZCaGNtbHpNUll3RkFZRFZRUUtEQTFPYjNaaGNHOXpkQ0JVUlZOVU1Ta3dKd1lKS29aSWh2Y05BUWtCRmhwbWJHOXlaVzUwTG5CcFoyOTFkRUJ1YjNaaGNHOXpkQzVtY2pBZUZ3MHhOREF5TVRNeE16VXpOREJhRncweE5UQXlNVE14TXpVek5EQmFNSEF4Q3pBSkJnTlZCQVlUQWtaU01RNHdEQVlEVlFRSURBVlFZWEpwY3pFT01Bd0dBMVVFQnd3RlVHRnlhWE14RmpBVUJnTlZCQW9NRFU1dmRtRndiM04wSUZSRlUxUXhLVEFuQmdrcWhraUc5dzBCQ1FFV0dtWnNiM0psYm5RdWNHbG5iM1YwUUc1dmRtRndiM04wTG1aeU1JR2ZNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0R05BRENCaVFLQmdRQ2hMRkhuM0xuTjRKUS83V0NkWXVweGtVZ2NOT1FuUEYreWxsKy9EUHB1eDlucGZZMDU5UElVYXRCOFg3a0NuNWk4dFJ3SXkvaWtISlI2TXI4K01QdmM2Vk9aRHhQTmRadk1vLzhsaHhyYk4zSmRydzN3aFptVS9LUFI5RjNCZEZkdStTTHpyTWwxVERVWmxQdFk5WHpVRlhjcU44SVhjeThUSnpDQmVOZXkzUUlEQVFBQk1BMEdDU3FHU0liM0RRRUJDd1VBQTRHQkFDdEo4ZmVHemUxTkhCNVZ3MThqTVVQdkhvN0gzR3dtajZaREFYUWxhaUFYTXVOQnhOWFZXVndpZmw2VituVzN3OVFhN0Zlby9uWi9PNFRVT0gxbnorYWRrbGNDRDRRcFphRUlibUFicmlQV0pLZ2I0TFdHaHFRcnV3WVI3SXRUUjFNTlg5Z0xiUDB6MHp2REVRbm50L1ZVV0ZFQkxTSnE0WjROcmU4TEZtUzI8L2RzOlg1MDlDZXJ0aWZpY2F0ZT4KICA8L2RzOlg1MDlEYXRhPgo8L2RzOktleUluZm8+PC9kczpTaWduYXR1cmU+PHNhbWxwOlN0YXR1cz4KICAgIDxzYW1scDpTdGF0dXNDb2RlIFZhbHVlPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6c3RhdHVzOlN1Y2Nlc3MiLz4KICA8L3NhbWxwOlN0YXR1cz4KCiAgPEFzc2VydGlvbiB4bWxucz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9Il83MDBhYzMyMC03NGZmLTAxMzItNWIxNC00OGUwZWIxNGExYzciIElzc3VlSW5zdGFudD0iMjAxNS0wMS0wMlQyMjo0ODo0OFoiIFZlcnNpb249IjIuMCI+CiAgICA8SXNzdWVyPmh0dHA6Ly9leGFtcGxlLmNvbTwvSXNzdWVyPgogICAgPFN1YmplY3Q+CiAgICAgIDxOYW1lSUQgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjE6bmFtZWlkLWZvcm1hdDplbWFpbEFkZHJlc3MiPnNhbWxAdXNlci5jb208L05hbWVJRD4KICAgICAgPFN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj4KICAgICAgICA8U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgSW5SZXNwb25zZVRvPSJfZWQ5MTVhNDAtNzRmYi0wMTMyLTViMTYtNDhlMGViMTRhMWM3IiBOb3RPbk9yQWZ0ZXI9IjIwMzgtMDEtMDJUMjI6NTE6NDhaIiBSZWNpcGllbnQ9Imh0dHA6Ly9sb2NhbGhvc3Q6OTAwMS92MS91c2Vycy9hdXRob3JpemUvc2FtbCIvPgogICAgICA8L1N1YmplY3RDb25maXJtYXRpb24+CiAgICA8L1N1YmplY3Q+CiAgICA8Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTUtMDEtMDJUMjI6NDg6NDNaIiBOb3RPbk9yQWZ0ZXI9IjIwMzgtMDEtMDJUMjM6NDg6NDhaIj4KICAgICAgPEF1ZGllbmNlUmVzdHJpY3Rpb24+CiAgICAgICAgPEF1ZGllbmNlPmh0dHA6Ly9sb2NhbGhvc3Q6OTAwMS88L0F1ZGllbmNlPgogICAgICAgIDxBdWRpZW5jZT5mbGF0X3dvcmxkPC9BdWRpZW5jZT4KICAgICAgPC9BdWRpZW5jZVJlc3RyaWN0aW9uPgogICAgPC9Db25kaXRpb25zPgogICAgPEF0dHJpYnV0ZVN0YXRlbWVudD4KICAgICAgPEF0dHJpYnV0ZSBOYW1lPSJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9lbWFpbGFkZHJlc3MiPgogICAgICAgIDxBdHRyaWJ1dGVWYWx1ZT5zYW1sQHVzZXIuY29tPC9BdHRyaWJ1dGVWYWx1ZT4KICAgICAgPC9BdHRyaWJ1dGU+CiAgICA8L0F0dHJpYnV0ZVN0YXRlbWVudD4KICAgIDxBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTUtMDEtMDJUMjI6NDg6NDhaIiBTZXNzaW9uSW5kZXg9Il83MDBhYzMyMC03NGZmLTAxMzItNWIxNC00OGUwZWIxNGExYzciPgogICAgICA8QXV0aG5Db250ZXh0PgogICAgICAgIDxBdXRobkNvbnRleHRDbGFzc1JlZj51cm46ZmVkZXJhdGlvbjphdXRoZW50aWNhdGlvbjp3aW5kb3dzPC9BdXRobkNvbnRleHRDbGFzc1JlZj4KICAgICAgPC9BdXRobkNvbnRleHQ+CiAgICA8L0F1dGhuU3RhdGVtZW50PgogIDwvQXNzZXJ0aW9uPgo8L3NhbWxwOlJlc3BvbnNlPgo= diff --git a/tests/src/OneLogin/saml2_tests/response_test.py b/tests/src/OneLogin/saml2_tests/response_test.py index 3ce359d9..cba1620e 100644 --- a/tests/src/OneLogin/saml2_tests/response_test.py +++ b/tests/src/OneLogin/saml2_tests/response_test.py @@ -1661,15 +1661,24 @@ def testIsValidSignFingerprint(self): # Modified message self.assertFalse(response_9.is_valid(self.get_request_data())) - def testIsValidSignWithEmptyReferenceURI(self): + def testMessageSignedIsValidSignWithEmptyReferenceURI(self): settings_info = self.loadSettingsJSON() del settings_info['idp']['x509cert'] - settings_info['idp']['certFingerprint'] = "194d97e4d8c9c8cfa4b721e5ee497fd9660e5213" + settings_info['idp']['certFingerprint'] = "657302a5e11a4794a1e50a705988d66c9377575d" settings = OneLogin_Saml2_Settings(settings_info) xml = self.file_contents(join(self.data_path, 'responses', 'response_without_reference_uri.xml.base64')) response = OneLogin_Saml2_Response(settings, xml) self.assertTrue(response.is_valid(self.get_request_data())) + def testAssertionSignedIsValidSignWithEmptyReferenceURI(self): + settings_info = self.loadSettingsJSON() + del settings_info['idp']['x509cert'] + settings_info['idp']['certFingerprint'] = "657302a5e11a4794a1e50a705988d66c9377575d" + settings = OneLogin_Saml2_Settings(settings_info) + xml = self.file_contents(join(self.data_path, 'responses', 'response_without_assertion_reference_uri.xml.base64')) + response = OneLogin_Saml2_Response(settings, xml) + self.assertTrue(response.is_valid(self.get_request_data())) + def testIsValidWithoutInResponseTo(self): """ If assertion contains InResponseTo but not the Response tag, we should From 2cda97555aa7fe928d05ee61a8323773a13c5687 Mon Sep 17 00:00:00 2001 From: Florent PIGOUT Date: Wed, 14 Aug 2019 17:12:52 +0200 Subject: [PATCH 012/205] Update changelog --- changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/changelog.md b/changelog.md index f0e28708..f93b5878 100644 --- a/changelog.md +++ b/changelog.md @@ -1,4 +1,7 @@ # python3-saml changelog +### 1.8.1 (unreleased) +* Do not touch message when validate his signature + ### 1.8.0 (Sep 11, 2019) * Set true as the default value for strict setting * [#152](https://github.com/onelogin/python3-saml/pull/152/files) Don't clean xsd and xsi namespaces From a6b07e92242fdf8b37d9384b5ac2e4d678d652ee Mon Sep 17 00:00:00 2001 From: Jordan Ephron Date: Mon, 18 Nov 2019 13:47:50 -0800 Subject: [PATCH 013/205] Fix typo: Attribute -> Assertion Fixes a typo in which the Assertion Consumer Service was erroniously referred to as the Attribute Consumer Service See line 497 of https://www.oasis-open.org/committees/download.php/56783/sstc-saml-profiles-errata-2.0-wd-07-diff.pdf --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 106e93c2..0217a7c2 100644 --- a/README.md +++ b/README.md @@ -601,7 +601,7 @@ auth.login() # Method that builds and sends the AuthNRequest The ``AuthNRequest`` will be sent signed or unsigned based on the security info of the ``advanced_settings.json`` file (i.e. ``authnRequestsSigned``). -The IdP will then return the SAML Response to the user's client. The client is then forwarded to the **Attribute Consumer Service (ACS)** of the SP with this information. +The IdP will then return the SAML Response to the user's client. The client is then forwarded to the **Assertion Consumer Service (ACS)** of the SP with this information. We can set a ``return_to`` url parameter to the login function and that will be converted as a ``RelayState`` parameter: @@ -650,7 +650,7 @@ saml_settings = OneLogin_Saml2_Settings(settings=None, custom_base_path=None, sp ``` to get the settings object and with the ``sp_validation_only=True`` parameter we will avoid the IdP settings validation. -***Attribute Consumer Service (ACS)*** +***Assertion Consumer Service (ACS)*** This code handles the SAML response that the IdP forwards to the SP through the user's client. From c13e998d36171d096518a76b4b6cef048b8ef717 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Tue, 19 Nov 2019 21:13:31 +0100 Subject: [PATCH 014/205] Fix failOnAuthnContextMismatch code --- README.md | 2 +- src/onelogin/saml2/authn_request.py | 4 +--- src/onelogin/saml2/response.py | 4 ++-- src/onelogin/saml2/settings.py | 1 + tests/src/OneLogin/saml2_tests/response_test.py | 2 +- 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 106e93c2..e9e689d2 100644 --- a/README.md +++ b/README.md @@ -410,7 +410,7 @@ In addition to the required settings data (idp, sp), extra settings can be defin "requestedAuthnContext": true, // Allows the authn comparison parameter to be set, defaults to 'exact' if the setting is not present. "requestedAuthnContextComparison": "exact", - // Set to true to check that the AuthnContext received matches the one requested. + // Set to true to check that the AuthnContext(s) received match(es) the requested. "failOnAuthnContextMismatch": false, // In some environment you will need to set how long the published metadata of the Service Provider gonna be valid. diff --git a/src/onelogin/saml2/authn_request.py b/src/onelogin/saml2/authn_request.py index 57da1561..73547735 100644 --- a/src/onelogin/saml2/authn_request.py +++ b/src/onelogin/saml2/authn_request.py @@ -95,9 +95,7 @@ def __init__(self, settings, force_authn=False, is_passive=False, set_nameid_pol requested_authn_context_str = '' if security['requestedAuthnContext'] is not False: - authn_comparison = 'exact' - if 'requestedAuthnContextComparison' in security.keys(): - authn_comparison = security['requestedAuthnContextComparison'] + authn_comparison = security['requestedAuthnContextComparison'] if security['requestedAuthnContext'] is True: requested_authn_context_str = """ diff --git a/src/onelogin/saml2/response.py b/src/onelogin/saml2/response.py index 8aa309c5..83eafe7d 100644 --- a/src/onelogin/saml2/response.py +++ b/src/onelogin/saml2/response.py @@ -166,10 +166,10 @@ def is_valid(self, request_data, request_id=None, raise_exceptions=False): requested_authn_contexts = security['requestedAuthnContext'] if security['failOnAuthnContextMismatch'] and requested_authn_contexts and requested_authn_contexts is not True: authn_contexts = self.get_authn_contexts() - unmatched_contexts = set(requested_authn_contexts).difference(authn_contexts) + unmatched_contexts = set(authn_contexts).difference(requested_authn_contexts) if unmatched_contexts: raise OneLogin_Saml2_ValidationError( - 'The AuthnContext "%s" didn\'t include requested context "%s"' % (', '.join(authn_contexts), ', '.join(unmatched_contexts)), + 'The AuthnContext "%s" was not a requested context "%s"' % (', '.join(unmatched_contexts), ', '.join(requested_authn_contexts)), OneLogin_Saml2_ValidationError.AUTHN_CONTEXT_MISMATCH ) diff --git a/src/onelogin/saml2/settings.py b/src/onelogin/saml2/settings.py index 5057a5ab..8acee0be 100644 --- a/src/onelogin/saml2/settings.py +++ b/src/onelogin/saml2/settings.py @@ -310,6 +310,7 @@ def __add_default_values(self): self.__sp.setdefault('privateKey', '') self.__security.setdefault('requestedAuthnContext', True) + self.__security.setdefault('requestedAuthnContextComparison', 'exact') self.__security.setdefault('failOnAuthnContextMismatch', False) def check_settings(self, settings): diff --git a/tests/src/OneLogin/saml2_tests/response_test.py b/tests/src/OneLogin/saml2_tests/response_test.py index 3ce359d9..ad0ef3b8 100644 --- a/tests/src/OneLogin/saml2_tests/response_test.py +++ b/tests/src/OneLogin/saml2_tests/response_test.py @@ -1056,7 +1056,7 @@ def testIsInValidAuthenticationContext(self): # check that we catch when the contexts don't match response = OneLogin_Saml2_Response(settings, message) self.assertFalse(response.is_valid(request_data)) - self.assertIn('The AuthnContext "%s" didn\'t include requested context "%s"' % (password_context, two_factor_context), response.get_error()) + self.assertIn('The AuthnContext "%s" was not a requested context "%s"' % (password_context, two_factor_context), response.get_error()) # now drop in the expected AuthnContextClassRef and see that it passes original_message = compat.to_string(OneLogin_Saml2_Utils.b64decode(message)) From 5f162dc9947bb34cd7502177f35e4edbf9a477dd Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Wed, 20 Nov 2019 10:20:46 +0100 Subject: [PATCH 015/205] Allow any number of decimal places for seconds on SAML datetimes --- src/onelogin/saml2/utils.py | 17 ++++++++++++++--- tests/src/OneLogin/saml2_tests/utils_test.py | 8 ++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/onelogin/saml2/utils.py b/src/onelogin/saml2/utils.py index 4647c230..3045f5fd 100644 --- a/src/onelogin/saml2/utils.py +++ b/src/onelogin/saml2/utils.py @@ -65,6 +65,10 @@ class OneLogin_Saml2_Utils(object): RESPONSE_SIGNATURE_XPATH = '/samlp:Response/ds:Signature' ASSERTION_SIGNATURE_XPATH = '/samlp:Response/saml:Assertion/ds:Signature' + TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ" + TIME_FORMAT_2 = "%Y-%m-%dT%H:%M:%S.%fZ" + TIME_FORMAT_WITH_FRAGMENT = re.compile(r'^(\d{4,4}-\d{2,2}-\d{2,2}T\d{2,2}:\d{2,2}:\d{2,2})(\.\d*)?Z?$') + @staticmethod def escape_url(url, lowercase_urlencoding=False): """ @@ -401,7 +405,7 @@ def parse_time_to_SAML(time): :rtype: string """ data = datetime.utcfromtimestamp(float(time)) - return data.strftime('%Y-%m-%dT%H:%M:%SZ') + return data.strftime(OneLogin_Saml2_Utils.TIME_FORMAT) @staticmethod def parse_SAML_to_time(timestr): @@ -416,9 +420,16 @@ def parse_SAML_to_time(timestr): :rtype: int """ try: - data = datetime.strptime(timestr, '%Y-%m-%dT%H:%M:%SZ') + data = datetime.strptime(timestr, OneLogin_Saml2_Utils.TIME_FORMAT) except ValueError: - data = datetime.strptime(timestr, '%Y-%m-%dT%H:%M:%S.%fZ') + try: + data = datetime.strptime(timestr, OneLogin_Saml2_Utils.TIME_FORMAT_2) + except ValueError: + elem = OneLogin_Saml2_Utils.TIME_FORMAT_WITH_FRAGMENT.match(timestr) + if not elem: + raise Exception("time data %s does not match format %s" % (timestr, r'yyyy-mm-ddThh:mm:ss(\.s+)?Z')) + data = datetime.strptime(elem.groups()[0] + "Z", OneLogin_Saml2_Utils.TIME_FORMAT) + return calendar.timegm(data.utctimetuple()) @staticmethod diff --git a/tests/src/OneLogin/saml2_tests/utils_test.py b/tests/src/OneLogin/saml2_tests/utils_test.py index 366a125b..597c4852 100644 --- a/tests/src/OneLogin/saml2_tests/utils_test.py +++ b/tests/src/OneLogin/saml2_tests/utils_test.py @@ -450,6 +450,14 @@ def testParseSAML2Time(self): saml_time2 = '2013-12-10T04:39:31.120Z' self.assertEqual(time, OneLogin_Saml2_Utils.parse_SAML_to_time(saml_time2)) + # Now test if toolkit supports microseconds + saml_time3 = '2013-12-10T04:39:31.120240Z' + self.assertEqual(time, OneLogin_Saml2_Utils.parse_SAML_to_time(saml_time3)) + + # Now test if toolkit supports nanoseconds + saml_time4 = '2013-12-10T04:39:31.120240360Z' + self.assertEqual(time, OneLogin_Saml2_Utils.parse_SAML_to_time(saml_time4)) + def testParseTime2SAML(self): """ Tests the parse_time_to_SAML method of the OneLogin_Saml2_Utils From 218cb497950a222b09160edfb888e46b4bef089d Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Wed, 20 Nov 2019 13:13:14 +0100 Subject: [PATCH 016/205] Update demos --- demo-django/demo/views.py | 5 +++-- demo-django/requirements.txt | 2 +- demo-flask/index.py | 6 ++++++ demo-flask/requirements.txt | 2 +- demo-flask/templates/index.html | 3 +++ 5 files changed, 14 insertions(+), 4 deletions(-) diff --git a/demo-django/demo/views.py b/demo-django/demo/views.py index 4c61e599..c02d226e 100644 --- a/demo-django/demo/views.py +++ b/demo-django/demo/views.py @@ -86,8 +86,7 @@ def index(request): request.session['samlSessionIndex'] = auth.get_session_index() if 'RelayState' in req['post_data'] and OneLogin_Saml2_Utils.get_self_url(req) != req['post_data']['RelayState']: return HttpResponseRedirect(auth.redirect_to(req['post_data']['RelayState'])) - else: - if auth.get_settings().is_debug_active(): + elif auth.get_settings().is_debug_active(): error_reason = auth.get_last_error_reason() elif 'sls' in req['get_data']: request_id = None @@ -101,6 +100,8 @@ def index(request): return HttpResponseRedirect(url) else: success_slo = True + elif auth.get_settings().is_debug_active(): + error_reason = auth.get_last_error_reason() if 'samlUserdata' in request.session: paint_logout = True diff --git a/demo-django/requirements.txt b/demo-django/requirements.txt index 1aa1df39..a4a895b0 100644 --- a/demo-django/requirements.txt +++ b/demo-django/requirements.txt @@ -1,2 +1,2 @@ -Django==1.11 +Django==1.11.26 python3-saml diff --git a/demo-flask/index.py b/demo-flask/index.py index b344da4c..c37dc9c7 100644 --- a/demo-flask/index.py +++ b/demo-flask/index.py @@ -39,6 +39,7 @@ def index(): req = prepare_flask_request(request) auth = init_saml_auth(req) errors = [] + error_reason = None not_auth_warn = False success_slo = False attributes = False @@ -86,6 +87,8 @@ def index(): self_url = OneLogin_Saml2_Utils.get_self_url(req) if 'RelayState' in request.form and self_url != request.form['RelayState']: return redirect(auth.redirect_to(request.form['RelayState'])) + elif auth.get_settings().is_debug_active(): + error_reason = auth.get_last_error_reason() elif 'sls' in request.args: request_id = None if 'LogoutRequestID' in session: @@ -98,6 +101,8 @@ def index(): return redirect(url) else: success_slo = True + elif auth.get_settings().is_debug_active(): + error_reason = auth.get_last_error_reason() if 'samlUserdata' in session: paint_logout = True @@ -107,6 +112,7 @@ def index(): return render_template( 'index.html', errors=errors, + error_reason=error_reason, not_auth_warn=not_auth_warn, success_slo=success_slo, attributes=attributes, diff --git a/demo-flask/requirements.txt b/demo-flask/requirements.txt index 335836f7..d9340937 100644 --- a/demo-flask/requirements.txt +++ b/demo-flask/requirements.txt @@ -1 +1 @@ -flask==0.10.1 +flask==1.0 diff --git a/demo-flask/templates/index.html b/demo-flask/templates/index.html index ad42cf5c..fd1ff051 100644 --- a/demo-flask/templates/index.html +++ b/demo-flask/templates/index.html @@ -10,6 +10,9 @@
  • {{err}}
  • {% endfor %} + {% if error_reason %} + {{error_reason}} + {% endif %} {% endif %} From 2fdd7ef4bb9291e7e5f61164cb02ab89702f10a9 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Wed, 20 Nov 2019 13:16:14 +0100 Subject: [PATCH 017/205] Fix #160. Fix get_attribute docstring --- src/onelogin/saml2/auth.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/onelogin/saml2/auth.py b/src/onelogin/saml2/auth.py index 2a9b7cde..1bf5f8a6 100644 --- a/src/onelogin/saml2/auth.py +++ b/src/onelogin/saml2/auth.py @@ -315,7 +315,7 @@ def get_attribute(self, name): :param name: Name of the attribute :type name: string - :returns: Attribute value if exists or [] + :returns: Attribute value if exists or None :rtype: string """ assert isinstance(name, compat.str_type) From 4d28828d8d67c4be2f44274e1fdaba3e31d1d0ce Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Wed, 20 Nov 2019 17:31:34 +0100 Subject: [PATCH 018/205] Update tornado version. Remove unnecesary doc. Improve demo --- README.md | 29 ++--------------------------- demo-tornado/requirements.txt | 9 +-------- demo-tornado/templates/index.html | 3 +++ demo-tornado/views.py | 24 ++++++++++++++++-------- 4 files changed, 22 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index a2d2ec7b..f0e16ff0 100644 --- a/README.md +++ b/README.md @@ -178,6 +178,8 @@ This folder contains a Pyramid project that will be used as demo to show how to This folder contains a Tornado project that will be used as demo to show how to add SAML support to the Tornado Framework. ``views.py`` (with its ``settings.py``) is the main Flask file that has all the code, this file uses the templates stored at the ``templates`` folder. In the ``saml`` folder we found the ``certs`` folder to store the X.509 public and private key, and the SAML toolkit settings (``settings.json`` and ``advanced_settings.json``). +It requires python3.5 (it's using tornado 6.0.3) + #### setup.py #### Setup script is the centre of all activity in building, distributing, and installing modules. @@ -1253,33 +1255,6 @@ First we need to edit the ``saml/settings.json`` file, configure the SP part and Once the SP is configured, the metadata of the SP is published at the ``/metadata`` url. Based on that info, configure the IdP. -##### Test with keycloack ##### - -You can test your SP with every compatible IdP, for example Keycloack by Red Hat (Check if you need also authorization and not only authentication ) - -###### Install Docker ###### - -Install docker as suggested by [docker guide](https://docs.docker.com/install/linux/docker-ce/ubuntu/) - -###### Keycloack starting ###### - -First run: -* docker run --name keycloackContainer -d -p 8080:8080 -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=admin -e DB_VENDOR=H2 jboss/keycloak - -After first run: -* sudo docker start keycloackContainer - -Remember to stop keycloack after usage: -* sudo docker stop keycloackContainer - - -###### Keycloack useful urls ###### - -* master: http://localhost:8080/auth/admin -* users: http://localhost:8080/auth/realms/idp_dacd/account/ -* saml request: http://localhost:8080/auth/realms/idp_dacd/protocol/saml -* metadata: http://localhost:8080/auth/realms/idp_dacd/protocol/saml/descriptor - #### How it works #### 1. First time you access to the main view (http://localhost:8000), you can select to login and return to the same view or login and be redirected to ``/?attrs`` (attrs view). diff --git a/demo-tornado/requirements.txt b/demo-tornado/requirements.txt index 66787013..17ee9ac2 100644 --- a/demo-tornado/requirements.txt +++ b/demo-tornado/requirements.txt @@ -1,8 +1 @@ -defusedxml==0.5.0 -isodate==0.6.0 -lxml==4.3.3 -pkgconfig==1.5.1 -python3-saml==1.6.0 -six==1.12.0 -tornado==6.0.2 -xmlsec==1.3.3 +tornado==6.0.3 diff --git a/demo-tornado/templates/index.html b/demo-tornado/templates/index.html index 52bee968..f8dfac0a 100644 --- a/demo-tornado/templates/index.html +++ b/demo-tornado/templates/index.html @@ -9,6 +9,9 @@ {% for err in errors %}
  • {{err}}
  • {% end %} + {% if error_reason %} + {{error_reason}} + {% end %} {% end %} diff --git a/demo-tornado/views.py b/demo-tornado/views.py index 5ac3e03a..ed195fb7 100644 --- a/demo-tornado/views.py +++ b/demo-tornado/views.py @@ -31,31 +31,36 @@ class IndexHandler(tornado.web.RequestHandler): def post(self): req = prepare_tornado_request(self.request) auth = init_saml_auth(req) + error_reason = None attributes = False paint_logout = False + success_slo = False auth.process_response() errors = auth.get_errors() not_auth_warn = not auth.is_authenticated() if len(errors) == 0: - session['samlUserdata'] = auth.get_attributes() - session['samlNameId'] = auth.get_nameid() - session['samlSessionIndex'] = auth.get_session_index() - self_url = OneLogin_Saml2_Utils.get_self_url(req) - if 'RelayState' in self.request.arguments and self_url != self.request.arguments['RelayState'][0].decode('utf-8'): + session['samlUserdata'] = auth.get_attributes() + session['samlNameId'] = auth.get_nameid() + session['samlSessionIndex'] = auth.get_session_index() + self_url = OneLogin_Saml2_Utils.get_self_url(req) + if 'RelayState' in self.request.arguments and self_url != self.request.arguments['RelayState'][0].decode('utf-8'): return self.redirect(self.request.arguments['RelayState'][0].decode('utf-8')) + elif auth.get_settings().is_debug_active(): + error_reason = auth.get_last_error_reason() if 'samlUserdata' in session: paint_logout = True if len(session['samlUserdata']) > 0: attributes = session['samlUserdata'].items() - self.render('index.html',errors=errors,not_auth_warn=not_auth_warn,attributes=attributes,paint_logout=paint_logout) + self.render('index.html',errors=errors,error_reason=error_reason,not_auth_warn=not_auth_warn,success_slo=success_slo,attributes=attributes,paint_logout=paint_logout) def get(self): req = prepare_tornado_request(self.request) auth = init_saml_auth(req) + error_reason = None errors = [] not_auth_warn = False success_slo = False @@ -90,6 +95,8 @@ def get(self): self_url = OneLogin_Saml2_Utils.get_self_url(req) if 'RelayState' in self.request.arguments and self_url != self.request.arguments['RelayState'][0].decode('utf-8'): return self.redirect(auth.redirect_to(self.request.arguments['RelayState'][0].decode('utf-8'))) + elif auth.get_settings().is_debug_active(): + error_reason = auth.get_last_error_reason() elif 'sls' in req['get_data']: print('-sls-') dscb = lambda: session.clear() ## clear out the session @@ -100,14 +107,15 @@ def get(self): return self.redirect(url) else: success_slo = True - + elif auth.get_settings().is_debug_active(): + error_reason = auth.get_last_error_reason() if 'samlUserdata' in session: print('-samlUserdata-') paint_logout = True if len(session['samlUserdata']) > 0: attributes = session['samlUserdata'].items() print("ATTRIBUTES", attributes) - self.render('index.html',errors=errors,not_auth_warn=not_auth_warn,success_slo=success_slo,attributes=attributes,paint_logout=paint_logout) + self.render('index.html',errors=errors,error_reason=error_reason,not_auth_warn=not_auth_warn,success_slo=success_slo,attributes=attributes,paint_logout=paint_logout) class AttrsHandler(tornado.web.RequestHandler): def get(self): From 4e5eb138183d8d2ae99b1eb3ef9ec9fe43e35cbd Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Wed, 20 Nov 2019 18:15:47 +0100 Subject: [PATCH 019/205] Fix flake8 --- demo-tornado/views.py | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/demo-tornado/views.py b/demo-tornado/views.py index ed195fb7..6fe72bcd 100644 --- a/demo-tornado/views.py +++ b/demo-tornado/views.py @@ -5,18 +5,18 @@ import tornado.httputil from onelogin.saml2.auth import OneLogin_Saml2_Auth -from onelogin.saml2.settings import OneLogin_Saml2_Settings from onelogin.saml2.utils import OneLogin_Saml2_Utils -##Global session info +# Global session info session = {} + class Application(tornado.web.Application): def __init__(self): handlers = [ (r"/", IndexHandler), (r"/attrs", AttrsHandler), - (r"/metadata",MetadataHandler), + (r"/metadata", MetadataHandler), ] settings = { "template_path": Settings.TEMPLATE_PATH, @@ -55,7 +55,7 @@ def post(self): if len(session['samlUserdata']) > 0: attributes = session['samlUserdata'].items() - self.render('index.html',errors=errors,error_reason=error_reason,not_auth_warn=not_auth_warn,success_slo=success_slo,attributes=attributes,paint_logout=paint_logout) + self.render('index.html', errors=errors, error_reason=error_reason, not_auth_warn=not_auth_warn, success_slo=success_slo, attributes=attributes, paint_logout=paint_logout) def get(self): req = prepare_tornado_request(self.request) @@ -99,7 +99,7 @@ def get(self): error_reason = auth.get_last_error_reason() elif 'sls' in req['get_data']: print('-sls-') - dscb = lambda: session.clear() ## clear out the session + dscb = lambda: session.clear() # clear out the session url = auth.process_slo(delete_session_cb=dscb) errors = auth.get_errors() if len(errors) == 0: @@ -115,7 +115,8 @@ def get(self): if len(session['samlUserdata']) > 0: attributes = session['samlUserdata'].items() print("ATTRIBUTES", attributes) - self.render('index.html',errors=errors,error_reason=error_reason,not_auth_warn=not_auth_warn,success_slo=success_slo,attributes=attributes,paint_logout=paint_logout) + self.render('index.html', errors=errors, error_reason=error_reason, not_auth_warn=not_auth_warn, success_slo=success_slo, attributes=attributes, paint_logout=paint_logout) + class AttrsHandler(tornado.web.RequestHandler): def get(self): @@ -127,27 +128,28 @@ def get(self): if len(session['samlUserdata']) > 0: attributes = session['samlUserdata'].items() - self.render('attrs.html',paint_logout=paint_logout,attributes=attributes) + self.render('attrs.html', paint_logout=paint_logout, attributes=attributes) + class MetadataHandler(tornado.web.RequestHandler): def get(self): req = prepare_tornado_request(self.request) auth = init_saml_auth(req) saml_settings = auth.get_settings() - #saml_settings = OneLogin_Saml2_Settings(settings=None, custom_base_path=settings.SAML_FOLDER, sp_validation_only=True) metadata = saml_settings.get_sp_metadata() errors = saml_settings.validate_metadata(metadata) if len(errors) == 0: - #resp = HttpResponse(content=metadata, content_type='text/xml') - self.set_header('Content-Type','text/xml') + # resp = HttpResponse(content=metadata, content_type='text/xml') + self.set_header('Content-Type', 'text/xml') self.write(metadata) else: - #resp = HttpResponseServerError(content=', '.join(errors)) + # resp = HttpResponseServerError(content=', '.join(errors)) self.write(', '.join(errors)) - #return resp + # return resp -def prepare_tornado_request(request): + +def prepare_tornado_request(request): dataDict = {} for key in request.arguments: @@ -164,6 +166,7 @@ def prepare_tornado_request(request): } return result + def init_saml_auth(req): auth = OneLogin_Saml2_Auth(req, custom_base_path=Settings.SAML_PATH) return auth From c660f6f639a783c66f05c003bc2a04ba6b162048 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Wed, 20 Nov 2019 18:17:19 +0100 Subject: [PATCH 020/205] Release 1.9.0 --- changelog.md | 7 +++++-- setup.py | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index f93b5878..b5e5da52 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,9 @@ # python3-saml changelog -### 1.8.1 (unreleased) -* Do not touch message when validate his signature +### 1.9.0 (Nov 20, 2019) +* Allow any number of decimal places for seconds on SAML datetimes +* Fix failOnAuthnContextMismatch code +* Improve signature validation when no reference uri +* Update demo versions. Improve them and add Tornado demo. ### 1.8.0 (Sep 11, 2019) * Set true as the default value for strict setting diff --git a/setup.py b/setup.py index cb483f2d..5a928564 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ setup( name='python3-saml', - version='1.8.0', + version='1.9.0', description='Onelogin Python Toolkit. Add SAML support to your Python software using this library', classifiers=[ 'Development Status :: 5 - Production/Stable', From 57da6e5c0e3a5c90092cd9213a49acd5a6a41b49 Mon Sep 17 00:00:00 2001 From: nirn Date: Thu, 19 Dec 2019 11:10:39 +0200 Subject: [PATCH 021/205] Add samlUserdata to demo-flask session Unlike demo-django, samlUserdata isn't set in demo-flask's session and /attrs/ always lacks attributes. --- demo-flask/index.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/demo-flask/index.py b/demo-flask/index.py index c37dc9c7..b523cb92 100644 --- a/demo-flask/index.py +++ b/demo-flask/index.py @@ -79,6 +79,7 @@ def index(): if len(errors) == 0: if 'AuthNRequestID' in session: del session['AuthNRequestID'] + session['samlUserdata'] = auth.get_attributes() session['samlNameId'] = auth.get_nameid() session['samlNameIdFormat'] = auth.get_nameid_format() session['samlNameIdNameQualifier'] = auth.get_nameid_nq() @@ -88,7 +89,7 @@ def index(): if 'RelayState' in request.form and self_url != request.form['RelayState']: return redirect(auth.redirect_to(request.form['RelayState'])) elif auth.get_settings().is_debug_active(): - error_reason = auth.get_last_error_reason() + error_reason = auth.get_last_error_reason() elif 'sls' in request.args: request_id = None if 'LogoutRequestID' in session: From 0c4f8463b74e77a4032476b74101cebc7f55046d Mon Sep 17 00:00:00 2001 From: Rahul Raina Date: Tue, 7 Jan 2020 17:53:28 +0800 Subject: [PATCH 022/205] Adding support to read idp.crt from certs folder --- src/onelogin/saml2/settings.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/onelogin/saml2/settings.py b/src/onelogin/saml2/settings.py index 8acee0be..1d8e9518 100644 --- a/src/onelogin/saml2/settings.py +++ b/src/onelogin/saml2/settings.py @@ -558,7 +558,12 @@ def get_idp_cert(self): :returns: IdP public cert :rtype: string """ - return self.__idp.get('x509cert') + cert = self.__idp.get('x509cert') + cert_file_name = self.__paths['cert'] + 'idp.crt' + if not cert and exists(cert_file_name): + with open(cert_file_name) as f: + cert = f.read() + return cert or None def get_idp_data(self): """ From 222620128c48615e37ca3f3dd76866308958043d Mon Sep 17 00:00:00 2001 From: Rahul Raina Date: Tue, 7 Jan 2020 18:23:57 +0800 Subject: [PATCH 023/205] Adding tests and new settings file --- tests/settings/settings9.json | 46 +++++++++++++++++++ .../src/OneLogin/saml2_tests/settings_test.py | 22 +++++++++ 2 files changed, 68 insertions(+) create mode 100644 tests/settings/settings9.json diff --git a/tests/settings/settings9.json b/tests/settings/settings9.json new file mode 100644 index 00000000..351c5c95 --- /dev/null +++ b/tests/settings/settings9.json @@ -0,0 +1,46 @@ +{ + "strict": false, + "debug": false, + "custom_base_path": "../../../tests/data/customPath/", + "sp": { + "entityId": "http://stuff.com/endpoints/metadata.php", + "assertionConsumerService": { + "url": "http://stuff.com/endpoints/endpoints/acs.php" + }, + "singleLogoutService": { + "url": "http://stuff.com/endpoints/endpoints/sls.php" + }, + "NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" + }, + "idp": { + "entityId": "http://idp.example.com/", + "singleSignOnService": { + "url": "http://idp.example.com/SSOService.php" + }, + "singleLogoutService": { + "url": "http://idp.example.com/SingleLogoutService.php" + } + }, + "security": { + "authnRequestsSigned": false, + "wantAssertionsSigned": false, + "signMetadata": false + }, + "contactPerson": { + "technical": { + "givenName": "technical_name", + "emailAddress": "technical@example.com" + }, + "support": { + "givenName": "support_name", + "emailAddress": "support@example.com" + } + }, + "organization": { + "en-US": { + "name": "sp_test", + "displayname": "SP test", + "url": "http://sp.example.com" + } + } +} diff --git a/tests/src/OneLogin/saml2_tests/settings_test.py b/tests/src/OneLogin/saml2_tests/settings_test.py index 942fdd96..32b037fe 100644 --- a/tests/src/OneLogin/saml2_tests/settings_test.py +++ b/tests/src/OneLogin/saml2_tests/settings_test.py @@ -222,6 +222,28 @@ def testGetSPKey(self): settings_3 = OneLogin_Saml2_Settings(settings_data, custom_base_path=custom_base_path) self.assertIsNone(settings_3.get_sp_key()) + def testGetIDPCert(self): + """ + Tests the get_idp_cert method of the OneLogin_Saml2_Settings + """ + + settings = OneLogin_Saml2_Settings(self.loadSettingsJSON('settings9.json')) + cert = "-----BEGIN CERTIFICATE-----\nMIICgTCCAeoCCQCbOlrWDdX7FTANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMC\nTk8xGDAWBgNVBAgTD0FuZHJlYXMgU29sYmVyZzEMMAoGA1UEBxMDRm9vMRAwDgYD\nVQQKEwdVTklORVRUMRgwFgYDVQQDEw9mZWlkZS5lcmxhbmcubm8xITAfBgkqhkiG\n9w0BCQEWEmFuZHJlYXNAdW5pbmV0dC5ubzAeFw0wNzA2MTUxMjAxMzVaFw0wNzA4\nMTQxMjAxMzVaMIGEMQswCQYDVQQGEwJOTzEYMBYGA1UECBMPQW5kcmVhcyBTb2xi\nZXJnMQwwCgYDVQQHEwNGb28xEDAOBgNVBAoTB1VOSU5FVFQxGDAWBgNVBAMTD2Zl\naWRlLmVybGFuZy5ubzEhMB8GCSqGSIb3DQEJARYSYW5kcmVhc0B1bmluZXR0Lm5v\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDivbhR7P516x/S3BqKxupQe0LO\nNoliupiBOesCO3SHbDrl3+q9IbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHIS\nKOtPlAeTZSnb8QAu7aRjZq3+PbrP5uW3TcfCGPtKTytHOge/OlJbo078dVhXQ14d\n1EDwXJW1rRXuUt4C8QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACDVfp86HObqY+e8\nBUoWQ9+VMQx1ASDohBjwOsg2WykUqRXF+dLfcUH9dWR63CtZIKFDbStNomPnQz7n\nbK+onygwBspVEbnHuUihZq3ZUdmumQqCw4Uvs/1Uvq3orOo/WJVhTyvLgFVK2Qar\nQ4/67OZfHd7R+POBXhophSMv1ZOo\n-----END CERTIFICATE-----\n" + self.assertEqual(cert, settings.get_idp_cert()) + + settings_data = self.loadSettingsJSON() + + settings = OneLogin_Saml2_Settings(settings_data) + settings_data['idp']['x509cert'] = cert + self.assertEqual(cert, settings.get_sp_cert()) + + del settings_data['idp']['x509cert'] + del settings_data['custom_base_path'] + custom_base_path = dirname(__file__) + + settings_3 = OneLogin_Saml2_Settings(settings_data, custom_base_path=custom_base_path) + self.assertIsNone(settings_3.get_idp_cert()) + def testFormatIdPCert(self): """ Tests the format_idp_cert method of the OneLogin_Saml2_Settings From 7668721e9677f087e256798a97f8c33a46872c69 Mon Sep 17 00:00:00 2001 From: Rahul Raina Date: Wed, 8 Jan 2020 14:32:13 +0800 Subject: [PATCH 024/205] Adding helper methods to load idp_cert --- src/onelogin/saml2/auth.py | 5 +- src/onelogin/saml2/logout_request.py | 2 +- src/onelogin/saml2/response.py | 2 +- tests/settings/settings10.json | 46 +++++++++++++++++++ .../src/OneLogin/saml2_tests/response_test.py | 6 +-- 5 files changed, 53 insertions(+), 8 deletions(-) create mode 100644 tests/settings/settings10.json diff --git a/src/onelogin/saml2/auth.py b/src/onelogin/saml2/auth.py index 1bf5f8a6..72d37847 100644 --- a/src/onelogin/saml2/auth.py +++ b/src/onelogin/saml2/auth.py @@ -605,8 +605,7 @@ def __validate_signature(self, data, saml_type, raise_exceptions=False): return True idp_data = self.get_settings().get_idp_data() - - exists_x509cert = 'x509cert' in idp_data and idp_data['x509cert'] + exists_x509cert = self.get_settings().get_idp_cert() is not None exists_multix509sign = 'x509certMulti' in idp_data and \ 'signing' in idp_data['x509certMulti'] and \ idp_data['x509certMulti']['signing'] @@ -646,7 +645,7 @@ def __validate_signature(self, data, saml_type, raise_exceptions=False): OneLogin_Saml2_ValidationError.INVALID_SIGNATURE ) else: - cert = idp_data['x509cert'] + cert = self.get_settings().get_idp_cert() if not OneLogin_Saml2_Utils.validate_binary_sign(signed_query, OneLogin_Saml2_Utils.b64decode(signature), diff --git a/src/onelogin/saml2/logout_request.py b/src/onelogin/saml2/logout_request.py index 252726b7..ef4d05e2 100644 --- a/src/onelogin/saml2/logout_request.py +++ b/src/onelogin/saml2/logout_request.py @@ -72,7 +72,7 @@ def __init__(self, settings, request=None, name_id=None, session_index=None, nq= if exists_multix509enc: cert = idp_data['x509certMulti']['encryption'][0] else: - cert = idp_data['x509cert'] + cert = self.__settings.get_idp_cert() if name_id is not None: if not name_id_format and sp_data['NameIDFormat'] != OneLogin_Saml2_Constants.NAMEID_UNSPECIFIED: diff --git a/src/onelogin/saml2/response.py b/src/onelogin/saml2/response.py index 83eafe7d..a5004fea 100644 --- a/src/onelogin/saml2/response.py +++ b/src/onelogin/saml2/response.py @@ -293,7 +293,7 @@ def is_valid(self, request_data, request_id=None, raise_exceptions=False): OneLogin_Saml2_ValidationError.NO_SIGNATURE_FOUND ) else: - cert = idp_data.get('x509cert', None) + cert = self.__settings.get_idp_cert() fingerprint = idp_data.get('certFingerprint', None) if fingerprint: fingerprint = OneLogin_Saml2_Utils.format_finger_print(fingerprint) diff --git a/tests/settings/settings10.json b/tests/settings/settings10.json new file mode 100644 index 00000000..f118b4d0 --- /dev/null +++ b/tests/settings/settings10.json @@ -0,0 +1,46 @@ +{ + "strict": false, + "debug": false, + "sp": { + "entityId": "http://stuff.com/endpoints/metadata.php", + "assertionConsumerService": { + "url": "http://stuff.com/endpoints/endpoints/acs.php" + }, + "singleLogoutService": { + "url": "http://stuff.com/endpoints/endpoints/sls.php" + }, + "NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" + }, + "idp": { + "entityId": "http://idp.example.com/", + "singleSignOnService": { + "url": "http://idp.example.com/SSOService.php" + }, + "singleLogoutService": { + "url": "http://idp.example.com/SingleLogoutService.php" + }, + "x509cert": "MIICbDCCAdWgAwIBAgIBADANBgkqhkiG9w0BAQ0FADBTMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lbG9naW4gSW5jMRgwFgYDVQQDDA9pZHAuZXhhbXBsZS5jb20wHhcNMTQwOTIzMTIyNDA4WhcNNDIwMjA4MTIyNDA4WjBTMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lbG9naW4gSW5jMRgwFgYDVQQDDA9pZHAuZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOWA+YHU7cvPOrBOfxCscsYTJB+kH3MaA9BFrSHFS+KcR6cw7oPSktIJxUgvDpQbtfNcOkE/tuOPBDoech7AXfvH6d7Bw7xtW8PPJ2mB5Hn/HGW2roYhxmfh3tR5SdwN6i4ERVF8eLkvwCHsNQyK2Ref0DAJvpBNZMHCpS24916/AgMBAAGjUDBOMB0GA1UdDgQWBBQ77/qVeiigfhYDITplCNtJKZTM8DAfBgNVHSMEGDAWgBQ77/qVeiigfhYDITplCNtJKZTM8DAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4GBAJO2j/1uO80E5C2PM6Fk9mzerrbkxl7AZ/mvlbOn+sNZE+VZ1AntYuG8ekbJpJtG1YfRfc7EA9mEtqvv4dhv7zBy4nK49OR+KpIBjItWB5kYvrqMLKBa32sMbgqqUqeF1ENXKjpvLSuPdfGJZA3dNa/+Dyb8GGqWe707zLyc5F8m" + }, + "security": { + "authnRequestsSigned": false, + "wantAssertionsSigned": false, + "signMetadata": false + }, + "contactPerson": { + "technical": { + "givenName": "technical_name", + "emailAddress": "technical@example.com" + }, + "support": { + "givenName": "support_name", + "emailAddress": "support@example.com" + } + }, + "organization": { + "en-US": { + "name": "sp_test", + "displayname": "SP test", + "url": "http://sp.example.com" + } + } +} diff --git a/tests/src/OneLogin/saml2_tests/response_test.py b/tests/src/OneLogin/saml2_tests/response_test.py index e6641b73..7d9934a1 100644 --- a/tests/src/OneLogin/saml2_tests/response_test.py +++ b/tests/src/OneLogin/saml2_tests/response_test.py @@ -1473,7 +1473,7 @@ def testIsValid2(self): response_2 = OneLogin_Saml2_Response(settings_2, xml_2) self.assertTrue(response_2.is_valid(self.get_request_data())) - settings_info_3 = self.loadSettingsJSON('settings2.json') + settings_info_3 = self.loadSettingsJSON('settings10.json') idp_cert = OneLogin_Saml2_Utils.format_cert(settings_info_3['idp']['x509cert']) settings_info_3['idp']['certFingerprint'] = OneLogin_Saml2_Utils.calculate_x509_fingerprint(idp_cert) settings_info_3['idp']['x509cert'] = '' @@ -1662,7 +1662,7 @@ def testIsValidSignFingerprint(self): self.assertFalse(response_9.is_valid(self.get_request_data())) def testMessageSignedIsValidSignWithEmptyReferenceURI(self): - settings_info = self.loadSettingsJSON() + settings_info = self.loadSettingsJSON("settings10.json") del settings_info['idp']['x509cert'] settings_info['idp']['certFingerprint'] = "657302a5e11a4794a1e50a705988d66c9377575d" settings = OneLogin_Saml2_Settings(settings_info) @@ -1671,7 +1671,7 @@ def testMessageSignedIsValidSignWithEmptyReferenceURI(self): self.assertTrue(response.is_valid(self.get_request_data())) def testAssertionSignedIsValidSignWithEmptyReferenceURI(self): - settings_info = self.loadSettingsJSON() + settings_info = self.loadSettingsJSON('settings10.json') del settings_info['idp']['x509cert'] settings_info['idp']['certFingerprint'] = "657302a5e11a4794a1e50a705988d66c9377575d" settings = OneLogin_Saml2_Settings(settings_info) From 70c9a50286b014f8bda2124227740ef47a6575fa Mon Sep 17 00:00:00 2001 From: Rahul Raina Date: Wed, 8 Jan 2020 14:37:34 +0800 Subject: [PATCH 025/205] Fixing spacing --- src/onelogin/saml2/auth.py | 1 + src/onelogin/saml2/response.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/onelogin/saml2/auth.py b/src/onelogin/saml2/auth.py index 72d37847..e432f769 100644 --- a/src/onelogin/saml2/auth.py +++ b/src/onelogin/saml2/auth.py @@ -605,6 +605,7 @@ def __validate_signature(self, data, saml_type, raise_exceptions=False): return True idp_data = self.get_settings().get_idp_data() + exists_x509cert = self.get_settings().get_idp_cert() is not None exists_multix509sign = 'x509certMulti' in idp_data and \ 'signing' in idp_data['x509certMulti'] and \ diff --git a/src/onelogin/saml2/response.py b/src/onelogin/saml2/response.py index a5004fea..1e7736a8 100644 --- a/src/onelogin/saml2/response.py +++ b/src/onelogin/saml2/response.py @@ -293,7 +293,7 @@ def is_valid(self, request_data, request_id=None, raise_exceptions=False): OneLogin_Saml2_ValidationError.NO_SIGNATURE_FOUND ) else: - cert = self.__settings.get_idp_cert() + cert = self.__settings.get_idp_cert() fingerprint = idp_data.get('certFingerprint', None) if fingerprint: fingerprint = OneLogin_Saml2_Utils.format_finger_print(fingerprint) From 08970a9dd1882af3cd5a07720e639700c134d991 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Wed, 15 Jan 2020 17:22:40 +0100 Subject: [PATCH 026/205] Fix flake8 --- demo-tornado/views.py | 1 + 1 file changed, 1 insertion(+) diff --git a/demo-tornado/views.py b/demo-tornado/views.py index 6fe72bcd..7d143087 100644 --- a/demo-tornado/views.py +++ b/demo-tornado/views.py @@ -171,6 +171,7 @@ def init_saml_auth(req): auth = OneLogin_Saml2_Auth(req, custom_base_path=Settings.SAML_PATH) return auth + if __name__ == "__main__": app = Application() http_server = tornado.httpserver.HTTPServer(app) From 6f8ca9c1f628db3a818312e678d5c2c108efbe9c Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Wed, 15 Jan 2020 18:30:29 +0100 Subject: [PATCH 027/205] Fix tests --- src/onelogin/saml2/settings.py | 2 +- tests/data/customPath/certs/idp.crt | 16 ++++++++++++++++ tests/src/OneLogin/saml2_tests/settings_test.py | 4 ++-- 3 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 tests/data/customPath/certs/idp.crt diff --git a/src/onelogin/saml2/settings.py b/src/onelogin/saml2/settings.py index 1d8e9518..404024ab 100644 --- a/src/onelogin/saml2/settings.py +++ b/src/onelogin/saml2/settings.py @@ -559,7 +559,7 @@ def get_idp_cert(self): :rtype: string """ cert = self.__idp.get('x509cert') - cert_file_name = self.__paths['cert'] + 'idp.crt' + cert_file_name = self.get_cert_path() + 'idp.crt' if not cert and exists(cert_file_name): with open(cert_file_name) as f: cert = f.read() diff --git a/tests/data/customPath/certs/idp.crt b/tests/data/customPath/certs/idp.crt new file mode 100644 index 00000000..483db3ce --- /dev/null +++ b/tests/data/customPath/certs/idp.crt @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIICgTCCAeoCCQCbOlrWDdX7FTANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMC +Tk8xGDAWBgNVBAgTD0FuZHJlYXMgU29sYmVyZzEMMAoGA1UEBxMDRm9vMRAwDgYD +VQQKEwdVTklORVRUMRgwFgYDVQQDEw9mZWlkZS5lcmxhbmcubm8xITAfBgkqhkiG +9w0BCQEWEmFuZHJlYXNAdW5pbmV0dC5ubzAeFw0wNzA2MTUxMjAxMzVaFw0wNzA4 +MTQxMjAxMzVaMIGEMQswCQYDVQQGEwJOTzEYMBYGA1UECBMPQW5kcmVhcyBTb2xi +ZXJnMQwwCgYDVQQHEwNGb28xEDAOBgNVBAoTB1VOSU5FVFQxGDAWBgNVBAMTD2Zl +aWRlLmVybGFuZy5ubzEhMB8GCSqGSIb3DQEJARYSYW5kcmVhc0B1bmluZXR0Lm5v +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDivbhR7P516x/S3BqKxupQe0LO +NoliupiBOesCO3SHbDrl3+q9IbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHIS +KOtPlAeTZSnb8QAu7aRjZq3+PbrP5uW3TcfCGPtKTytHOge/OlJbo078dVhXQ14d +1EDwXJW1rRXuUt4C8QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACDVfp86HObqY+e8 +BUoWQ9+VMQx1ASDohBjwOsg2WykUqRXF+dLfcUH9dWR63CtZIKFDbStNomPnQz7n +bK+onygwBspVEbnHuUihZq3ZUdmumQqCw4Uvs/1Uvq3orOo/WJVhTyvLgFVK2Qar +Q4/67OZfHd7R+POBXhophSMv1ZOo +-----END CERTIFICATE----- \ No newline at end of file diff --git a/tests/src/OneLogin/saml2_tests/settings_test.py b/tests/src/OneLogin/saml2_tests/settings_test.py index 32b037fe..837af55f 100644 --- a/tests/src/OneLogin/saml2_tests/settings_test.py +++ b/tests/src/OneLogin/saml2_tests/settings_test.py @@ -228,14 +228,14 @@ def testGetIDPCert(self): """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON('settings9.json')) - cert = "-----BEGIN CERTIFICATE-----\nMIICgTCCAeoCCQCbOlrWDdX7FTANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMC\nTk8xGDAWBgNVBAgTD0FuZHJlYXMgU29sYmVyZzEMMAoGA1UEBxMDRm9vMRAwDgYD\nVQQKEwdVTklORVRUMRgwFgYDVQQDEw9mZWlkZS5lcmxhbmcubm8xITAfBgkqhkiG\n9w0BCQEWEmFuZHJlYXNAdW5pbmV0dC5ubzAeFw0wNzA2MTUxMjAxMzVaFw0wNzA4\nMTQxMjAxMzVaMIGEMQswCQYDVQQGEwJOTzEYMBYGA1UECBMPQW5kcmVhcyBTb2xi\nZXJnMQwwCgYDVQQHEwNGb28xEDAOBgNVBAoTB1VOSU5FVFQxGDAWBgNVBAMTD2Zl\naWRlLmVybGFuZy5ubzEhMB8GCSqGSIb3DQEJARYSYW5kcmVhc0B1bmluZXR0Lm5v\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDivbhR7P516x/S3BqKxupQe0LO\nNoliupiBOesCO3SHbDrl3+q9IbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHIS\nKOtPlAeTZSnb8QAu7aRjZq3+PbrP5uW3TcfCGPtKTytHOge/OlJbo078dVhXQ14d\n1EDwXJW1rRXuUt4C8QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACDVfp86HObqY+e8\nBUoWQ9+VMQx1ASDohBjwOsg2WykUqRXF+dLfcUH9dWR63CtZIKFDbStNomPnQz7n\nbK+onygwBspVEbnHuUihZq3ZUdmumQqCw4Uvs/1Uvq3orOo/WJVhTyvLgFVK2Qar\nQ4/67OZfHd7R+POBXhophSMv1ZOo\n-----END CERTIFICATE-----\n" + cert = "-----BEGIN CERTIFICATE-----\nMIICgTCCAeoCCQCbOlrWDdX7FTANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMC\nTk8xGDAWBgNVBAgTD0FuZHJlYXMgU29sYmVyZzEMMAoGA1UEBxMDRm9vMRAwDgYD\nVQQKEwdVTklORVRUMRgwFgYDVQQDEw9mZWlkZS5lcmxhbmcubm8xITAfBgkqhkiG\n9w0BCQEWEmFuZHJlYXNAdW5pbmV0dC5ubzAeFw0wNzA2MTUxMjAxMzVaFw0wNzA4\nMTQxMjAxMzVaMIGEMQswCQYDVQQGEwJOTzEYMBYGA1UECBMPQW5kcmVhcyBTb2xi\nZXJnMQwwCgYDVQQHEwNGb28xEDAOBgNVBAoTB1VOSU5FVFQxGDAWBgNVBAMTD2Zl\naWRlLmVybGFuZy5ubzEhMB8GCSqGSIb3DQEJARYSYW5kcmVhc0B1bmluZXR0Lm5v\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDivbhR7P516x/S3BqKxupQe0LO\nNoliupiBOesCO3SHbDrl3+q9IbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHIS\nKOtPlAeTZSnb8QAu7aRjZq3+PbrP5uW3TcfCGPtKTytHOge/OlJbo078dVhXQ14d\n1EDwXJW1rRXuUt4C8QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACDVfp86HObqY+e8\nBUoWQ9+VMQx1ASDohBjwOsg2WykUqRXF+dLfcUH9dWR63CtZIKFDbStNomPnQz7n\nbK+onygwBspVEbnHuUihZq3ZUdmumQqCw4Uvs/1Uvq3orOo/WJVhTyvLgFVK2Qar\nQ4/67OZfHd7R+POBXhophSMv1ZOo\n-----END CERTIFICATE-----" self.assertEqual(cert, settings.get_idp_cert()) settings_data = self.loadSettingsJSON() settings = OneLogin_Saml2_Settings(settings_data) settings_data['idp']['x509cert'] = cert - self.assertEqual(cert, settings.get_sp_cert()) + self.assertEqual(cert, settings.get_idp_cert()) del settings_data['idp']['x509cert'] del settings_data['custom_base_path'] From 589425d1fbd2161ce2a566dfcb92835a221a3541 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Fri, 21 Feb 2020 10:47:09 +0100 Subject: [PATCH 028/205] Add sha256 instead sha1 algorithm for sign/digest as recommended value on documentation and settings --- README.md | 4 ++-- demo-django/saml/advanced_settings.json | 4 ++-- demo-flask/saml/advanced_settings.json | 4 ++-- demo-tornado/saml/advanced_settings.json | 4 ++-- demo_pyramid/demo_pyramid/saml/advanced_settings.json | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index f0e16ff0..97f43add 100644 --- a/README.md +++ b/README.md @@ -431,14 +431,14 @@ In addition to the required settings data (idp, sp), extra settings can be defin // 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256' // 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384' // 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512' - "signatureAlgorithm": "http://www.w3.org/2000/09/xmldsig#rsa-sha1", + "signatureAlgorithm": "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", // Algorithm that the toolkit will use on digest process. Options: // 'http://www.w3.org/2000/09/xmldsig#sha1' // 'http://www.w3.org/2001/04/xmlenc#sha256' // 'http://www.w3.org/2001/04/xmldsig-more#sha384' // 'http://www.w3.org/2001/04/xmlenc#sha512' - 'digestAlgorithm': "http://www.w3.org/2000/09/xmldsig#sha1" + 'digestAlgorithm': "http://www.w3.org/2001/04/xmlenc#sha256" }, // Contact information template, it is recommended to suply a diff --git a/demo-django/saml/advanced_settings.json b/demo-django/saml/advanced_settings.json index 3115e17e..1307b0ae 100644 --- a/demo-django/saml/advanced_settings.json +++ b/demo-django/saml/advanced_settings.json @@ -10,8 +10,8 @@ "wantNameId" : true, "wantNameIdEncrypted": false, "wantAssertionsEncrypted": false, - "signatureAlgorithm": "http://www.w3.org/2000/09/xmldsig#rsa-sha1", - "digestAlgorithm": "http://www.w3.org/2000/09/xmldsig#sha1" + "signatureAlgorithm": "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", + "digestAlgorithm": "http://www.w3.org/2001/04/xmlenc#sha256" }, "contactPerson": { "technical": { diff --git a/demo-flask/saml/advanced_settings.json b/demo-flask/saml/advanced_settings.json index 3115e17e..1307b0ae 100644 --- a/demo-flask/saml/advanced_settings.json +++ b/demo-flask/saml/advanced_settings.json @@ -10,8 +10,8 @@ "wantNameId" : true, "wantNameIdEncrypted": false, "wantAssertionsEncrypted": false, - "signatureAlgorithm": "http://www.w3.org/2000/09/xmldsig#rsa-sha1", - "digestAlgorithm": "http://www.w3.org/2000/09/xmldsig#sha1" + "signatureAlgorithm": "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", + "digestAlgorithm": "http://www.w3.org/2001/04/xmlenc#sha256" }, "contactPerson": { "technical": { diff --git a/demo-tornado/saml/advanced_settings.json b/demo-tornado/saml/advanced_settings.json index 3115e17e..1307b0ae 100644 --- a/demo-tornado/saml/advanced_settings.json +++ b/demo-tornado/saml/advanced_settings.json @@ -10,8 +10,8 @@ "wantNameId" : true, "wantNameIdEncrypted": false, "wantAssertionsEncrypted": false, - "signatureAlgorithm": "http://www.w3.org/2000/09/xmldsig#rsa-sha1", - "digestAlgorithm": "http://www.w3.org/2000/09/xmldsig#sha1" + "signatureAlgorithm": "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", + "digestAlgorithm": "http://www.w3.org/2001/04/xmlenc#sha256" }, "contactPerson": { "technical": { diff --git a/demo_pyramid/demo_pyramid/saml/advanced_settings.json b/demo_pyramid/demo_pyramid/saml/advanced_settings.json index 3115e17e..1307b0ae 100644 --- a/demo_pyramid/demo_pyramid/saml/advanced_settings.json +++ b/demo_pyramid/demo_pyramid/saml/advanced_settings.json @@ -10,8 +10,8 @@ "wantNameId" : true, "wantNameIdEncrypted": false, "wantAssertionsEncrypted": false, - "signatureAlgorithm": "http://www.w3.org/2000/09/xmldsig#rsa-sha1", - "digestAlgorithm": "http://www.w3.org/2000/09/xmldsig#sha1" + "signatureAlgorithm": "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", + "digestAlgorithm": "http://www.w3.org/2001/04/xmlenc#sha256" }, "contactPerson": { "technical": { From 802c6121b6ae97cbf063cfe04d579753dff6b23a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Jun 2020 17:32:18 +0000 Subject: [PATCH 029/205] Bump django from 1.11.26 to 1.11.29 in /demo-django Bumps [django](https://github.com/django/django) from 1.11.26 to 1.11.29. - [Release notes](https://github.com/django/django/releases) - [Commits](https://github.com/django/django/compare/1.11.26...1.11.29) Signed-off-by: dependabot[bot] --- demo-django/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo-django/requirements.txt b/demo-django/requirements.txt index a4a895b0..46ceaced 100644 --- a/demo-django/requirements.txt +++ b/demo-django/requirements.txt @@ -1,2 +1,2 @@ -Django==1.11.26 +Django==1.11.29 python3-saml From b192e2a3585bdf24a461c76aa8fe6da2952340c1 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Fri, 21 Aug 2020 16:22:32 +0300 Subject: [PATCH 030/205] Add OneLogin_Saml2_Authn_Request._generate_request_id() override point Refs https://github.com/onelogin/python3-saml/issues/208#issuecomment-678099281 --- src/onelogin/saml2/authn_request.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/onelogin/saml2/authn_request.py b/src/onelogin/saml2/authn_request.py index 73547735..ef9ed603 100644 --- a/src/onelogin/saml2/authn_request.py +++ b/src/onelogin/saml2/authn_request.py @@ -47,8 +47,7 @@ def __init__(self, settings, force_authn=False, is_passive=False, set_nameid_pol idp_data = self.__settings.get_idp_data() security = self.__settings.get_security_data() - uid = OneLogin_Saml2_Utils.generate_unique_id() - self.__id = uid + self.__id = self._generate_request_id() issue_instant = OneLogin_Saml2_Utils.parse_time_to_SAML(OneLogin_Saml2_Utils.now()) destination = idp_data['singleSignOnService']['url'] @@ -113,7 +112,7 @@ def __init__(self, settings, force_authn=False, is_passive=False, set_nameid_pol request = OneLogin_Saml2_Templates.AUTHN_REQUEST % \ { - 'id': uid, + 'id': self.__id, 'provider_name': provider_name_str, 'force_authn_str': force_authn_str, 'is_passive_str': is_passive_str, @@ -129,6 +128,14 @@ def __init__(self, settings, force_authn=False, is_passive=False, set_nameid_pol self.__authn_request = request + def _generate_request_id(self): + """ + Generate an unique request ID. + + You can override this in a subclass. + """ + return OneLogin_Saml2_Utils.generate_unique_id() + def get_request(self, deflate=True): """ Returns unsigned AuthnRequest. From 53db80de9e4d853742565e500f640bfcb01ad295 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Fri, 21 Aug 2020 16:27:15 +0300 Subject: [PATCH 031/205] Make request/response/metadata classes overridable by subclassing --- src/onelogin/saml2/auth.py | 17 +++++++++++------ src/onelogin/saml2/settings.py | 10 ++++++---- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/onelogin/saml2/auth.py b/src/onelogin/saml2/auth.py index e432f769..cd7c1607 100644 --- a/src/onelogin/saml2/auth.py +++ b/src/onelogin/saml2/auth.py @@ -34,6 +34,11 @@ class OneLogin_Saml2_Auth(object): SAML Response, a Logout Request or a Logout Response). """ + authn_request_class = OneLogin_Saml2_Authn_Request + logout_request_class = OneLogin_Saml2_Logout_Request + logout_response_class = OneLogin_Saml2_Logout_Response + response_class = OneLogin_Saml2_Response + def __init__(self, request_data, old_settings=None, custom_base_path=None): """ Initializes the SP SAML instance. @@ -102,7 +107,7 @@ def process_response(self, request_id=None): if 'post_data' in self.__request_data and 'SAMLResponse' in self.__request_data['post_data']: # AuthnResponse -- HTTP_POST Binding - response = OneLogin_Saml2_Response(self.__settings, self.__request_data['post_data']['SAMLResponse']) + response = self.response_class(self.__settings, self.__request_data['post_data']['SAMLResponse']) self.__last_response = response.get_xml_document() if response.is_valid(self.__request_data, request_id): @@ -147,7 +152,7 @@ def process_slo(self, keep_local_session=False, request_id=None, delete_session_ get_data = 'get_data' in self.__request_data and self.__request_data['get_data'] if get_data and 'SAMLResponse' in get_data: - logout_response = OneLogin_Saml2_Logout_Response(self.__settings, get_data['SAMLResponse']) + logout_response = self.logout_response_class(self.__settings, get_data['SAMLResponse']) self.__last_response = logout_response.get_xml() if not self.validate_response_signature(get_data): self.__errors.append('invalid_logout_response_signature') @@ -163,7 +168,7 @@ def process_slo(self, keep_local_session=False, request_id=None, delete_session_ OneLogin_Saml2_Utils.delete_local_session(delete_session_cb) elif get_data and 'SAMLRequest' in get_data: - logout_request = OneLogin_Saml2_Logout_Request(self.__settings, get_data['SAMLRequest']) + logout_request = self.logout_request_class(self.__settings, get_data['SAMLRequest']) self.__last_request = logout_request.get_xml() if not self.validate_request_signature(get_data): self.__errors.append("invalid_logout_request_signature") @@ -177,7 +182,7 @@ def process_slo(self, keep_local_session=False, request_id=None, delete_session_ in_response_to = logout_request.id self.__last_message_id = logout_request.id - response_builder = OneLogin_Saml2_Logout_Response(self.__settings) + response_builder = self.logout_response_class(self.__settings) response_builder.build(in_response_to) self.__last_response = response_builder.get_xml() logout_response = response_builder.get_response() @@ -371,7 +376,7 @@ def login(self, return_to=None, force_authn=False, is_passive=False, set_nameid_ :returns: Redirection URL :rtype: string """ - authn_request = OneLogin_Saml2_Authn_Request(self.__settings, force_authn, is_passive, set_nameid_policy, name_id_value_req) + authn_request = self.authn_request_class(self.__settings, force_authn, is_passive, set_nameid_policy, name_id_value_req) self.__last_request = authn_request.get_xml() self.__last_request_id = authn_request.get_id() @@ -425,7 +430,7 @@ def logout(self, return_to=None, name_id=None, session_index=None, nq=None, name if name_id_format is None and self.__nameid_format is not None: name_id_format = self.__nameid_format - logout_request = OneLogin_Saml2_Logout_Request( + logout_request = self.logout_request_class( self.__settings, name_id=name_id, session_index=session_index, diff --git a/src/onelogin/saml2/settings.py b/src/onelogin/saml2/settings.py index 404024ab..6df22432 100644 --- a/src/onelogin/saml2/settings.py +++ b/src/onelogin/saml2/settings.py @@ -66,6 +66,8 @@ class OneLogin_Saml2_Settings(object): """ + metadata_class = OneLogin_Saml2_Metadata + def __init__(self, settings=None, custom_base_path=None, sp_validation_only=False): """ Initializes the settings: @@ -616,7 +618,7 @@ def get_sp_metadata(self): :returns: SP metadata (xml) :rtype: string """ - metadata = OneLogin_Saml2_Metadata.builder( + metadata = self.metadata_class.builder( self.__sp, self.__security['authnRequestsSigned'], self.__security['wantAssertionsSigned'], self.__security['metadataValidUntil'], @@ -627,10 +629,10 @@ def get_sp_metadata(self): add_encryption = self.__security['wantNameIdEncrypted'] or self.__security['wantAssertionsEncrypted'] cert_new = self.get_sp_cert_new() - metadata = OneLogin_Saml2_Metadata.add_x509_key_descriptors(metadata, cert_new, add_encryption) + metadata = self.metadata_class.add_x509_key_descriptors(metadata, cert_new, add_encryption) cert = self.get_sp_cert() - metadata = OneLogin_Saml2_Metadata.add_x509_key_descriptors(metadata, cert, add_encryption) + metadata = self.metadata_class.add_x509_key_descriptors(metadata, cert, add_encryption) # Sign metadata if 'signMetadata' in self.__security and self.__security['signMetadata'] is not False: @@ -684,7 +686,7 @@ def get_sp_metadata(self): signature_algorithm = self.__security['signatureAlgorithm'] digest_algorithm = self.__security['digestAlgorithm'] - metadata = OneLogin_Saml2_Metadata.sign_metadata(metadata, key_metadata, cert_metadata, signature_algorithm, digest_algorithm) + metadata = self.metadata_class.sign_metadata(metadata, key_metadata, cert_metadata, signature_algorithm, digest_algorithm) return metadata From ca04d1d92864726c7b8594f2dd357e4b839dbeb1 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Sat, 22 Aug 2020 13:34:54 +0300 Subject: [PATCH 032/205] Make some staticmethods classmethods instead --- src/onelogin/saml2/idp_metadata_parser.py | 15 ++++++++------- src/onelogin/saml2/logout_request.py | 14 +++++++------- src/onelogin/saml2/metadata.py | 16 ++++++++-------- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/onelogin/saml2/idp_metadata_parser.py b/src/onelogin/saml2/idp_metadata_parser.py index 88411f45..1172bd06 100644 --- a/src/onelogin/saml2/idp_metadata_parser.py +++ b/src/onelogin/saml2/idp_metadata_parser.py @@ -25,8 +25,8 @@ class OneLogin_Saml2_IdPMetadataParser(object): A class that contain methods related to obtaining and parsing metadata from IdP """ - @staticmethod - def get_metadata(url, validate_cert=True): + @classmethod + def get_metadata(cls, url, validate_cert=True): """ Gets the metadata XML from the provided URL :param url: Url where the XML of the Identity Provider Metadata is published. @@ -63,8 +63,8 @@ def get_metadata(url, validate_cert=True): return xml - @staticmethod - def parse_remote(url, validate_cert=True, entity_id=None, **kwargs): + @classmethod + def parse_remote(cls, url, validate_cert=True, entity_id=None, **kwargs): """ Gets the metadata XML from the provided URL and parse it, returning a dict with extracted data :param url: Url where the XML of the Identity Provider Metadata is published. @@ -80,11 +80,12 @@ def parse_remote(url, validate_cert=True, entity_id=None, **kwargs): :returns: settings dict with extracted data :rtype: dict """ - idp_metadata = OneLogin_Saml2_IdPMetadataParser.get_metadata(url, validate_cert) - return OneLogin_Saml2_IdPMetadataParser.parse(idp_metadata, entity_id=entity_id, **kwargs) + idp_metadata = cls.get_metadata(url, validate_cert) + return cls.parse(idp_metadata, entity_id=entity_id, **kwargs) - @staticmethod + @classmethod def parse( + cls, idp_metadata, required_sso_binding=OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT, required_slo_binding=OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT, diff --git a/src/onelogin/saml2/logout_request.py b/src/onelogin/saml2/logout_request.py index ef4d05e2..cd59e368 100644 --- a/src/onelogin/saml2/logout_request.py +++ b/src/onelogin/saml2/logout_request.py @@ -203,8 +203,8 @@ def get_nameid_data(request, key=None): return name_id_data - @staticmethod - def get_nameid(request, key=None): + @classmethod + def get_nameid(cls, request, key=None): """ Gets the NameID of the Logout Request Message :param request: Logout Request Message @@ -214,11 +214,11 @@ def get_nameid(request, key=None): :return: Name ID Value :rtype: string """ - name_id = OneLogin_Saml2_Logout_Request.get_nameid_data(request, key) + name_id = cls.get_nameid_data(request, key) return name_id['Value'] - @staticmethod - def get_nameid_format(request, key=None): + @classmethod + def get_nameid_format(cls, request, key=None): """ Gets the NameID Format of the Logout Request Message :param request: Logout Request Message @@ -229,7 +229,7 @@ def get_nameid_format(request, key=None): :rtype: string """ name_id_format = None - name_id_data = OneLogin_Saml2_Logout_Request.get_nameid_data(request, key) + name_id_data = cls.get_nameid_data(request, key) if name_id_data and 'Format' in name_id_data.keys(): name_id_format = name_id_data['Format'] return name_id_format @@ -326,7 +326,7 @@ def is_valid(self, request_data, raise_exceptions=False): ) # Check issuer - issuer = OneLogin_Saml2_Logout_Request.get_issuer(root) + issuer = self.get_issuer(root) if issuer is not None and issuer != idp_entity_id: raise OneLogin_Saml2_ValidationError( 'Invalid issuer in the Logout Request (expected %(idpEntityId)s, got %(issuer)s)' % diff --git a/src/onelogin/saml2/metadata.py b/src/onelogin/saml2/metadata.py index 0aab105c..9528b0e8 100644 --- a/src/onelogin/saml2/metadata.py +++ b/src/onelogin/saml2/metadata.py @@ -34,8 +34,8 @@ class OneLogin_Saml2_Metadata(object): TIME_VALID = 172800 # 2 days TIME_CACHED = 604800 # 1 week - @staticmethod - def builder(sp, authnsign=False, wsign=False, valid_until=None, cache_duration=None, contacts=None, organization=None): + @classmethod + def builder(cls, sp, authnsign=False, wsign=False, valid_until=None, cache_duration=None, contacts=None, organization=None): """ Builds the metadata of the SP @@ -61,7 +61,7 @@ def builder(sp, authnsign=False, wsign=False, valid_until=None, cache_duration=N :type organization: dict """ if valid_until is None: - valid_until = int(time()) + OneLogin_Saml2_Metadata.TIME_VALID + valid_until = int(time()) + cls.TIME_VALID if not isinstance(valid_until, basestring): if isinstance(valid_until, datetime): valid_until_time = valid_until.timetuple() @@ -72,7 +72,7 @@ def builder(sp, authnsign=False, wsign=False, valid_until=None, cache_duration=N valid_until_str = valid_until if cache_duration is None: - cache_duration = OneLogin_Saml2_Metadata.TIME_CACHED + cache_duration = cls.TIME_CACHED if not isinstance(cache_duration, compat.str_type): cache_duration_str = 'PT%sS' % cache_duration # Period of Time x Seconds else: @@ -228,8 +228,8 @@ def __add_x509_key_descriptors(root, cert, signing): x509_certificate.text = OneLogin_Saml2_Utils.format_cert(cert, False) key_descriptor.set('use', ('encryption', 'signing')[signing]) - @staticmethod - def add_x509_key_descriptors(metadata, cert=None, add_encryption=True): + @classmethod + def add_x509_key_descriptors(cls, metadata, cert=None, add_encryption=True): """ Adds the x509 descriptors (sign/encryption) to the metadata The same cert will be used for sign/encrypt @@ -260,6 +260,6 @@ def add_x509_key_descriptors(metadata, cert=None, add_encryption=True): raise Exception('Malformed metadata.') if add_encryption: - OneLogin_Saml2_Metadata.__add_x509_key_descriptors(sp_sso_descriptor, cert, False) - OneLogin_Saml2_Metadata.__add_x509_key_descriptors(sp_sso_descriptor, cert, True) + cls.__add_x509_key_descriptors(sp_sso_descriptor, cert, False) + cls.__add_x509_key_descriptors(sp_sso_descriptor, cert, True) return OneLogin_Saml2_XML.to_string(root) From 468c747338f7fe2a7549382bc2fd294282658686 Mon Sep 17 00:00:00 2001 From: Florian Best Date: Wed, 4 Nov 2020 15:38:06 +0100 Subject: [PATCH 033/205] tornado-demo: Fix autoreloading --- demo-tornado/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo-tornado/views.py b/demo-tornado/views.py index 7d143087..70cfea68 100644 --- a/demo-tornado/views.py +++ b/demo-tornado/views.py @@ -21,7 +21,7 @@ def __init__(self): settings = { "template_path": Settings.TEMPLATE_PATH, "saml_path": Settings.SAML_PATH, - "autorealod": True, + "autoreload": True, "debug": True } tornado.web.Application.__init__(self, handlers, **settings) From 51da7ec96414f4387c430f36e1584e1f1fabe936 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Tue, 24 Nov 2020 18:08:37 +0100 Subject: [PATCH 034/205] Fix #225 Fix get_session_expiration signature --- src/onelogin/saml2/auth.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/onelogin/saml2/auth.py b/src/onelogin/saml2/auth.py index e432f769..76a79272 100644 --- a/src/onelogin/saml2/auth.py +++ b/src/onelogin/saml2/auth.py @@ -279,7 +279,7 @@ def get_session_expiration(self): """ Returns the SessionNotOnOrAfter from the AuthnStatement. :returns: The SessionNotOnOrAfter of the assertion - :rtype: DateTime|None + :rtype: unix/posix timestamp|None """ return self.__session_expiration From 082dfc75ceadaf317f9b8c66bdcec3a35f4105a2 Mon Sep 17 00:00:00 2001 From: BeritJanssen Date: Thu, 3 Dec 2020 12:45:13 +0100 Subject: [PATCH 035/205] adding returnUrl parameter to singleLogoutService of IdP --- README.md | 3 +++ src/onelogin/saml2/auth.py | 12 +++++++++++- src/onelogin/saml2/logout_response.py | 3 ++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 97f43add..c172f276 100644 --- a/README.md +++ b/README.md @@ -304,6 +304,9 @@ This is the ``settings.json`` file: "singleLogoutService": { // URL Location of the IdP where SLO Request will be sent. "url": "https://app.onelogin.com/trust/saml2/http-redirect/slo/", + // URL Location where the from the SP will returned (after IdP-initiated logout) + // OPTIONAL: only specify if different from url parameter + "returnUrl": "/slo_return/" // SAML protocol binding to be used when returning the // message. OneLogin Toolkit supports the HTTP-Redirect binding // only for this endpoint. diff --git a/src/onelogin/saml2/auth.py b/src/onelogin/saml2/auth.py index 76a79272..37aada61 100644 --- a/src/onelogin/saml2/auth.py +++ b/src/onelogin/saml2/auth.py @@ -190,7 +190,7 @@ def process_slo(self, keep_local_session=False, request_id=None, delete_session_ if security['logoutResponseSigned']: self.add_response_signature(parameters, security['signatureAlgorithm']) - return self.redirect_to(self.get_slo_url(), parameters) + return self.redirect_to(self.get_slo_return_url(), parameters) else: self.__errors.append('invalid_binding') raise OneLogin_Saml2_Error( @@ -468,6 +468,16 @@ def get_slo_url(self): if 'url' in idp_data['singleLogoutService']: return idp_data['singleLogoutService']['url'] + def get_return_slo_url(self): + """ + Gets the SLO return URL for IdP-initiated logout. + + :returns: an URL, the SLO return endpoint of the IdP + :rtype: string + """ + slo_data = self.__settings.get_idp_data()['singeLogoutService'] + return slo_data.get('returnUrl', self.get_slo_url()) + def add_request_signature(self, request_data, sign_algorithm=OneLogin_Saml2_Constants.RSA_SHA1): """ Builds the Signature of the SAML Request. diff --git a/src/onelogin/saml2/logout_response.py b/src/onelogin/saml2/logout_response.py index 6dd0a6d8..8400818d 100644 --- a/src/onelogin/saml2/logout_response.py +++ b/src/onelogin/saml2/logout_response.py @@ -162,12 +162,13 @@ def build(self, in_response_to): uid = OneLogin_Saml2_Utils.generate_unique_id() issue_instant = OneLogin_Saml2_Utils.parse_time_to_SAML(OneLogin_Saml2_Utils.now()) + destination = idp_data['singeLogoutService'].get('returnUrl', idp_data['singeLogoutService']['url']) logout_response = OneLogin_Saml2_Templates.LOGOUT_RESPONSE % \ { 'id': uid, 'issue_instant': issue_instant, - 'destination': idp_data['singleLogoutService']['url'], + 'destination': destination, 'in_response_to': in_response_to, 'entity_id': sp_data['entityId'], 'status': "urn:oasis:names:tc:SAML:2.0:status:Success" From b7402c0029d63e6a7fecb9955bc33d5b10b33fac Mon Sep 17 00:00:00 2001 From: BeritJanssen Date: Thu, 3 Dec 2020 13:06:45 +0100 Subject: [PATCH 036/205] correct typo --- README.md | 2 +- src/onelogin/saml2/auth.py | 2 +- src/onelogin/saml2/logout_response.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c172f276..6d3602d4 100644 --- a/README.md +++ b/README.md @@ -306,7 +306,7 @@ This is the ``settings.json`` file: "url": "https://app.onelogin.com/trust/saml2/http-redirect/slo/", // URL Location where the from the SP will returned (after IdP-initiated logout) // OPTIONAL: only specify if different from url parameter - "returnUrl": "/slo_return/" + "returnUrl": "https://app.onelogin.com/trust/saml2/http-redirect/slo_return/" // SAML protocol binding to be used when returning the // message. OneLogin Toolkit supports the HTTP-Redirect binding // only for this endpoint. diff --git a/src/onelogin/saml2/auth.py b/src/onelogin/saml2/auth.py index 37aada61..cd8d1624 100644 --- a/src/onelogin/saml2/auth.py +++ b/src/onelogin/saml2/auth.py @@ -475,7 +475,7 @@ def get_return_slo_url(self): :returns: an URL, the SLO return endpoint of the IdP :rtype: string """ - slo_data = self.__settings.get_idp_data()['singeLogoutService'] + slo_data = self.__settings.get_idp_data()['singleLogoutService'] return slo_data.get('returnUrl', self.get_slo_url()) def add_request_signature(self, request_data, sign_algorithm=OneLogin_Saml2_Constants.RSA_SHA1): diff --git a/src/onelogin/saml2/logout_response.py b/src/onelogin/saml2/logout_response.py index 8400818d..bb5c3fbb 100644 --- a/src/onelogin/saml2/logout_response.py +++ b/src/onelogin/saml2/logout_response.py @@ -162,7 +162,7 @@ def build(self, in_response_to): uid = OneLogin_Saml2_Utils.generate_unique_id() issue_instant = OneLogin_Saml2_Utils.parse_time_to_SAML(OneLogin_Saml2_Utils.now()) - destination = idp_data['singeLogoutService'].get('returnUrl', idp_data['singeLogoutService']['url']) + destination = idp_data['singleLogoutService'].get('returnUrl', idp_data['singleLogoutService']['url']) logout_response = OneLogin_Saml2_Templates.LOGOUT_RESPONSE % \ { From da8a673b6c19892348d1e86e0a5b3fbd68360414 Mon Sep 17 00:00:00 2001 From: BeritJanssen Date: Thu, 3 Dec 2020 13:13:12 +0100 Subject: [PATCH 037/205] correct function name --- src/onelogin/saml2/auth.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/onelogin/saml2/auth.py b/src/onelogin/saml2/auth.py index cd8d1624..ac79fb7b 100644 --- a/src/onelogin/saml2/auth.py +++ b/src/onelogin/saml2/auth.py @@ -468,7 +468,7 @@ def get_slo_url(self): if 'url' in idp_data['singleLogoutService']: return idp_data['singleLogoutService']['url'] - def get_return_slo_url(self): + def get_slo_return_url(self): """ Gets the SLO return URL for IdP-initiated logout. From 90835cdbbe787c0fae0a378a66da1800952085c4 Mon Sep 17 00:00:00 2001 From: BeritJanssen Date: Thu, 3 Dec 2020 14:17:16 +0100 Subject: [PATCH 038/205] test implemented --- tests/settings/settings1.json | 3 ++- tests/src/OneLogin/saml2_tests/auth_test.py | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/tests/settings/settings1.json b/tests/settings/settings1.json index 69d7d25e..276a8964 100644 --- a/tests/settings/settings1.json +++ b/tests/settings/settings1.json @@ -18,7 +18,8 @@ "url": "http://idp.example.com/SSOService.php" }, "singleLogoutService": { - "url": "http://idp.example.com/SingleLogoutService.php" + "url": "http://idp.example.com/SingleLogoutService.php", + "returnUrl": "http://idp.example.com/SingleLogoutReturn.php" }, "x509cert": "MIICgTCCAeoCCQCbOlrWDdX7FTANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMCTk8xGDAWBgNVBAgTD0FuZHJlYXMgU29sYmVyZzEMMAoGA1UEBxMDRm9vMRAwDgYDVQQKEwdVTklORVRUMRgwFgYDVQQDEw9mZWlkZS5lcmxhbmcubm8xITAfBgkqhkiG9w0BCQEWEmFuZHJlYXNAdW5pbmV0dC5ubzAeFw0wNzA2MTUxMjAxMzVaFw0wNzA4MTQxMjAxMzVaMIGEMQswCQYDVQQGEwJOTzEYMBYGA1UECBMPQW5kcmVhcyBTb2xiZXJnMQwwCgYDVQQHEwNGb28xEDAOBgNVBAoTB1VOSU5FVFQxGDAWBgNVBAMTD2ZlaWRlLmVybGFuZy5ubzEhMB8GCSqGSIb3DQEJARYSYW5kcmVhc0B1bmluZXR0Lm5vMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDivbhR7P516x/S3BqKxupQe0LONoliupiBOesCO3SHbDrl3+q9IbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHISKOtPlAeTZSnb8QAu7aRjZq3+PbrP5uW3TcfCGPtKTytHOge/OlJbo078dVhXQ14d1EDwXJW1rRXuUt4C8QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACDVfp86HObqY+e8BUoWQ9+VMQx1ASDohBjwOsg2WykUqRXF+dLfcUH9dWR63CtZIKFDbStNomPnQz7nbK+onygwBspVEbnHuUihZq3ZUdmumQqCw4Uvs/1Uvq3orOo/WJVhTyvLgFVK2QarQ4/67OZfHd7R+POBXhophSMv1ZOo" }, diff --git a/tests/src/OneLogin/saml2_tests/auth_test.py b/tests/src/OneLogin/saml2_tests/auth_test.py index c7c391c7..b78ecdfb 100644 --- a/tests/src/OneLogin/saml2_tests/auth_test.py +++ b/tests/src/OneLogin/saml2_tests/auth_test.py @@ -87,6 +87,21 @@ def testGetSLOurl(self): slo_url = settings_info['idp']['singleLogoutService']['url'] self.assertEqual(auth.get_slo_url(), slo_url) + def testGetSLOReturnUrl(self): + """ + Tests the get_slo_return_url method of the OneLogin_Saml2_Auth class + """ + settings_info = self.loadSettingsJSON() + auth = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings_info) + slo_url = settings_info['idp']['singleLogoutService']['returnUrl'] + self.assertEqual(auth.get_slo_return_url(), slo_url) + # test that the function falls back to the url setting if returnUrl is not set + settings_info['idp']['singleLogoutService'].pop('returnUrl') + auth = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings_info) + slo_url = settings_info['idp']['singleLogoutService']['url'] + self.assertEqual(auth.get_slo_return_url(), slo_url) + + def testGetSessionIndex(self): """ Tests the get_session_index method of the OneLogin_Saml2_Auth class From b266fd5a2f475a2b0abcf63a935a06cea855bbb6 Mon Sep 17 00:00:00 2001 From: BeritJanssen Date: Thu, 3 Dec 2020 14:44:01 +0100 Subject: [PATCH 039/205] fixed tests --- tests/settings/settings1.json | 3 +-- tests/src/OneLogin/saml2_tests/auth_test.py | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/settings/settings1.json b/tests/settings/settings1.json index 276a8964..69d7d25e 100644 --- a/tests/settings/settings1.json +++ b/tests/settings/settings1.json @@ -18,8 +18,7 @@ "url": "http://idp.example.com/SSOService.php" }, "singleLogoutService": { - "url": "http://idp.example.com/SingleLogoutService.php", - "returnUrl": "http://idp.example.com/SingleLogoutReturn.php" + "url": "http://idp.example.com/SingleLogoutService.php" }, "x509cert": "MIICgTCCAeoCCQCbOlrWDdX7FTANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMCTk8xGDAWBgNVBAgTD0FuZHJlYXMgU29sYmVyZzEMMAoGA1UEBxMDRm9vMRAwDgYDVQQKEwdVTklORVRUMRgwFgYDVQQDEw9mZWlkZS5lcmxhbmcubm8xITAfBgkqhkiG9w0BCQEWEmFuZHJlYXNAdW5pbmV0dC5ubzAeFw0wNzA2MTUxMjAxMzVaFw0wNzA4MTQxMjAxMzVaMIGEMQswCQYDVQQGEwJOTzEYMBYGA1UECBMPQW5kcmVhcyBTb2xiZXJnMQwwCgYDVQQHEwNGb28xEDAOBgNVBAoTB1VOSU5FVFQxGDAWBgNVBAMTD2ZlaWRlLmVybGFuZy5ubzEhMB8GCSqGSIb3DQEJARYSYW5kcmVhc0B1bmluZXR0Lm5vMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDivbhR7P516x/S3BqKxupQe0LONoliupiBOesCO3SHbDrl3+q9IbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHISKOtPlAeTZSnb8QAu7aRjZq3+PbrP5uW3TcfCGPtKTytHOge/OlJbo078dVhXQ14d1EDwXJW1rRXuUt4C8QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACDVfp86HObqY+e8BUoWQ9+VMQx1ASDohBjwOsg2WykUqRXF+dLfcUH9dWR63CtZIKFDbStNomPnQz7nbK+onygwBspVEbnHuUihZq3ZUdmumQqCw4Uvs/1Uvq3orOo/WJVhTyvLgFVK2QarQ4/67OZfHd7R+POBXhophSMv1ZOo" }, diff --git a/tests/src/OneLogin/saml2_tests/auth_test.py b/tests/src/OneLogin/saml2_tests/auth_test.py index b78ecdfb..8ca6c814 100644 --- a/tests/src/OneLogin/saml2_tests/auth_test.py +++ b/tests/src/OneLogin/saml2_tests/auth_test.py @@ -92,6 +92,7 @@ def testGetSLOReturnUrl(self): Tests the get_slo_return_url method of the OneLogin_Saml2_Auth class """ settings_info = self.loadSettingsJSON() + settings_info['idp']['singleLogoutService']['returnUrl'] = "http://idp.example.com/SingleLogoutReturn.php" auth = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings_info) slo_url = settings_info['idp']['singleLogoutService']['returnUrl'] self.assertEqual(auth.get_slo_return_url(), slo_url) From ae5cae2524a04c47f78c3994c2a463e1a225f1e9 Mon Sep 17 00:00:00 2001 From: BeritJanssen Date: Thu, 3 Dec 2020 14:54:26 +0100 Subject: [PATCH 040/205] rename returnUrl to responseUrl --- README.md | 2 +- src/onelogin/saml2/auth.py | 6 +++--- src/onelogin/saml2/logout_response.py | 2 +- tests/src/OneLogin/saml2_tests/auth_test.py | 16 ++++++++-------- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 6d3602d4..8e3ccbec 100644 --- a/README.md +++ b/README.md @@ -306,7 +306,7 @@ This is the ``settings.json`` file: "url": "https://app.onelogin.com/trust/saml2/http-redirect/slo/", // URL Location where the from the SP will returned (after IdP-initiated logout) // OPTIONAL: only specify if different from url parameter - "returnUrl": "https://app.onelogin.com/trust/saml2/http-redirect/slo_return/" + "responseUrl": "https://app.onelogin.com/trust/saml2/http-redirect/slo_return/" // SAML protocol binding to be used when returning the // message. OneLogin Toolkit supports the HTTP-Redirect binding // only for this endpoint. diff --git a/src/onelogin/saml2/auth.py b/src/onelogin/saml2/auth.py index ac79fb7b..041f9445 100644 --- a/src/onelogin/saml2/auth.py +++ b/src/onelogin/saml2/auth.py @@ -190,7 +190,7 @@ def process_slo(self, keep_local_session=False, request_id=None, delete_session_ if security['logoutResponseSigned']: self.add_response_signature(parameters, security['signatureAlgorithm']) - return self.redirect_to(self.get_slo_return_url(), parameters) + return self.redirect_to(self.get_slo_response_url(), parameters) else: self.__errors.append('invalid_binding') raise OneLogin_Saml2_Error( @@ -468,7 +468,7 @@ def get_slo_url(self): if 'url' in idp_data['singleLogoutService']: return idp_data['singleLogoutService']['url'] - def get_slo_return_url(self): + def get_slo_response_url(self): """ Gets the SLO return URL for IdP-initiated logout. @@ -476,7 +476,7 @@ def get_slo_return_url(self): :rtype: string """ slo_data = self.__settings.get_idp_data()['singleLogoutService'] - return slo_data.get('returnUrl', self.get_slo_url()) + return slo_data.get('responseUrl', self.get_slo_url()) def add_request_signature(self, request_data, sign_algorithm=OneLogin_Saml2_Constants.RSA_SHA1): """ diff --git a/src/onelogin/saml2/logout_response.py b/src/onelogin/saml2/logout_response.py index bb5c3fbb..7ce7f97d 100644 --- a/src/onelogin/saml2/logout_response.py +++ b/src/onelogin/saml2/logout_response.py @@ -162,7 +162,7 @@ def build(self, in_response_to): uid = OneLogin_Saml2_Utils.generate_unique_id() issue_instant = OneLogin_Saml2_Utils.parse_time_to_SAML(OneLogin_Saml2_Utils.now()) - destination = idp_data['singleLogoutService'].get('returnUrl', idp_data['singleLogoutService']['url']) + destination = idp_data['singleLogoutService'].get('responseUrl', idp_data['singleLogoutService']['url']) logout_response = OneLogin_Saml2_Templates.LOGOUT_RESPONSE % \ { diff --git a/tests/src/OneLogin/saml2_tests/auth_test.py b/tests/src/OneLogin/saml2_tests/auth_test.py index 8ca6c814..dcb35f72 100644 --- a/tests/src/OneLogin/saml2_tests/auth_test.py +++ b/tests/src/OneLogin/saml2_tests/auth_test.py @@ -87,20 +87,20 @@ def testGetSLOurl(self): slo_url = settings_info['idp']['singleLogoutService']['url'] self.assertEqual(auth.get_slo_url(), slo_url) - def testGetSLOReturnUrl(self): + def testGetSLOresponseUrl(self): """ - Tests the get_slo_return_url method of the OneLogin_Saml2_Auth class + Tests the get_slo_response_url method of the OneLogin_Saml2_Auth class """ settings_info = self.loadSettingsJSON() - settings_info['idp']['singleLogoutService']['returnUrl'] = "http://idp.example.com/SingleLogoutReturn.php" + settings_info['idp']['singleLogoutService']['responseUrl'] = "http://idp.example.com/SingleLogoutReturn.php" auth = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings_info) - slo_url = settings_info['idp']['singleLogoutService']['returnUrl'] - self.assertEqual(auth.get_slo_return_url(), slo_url) - # test that the function falls back to the url setting if returnUrl is not set - settings_info['idp']['singleLogoutService'].pop('returnUrl') + slo_url = settings_info['idp']['singleLogoutService']['responseUrl'] + self.assertEqual(auth.get_slo_response_url(), slo_url) + # test that the function falls back to the url setting if responseUrl is not set + settings_info['idp']['singleLogoutService'].pop('responseUrl') auth = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings_info) slo_url = settings_info['idp']['singleLogoutService']['url'] - self.assertEqual(auth.get_slo_return_url(), slo_url) + self.assertEqual(auth.get_slo_response_url(), slo_url) def testGetSessionIndex(self): From d6385fb25f3978b13cb0a563b9294e6c303d70e3 Mon Sep 17 00:00:00 2001 From: "Volm, David" Date: Thu, 3 Dec 2020 10:19:12 -0600 Subject: [PATCH 041/205] Optionally set request.scheme based on X-Forwarded-Proto header in demo_pyramid --- demo_pyramid/demo_pyramid/views.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/demo_pyramid/demo_pyramid/views.py b/demo_pyramid/demo_pyramid/views.py index 86814a99..b393f14d 100644 --- a/demo_pyramid/demo_pyramid/views.py +++ b/demo_pyramid/demo_pyramid/views.py @@ -16,6 +16,10 @@ def init_saml_auth(req): def prepare_pyramid_request(request): # If server is behind proxys or balancers use the HTTP_X_FORWARDED fields + + if 'X-Forwarded-Proto' in request.headers: + request.scheme = request.headers['X-Forwarded-Proto'] + return { 'https': 'on' if request.scheme == 'https' else 'off', 'http_host': request.host, From f11d48f5a8df7224a951fd3f67818d844a0411ae Mon Sep 17 00:00:00 2001 From: Tessa Bloomer Date: Tue, 8 Dec 2020 13:59:30 -0600 Subject: [PATCH 042/205] Resolved issue 226 with testing --- src/onelogin/saml2/response.py | 12 +++- .../src/OneLogin/saml2_tests/response_test.py | 60 ++++++++++++++++++- 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/src/onelogin/saml2/response.py b/src/onelogin/saml2/response.py index 1e7736a8..a40ab7e6 100644 --- a/src/onelogin/saml2/response.py +++ b/src/onelogin/saml2/response.py @@ -10,6 +10,7 @@ """ from copy import deepcopy +from urllib.parse import urlsplit, urlunsplit from onelogin.saml2.constants import OneLogin_Saml2_Constants from onelogin.saml2.utils import OneLogin_Saml2_Utils, OneLogin_Saml2_Error, OneLogin_Saml2_ValidationError, return_false_on_exception from onelogin.saml2.xml_utils import OneLogin_Saml2_XML @@ -191,7 +192,7 @@ def is_valid(self, request_data, request_id=None, raise_exceptions=False): # Checks destination destination = self.document.get('Destination', None) if destination: - if not destination.startswith(current_url): + if not self.__standardize_url(destination).startswith(self.__standardize_url(current_url)): # TODO: Review if following lines are required, since we can control the # request_data # current_url_routed = OneLogin_Saml2_Utils.get_self_routed_url_no_query(request_data) @@ -865,6 +866,15 @@ def __decrypt_assertion(self, xml): decrypted = OneLogin_Saml2_Utils.decrypt_element(encrypted_data, key, debug=debug, inplace=True) xml.replace(encrypted_assertion_nodes[0], decrypted) return xml + + def __standardize_url(self, url): + try: + parsed = list(urlsplit(url)) + parsed[1] = parsed[1].lower() + standardized_url = urlunsplit(parsed) + return standardized_url + except Exception: + return url def get_error(self): """ diff --git a/tests/src/OneLogin/saml2_tests/response_test.py b/tests/src/OneLogin/saml2_tests/response_test.py index 7d9934a1..41d72fab 100644 --- a/tests/src/OneLogin/saml2_tests/response_test.py +++ b/tests/src/OneLogin/saml2_tests/response_test.py @@ -18,7 +18,6 @@ from onelogin.saml2.settings import OneLogin_Saml2_Settings from onelogin.saml2.utils import OneLogin_Saml2_Utils - class OneLogin_Saml2_Response_Test(unittest.TestCase): data_path = join(dirname(dirname(dirname(dirname(__file__)))), 'data') settings_path = join(dirname(dirname(dirname(dirname(__file__)))), 'settings') @@ -50,6 +49,24 @@ def get_request_data(self): 'script_name': 'index.html' } + def get_request_data_domain_capitalized(self): + return { + 'http_host': 'StuFF.Com', + 'script_name': 'endpoints/endpoints/acs.php' + } + + def get_request_data_path_capitalized(self): + return { + 'http_host': 'stuff.com', + 'script_name': 'Endpoints/endPoints/acs.php' + } + + def get_request_data_both_capitalized(self): + return { + 'http_host': 'StuFF.Com', + 'script_name': 'Endpoints/endPoints/aCs.php' + } + def testConstruct(self): """ Tests the OneLogin_Saml2_Response Constructor. @@ -977,7 +994,7 @@ def testIsInValidDuplicatedAttrs(self): response = OneLogin_Saml2_Response(settings, xml) with self.assertRaisesRegex(Exception, 'Found an Attribute element with duplicated Name'): response.get_attributes() - + def testIsInValidDestination(self): """ Tests the is_valid method of the OneLogin_Saml2_Response class @@ -1014,6 +1031,45 @@ def testIsInValidDestination(self): self.assertFalse(response_5.is_valid(self.get_request_data())) self.assertIn('A valid SubjectConfirmation was not found on this Response', response_5.get_error()) + settings.set_strict(True) + response_2 = OneLogin_Saml2_Response(settings, message) + self.assertFalse(response_2.is_valid(self.get_request_data())) + self.assertIn('The response was received at', response_2.get_error()) + + def testIsInValidCapitalizationOfDestinationElements(self): + """ + Tests the is_valid method of the OneLogin_Saml2_Response class + Case Invalid Response due to differences in capitalization of path + """ + + settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) + message = self.file_contents(join(self.data_path, 'responses', 'unsigned_response.xml.base64')) + + #Test path capitalized + settings.set_strict(True) + response = OneLogin_Saml2_Response(settings, message) + self.assertFalse(response.is_valid(self.get_request_data_path_capitalized())) + self.assertIn('The response was received at', response.get_error()) + + #Test both domain and path capitalized + response_2 = OneLogin_Saml2_Response(settings, message) + self.assertFalse(response_2.is_valid(self.get_request_data_both_capitalized())) + self.assertIn('The response was received at', response_2.get_error()) + + def testIsValidCapitalizationOfDestinationHost(self): + """ + Tests the is_valid method of the OneLogin_Saml2_Response class + Case Valid Response, even if host is differently capitalized (per RFC) + """ + settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) + message = self.file_contents(join(self.data_path, 'responses', 'unsigned_response.xml.base64')) + + #Test path capitalized + settings.set_strict(True) + response = OneLogin_Saml2_Response(settings, message) + self.assertFalse(response.is_valid(self.get_request_data_domain_capitalized())) + self.assertNotIn('The response was received at', response.get_error()) + def testIsInValidAudience(self): """ Tests the is_valid method of the OneLogin_Saml2_Response class From 1cb34cdb0a21f9ce6a1e54c3604188b09a677f1c Mon Sep 17 00:00:00 2001 From: Tessa Bloomer Date: Tue, 8 Dec 2020 14:01:48 -0600 Subject: [PATCH 043/205] Fixed comment --- tests/src/OneLogin/saml2_tests/response_test.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/src/OneLogin/saml2_tests/response_test.py b/tests/src/OneLogin/saml2_tests/response_test.py index 41d72fab..e3e6b390 100644 --- a/tests/src/OneLogin/saml2_tests/response_test.py +++ b/tests/src/OneLogin/saml2_tests/response_test.py @@ -1036,7 +1036,7 @@ def testIsInValidDestination(self): self.assertFalse(response_2.is_valid(self.get_request_data())) self.assertIn('The response was received at', response_2.get_error()) - def testIsInValidCapitalizationOfDestinationElements(self): + def testIsInValidDestinationCapitalizationOfElements(self): """ Tests the is_valid method of the OneLogin_Saml2_Response class Case Invalid Response due to differences in capitalization of path @@ -1056,7 +1056,7 @@ def testIsInValidCapitalizationOfDestinationElements(self): self.assertFalse(response_2.is_valid(self.get_request_data_both_capitalized())) self.assertIn('The response was received at', response_2.get_error()) - def testIsValidCapitalizationOfDestinationHost(self): + def testIsValidDestinationCapitalizationOfHost(self): """ Tests the is_valid method of the OneLogin_Saml2_Response class Case Valid Response, even if host is differently capitalized (per RFC) @@ -1064,11 +1064,13 @@ def testIsValidCapitalizationOfDestinationHost(self): settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) message = self.file_contents(join(self.data_path, 'responses', 'unsigned_response.xml.base64')) - #Test path capitalized + #Test domain capitalized settings.set_strict(True) response = OneLogin_Saml2_Response(settings, message) self.assertFalse(response.is_valid(self.get_request_data_domain_capitalized())) self.assertNotIn('The response was received at', response.get_error()) + #Assert we got past the destination check, which appears later + self.assertIn('A valid SubjectConfirmation was not found', response.get_error()) def testIsInValidAudience(self): """ From 534974ac6c45a71f9a8dc67c547d4ef34ff5869e Mon Sep 17 00:00:00 2001 From: Tessa Bloomer Date: Tue, 8 Dec 2020 14:56:28 -0600 Subject: [PATCH 044/205] suggested changes --- src/onelogin/saml2/response.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/onelogin/saml2/response.py b/src/onelogin/saml2/response.py index a40ab7e6..248d2f77 100644 --- a/src/onelogin/saml2/response.py +++ b/src/onelogin/saml2/response.py @@ -192,7 +192,7 @@ def is_valid(self, request_data, request_id=None, raise_exceptions=False): # Checks destination destination = self.document.get('Destination', None) if destination: - if not self.__standardize_url(destination).startswith(self.__standardize_url(current_url)): + if not self.__normalize_url(destination).startswith(self.__normalize_url(current_url)): # TODO: Review if following lines are required, since we can control the # request_data # current_url_routed = OneLogin_Saml2_Utils.get_self_routed_url_no_query(request_data) @@ -867,12 +867,24 @@ def __decrypt_assertion(self, xml): xml.replace(encrypted_assertion_nodes[0], decrypted) return xml - def __standardize_url(self, url): + def __normalize_url(self, url): + """ + Returns normalized URL for comparison. + This method converts the hostname to lowercase, as it should be case-insensitive (per RFC 4343) + If standardization fails, the original URL is returned + Python documentation indicates that URL split also normalizes query strings if empty query fields are present + + :param url: URL + :type url: String + + :returns: A normalized URL, or the given URL string if parsing fails + :rtype: list + """ try: parsed = list(urlsplit(url)) parsed[1] = parsed[1].lower() - standardized_url = urlunsplit(parsed) - return standardized_url + normalized_url = urlunsplit(parsed) + return normalized_url except Exception: return url From 021ee793075b378809be6f4c9270106857c38b78 Mon Sep 17 00:00:00 2001 From: Tessa Bloomer Date: Tue, 8 Dec 2020 15:10:58 -0600 Subject: [PATCH 045/205] Clarification in comment --- src/onelogin/saml2/response.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/onelogin/saml2/response.py b/src/onelogin/saml2/response.py index 248d2f77..fc233272 100644 --- a/src/onelogin/saml2/response.py +++ b/src/onelogin/saml2/response.py @@ -870,7 +870,7 @@ def __decrypt_assertion(self, xml): def __normalize_url(self, url): """ Returns normalized URL for comparison. - This method converts the hostname to lowercase, as it should be case-insensitive (per RFC 4343) + This method converts the netloc to lowercase, as it should be case-insensitive (per RFC 4343, RFC 7617) If standardization fails, the original URL is returned Python documentation indicates that URL split also normalizes query strings if empty query fields are present @@ -882,6 +882,8 @@ def __normalize_url(self, url): """ try: parsed = list(urlsplit(url)) + #scheme and hostname converted to lowercase + parsed[0] = parsed[0].lower() parsed[1] = parsed[1].lower() normalized_url = urlunsplit(parsed) return normalized_url From 9c40592324ca45f6549349e2f767e2da24b66bc7 Mon Sep 17 00:00:00 2001 From: Tessa Bloomer Date: Tue, 8 Dec 2020 15:22:20 -0600 Subject: [PATCH 046/205] Changes suggested --- src/onelogin/saml2/response.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/onelogin/saml2/response.py b/src/onelogin/saml2/response.py index fc233272..2b4fdc25 100644 --- a/src/onelogin/saml2/response.py +++ b/src/onelogin/saml2/response.py @@ -881,11 +881,8 @@ def __normalize_url(self, url): :rtype: list """ try: - parsed = list(urlsplit(url)) - #scheme and hostname converted to lowercase - parsed[0] = parsed[0].lower() - parsed[1] = parsed[1].lower() - normalized_url = urlunsplit(parsed) + scheme, netloc, *rest = urlsplit(url) + normalized_url = urlunsplit((scheme.lower(), netloc.lower(), *rest)) return normalized_url except Exception: return url From 707fd2a4afa73fa7d2cd454e17bba99003831b77 Mon Sep 17 00:00:00 2001 From: "Volm, David" Date: Fri, 11 Dec 2020 17:31:47 -0600 Subject: [PATCH 047/205] Optionally set request.server_port based on X-Forwarded-Port header in demo_pyramid --- demo_pyramid/demo_pyramid/views.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/demo_pyramid/demo_pyramid/views.py b/demo_pyramid/demo_pyramid/views.py index b393f14d..a213e367 100644 --- a/demo_pyramid/demo_pyramid/views.py +++ b/demo_pyramid/demo_pyramid/views.py @@ -19,6 +19,8 @@ def prepare_pyramid_request(request): if 'X-Forwarded-Proto' in request.headers: request.scheme = request.headers['X-Forwarded-Proto'] + if 'X-Forwarded-Port' in request.headers: + request.server_port = int(request.headers['X-Forwarded-Port']) return { 'https': 'on' if request.scheme == 'https' else 'off', From 92fae88f373ccc387f423689e113eff9391d51bd Mon Sep 17 00:00:00 2001 From: Tessa Bloomer Date: Mon, 4 Jan 2021 16:32:14 -0600 Subject: [PATCH 048/205] Suggested edits made --- src/onelogin/saml2/logout_request.py | 2 +- src/onelogin/saml2/logout_response.py | 2 +- src/onelogin/saml2/response.py | 22 +--------------------- src/onelogin/saml2/utils.py | 21 +++++++++++++++++++++ 4 files changed, 24 insertions(+), 23 deletions(-) diff --git a/src/onelogin/saml2/logout_request.py b/src/onelogin/saml2/logout_request.py index ef4d05e2..7d83e835 100644 --- a/src/onelogin/saml2/logout_request.py +++ b/src/onelogin/saml2/logout_request.py @@ -314,7 +314,7 @@ def is_valid(self, request_data, raise_exceptions=False): if root.get('Destination', None): destination = root.get('Destination') if destination != '': - if current_url not in destination: + if OneLogin_Saml2_Utils.normalize_url(current_url) not in OneLogin_Saml2_Utils.normalize_url(destination): raise OneLogin_Saml2_ValidationError( 'The LogoutRequest was received at ' '%(currentURL)s instead of %(destination)s' % diff --git a/src/onelogin/saml2/logout_response.py b/src/onelogin/saml2/logout_response.py index 6dd0a6d8..bc4a3bfb 100644 --- a/src/onelogin/saml2/logout_response.py +++ b/src/onelogin/saml2/logout_response.py @@ -118,7 +118,7 @@ def is_valid(self, request_data, request_id=None, raise_exceptions=False): # Check destination destination = self.document.get('Destination', None) - if destination and current_url not in destination: + if destination and OneLogin_Saml2_Utils.normalize_url(current_url) not in OneLogin_Saml2_Utils.normalize_url(destination): raise OneLogin_Saml2_ValidationError( 'The LogoutResponse was received at %s instead of %s' % (current_url, destination), OneLogin_Saml2_ValidationError.WRONG_DESTINATION diff --git a/src/onelogin/saml2/response.py b/src/onelogin/saml2/response.py index 2b4fdc25..37402459 100644 --- a/src/onelogin/saml2/response.py +++ b/src/onelogin/saml2/response.py @@ -192,7 +192,7 @@ def is_valid(self, request_data, request_id=None, raise_exceptions=False): # Checks destination destination = self.document.get('Destination', None) if destination: - if not self.__normalize_url(destination).startswith(self.__normalize_url(current_url)): + if not OneLogin_Saml2_Utils.normalize_url(destination).startswith(OneLogin_Saml2_Utils.normalize_url(current_url)): # TODO: Review if following lines are required, since we can control the # request_data # current_url_routed = OneLogin_Saml2_Utils.get_self_routed_url_no_query(request_data) @@ -867,26 +867,6 @@ def __decrypt_assertion(self, xml): xml.replace(encrypted_assertion_nodes[0], decrypted) return xml - def __normalize_url(self, url): - """ - Returns normalized URL for comparison. - This method converts the netloc to lowercase, as it should be case-insensitive (per RFC 4343, RFC 7617) - If standardization fails, the original URL is returned - Python documentation indicates that URL split also normalizes query strings if empty query fields are present - - :param url: URL - :type url: String - - :returns: A normalized URL, or the given URL string if parsing fails - :rtype: list - """ - try: - scheme, netloc, *rest = urlsplit(url) - normalized_url = urlunsplit((scheme.lower(), netloc.lower(), *rest)) - return normalized_url - except Exception: - return url - def get_error(self): """ After executing a validation process, if it fails this method returns the cause diff --git a/src/onelogin/saml2/utils.py b/src/onelogin/saml2/utils.py index b0856ebc..7a0d2f13 100644 --- a/src/onelogin/saml2/utils.py +++ b/src/onelogin/saml2/utils.py @@ -1062,3 +1062,24 @@ def validate_binary_sign(signed_query, signature, cert=None, algorithm=OneLogin_ if debug: print(e) return False + + @staticmethod + def normalize_url(self, url): + """ + Returns normalized URL for comparison. + This method converts the netloc to lowercase, as it should be case-insensitive (per RFC 4343, RFC 7617) + If standardization fails, the original URL is returned + Python documentation indicates that URL split also normalizes query strings if empty query fields are present + + :param url: URL + :type url: String + + :returns: A normalized URL, or the given URL string if parsing fails + :rtype: String + """ + try: + scheme, netloc, *rest = urlsplit(url) + normalized_url = urlunsplit((scheme.lower(), netloc.lower(), *rest)) + return normalized_url + except Exception: + return url \ No newline at end of file From 8d9ea1a232ef24c6ce49fe98d3dfae5ef395b4d2 Mon Sep 17 00:00:00 2001 From: Tessa Bloomer Date: Mon, 4 Jan 2021 17:21:33 -0600 Subject: [PATCH 049/205] Fixed tests --- src/onelogin/saml2/logout_response.py | 2 +- src/onelogin/saml2/response.py | 2 +- src/onelogin/saml2/utils.py | 6 +++--- tests/src/OneLogin/saml2_tests/response_test.py | 2 +- tests/src/OneLogin/saml2_tests/utils_test.py | 12 ++++++++++++ 5 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/onelogin/saml2/logout_response.py b/src/onelogin/saml2/logout_response.py index bc4a3bfb..86de96c6 100644 --- a/src/onelogin/saml2/logout_response.py +++ b/src/onelogin/saml2/logout_response.py @@ -118,7 +118,7 @@ def is_valid(self, request_data, request_id=None, raise_exceptions=False): # Check destination destination = self.document.get('Destination', None) - if destination and OneLogin_Saml2_Utils.normalize_url(current_url) not in OneLogin_Saml2_Utils.normalize_url(destination): + if destination and OneLogin_Saml2_Utils.normalize_url(url=current_url) not in OneLogin_Saml2_Utils.normalize_url(url=destination): raise OneLogin_Saml2_ValidationError( 'The LogoutResponse was received at %s instead of %s' % (current_url, destination), OneLogin_Saml2_ValidationError.WRONG_DESTINATION diff --git a/src/onelogin/saml2/response.py b/src/onelogin/saml2/response.py index 37402459..54d140c3 100644 --- a/src/onelogin/saml2/response.py +++ b/src/onelogin/saml2/response.py @@ -192,7 +192,7 @@ def is_valid(self, request_data, request_id=None, raise_exceptions=False): # Checks destination destination = self.document.get('Destination', None) if destination: - if not OneLogin_Saml2_Utils.normalize_url(destination).startswith(OneLogin_Saml2_Utils.normalize_url(current_url)): + if not OneLogin_Saml2_Utils.normalize_url(url=destination).startswith(OneLogin_Saml2_Utils.normalize_url(url=current_url)): # TODO: Review if following lines are required, since we can control the # request_data # current_url_routed = OneLogin_Saml2_Utils.get_self_routed_url_no_query(request_data) diff --git a/src/onelogin/saml2/utils.py b/src/onelogin/saml2/utils.py index 7a0d2f13..3c22e406 100644 --- a/src/onelogin/saml2/utils.py +++ b/src/onelogin/saml2/utils.py @@ -20,7 +20,7 @@ from functools import wraps from uuid import uuid4 from xml.dom.minidom import Element - +from urllib.parse import urlsplit, urlunsplit import zlib import xmlsec @@ -1063,8 +1063,8 @@ def validate_binary_sign(signed_query, signature, cert=None, algorithm=OneLogin_ print(e) return False - @staticmethod - def normalize_url(self, url): + @staticmethod + def normalize_url(url): """ Returns normalized URL for comparison. This method converts the netloc to lowercase, as it should be case-insensitive (per RFC 4343, RFC 7617) diff --git a/tests/src/OneLogin/saml2_tests/response_test.py b/tests/src/OneLogin/saml2_tests/response_test.py index e3e6b390..efe6fd0f 100644 --- a/tests/src/OneLogin/saml2_tests/response_test.py +++ b/tests/src/OneLogin/saml2_tests/response_test.py @@ -1063,12 +1063,12 @@ def testIsValidDestinationCapitalizationOfHost(self): """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) message = self.file_contents(join(self.data_path, 'responses', 'unsigned_response.xml.base64')) - #Test domain capitalized settings.set_strict(True) response = OneLogin_Saml2_Response(settings, message) self.assertFalse(response.is_valid(self.get_request_data_domain_capitalized())) self.assertNotIn('The response was received at', response.get_error()) + #Assert we got past the destination check, which appears later self.assertIn('A valid SubjectConfirmation was not found', response.get_error()) diff --git a/tests/src/OneLogin/saml2_tests/utils_test.py b/tests/src/OneLogin/saml2_tests/utils_test.py index 597c4852..c332a5e8 100644 --- a/tests/src/OneLogin/saml2_tests/utils_test.py +++ b/tests/src/OneLogin/saml2_tests/utils_test.py @@ -917,3 +917,15 @@ def testValidateSign(self): # Signature Wrapping attack wrapping_attack1 = b64decode(self.file_contents(join(self.data_path, 'responses', 'invalids', 'signature_wrapping_attack.xml.base64'))) self.assertFalse(OneLogin_Saml2_Utils.validate_sign(wrapping_attack1, cert)) + + def testNormalizeUrl(self): + base_url = 'https://blah.com/path' + capital_scheme = 'hTTps://blah.com/path' + capital_domain = 'https://blAH.Com/path' + capital_path = 'https://blah.com/PAth' + capital_all = 'HTTPS://BLAH.COM/PATH' + + self.assertIn(base_url, OneLogin_Saml2_Utils.normalize_url(capital_scheme)) + self.assertIn(base_url, OneLogin_Saml2_Utils.normalize_url(capital_domain)) + self.assertNotIn(base_url, OneLogin_Saml2_Utils.normalize_url(capital_path)) + self.assertNotIn(base_url, OneLogin_Saml2_Utils.normalize_url(capital_all)) From a8d03a512825f098d347d9a44b2006391c84508c Mon Sep 17 00:00:00 2001 From: Tessa Bloomer Date: Mon, 4 Jan 2021 17:32:22 -0600 Subject: [PATCH 050/205] more testing --- .../saml2_tests/logout_request_test.py | 64 +++++++++++++++++++ .../saml2_tests/logout_response_test.py | 58 +++++++++++++++++ 2 files changed, 122 insertions(+) diff --git a/tests/src/OneLogin/saml2_tests/logout_request_test.py b/tests/src/OneLogin/saml2_tests/logout_request_test.py index d4c8ee6c..e1510c4d 100644 --- a/tests/src/OneLogin/saml2_tests/logout_request_test.py +++ b/tests/src/OneLogin/saml2_tests/logout_request_test.py @@ -413,6 +413,70 @@ def testIsValid(self): logout_request5 = OneLogin_Saml2_Logout_Request(settings, OneLogin_Saml2_Utils.b64encode(request)) self.assertTrue(logout_request5.is_valid(request_data)) + def testIsValidWithCapitalization(self): + """ + Tests the is_valid method of the OneLogin_Saml2_LogoutRequest + """ + request_data = { + 'http_host': 'exaMPLe.com', + 'script_name': 'index.html' + } + request = self.file_contents(join(self.data_path, 'logout_requests', 'logout_request.xml')) + settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) + + logout_request = OneLogin_Saml2_Logout_Request(settings, OneLogin_Saml2_Utils.b64encode(request)) + self.assertTrue(logout_request.is_valid(request_data)) + + settings.set_strict(True) + logout_request2 = OneLogin_Saml2_Logout_Request(settings, OneLogin_Saml2_Utils.b64encode(request)) + self.assertFalse(logout_request2.is_valid(request_data)) + + settings.set_strict(False) + dom = parseString(request) + logout_request3 = OneLogin_Saml2_Logout_Request(settings, OneLogin_Saml2_Utils.b64encode(dom.toxml())) + self.assertTrue(logout_request3.is_valid(request_data)) + + settings.set_strict(True) + logout_request4 = OneLogin_Saml2_Logout_Request(settings, OneLogin_Saml2_Utils.b64encode(dom.toxml())) + self.assertFalse(logout_request4.is_valid(request_data)) + + current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data) + request = request.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url.lower()) + logout_request5 = OneLogin_Saml2_Logout_Request(settings, OneLogin_Saml2_Utils.b64encode(request)) + self.assertTrue(logout_request5.is_valid(request_data)) + + def testIsInValidWithCapitalization(self): + """ + Tests the is_valid method of the OneLogin_Saml2_LogoutRequest + """ + request_data = { + 'http_host': 'example.com', + 'script_name': 'INdex.html' + } + request = self.file_contents(join(self.data_path, 'logout_requests', 'logout_request.xml')) + settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) + + logout_request = OneLogin_Saml2_Logout_Request(settings, OneLogin_Saml2_Utils.b64encode(request)) + self.assertTrue(logout_request.is_valid(request_data)) + + settings.set_strict(True) + logout_request2 = OneLogin_Saml2_Logout_Request(settings, OneLogin_Saml2_Utils.b64encode(request)) + self.assertFalse(logout_request2.is_valid(request_data)) + + settings.set_strict(False) + dom = parseString(request) + logout_request3 = OneLogin_Saml2_Logout_Request(settings, OneLogin_Saml2_Utils.b64encode(dom.toxml())) + self.assertTrue(logout_request3.is_valid(request_data)) + + settings.set_strict(True) + logout_request4 = OneLogin_Saml2_Logout_Request(settings, OneLogin_Saml2_Utils.b64encode(dom.toxml())) + self.assertFalse(logout_request4.is_valid(request_data)) + + current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data) + request = request.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url.lower()) + logout_request5 = OneLogin_Saml2_Logout_Request(settings, OneLogin_Saml2_Utils.b64encode(request)) + self.assertFalse(logout_request5.is_valid(request_data)) + def testIsValidWithXMLEncoding(self): """ Tests the is_valid method of the OneLogin_Saml2_LogoutRequest diff --git a/tests/src/OneLogin/saml2_tests/logout_response_test.py b/tests/src/OneLogin/saml2_tests/logout_response_test.py index 054e9762..3ab17653 100644 --- a/tests/src/OneLogin/saml2_tests/logout_response_test.py +++ b/tests/src/OneLogin/saml2_tests/logout_response_test.py @@ -276,6 +276,64 @@ def testIsValid(self): response_3 = OneLogin_Saml2_Logout_Response(settings, message_3) self.assertTrue(response_3.is_valid(request_data)) + def testIsValidWithCapitalization(self): + """ + Tests the is_valid method of the OneLogin_Saml2_LogoutResponse + """ + request_data = { + 'http_host': 'exaMPLe.com', + 'script_name': 'index.html', + 'get_data': {} + } + settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) + message = self.file_contents(join(self.data_path, 'logout_responses', 'logout_response_deflated.xml.base64')) + + response = OneLogin_Saml2_Logout_Response(settings, message) + self.assertTrue(response.is_valid(request_data)) + + settings.set_strict(True) + response_2 = OneLogin_Saml2_Logout_Response(settings, message) + with self.assertRaisesRegex(Exception, 'The LogoutResponse was received at'): + response_2.is_valid(request_data, raise_exceptions=True) + + plain_message = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(message)) + + current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data).lower() + plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url) + message_3 = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message) + + response_3 = OneLogin_Saml2_Logout_Response(settings, message_3) + self.assertTrue(response_3.is_valid(request_data)) + + def testIsInValidWithCapitalization(self): + """ + Tests the is_valid method of the OneLogin_Saml2_LogoutResponse + """ + request_data = { + 'http_host': 'example.com', + 'script_name': 'INdex.html', + 'get_data': {} + } + settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) + message = self.file_contents(join(self.data_path, 'logout_responses', 'logout_response_deflated.xml.base64')) + + response = OneLogin_Saml2_Logout_Response(settings, message) + self.assertTrue(response.is_valid(request_data)) + + settings.set_strict(True) + response_2 = OneLogin_Saml2_Logout_Response(settings, message) + with self.assertRaisesRegex(Exception, 'The LogoutResponse was received at'): + response_2.is_valid(request_data, raise_exceptions=True) + + plain_message = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(message)) + current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data).lower() + plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url) + message_3 = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message) + + response_3 = OneLogin_Saml2_Logout_Response(settings, message_3) + self.assertFalse(response_3.is_valid(request_data)) + + def testIsValidWithXMLEncoding(self): """ Tests the is_valid method of the OneLogin_Saml2_LogoutResponse From 9fdb11d1cd0190cfc783306cf9aed5133121a1ce Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Fri, 8 Jan 2021 19:48:37 +0100 Subject: [PATCH 051/205] Adding get_idp_sso_url, get_idp_slo_url and get_idp_slo_response_url methods to the Settings class and use it in the toolkit --- src/onelogin/saml2/auth.py | 10 ++---- src/onelogin/saml2/logout_request.py | 2 +- src/onelogin/saml2/logout_response.py | 3 +- src/onelogin/saml2/settings.py | 31 ++++++++++++++++ .../src/OneLogin/saml2_tests/settings_test.py | 35 +++++++++++++++++++ 5 files changed, 71 insertions(+), 10 deletions(-) diff --git a/src/onelogin/saml2/auth.py b/src/onelogin/saml2/auth.py index 041f9445..67da5183 100644 --- a/src/onelogin/saml2/auth.py +++ b/src/onelogin/saml2/auth.py @@ -454,8 +454,7 @@ def get_sso_url(self): :returns: An URL, the SSO endpoint of the IdP :rtype: string """ - idp_data = self.__settings.get_idp_data() - return idp_data['singleSignOnService']['url'] + return self.__settings.get_idp_sso_url() def get_slo_url(self): """ @@ -464,9 +463,7 @@ def get_slo_url(self): :returns: An URL, the SLO endpoint of the IdP :rtype: string """ - idp_data = self.__settings.get_idp_data() - if 'url' in idp_data['singleLogoutService']: - return idp_data['singleLogoutService']['url'] + return self.__settings.get_idp_slo_url() def get_slo_response_url(self): """ @@ -475,8 +472,7 @@ def get_slo_response_url(self): :returns: an URL, the SLO return endpoint of the IdP :rtype: string """ - slo_data = self.__settings.get_idp_data()['singleLogoutService'] - return slo_data.get('responseUrl', self.get_slo_url()) + return self.__settings.get_idp_slo_response_url() def add_request_signature(self, request_data, sign_algorithm=OneLogin_Saml2_Constants.RSA_SHA1): """ diff --git a/src/onelogin/saml2/logout_request.py b/src/onelogin/saml2/logout_request.py index ef4d05e2..78b01de6 100644 --- a/src/onelogin/saml2/logout_request.py +++ b/src/onelogin/saml2/logout_request.py @@ -110,7 +110,7 @@ def __init__(self, settings, request=None, name_id=None, session_index=None, nq= { 'id': uid, 'issue_instant': issue_instant, - 'single_logout_url': idp_data['singleLogoutService']['url'], + 'single_logout_url': self.__settings.get_idp_slo_response_url(), 'entity_id': sp_data['entityId'], 'name_id': name_id_obj, 'session_index': session_index_str, diff --git a/src/onelogin/saml2/logout_response.py b/src/onelogin/saml2/logout_response.py index 7ce7f97d..de827f6a 100644 --- a/src/onelogin/saml2/logout_response.py +++ b/src/onelogin/saml2/logout_response.py @@ -162,13 +162,12 @@ def build(self, in_response_to): uid = OneLogin_Saml2_Utils.generate_unique_id() issue_instant = OneLogin_Saml2_Utils.parse_time_to_SAML(OneLogin_Saml2_Utils.now()) - destination = idp_data['singleLogoutService'].get('responseUrl', idp_data['singleLogoutService']['url']) logout_response = OneLogin_Saml2_Templates.LOGOUT_RESPONSE % \ { 'id': uid, 'issue_instant': issue_instant, - 'destination': destination, + 'destination': self.__settings.get_idp_slo_response_url(), 'in_response_to': in_response_to, 'entity_id': sp_data['entityId'], 'status': "urn:oasis:names:tc:SAML:2.0:status:Success" diff --git a/src/onelogin/saml2/settings.py b/src/onelogin/saml2/settings.py index 404024ab..5df25991 100644 --- a/src/onelogin/saml2/settings.py +++ b/src/onelogin/saml2/settings.py @@ -506,6 +506,37 @@ def check_sp_certs(self): cert = self.get_sp_cert() return key is not None and cert is not None + def get_idp_sso_url(self): + """ + Gets the IdP SSO URL. + + :returns: An URL, the SSO endpoint of the IdP + :rtype: string + """ + idp_data = self.get_idp_data() + return idp_data['singleSignOnService']['url'] + + def get_idp_slo_url(self): + """ + Gets the IdP SLO URL. + + :returns: An URL, the SLO endpoint of the IdP + :rtype: string + """ + idp_data = self.get_idp_data() + if 'url' in idp_data['singleLogoutService']: + return idp_data['singleLogoutService']['url'] + + def get_idp_slo_response_url(self): + """ + Gets the IdP SLO return URL for IdP-initiated logout. + + :returns: an URL, the SLO return endpoint of the IdP + :rtype: string + """ + slo_data = self.get_idp_data()['singleLogoutService'] + return slo_data.get('responseUrl', self.get_idp_slo_url()) + def get_sp_key(self): """ Returns the x509 private key of the SP. diff --git a/tests/src/OneLogin/saml2_tests/settings_test.py b/tests/src/OneLogin/saml2_tests/settings_test.py index 837af55f..fb7ba7fe 100644 --- a/tests/src/OneLogin/saml2_tests/settings_test.py +++ b/tests/src/OneLogin/saml2_tests/settings_test.py @@ -165,6 +165,41 @@ def testGetSchemasPath(self): base = settings.get_base_path() self.assertEqual(join(base, 'lib', 'schemas') + sep, settings.get_schemas_path()) + def testGetIdPSSOurl(self): + """ + Tests the get_idp_sso_url method of the OneLogin_Saml2_Settings class + """ + settings_info = self.loadSettingsJSON() + settings = OneLogin_Saml2_Settings(settings_info) + + sso_url = settings_info['idp']['singleSignOnService']['url'] + self.assertEqual(settings.get_idp_sso_url(), sso_url) + + def testGetIdPSLOurl(self): + """ + Tests the get_idp_slo_url method of the OneLogin_Saml2_Settings class + """ + settings_info = self.loadSettingsJSON() + settings = OneLogin_Saml2_Settings(settings_info) + + slo_url = settings_info['idp']['singleLogoutService']['url'] + self.assertEqual(settings.get_idp_slo_url(), slo_url) + + def testGetIdPSLOresponseUrl(self): + """ + Tests the get_idp_slo_response_url method of the OneLogin_Saml2_Settings class + """ + settings_info = self.loadSettingsJSON() + settings_info['idp']['singleLogoutService']['responseUrl'] = "http://idp.example.com/SingleLogoutReturn.php" + settings = OneLogin_Saml2_Settings(settings_info) + slo_url = settings_info['idp']['singleLogoutService']['responseUrl'] + self.assertEqual(settings.get_idp_slo_response_url(), slo_url) + # test that the function falls back to the url setting if responseUrl is not set + settings_info['idp']['singleLogoutService'].pop('responseUrl') + settings = OneLogin_Saml2_Settings(settings_info) + slo_url = settings_info['idp']['singleLogoutService']['url'] + self.assertEqual(settings.get_idp_slo_response_url(), slo_url) + def testGetSPCert(self): """ Tests the get_sp_cert method of the OneLogin_Saml2_Settings From fa1ddab1573eb710583046633bce47ace33006aa Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Sat, 9 Jan 2021 00:07:24 +0100 Subject: [PATCH 052/205] Update Readme.md --- README.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 8e3ccbec..e98b60c4 100644 --- a/README.md +++ b/README.md @@ -241,11 +241,13 @@ This is the ``settings.json`` file: // HTTP-POST binding only. "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" }, - // Specifies info about where and how the message MUST be - // returned to the requester, in this case our SP. + // Specifies info about where and how the message MUST be sent. "singleLogoutService": { - // URL Location where the from the IdP will be returned + // URL Location where the from the IdP will be sent (IdP-initiated logout) "url": "https:///?sls", + // URL Location where the from the IdP will sent (SP-initiated logout, reply) + // OPTIONAL: only specify if different from url parameter + //"responseUrl": "https:///?sls", // SAML protocol binding to be used when returning the // message. OneLogin Toolkit supports the HTTP-Redirect binding // only for this endpoint. @@ -302,11 +304,11 @@ This is the ``settings.json`` file: }, // SLO endpoint info of the IdP. "singleLogoutService": { - // URL Location of the IdP where SLO Request will be sent. + // URL Location where the from the IdP will be sent (IdP-initiated logout) "url": "https://app.onelogin.com/trust/saml2/http-redirect/slo/", - // URL Location where the from the SP will returned (after IdP-initiated logout) + // URL Location where the from the IdP will sent (SP-initiated logout, reply) // OPTIONAL: only specify if different from url parameter - "responseUrl": "https://app.onelogin.com/trust/saml2/http-redirect/slo_return/" + "responseUrl": "https://app.onelogin.com/trust/saml2/http-redirect/slo_return/", // SAML protocol binding to be used when returning the // message. OneLogin Toolkit supports the HTTP-Redirect binding // only for this endpoint. From ee8b5d7c2426506be9873a82772c231743bc052d Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Sat, 9 Jan 2021 00:30:17 +0100 Subject: [PATCH 053/205] Refactor --- src/onelogin/saml2/settings.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/onelogin/saml2/settings.py b/src/onelogin/saml2/settings.py index 5df25991..e51a7124 100644 --- a/src/onelogin/saml2/settings.py +++ b/src/onelogin/saml2/settings.py @@ -534,8 +534,9 @@ def get_idp_slo_response_url(self): :returns: an URL, the SLO return endpoint of the IdP :rtype: string """ - slo_data = self.get_idp_data()['singleLogoutService'] - return slo_data.get('responseUrl', self.get_idp_slo_url()) + idp_data = self.get_idp_data() + if 'url' in idp_data['singleLogoutService']: + return idp_data['singleLogoutService'].get('responseUrl', self.get_idp_slo_url()) def get_sp_key(self): """ From 3aa3c575ffc4e55a3e06a1535dbe3094e7a31ae0 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Sat, 9 Jan 2021 00:56:03 +0100 Subject: [PATCH 054/205] See #223 Fix doc signature of get_attribute method --- src/onelogin/saml2/auth.py | 4 ++-- tests/src/OneLogin/saml2_tests/auth_test.py | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/onelogin/saml2/auth.py b/src/onelogin/saml2/auth.py index 67da5183..c173b1b5 100644 --- a/src/onelogin/saml2/auth.py +++ b/src/onelogin/saml2/auth.py @@ -315,8 +315,8 @@ def get_attribute(self, name): :param name: Name of the attribute :type name: string - :returns: Attribute value if exists or None - :rtype: string + :returns: Attribute value(s) if exists or None + :rtype: list """ assert isinstance(name, compat.str_type) return self.__attributes.get(name) diff --git a/tests/src/OneLogin/saml2_tests/auth_test.py b/tests/src/OneLogin/saml2_tests/auth_test.py index dcb35f72..81d009e9 100644 --- a/tests/src/OneLogin/saml2_tests/auth_test.py +++ b/tests/src/OneLogin/saml2_tests/auth_test.py @@ -102,7 +102,6 @@ def testGetSLOresponseUrl(self): slo_url = settings_info['idp']['singleLogoutService']['url'] self.assertEqual(auth.get_slo_response_url(), slo_url) - def testGetSessionIndex(self): """ Tests the get_session_index method of the OneLogin_Saml2_Auth class From a1e1f8b4d0fa4cd17467446f5cbe72dc1fc80bd8 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Sat, 9 Jan 2021 02:07:49 +0100 Subject: [PATCH 055/205] Close #217. Support single-label-domains as valid. New security parameter allowSingleLabelDomains --- README.md | 4 +++ demo-django/saml/advanced_settings.json | 1 + demo-flask/saml/advanced_settings.json | 1 + demo-tornado/saml/advanced_settings.json | 1 + .../demo_pyramid/saml/advanced_settings.json | 1 + src/onelogin/saml2/logout_response.py | 1 - src/onelogin/saml2/settings.py | 33 +++++++++++++++---- .../src/OneLogin/saml2_tests/settings_test.py | 19 +++++++---- 8 files changed, 47 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index e98b60c4..c5528a2f 100644 --- a/README.md +++ b/README.md @@ -430,6 +430,10 @@ In addition to the required settings data (idp, sp), extra settings can be defin // Provide the desire Duration, for example PT518400S (6 days) "metadataCacheDuration": null, + // If enabled, URLs with single-label-domains will + // be allowed and not rejected by the settings validator (Enable it under Docker/Kubernetes/testing env, not recommended on production) + "allowSingleLabelDomains": false, + // Algorithm that the toolkit will use on signing process. Options: // 'http://www.w3.org/2000/09/xmldsig#rsa-sha1' // 'http://www.w3.org/2000/09/xmldsig#dsa-sha1' diff --git a/demo-django/saml/advanced_settings.json b/demo-django/saml/advanced_settings.json index 1307b0ae..fef16fe9 100644 --- a/demo-django/saml/advanced_settings.json +++ b/demo-django/saml/advanced_settings.json @@ -10,6 +10,7 @@ "wantNameId" : true, "wantNameIdEncrypted": false, "wantAssertionsEncrypted": false, + "allowSingleLabelDomains": false, "signatureAlgorithm": "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", "digestAlgorithm": "http://www.w3.org/2001/04/xmlenc#sha256" }, diff --git a/demo-flask/saml/advanced_settings.json b/demo-flask/saml/advanced_settings.json index 1307b0ae..fef16fe9 100644 --- a/demo-flask/saml/advanced_settings.json +++ b/demo-flask/saml/advanced_settings.json @@ -10,6 +10,7 @@ "wantNameId" : true, "wantNameIdEncrypted": false, "wantAssertionsEncrypted": false, + "allowSingleLabelDomains": false, "signatureAlgorithm": "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", "digestAlgorithm": "http://www.w3.org/2001/04/xmlenc#sha256" }, diff --git a/demo-tornado/saml/advanced_settings.json b/demo-tornado/saml/advanced_settings.json index 1307b0ae..fef16fe9 100644 --- a/demo-tornado/saml/advanced_settings.json +++ b/demo-tornado/saml/advanced_settings.json @@ -10,6 +10,7 @@ "wantNameId" : true, "wantNameIdEncrypted": false, "wantAssertionsEncrypted": false, + "allowSingleLabelDomains": false, "signatureAlgorithm": "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", "digestAlgorithm": "http://www.w3.org/2001/04/xmlenc#sha256" }, diff --git a/demo_pyramid/demo_pyramid/saml/advanced_settings.json b/demo_pyramid/demo_pyramid/saml/advanced_settings.json index 1307b0ae..fef16fe9 100644 --- a/demo_pyramid/demo_pyramid/saml/advanced_settings.json +++ b/demo_pyramid/demo_pyramid/saml/advanced_settings.json @@ -10,6 +10,7 @@ "wantNameId" : true, "wantNameIdEncrypted": false, "wantAssertionsEncrypted": false, + "allowSingleLabelDomains": false, "signatureAlgorithm": "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", "digestAlgorithm": "http://www.w3.org/2001/04/xmlenc#sha256" }, diff --git a/src/onelogin/saml2/logout_response.py b/src/onelogin/saml2/logout_response.py index de827f6a..7548d57f 100644 --- a/src/onelogin/saml2/logout_response.py +++ b/src/onelogin/saml2/logout_response.py @@ -158,7 +158,6 @@ def build(self, in_response_to): :type in_response_to: string """ sp_data = self.__settings.get_sp_data() - idp_data = self.__settings.get_idp_data() uid = OneLogin_Saml2_Utils.generate_unique_id() issue_instant = OneLogin_Saml2_Utils.parse_time_to_SAML(OneLogin_Saml2_Utils.now()) diff --git a/src/onelogin/saml2/settings.py b/src/onelogin/saml2/settings.py index e51a7124..128802f5 100644 --- a/src/onelogin/saml2/settings.py +++ b/src/onelogin/saml2/settings.py @@ -39,10 +39,19 @@ r'\[?[A-F0-9]*:[A-F0-9:]+\]?)' # ...or ipv6 r'(?::\d+)?' # optional port r'(?:/?|[/?]\S+)$', re.IGNORECASE) +url_regex_single_label_domain = re.compile( + r'^(?:[a-z0-9\.\-]*)://' # scheme is validated separately + r'(?:(?:[A-Z0-9_](?:[A-Z0-9-_]{0,61}[A-Z0-9_])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' # domain... + r'(?:[A-Z0-9_](?:[A-Z0-9-_]{0,61}[A-Z0-9_]))|' # single-label-domain + r'localhost|' # localhost... + r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|' # ...or ipv4 + r'\[?[A-F0-9]*:[A-F0-9:]+\]?)' # ...or ipv6 + r'(?::\d+)?' # optional port + r'(?:/?|[/?]\S+)$', re.IGNORECASE) url_schemes = ['http', 'https', 'ftp', 'ftps'] -def validate_url(url): +def validate_url(url, allow_single_label_domain=False): """ Auxiliary method to validate an urllib :param url: An url to be validated @@ -54,8 +63,12 @@ def validate_url(url): scheme = url.split('://')[0].lower() if scheme not in url_schemes: return False - if not bool(url_regex.search(url)): - return False + if allow_single_label_domain: + if not bool(url_regex_single_label_domain.search(url)): + return False + else: + if not bool(url_regex.search(url)): + return False return True @@ -353,17 +366,18 @@ def check_idp_settings(self, settings): if not settings.get('idp'): errors.append('idp_not_found') else: + allow_single_domain_urls = self._get_allow_single_label_domain(settings) idp = settings['idp'] if not idp.get('entityId'): errors.append('idp_entityId_not_found') if not idp.get('singleSignOnService', {}).get('url'): errors.append('idp_sso_not_found') - elif not validate_url(idp['singleSignOnService']['url']): + elif not validate_url(idp['singleSignOnService']['url'], allow_single_domain_urls): errors.append('idp_sso_url_invalid') slo_url = idp.get('singleLogoutService', {}).get('url') - if slo_url and not validate_url(slo_url): + if slo_url and not validate_url(slo_url, allow_single_domain_urls): errors.append('idp_slo_url_invalid') if 'security' in settings: @@ -407,6 +421,7 @@ def check_sp_settings(self, settings): if not settings.get('sp'): errors.append('sp_not_found') else: + allow_single_domain_urls = self._get_allow_single_label_domain(settings) # check_sp_certs uses self.__sp so I add it old_sp = self.__sp self.__sp = settings['sp'] @@ -419,7 +434,7 @@ def check_sp_settings(self, settings): if not sp.get('assertionConsumerService', {}).get('url'): errors.append('sp_acs_not_found') - elif not validate_url(sp['assertionConsumerService']['url']): + elif not validate_url(sp['assertionConsumerService']['url'], allow_single_domain_urls): errors.append('sp_acs_url_invalid') if sp.get('attributeConsumingService'): @@ -448,7 +463,7 @@ def check_sp_settings(self, settings): errors.append('sp_attributeConsumingService_serviceDescription_type_invalid') slo_url = sp.get('singleLogoutService', {}).get('url') - if slo_url and not validate_url(slo_url): + if slo_url and not validate_url(slo_url, allow_single_domain_urls): errors.append('sp_sls_url_invalid') if 'signMetadata' in security and isinstance(security['signMetadata'], dict): @@ -833,3 +848,7 @@ def is_debug_active(self): :rtype: boolean """ return self.__debug + + def _get_allow_single_label_domain(self, settings): + security = settings.get('security', {}) + return 'allowSingleLabelDomains' in security.keys() and security['allowSingleLabelDomains'] diff --git a/tests/src/OneLogin/saml2_tests/settings_test.py b/tests/src/OneLogin/saml2_tests/settings_test.py index fb7ba7fe..25e87e38 100644 --- a/tests/src/OneLogin/saml2_tests/settings_test.py +++ b/tests/src/OneLogin/saml2_tests/settings_test.py @@ -71,11 +71,18 @@ def testLoadSettingsFromDict(self): except Exception as e: self.assertIn('Invalid dict settings: idp_sso_url_invalid', str(e)) + settings_info['idp']['singleSignOnService']['url'] = 'http://single-label-domain' + settings_info['security'] = {} + settings_info['security']['allowSingleLabelDomains'] = True + settings_4 = OneLogin_Saml2_Settings(settings_info) + self.assertEqual(len(settings_4.get_errors()), 0) + + del settings_info['security'] del settings_info['sp'] del settings_info['idp'] try: - settings_4 = OneLogin_Saml2_Settings(settings_info) - self.assertNotEqual(len(settings_4.get_errors()), 0) + settings_5 = OneLogin_Saml2_Settings(settings_info) + self.assertNotEqual(len(settings_5.get_errors()), 0) except Exception as e: self.assertIn('Invalid dict settings', str(e)) self.assertIn('idp_not_found', str(e)) @@ -85,8 +92,8 @@ def testLoadSettingsFromDict(self): settings_info['security']['authnRequestsSigned'] = True settings_info['custom_base_path'] = dirname(__file__) try: - settings_5 = OneLogin_Saml2_Settings(settings_info) - self.assertNotEqual(len(settings_5.get_errors()), 0) + settings_6 = OneLogin_Saml2_Settings(settings_info) + self.assertNotEqual(len(settings_6.get_errors()), 0) except Exception as e: self.assertIn('Invalid dict settings: sp_cert_not_found_and_required', str(e)) @@ -94,8 +101,8 @@ def testLoadSettingsFromDict(self): settings_info['security']['nameIdEncrypted'] = True del settings_info['idp']['x509cert'] try: - settings_6 = OneLogin_Saml2_Settings(settings_info) - self.assertNotEqual(len(settings_6.get_errors()), 0) + settings_7 = OneLogin_Saml2_Settings(settings_info) + self.assertNotEqual(len(settings_7.get_errors()), 0) except Exception as e: self.assertIn('Invalid dict settings: idp_cert_not_found_and_required', str(e)) From 8c828e3f615617a6e34f3e400f55d21cc6b5d3a9 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Sat, 9 Jan 2021 02:12:59 +0100 Subject: [PATCH 056/205] Add doc to validate_url method --- src/onelogin/saml2/settings.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/onelogin/saml2/settings.py b/src/onelogin/saml2/settings.py index 128802f5..924fb0c2 100644 --- a/src/onelogin/saml2/settings.py +++ b/src/onelogin/saml2/settings.py @@ -56,6 +56,8 @@ def validate_url(url, allow_single_label_domain=False): Auxiliary method to validate an urllib :param url: An url to be validated :type url: string + :param allow_single_label_domain: In order to allow or not single label domain + :type url: bool :returns: True if the url is valid :rtype: bool """ From f1e4af244efd6c16921653ffa5f76a71f12f7985 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Sat, 9 Jan 2021 03:36:15 +0100 Subject: [PATCH 057/205] Remove external lib method get_ext_lib_path. Add set_cert_path in order to allow set the cert path in a different folder than the toolkit --- src/onelogin/saml2/settings.py | 18 +++---- .../src/OneLogin/saml2_tests/settings_test.py | 48 +++++++++++++++---- 2 files changed, 45 insertions(+), 21 deletions(-) diff --git a/src/onelogin/saml2/settings.py b/src/onelogin/saml2/settings.py index 924fb0c2..04a66047 100644 --- a/src/onelogin/saml2/settings.py +++ b/src/onelogin/saml2/settings.py @@ -153,8 +153,7 @@ def __load_paths(self, base_path=None): self.__paths = { 'base': base_path, 'cert': base_path + 'certs' + sep, - 'lib': base_path + 'lib' + sep, - 'extlib': base_path + 'extlib' + sep, + 'lib': dirname(__file__) + sep } def __update_paths(self, settings): @@ -187,6 +186,12 @@ def get_cert_path(self): """ return self.__paths['cert'] + def set_cert_path(self, path): + """ + Set a new cert path + """ + self.__paths['cert'] = path + def get_lib_path(self): """ Returns lib path @@ -196,15 +201,6 @@ def get_lib_path(self): """ return self.__paths['lib'] - def get_ext_lib_path(self): - """ - Returns external lib path - - :return: The external library folder path - :rtype: string - """ - return self.__paths['extlib'] - def get_schemas_path(self): """ Returns schema path diff --git a/tests/src/OneLogin/saml2_tests/settings_test.py b/tests/src/OneLogin/saml2_tests/settings_test.py index 25e87e38..41e6de6f 100644 --- a/tests/src/OneLogin/saml2_tests/settings_test.py +++ b/tests/src/OneLogin/saml2_tests/settings_test.py @@ -148,29 +148,57 @@ def testGetCertPath(self): settings = OneLogin_Saml2_Settings(custom_base_path=self.settings_path) self.assertEqual(self.settings_path + sep + 'certs' + sep, settings.get_cert_path()) - def testGetLibPath(self): + def testSetCertPath(self): """ - Tests getLibPath method of the OneLogin_Saml2_Settings + Tests setCertPath method of the OneLogin_Saml2_Settings """ settings = OneLogin_Saml2_Settings(custom_base_path=self.settings_path) - base = settings.get_base_path() - self.assertEqual(join(base, 'lib') + sep, settings.get_lib_path()) + self.assertEqual(self.settings_path + sep + 'certs' + sep, settings.get_cert_path()) - def testGetExtLibPath(self): + settings.set_cert_path('/tmp') + self.assertEqual('/tmp', settings.get_cert_path()) + + def testGetLibPath(self): """ - Tests getExtLibPath method of the OneLogin_Saml2_Settings + Tests getLibPath method of the OneLogin_Saml2_Settings """ + settingsInfo = self.loadSettingsJSON() + settings = OneLogin_Saml2_Settings(settingsInfo) + path = settings.get_base_path() + self.assertEqual(settings.get_lib_path(), join(dirname(dirname(dirname(dirname(dirname(__file__))))), 'src/onelogin/saml2/')) + self.assertEqual(path, join(dirname(dirname(dirname(dirname(dirname(__file__))))), 'src/onelogin/saml2/../../../tests/data/customPath/')) + + del settingsInfo['custom_base_path'] + settings = OneLogin_Saml2_Settings(settingsInfo) + path = settings.get_base_path() + self.assertEqual(settings.get_lib_path(), join(dirname(dirname(dirname(dirname(dirname(__file__))))), 'src/onelogin/saml2/')) + self.assertEqual(path, join(dirname(dirname(dirname(dirname(dirname(__file__))))), 'src/')) + settings = OneLogin_Saml2_Settings(custom_base_path=self.settings_path) - base = settings.get_base_path() - self.assertEqual(join(base, 'extlib') + sep, settings.get_ext_lib_path()) + path = settings.get_base_path() + self.assertEqual(settings.get_lib_path(), join(dirname(dirname(dirname(dirname(dirname(__file__))))), 'src/onelogin/saml2/')) + self.assertEqual(path, join(dirname(dirname(dirname(dirname(__file__)))), 'settings/')) def testGetSchemasPath(self): """ Tests getSchemasPath method of the OneLogin_Saml2_Settings """ + settingsInfo = self.loadSettingsJSON() + settings = OneLogin_Saml2_Settings(settingsInfo) + path = settings.get_base_path() + self.assertEqual(settings.get_schemas_path(), join(dirname(dirname(dirname(dirname(dirname(__file__))))), 'src/onelogin/saml2/schemas/')) + self.assertEqual(path, join(dirname(dirname(dirname(dirname(dirname(__file__))))), 'src/onelogin/saml2/../../../tests/data/customPath/')) + + del settingsInfo['custom_base_path'] + settings = OneLogin_Saml2_Settings(settingsInfo) + path = settings.get_base_path() + self.assertEqual(settings.get_schemas_path(), join(dirname(dirname(dirname(dirname(dirname(__file__))))), 'src/onelogin/saml2/schemas/')) + self.assertEqual(path, join(dirname(dirname(dirname(dirname(dirname(__file__))))), 'src/')) + settings = OneLogin_Saml2_Settings(custom_base_path=self.settings_path) - base = settings.get_base_path() - self.assertEqual(join(base, 'lib', 'schemas') + sep, settings.get_schemas_path()) + path = settings.get_base_path() + self.assertEqual(settings.get_schemas_path(), join(dirname(dirname(dirname(dirname(dirname(__file__))))), 'src/onelogin/saml2/schemas/')) + self.assertEqual(path, join(dirname(dirname(dirname(dirname(__file__)))), 'settings/')) def testGetIdPSSOurl(self): """ From 52f0daa7e88bb4bf5bcc6b6423852767dbba98f6 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Mon, 11 Jan 2021 17:39:07 +0100 Subject: [PATCH 058/205] Close #218. Add support for get_friendlyname_attributes and get_friendlyname_attribute --- src/onelogin/saml2/auth.py | 24 +++++++++++++ src/onelogin/saml2/response.py | 36 +++++++++++++++++++ .../response1_with_friendlyname.xml.base64 | 1 + tests/src/OneLogin/saml2_tests/auth_test.py | 2 ++ .../src/OneLogin/saml2_tests/response_test.py | 26 +++++++++++++- 5 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 tests/data/responses/response1_with_friendlyname.xml.base64 diff --git a/src/onelogin/saml2/auth.py b/src/onelogin/saml2/auth.py index c173b1b5..67559abf 100644 --- a/src/onelogin/saml2/auth.py +++ b/src/onelogin/saml2/auth.py @@ -53,6 +53,7 @@ def __init__(self, request_data, old_settings=None, custom_base_path=None): else: self.__settings = OneLogin_Saml2_Settings(old_settings, custom_base_path) self.__attributes = dict() + self.__friendlyname_attributes = dict() self.__nameid = None self.__nameid_format = None self.__nameid_nq = None @@ -107,6 +108,7 @@ def process_response(self, request_id=None): if response.is_valid(self.__request_data, request_id): self.__attributes = response.get_attributes() + self.__friendlyname_attributes = response.get_friendlyname_attributes() self.__nameid = response.get_nameid() self.__nameid_format = response.get_nameid_format() self.__nameid_nq = response.get_nameid_nq() @@ -231,6 +233,15 @@ def get_attributes(self): """ return self.__attributes + def get_friendlyname_attributes(self): + """ + Returns the set of SAML attributes indexed by FiendlyName. + + :returns: SAML attributes + :rtype: dict + """ + return self.__friendlyname_attributes + def get_nameid(self): """ Returns the nameID. @@ -321,6 +332,19 @@ def get_attribute(self, name): assert isinstance(name, compat.str_type) return self.__attributes.get(name) + def get_friendlyname_attribute(self, friendlyname): + """ + Returns the requested SAML attribute searched by FriendlyName. + + :param friendlyname: FriendlyName of the attribute + :type friendlyname: string + + :returns: Attribute value(s) if exists or None + :rtype: list + """ + assert isinstance(friendlyname, compat.str_type) + return self.__friendlyname_attributes.get(friendlyname) + def get_last_request_id(self): """ :returns: The ID of the last Request SAML message generated. diff --git a/src/onelogin/saml2/response.py b/src/onelogin/saml2/response.py index 1e7736a8..792c88af 100644 --- a/src/onelogin/saml2/response.py +++ b/src/onelogin/saml2/response.py @@ -603,6 +603,42 @@ def get_attributes(self): attributes[attr_name] = values return attributes + def get_friendlyname_attributes(self): + """ + Gets the Attributes from the AttributeStatement element indexed by FiendlyName. + EncryptedAttributes are not supported + """ + attributes = {} + attribute_nodes = self.__query_assertion('/saml:AttributeStatement/saml:Attribute') + for attribute_node in attribute_nodes: + attr_friendlyname = attribute_node.get('FriendlyName') + if attr_friendlyname: + if attr_friendlyname in attributes.keys(): + raise OneLogin_Saml2_ValidationError( + 'Found an Attribute element with duplicated FriendlyName', + OneLogin_Saml2_ValidationError.DUPLICATED_ATTRIBUTE_NAME_FOUND + ) + + values = [] + for attr in attribute_node.iterchildren('{%s}AttributeValue' % OneLogin_Saml2_Constants.NSMAP['saml']): + attr_text = OneLogin_Saml2_XML.element_text(attr) + if attr_text: + attr_text = attr_text.strip() + if attr_text: + values.append(attr_text) + + # Parse any nested NameID children + for nameid in attr.iterchildren('{%s}NameID' % OneLogin_Saml2_Constants.NSMAP['saml']): + values.append({ + 'NameID': { + 'Format': nameid.get('Format'), + 'NameQualifier': nameid.get('NameQualifier'), + 'value': nameid.text + } + }) + attributes[attr_friendlyname] = values + return attributes + def validate_num_assertions(self): """ Verifies that the document only contains a single Assertion (encrypted or not) diff --git a/tests/data/responses/response1_with_friendlyname.xml.base64 b/tests/data/responses/response1_with_friendlyname.xml.base64 new file mode 100644 index 00000000..a17c3941 --- /dev/null +++ b/tests/data/responses/response1_with_friendlyname.xml.base64 @@ -0,0 +1 @@ +PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIElEPSJHT1NBTUxSMTI5MDExNzQ1NzE3OTQiIFZlcnNpb249IjIuMCIgSXNzdWVJbnN0YW50PSIyMDEwLTExLTE4VDIxOjU3OjM3WiIgRGVzdGluYXRpb249IntyZWNpcGllbnR9Ij4NCiAgPHNhbWxwOlN0YXR1cz4NCiAgICA8c2FtbHA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+PC9zYW1scDpTdGF0dXM+DQogIDxzYW1sOkFzc2VydGlvbiB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIFZlcnNpb249IjIuMCIgSUQ9InBmeDhmZmIzOTgzLWNiZjYtOTJhMS1mMmM0LTYxOWFlMWJlMWM4NiIgSXNzdWVJbnN0YW50PSIyMDEwLTExLTE4VDIxOjU3OjM3WiI+DQogICAgPHNhbWw6SXNzdWVyPmh0dHBzOi8vYXBwLm9uZWxvZ2luLmNvbS9zYW1sL21ldGFkYXRhLzEzNTkwPC9zYW1sOklzc3Vlcj48ZHM6U2lnbmF0dXJlIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj4NCiAgPGRzOlNpZ25lZEluZm8+PGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz4NCiAgICA8ZHM6U2lnbmF0dXJlTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3JzYS1zaGExIi8+DQogIDxkczpSZWZlcmVuY2UgVVJJPSIjcGZ4OGZmYjM5ODMtY2JmNi05MmExLWYyYzQtNjE5YWUxYmUxYzg2Ij48ZHM6VHJhbnNmb3Jtcz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PC9kczpUcmFuc2Zvcm1zPjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjc2hhMSIvPjxkczpEaWdlc3RWYWx1ZT5oZ3VRYkNIYW5pYmJEQzdxM1p6eHpIY1Bvbkk9PC9kczpEaWdlc3RWYWx1ZT48L2RzOlJlZmVyZW5jZT48L2RzOlNpZ25lZEluZm8+PGRzOlNpZ25hdHVyZVZhbHVlPkdhbmNEOXZSb2g5TWJUMDAyRHk3OXQxbTZJNllmaFVLUGZibGttcDJ1ZG9sWHVqdjZlMU1XdnNWbXhOenRzSUdseEFhMHFLRGlTTXpDTkRac2szanN5c1VsMW5BS25BZzE4NWpmWGpzemhzZG1SK005MWR4azZrZmNMVW9zT29sb3ZhZFdMUFdxbjdQM2o4LzV4enA5THBSQTNndkI0MTgyUlNpcldDQlhQUT08L2RzOlNpZ25hdHVyZVZhbHVlPg0KPGRzOktleUluZm8+PGRzOlg1MDlEYXRhPjxkczpYNTA5Q2VydGlmaWNhdGU+TUlJQ2dUQ0NBZW9DQ1FDYk9scldEZFg3RlRBTkJna3Foa2lHOXcwQkFRVUZBRENCaERFTE1Ba0dBMVVFQmhNQ1RrOHhHREFXQmdOVkJBZ1REMEZ1WkhKbFlYTWdVMjlzWW1WeVp6RU1NQW9HQTFVRUJ4TURSbTl2TVJBd0RnWURWUVFLRXdkVlRrbE9SVlJVTVJnd0ZnWURWUVFERXc5bVpXbGtaUzVsY214aGJtY3VibTh4SVRBZkJna3Foa2lHOXcwQkNRRVdFbUZ1WkhKbFlYTkFkVzVwYm1WMGRDNXViekFlRncwd056QTJNVFV4TWpBeE16VmFGdzB3TnpBNE1UUXhNakF4TXpWYU1JR0VNUXN3Q1FZRFZRUUdFd0pPVHpFWU1CWUdBMVVFQ0JNUFFXNWtjbVZoY3lCVGIyeGlaWEpuTVF3d0NnWURWUVFIRXdOR2IyOHhFREFPQmdOVkJBb1RCMVZPU1U1RlZGUXhHREFXQmdOVkJBTVREMlpsYVdSbExtVnliR0Z1Wnk1dWJ6RWhNQjhHQ1NxR1NJYjNEUUVKQVJZU1lXNWtjbVZoYzBCMWJtbHVaWFIwTG01dk1JR2ZNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0R05BRENCaVFLQmdRRGl2YmhSN1A1MTZ4L1MzQnFLeHVwUWUwTE9Ob2xpdXBpQk9lc0NPM1NIYkRybDMrcTlJYmZuZm1FMDRyTnVNY1BzSXhCMTYxVGREcEllc0xDbjdjOGFQSElTS090UGxBZVRaU25iOFFBdTdhUmpacTMrUGJyUDV1VzNUY2ZDR1B0S1R5dEhPZ2UvT2xKYm8wNzhkVmhYUTE0ZDFFRHdYSlcxclJYdVV0NEM4UUlEQVFBQk1BMEdDU3FHU0liM0RRRUJCUVVBQTRHQkFDRFZmcDg2SE9icVkrZThCVW9XUTkrVk1ReDFBU0RvaEJqd09zZzJXeWtVcVJYRitkTGZjVUg5ZFdSNjNDdFpJS0ZEYlN0Tm9tUG5RejduYksrb255Z3dCc3BWRWJuSHVVaWhacTNaVWRtdW1RcUN3NFV2cy8xVXZxM29yT28vV0pWaFR5dkxnRlZLMlFhclE0LzY3T1pmSGQ3UitQT0JYaG9waFNNdjFaT288L2RzOlg1MDlDZXJ0aWZpY2F0ZT48L2RzOlg1MDlEYXRhPjwvZHM6S2V5SW5mbz48L2RzOlNpZ25hdHVyZT4NCiAgICA8c2FtbDpTdWJqZWN0Pg0KICAgICAgPHNhbWw6TmFtZUlEIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4xOm5hbWVpZC1mb3JtYXQ6ZW1haWxBZGRyZXNzIj5zdXBwb3J0QG9uZWxvZ2luLmNvbTwvc2FtbDpOYW1lSUQ+DQogICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+DQogICAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjAxMC0xMS0xOFQyMjowMjozN1oiIFJlY2lwaWVudD0ie3JlY2lwaWVudH0iLz48L3NhbWw6U3ViamVjdENvbmZpcm1hdGlvbj4NCiAgICA8L3NhbWw6U3ViamVjdD4NCiAgICA8c2FtbDpDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAxMC0xMS0xOFQyMTo1MjozN1oiIE5vdE9uT3JBZnRlcj0iMjAxMC0xMS0xOFQyMjowMjozN1oiPg0KICAgICAgPHNhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj4NCiAgICAgICAgPHNhbWw6QXVkaWVuY2U+e2F1ZGllbmNlfTwvc2FtbDpBdWRpZW5jZT4NCiAgICAgIDwvc2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPg0KICAgIDwvc2FtbDpDb25kaXRpb25zPg0KICAgIDxzYW1sOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAxMC0xMS0xOFQyMTo1NzozN1oiIFNlc3Npb25Ob3RPbk9yQWZ0ZXI9IjIwMTAtMTEtMTlUMjE6NTc6MzdaIiBTZXNzaW9uSW5kZXg9Il81MzFjMzJkMjgzYmRmZjdlMDRlNDg3YmNkYmM0ZGQ4ZCI+DQogICAgICA8c2FtbDpBdXRobkNvbnRleHQ+DQogICAgICAgIDxzYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkPC9zYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPg0KICAgICAgPC9zYW1sOkF1dGhuQ29udGV4dD4NCiAgICA8L3NhbWw6QXV0aG5TdGF0ZW1lbnQ+DQogICAgPHNhbWw6QXR0cmlidXRlU3RhdGVtZW50Pg0KICAgICAgPHNhbWw6QXR0cmlidXRlIE5hbWU9InVpZCIgRnJpZW5kbHlOYW1lPSJ1c2VybmFtZSI+DQogICAgICAgIDxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeHNpOnR5cGU9InhzOnN0cmluZyI+ZGVtbzwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT4NCiAgICAgIDwvc2FtbDpBdHRyaWJ1dGU+DQogICAgICA8c2FtbDpBdHRyaWJ1dGUgTmFtZT0iYW5vdGhlcl92YWx1ZSI+DQogICAgICAgIDxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeHNpOnR5cGU9InhzOnN0cmluZyI+dmFsdWU8L3NhbWw6QXR0cmlidXRlVmFsdWU+DQogICAgICA8L3NhbWw6QXR0cmlidXRlPg0KICAgIDwvc2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+DQogIDwvc2FtbDpBc3NlcnRpb24+DQo8L3NhbWxwOlJlc3BvbnNlPg== \ No newline at end of file diff --git a/tests/src/OneLogin/saml2_tests/auth_test.py b/tests/src/OneLogin/saml2_tests/auth_test.py index 81d009e9..2ccd3994 100644 --- a/tests/src/OneLogin/saml2_tests/auth_test.py +++ b/tests/src/OneLogin/saml2_tests/auth_test.py @@ -240,6 +240,8 @@ def testProcessResponseValid(self): attributes = auth.get_attributes() self.assertNotEqual(len(attributes), 0) self.assertEqual(auth.get_attribute('mail'), attributes['mail']) + friendlyname_attributes = auth.get_friendlyname_attributes() + self.assertEqual(len(friendlyname_attributes), 0) session_index = auth.get_session_index() self.assertEqual('_6273d77b8cde0c333ec79d22a9fa0003b9fe2d75cb', session_index) self.assertEqual("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", auth.get_nameid_format()) diff --git a/tests/src/OneLogin/saml2_tests/response_test.py b/tests/src/OneLogin/saml2_tests/response_test.py index 7d9934a1..c6eb7eac 100644 --- a/tests/src/OneLogin/saml2_tests/response_test.py +++ b/tests/src/OneLogin/saml2_tests/response_test.py @@ -656,7 +656,7 @@ def testGetSessionIndex(self): def testGetAttributes(self): """ - Tests the getAttributes method of the OneLogin_Saml2_Response + Tests the get_attributes method of the OneLogin_Saml2_Response """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) xml = self.file_contents(join(self.data_path, 'responses', 'response1.xml.base64')) @@ -678,6 +678,30 @@ def testGetAttributes(self): response_3 = OneLogin_Saml2_Response(settings, xml_3) self.assertEqual({}, response_3.get_attributes()) + def testGetFriendlyAttributes(self): + """ + Tests the get_friendlyname_attributes method of the OneLogin_Saml2_Response + """ + settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) + xml = self.file_contents(join(self.data_path, 'responses', 'response1.xml.base64')) + response = OneLogin_Saml2_Response(settings, xml) + self.assertEqual({}, response.get_friendlyname_attributes()) + + expected_attributes = { + 'username': ['demo'] + } + xml_2 = self.file_contents(join(self.data_path, 'responses', 'response1_with_friendlyname.xml.base64')) + response_2 = OneLogin_Saml2_Response(settings, xml_2) + self.assertEqual(expected_attributes, response_2.get_friendlyname_attributes()) + + xml_3 = self.file_contents(join(self.data_path, 'responses', 'response2.xml.base64')) + response_3 = OneLogin_Saml2_Response(settings, xml_3) + self.assertEqual({}, response_3.get_friendlyname_attributes()) + + xml_4 = self.file_contents(join(self.data_path, 'responses', 'invalids', 'encrypted_attrs.xml.base64')) + response_4 = OneLogin_Saml2_Response(settings, xml_4) + self.assertEqual({}, response_4.get_friendlyname_attributes()) + def testGetNestedNameIDAttributes(self): """ Tests the getAttributes method of the OneLogin_Saml2_Response with nested From 06e15d3cbc9db1f7d0a8fb265b7dca2583cc4b92 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Mon, 11 Jan 2021 21:06:53 +0100 Subject: [PATCH 059/205] Adapt code to work with py2.7 as well. Follow same pattern on response, logoutrequest and logoutresponse destination check --- src/onelogin/saml2/logout_request.py | 25 ++++++++++++------------- src/onelogin/saml2/logout_response.py | 11 ++++++----- src/onelogin/saml2/response.py | 1 - src/onelogin/saml2/utils.py | 8 ++++---- 4 files changed, 22 insertions(+), 23 deletions(-) diff --git a/src/onelogin/saml2/logout_request.py b/src/onelogin/saml2/logout_request.py index 6ee8b553..865841f1 100644 --- a/src/onelogin/saml2/logout_request.py +++ b/src/onelogin/saml2/logout_request.py @@ -311,19 +311,18 @@ def is_valid(self, request_data, raise_exceptions=False): ) # Check destination - if root.get('Destination', None): - destination = root.get('Destination') - if destination != '': - if OneLogin_Saml2_Utils.normalize_url(current_url) not in OneLogin_Saml2_Utils.normalize_url(destination): - raise OneLogin_Saml2_ValidationError( - 'The LogoutRequest was received at ' - '%(currentURL)s instead of %(destination)s' % - { - 'currentURL': current_url, - 'destination': destination, - }, - OneLogin_Saml2_ValidationError.WRONG_DESTINATION - ) + destination = root.get('Destination', None) + if destination: + if not OneLogin_Saml2_Utils.normalize_url(url=destination).startswith(OneLogin_Saml2_Utils.normalize_url(url=current_url)): + raise OneLogin_Saml2_ValidationError( + 'The LogoutRequest was received at ' + '%(currentURL)s instead of %(destination)s' % + { + 'currentURL': current_url, + 'destination': destination, + }, + OneLogin_Saml2_ValidationError.WRONG_DESTINATION + ) # Check issuer issuer = OneLogin_Saml2_Logout_Request.get_issuer(root) diff --git a/src/onelogin/saml2/logout_response.py b/src/onelogin/saml2/logout_response.py index 7c89f9e3..b31f64da 100644 --- a/src/onelogin/saml2/logout_response.py +++ b/src/onelogin/saml2/logout_response.py @@ -118,11 +118,12 @@ def is_valid(self, request_data, request_id=None, raise_exceptions=False): # Check destination destination = self.document.get('Destination', None) - if destination and OneLogin_Saml2_Utils.normalize_url(url=current_url) not in OneLogin_Saml2_Utils.normalize_url(url=destination): - raise OneLogin_Saml2_ValidationError( - 'The LogoutResponse was received at %s instead of %s' % (current_url, destination), - OneLogin_Saml2_ValidationError.WRONG_DESTINATION - ) + if destination: + if not OneLogin_Saml2_Utils.normalize_url(url=destination).startswith(OneLogin_Saml2_Utils.normalize_url(url=current_url)): + raise OneLogin_Saml2_ValidationError( + 'The LogoutResponse was received at %s instead of %s' % (current_url, destination), + OneLogin_Saml2_ValidationError.WRONG_DESTINATION + ) if security['wantMessagesSigned']: if 'Signature' not in get_data: diff --git a/src/onelogin/saml2/response.py b/src/onelogin/saml2/response.py index 8030de26..6de385e9 100644 --- a/src/onelogin/saml2/response.py +++ b/src/onelogin/saml2/response.py @@ -10,7 +10,6 @@ """ from copy import deepcopy -from urllib.parse import urlsplit, urlunsplit from onelogin.saml2.constants import OneLogin_Saml2_Constants from onelogin.saml2.utils import OneLogin_Saml2_Utils, OneLogin_Saml2_Error, OneLogin_Saml2_ValidationError, return_false_on_exception from onelogin.saml2.xml_utils import OneLogin_Saml2_XML diff --git a/src/onelogin/saml2/utils.py b/src/onelogin/saml2/utils.py index 3c22e406..bc618f03 100644 --- a/src/onelogin/saml2/utils.py +++ b/src/onelogin/saml2/utils.py @@ -20,7 +20,6 @@ from functools import wraps from uuid import uuid4 from xml.dom.minidom import Element -from urllib.parse import urlsplit, urlunsplit import zlib import xmlsec @@ -31,8 +30,9 @@ try: - from urllib.parse import quote_plus # py3 + from urllib.parse import quote_plus, urlsplit, urlunsplit # py3 except ImportError: + from urlparse import urlsplit, urlunsplit from urllib import quote_plus # py2 @@ -1078,8 +1078,8 @@ def normalize_url(url): :rtype: String """ try: - scheme, netloc, *rest = urlsplit(url) - normalized_url = urlunsplit((scheme.lower(), netloc.lower(), *rest)) + scheme, netloc, path, query, fragment = urlsplit(url) + normalized_url = urlunsplit((scheme.lower(), netloc.lower(), path, query, fragment)) return normalized_url except Exception: return url \ No newline at end of file From a281bc5482a8b1e10f36ad1a8bb9836b5a9d50ca Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Mon, 11 Jan 2021 21:53:03 +0100 Subject: [PATCH 060/205] pep8 fixes --- .../OneLogin/saml2_tests/logout_response_test.py | 3 +-- tests/src/OneLogin/saml2_tests/response_test.py | 16 ++++++++-------- tests/src/OneLogin/saml2_tests/settings_test.py | 2 +- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/tests/src/OneLogin/saml2_tests/logout_response_test.py b/tests/src/OneLogin/saml2_tests/logout_response_test.py index 3ab17653..90872f10 100644 --- a/tests/src/OneLogin/saml2_tests/logout_response_test.py +++ b/tests/src/OneLogin/saml2_tests/logout_response_test.py @@ -297,7 +297,7 @@ def testIsValidWithCapitalization(self): response_2.is_valid(request_data, raise_exceptions=True) plain_message = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(message)) - + current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data).lower() plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url) message_3 = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message) @@ -333,7 +333,6 @@ def testIsInValidWithCapitalization(self): response_3 = OneLogin_Saml2_Logout_Response(settings, message_3) self.assertFalse(response_3.is_valid(request_data)) - def testIsValidWithXMLEncoding(self): """ Tests the is_valid method of the OneLogin_Saml2_LogoutResponse diff --git a/tests/src/OneLogin/saml2_tests/response_test.py b/tests/src/OneLogin/saml2_tests/response_test.py index cdecf91f..3ace583e 100644 --- a/tests/src/OneLogin/saml2_tests/response_test.py +++ b/tests/src/OneLogin/saml2_tests/response_test.py @@ -18,6 +18,7 @@ from onelogin.saml2.settings import OneLogin_Saml2_Settings from onelogin.saml2.utils import OneLogin_Saml2_Utils + class OneLogin_Saml2_Response_Test(unittest.TestCase): data_path = join(dirname(dirname(dirname(dirname(__file__)))), 'data') settings_path = join(dirname(dirname(dirname(dirname(__file__)))), 'settings') @@ -54,7 +55,7 @@ def get_request_data_domain_capitalized(self): 'http_host': 'StuFF.Com', 'script_name': 'endpoints/endpoints/acs.php' } - + def get_request_data_path_capitalized(self): return { 'http_host': 'stuff.com', @@ -1018,7 +1019,7 @@ def testIsInValidDuplicatedAttrs(self): response = OneLogin_Saml2_Response(settings, xml) with self.assertRaisesRegex(Exception, 'Found an Attribute element with duplicated Name'): response.get_attributes() - + def testIsInValidDestination(self): """ Tests the is_valid method of the OneLogin_Saml2_Response class @@ -1065,17 +1066,16 @@ def testIsInValidDestinationCapitalizationOfElements(self): Tests the is_valid method of the OneLogin_Saml2_Response class Case Invalid Response due to differences in capitalization of path """ - settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) message = self.file_contents(join(self.data_path, 'responses', 'unsigned_response.xml.base64')) - - #Test path capitalized + + # Test path capitalized settings.set_strict(True) response = OneLogin_Saml2_Response(settings, message) self.assertFalse(response.is_valid(self.get_request_data_path_capitalized())) self.assertIn('The response was received at', response.get_error()) - #Test both domain and path capitalized + # Test both domain and path capitalized response_2 = OneLogin_Saml2_Response(settings, message) self.assertFalse(response_2.is_valid(self.get_request_data_both_capitalized())) self.assertIn('The response was received at', response_2.get_error()) @@ -1087,13 +1087,13 @@ def testIsValidDestinationCapitalizationOfHost(self): """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) message = self.file_contents(join(self.data_path, 'responses', 'unsigned_response.xml.base64')) - #Test domain capitalized + # Test domain capitalized settings.set_strict(True) response = OneLogin_Saml2_Response(settings, message) self.assertFalse(response.is_valid(self.get_request_data_domain_capitalized())) self.assertNotIn('The response was received at', response.get_error()) - #Assert we got past the destination check, which appears later + # Assert we got past the destination check, which appears later self.assertIn('A valid SubjectConfirmation was not found', response.get_error()) def testIsInValidAudience(self): diff --git a/tests/src/OneLogin/saml2_tests/settings_test.py b/tests/src/OneLogin/saml2_tests/settings_test.py index 41e6de6f..ddf8c531 100644 --- a/tests/src/OneLogin/saml2_tests/settings_test.py +++ b/tests/src/OneLogin/saml2_tests/settings_test.py @@ -198,7 +198,7 @@ def testGetSchemasPath(self): settings = OneLogin_Saml2_Settings(custom_base_path=self.settings_path) path = settings.get_base_path() self.assertEqual(settings.get_schemas_path(), join(dirname(dirname(dirname(dirname(dirname(__file__))))), 'src/onelogin/saml2/schemas/')) - self.assertEqual(path, join(dirname(dirname(dirname(dirname(__file__)))), 'settings/')) + self.assertEqual(path, join(dirname(dirname(dirname(dirname(__file__)))), 'settings/')) def testGetIdPSSOurl(self): """ From 8d130bf62ad27752e10d9cf3af59f9144585f1a2 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Tue, 12 Jan 2021 12:56:12 +0100 Subject: [PATCH 061/205] More pep8 fixes --- src/onelogin/saml2/response.py | 2 +- src/onelogin/saml2/utils.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/onelogin/saml2/response.py b/src/onelogin/saml2/response.py index 6de385e9..677ef6b7 100644 --- a/src/onelogin/saml2/response.py +++ b/src/onelogin/saml2/response.py @@ -901,7 +901,7 @@ def __decrypt_assertion(self, xml): decrypted = OneLogin_Saml2_Utils.decrypt_element(encrypted_data, key, debug=debug, inplace=True) xml.replace(encrypted_assertion_nodes[0], decrypted) return xml - + def get_error(self): """ After executing a validation process, if it fails this method returns the cause diff --git a/src/onelogin/saml2/utils.py b/src/onelogin/saml2/utils.py index bc618f03..4ca5962c 100644 --- a/src/onelogin/saml2/utils.py +++ b/src/onelogin/saml2/utils.py @@ -1066,7 +1066,7 @@ def validate_binary_sign(signed_query, signature, cert=None, algorithm=OneLogin_ @staticmethod def normalize_url(url): """ - Returns normalized URL for comparison. + Returns normalized URL for comparison. This method converts the netloc to lowercase, as it should be case-insensitive (per RFC 4343, RFC 7617) If standardization fails, the original URL is returned Python documentation indicates that URL split also normalizes query strings if empty query fields are present @@ -1082,4 +1082,4 @@ def normalize_url(url): normalized_url = urlunsplit((scheme.lower(), netloc.lower(), path, query, fragment)) return normalized_url except Exception: - return url \ No newline at end of file + return url From 95f06d340b0727a65a081575e2f8750353b2a4e5 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Tue, 12 Jan 2021 13:15:25 +0100 Subject: [PATCH 062/205] Add _generate_request_id to logout_request and logout_response and replace staticmethod by classmethod --- src/onelogin/saml2/logout_request.py | 27 ++++++++++++++++----------- src/onelogin/saml2/logout_response.py | 11 +++++++++-- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/onelogin/saml2/logout_request.py b/src/onelogin/saml2/logout_request.py index 51944a62..b7d77ca6 100644 --- a/src/onelogin/saml2/logout_request.py +++ b/src/onelogin/saml2/logout_request.py @@ -59,8 +59,7 @@ def __init__(self, settings, request=None, name_id=None, session_index=None, nq= idp_data = self.__settings.get_idp_data() security = self.__settings.get_security_data() - uid = OneLogin_Saml2_Utils.generate_unique_id() - self.id = uid + self.id = self._generate_request_id() issue_instant = OneLogin_Saml2_Utils.parse_time_to_SAML(OneLogin_Saml2_Utils.now()) @@ -108,7 +107,7 @@ def __init__(self, settings, request=None, name_id=None, session_index=None, nq= logout_request = OneLogin_Saml2_Templates.LOGOUT_REQUEST % \ { - 'id': uid, + 'id': self.id, 'issue_instant': issue_instant, 'single_logout_url': self.__settings.get_idp_slo_response_url(), 'entity_id': sp_data['entityId'], @@ -144,8 +143,8 @@ def get_xml(self): """ return self.__logout_request - @staticmethod - def get_id(request): + @classmethod + def get_id(cls, request): """ Returns the ID of the Logout Request :param request: Logout Request Message @@ -157,8 +156,8 @@ def get_id(request): elem = OneLogin_Saml2_XML.to_etree(request) return elem.get('ID', None) - @staticmethod - def get_nameid_data(request, key=None): + @classmethod + def get_nameid_data(cls, request, key=None): """ Gets the NameID Data of the the Logout Request :param request: Logout Request Message @@ -234,8 +233,8 @@ def get_nameid_format(cls, request, key=None): name_id_format = name_id_data['Format'] return name_id_format - @staticmethod - def get_issuer(request): + @classmethod + def get_issuer(cls, request): """ Gets the Issuer of the Logout Request Message :param request: Logout Request Message @@ -251,8 +250,8 @@ def get_issuer(request): issuer = OneLogin_Saml2_XML.element_text(issuer_nodes[0]) return issuer - @staticmethod - def get_session_indexes(request): + @classmethod + def get_session_indexes(cls, request): """ Gets the SessionIndexes from the Logout Request :param request: Logout Request Message @@ -359,3 +358,9 @@ def get_error(self): After executing a validation process, if it fails this method returns the cause """ return self.__error + + def _generate_request_id(self): + """ + Generate an unique logout request ID. + """ + return OneLogin_Saml2_Utils.generate_unique_id() diff --git a/src/onelogin/saml2/logout_response.py b/src/onelogin/saml2/logout_response.py index b31f64da..73decb10 100644 --- a/src/onelogin/saml2/logout_response.py +++ b/src/onelogin/saml2/logout_response.py @@ -160,12 +160,13 @@ def build(self, in_response_to): """ sp_data = self.__settings.get_sp_data() - uid = OneLogin_Saml2_Utils.generate_unique_id() + self.id = self._generate_request_id() + issue_instant = OneLogin_Saml2_Utils.parse_time_to_SAML(OneLogin_Saml2_Utils.now()) logout_response = OneLogin_Saml2_Templates.LOGOUT_RESPONSE % \ { - 'id': uid, + 'id': self.id, 'issue_instant': issue_instant, 'destination': self.__settings.get_idp_slo_response_url(), 'in_response_to': in_response_to, @@ -211,3 +212,9 @@ def get_xml(self): :rtype: string """ return self.__logout_response + + def _generate_request_id(self): + """ + Generate an unique logout response ID. + """ + return OneLogin_Saml2_Utils.generate_unique_id() \ No newline at end of file From c25df8164563df28ec94dd6f5d7a9a6c65c06e83 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Wed, 13 Jan 2021 12:51:13 +0100 Subject: [PATCH 063/205] Added custom lxml parser based on the one defined at xmldefused. Update copyright --- LICENSE | 2 +- setup.py | 5 +- src/onelogin/__init__.py | 2 +- src/onelogin/saml2/__init__.py | 2 +- src/onelogin/saml2/auth.py | 14 +- src/onelogin/saml2/authn_request.py | 4 +- src/onelogin/saml2/compat.py | 2 +- src/onelogin/saml2/constants.py | 2 +- src/onelogin/saml2/errors.py | 2 +- src/onelogin/saml2/idp_metadata_parser.py | 2 +- src/onelogin/saml2/logout_request.py | 2 +- src/onelogin/saml2/logout_response.py | 4 +- src/onelogin/saml2/metadata.py | 2 +- src/onelogin/saml2/response.py | 2 +- src/onelogin/saml2/settings.py | 2 +- src/onelogin/saml2/utils.py | 2 +- src/onelogin/saml2/xml_templates.py | 2 +- src/onelogin/saml2/xml_utils.py | 9 +- src/onelogin/saml2/xmlparser.py | 147 ++++++++++++++++++ tests/src/OneLogin/saml2_tests/auth_test.py | 2 +- .../saml2_tests/authn_request_test.py | 2 +- tests/src/OneLogin/saml2_tests/error_test.py | 2 +- .../saml2_tests/idp_metadata_parser_test.py | 2 +- .../saml2_tests/logout_request_test.py | 2 +- .../saml2_tests/logout_response_test.py | 2 +- .../src/OneLogin/saml2_tests/metadata_test.py | 2 +- .../src/OneLogin/saml2_tests/response_test.py | 2 +- .../src/OneLogin/saml2_tests/settings_test.py | 2 +- .../saml2_tests/signed_response_test.py | 2 +- tests/src/OneLogin/saml2_tests/utils_test.py | 4 +- .../OneLogin/saml2_tests/xml_utils_test.py | 2 +- 31 files changed, 191 insertions(+), 44 deletions(-) create mode 100644 src/onelogin/saml2/xmlparser.py diff --git a/LICENSE b/LICENSE index 1c8f814e..734c18ba 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2010-2018 OneLogin, Inc. +Copyright (c) 2010-2021 OneLogin, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/setup.py b/setup.py index 5a928564..3053b82c 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ #! /usr/bin/env python # -*- coding: utf-8 -*- -# Copyright (c) 2010-2018 OneLogin, Inc. +# Copyright (c) 2010-2021 OneLogin, Inc. # MIT License from setuptools import setup @@ -37,8 +37,9 @@ test_suite='tests', install_requires=[ 'isodate>=0.5.0', + 'lxml>=3.3.5', 'xmlsec>=0.6.0', - 'defusedxml>=0.5.0' + 'defusedxml==0.6.0' ], dependency_links=['http://github.com/mehcode/python-xmlsec/tarball/master'], extras_require={ diff --git a/src/onelogin/__init__.py b/src/onelogin/__init__.py index ba664a65..52ea1212 100644 --- a/src/onelogin/__init__.py +++ b/src/onelogin/__init__.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ -Copyright (c) 2010-2018 OneLogin, Inc. +Copyright (c) 2010-2021 OneLogin, Inc. MIT License Add SAML support to your Python softwares using this library. diff --git a/src/onelogin/saml2/__init__.py b/src/onelogin/saml2/__init__.py index ba664a65..52ea1212 100644 --- a/src/onelogin/saml2/__init__.py +++ b/src/onelogin/saml2/__init__.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ -Copyright (c) 2010-2018 OneLogin, Inc. +Copyright (c) 2010-2021 OneLogin, Inc. MIT License Add SAML support to your Python softwares using this library. diff --git a/src/onelogin/saml2/auth.py b/src/onelogin/saml2/auth.py index f8bddda4..a67e2071 100644 --- a/src/onelogin/saml2/auth.py +++ b/src/onelogin/saml2/auth.py @@ -2,7 +2,7 @@ """ OneLogin_Saml2_Auth class -Copyright (c) 2010-2018 OneLogin, Inc. +Copyright (c) 2010-2021 OneLogin, Inc. MIT License Main class of OneLogin's Python Toolkit. @@ -12,16 +12,16 @@ """ import xmlsec -from defusedxml.lxml import tostring from onelogin.saml2 import compat -from onelogin.saml2.settings import OneLogin_Saml2_Settings -from onelogin.saml2.response import OneLogin_Saml2_Response -from onelogin.saml2.logout_response import OneLogin_Saml2_Logout_Response +from onelogin.saml2.authn_request import OneLogin_Saml2_Authn_Request from onelogin.saml2.constants import OneLogin_Saml2_Constants -from onelogin.saml2.utils import OneLogin_Saml2_Utils, OneLogin_Saml2_Error, OneLogin_Saml2_ValidationError from onelogin.saml2.logout_request import OneLogin_Saml2_Logout_Request -from onelogin.saml2.authn_request import OneLogin_Saml2_Authn_Request +from onelogin.saml2.logout_response import OneLogin_Saml2_Logout_Response +from onelogin.saml2.response import OneLogin_Saml2_Response +from onelogin.saml2.settings import OneLogin_Saml2_Settings +from onelogin.saml2.utils import OneLogin_Saml2_Utils, OneLogin_Saml2_Error, OneLogin_Saml2_ValidationError +from onelogin.saml2.xmlparser import tostring class OneLogin_Saml2_Auth(object): diff --git a/src/onelogin/saml2/authn_request.py b/src/onelogin/saml2/authn_request.py index ef9ed603..48ad9d2a 100644 --- a/src/onelogin/saml2/authn_request.py +++ b/src/onelogin/saml2/authn_request.py @@ -2,7 +2,7 @@ """ OneLogin_Saml2_Authn_Request class -Copyright (c) 2010-2018 OneLogin, Inc. +Copyright (c) 2010-2021 OneLogin, Inc. MIT License AuthNRequest class of OneLogin's Python Toolkit. @@ -131,8 +131,6 @@ def __init__(self, settings, force_authn=False, is_passive=False, set_nameid_pol def _generate_request_id(self): """ Generate an unique request ID. - - You can override this in a subclass. """ return OneLogin_Saml2_Utils.generate_unique_id() diff --git a/src/onelogin/saml2/compat.py b/src/onelogin/saml2/compat.py index b69e61e4..90bcfac7 100644 --- a/src/onelogin/saml2/compat.py +++ b/src/onelogin/saml2/compat.py @@ -2,7 +2,7 @@ """ py3 compatibility class -Copyright (c) 2010-2018 OneLogin, Inc. +Copyright (c) 2010-2021 OneLogin, Inc. MIT License """ diff --git a/src/onelogin/saml2/constants.py b/src/onelogin/saml2/constants.py index e0778bd2..e85a7fb9 100644 --- a/src/onelogin/saml2/constants.py +++ b/src/onelogin/saml2/constants.py @@ -2,7 +2,7 @@ """ OneLogin_Saml2_Constants class -Copyright (c) 2010-2018 OneLogin, Inc. +Copyright (c) 2010-2021 OneLogin, Inc. MIT License Constants class of OneLogin's Python Toolkit. diff --git a/src/onelogin/saml2/errors.py b/src/onelogin/saml2/errors.py index 6faba2e2..6a50f9f1 100644 --- a/src/onelogin/saml2/errors.py +++ b/src/onelogin/saml2/errors.py @@ -2,7 +2,7 @@ """ OneLogin_Saml2_Error class -Copyright (c) 2010-2018 OneLogin, Inc. +Copyright (c) 2010-2021 OneLogin, Inc. MIT License Error class of OneLogin's Python Toolkit. diff --git a/src/onelogin/saml2/idp_metadata_parser.py b/src/onelogin/saml2/idp_metadata_parser.py index 1172bd06..2d7eec2c 100644 --- a/src/onelogin/saml2/idp_metadata_parser.py +++ b/src/onelogin/saml2/idp_metadata_parser.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ OneLogin_Saml2_IdPMetadataParser class -Copyright (c) 2010-2018 OneLogin, Inc. +Copyright (c) 2010-2021 OneLogin, Inc. MIT License Metadata class of OneLogin's Python Toolkit. """ diff --git a/src/onelogin/saml2/logout_request.py b/src/onelogin/saml2/logout_request.py index b7d77ca6..caf0701f 100644 --- a/src/onelogin/saml2/logout_request.py +++ b/src/onelogin/saml2/logout_request.py @@ -2,7 +2,7 @@ """ OneLogin_Saml2_Logout_Request class -Copyright (c) 2010-2018 OneLogin, Inc. +Copyright (c) 2010-2021 OneLogin, Inc. MIT License Logout Request class of OneLogin's Python Toolkit. diff --git a/src/onelogin/saml2/logout_response.py b/src/onelogin/saml2/logout_response.py index 73decb10..bd942200 100644 --- a/src/onelogin/saml2/logout_response.py +++ b/src/onelogin/saml2/logout_response.py @@ -2,7 +2,7 @@ """ OneLogin_Saml2_Logout_Response class -Copyright (c) 2010-2018 OneLogin, Inc. +Copyright (c) 2010-2021 OneLogin, Inc. MIT License Logout Response class of OneLogin's Python Toolkit. @@ -217,4 +217,4 @@ def _generate_request_id(self): """ Generate an unique logout response ID. """ - return OneLogin_Saml2_Utils.generate_unique_id() \ No newline at end of file + return OneLogin_Saml2_Utils.generate_unique_id() diff --git a/src/onelogin/saml2/metadata.py b/src/onelogin/saml2/metadata.py index 9528b0e8..89e0af8f 100644 --- a/src/onelogin/saml2/metadata.py +++ b/src/onelogin/saml2/metadata.py @@ -2,7 +2,7 @@ """ OneLoginSaml2Metadata class -Copyright (c) 2010-2018 OneLogin, Inc. +Copyright (c) 2010-2021 OneLogin, Inc. MIT License Metadata class of OneLogin's Python Toolkit. diff --git a/src/onelogin/saml2/response.py b/src/onelogin/saml2/response.py index 677ef6b7..04264919 100644 --- a/src/onelogin/saml2/response.py +++ b/src/onelogin/saml2/response.py @@ -2,7 +2,7 @@ """ OneLogin_Saml2_Response class -Copyright (c) 2010-2018 OneLogin, Inc. +Copyright (c) 2010-2021 OneLogin, Inc. MIT License SAML Response class of OneLogin's Python Toolkit. diff --git a/src/onelogin/saml2/settings.py b/src/onelogin/saml2/settings.py index 09e593e7..ab3dbe37 100644 --- a/src/onelogin/saml2/settings.py +++ b/src/onelogin/saml2/settings.py @@ -2,7 +2,7 @@ """ OneLogin_Saml2_Settings class -Copyright (c) 2010-2018 OneLogin, Inc. +Copyright (c) 2010-2021 OneLogin, Inc. MIT License Setting class of OneLogin's Python Toolkit. diff --git a/src/onelogin/saml2/utils.py b/src/onelogin/saml2/utils.py index 4ca5962c..1290033e 100644 --- a/src/onelogin/saml2/utils.py +++ b/src/onelogin/saml2/utils.py @@ -2,7 +2,7 @@ """ OneLogin_Saml2_Utils class -Copyright (c) 2010-2018 OneLogin, Inc. +Copyright (c) 2010-2021 OneLogin, Inc. MIT License Auxiliary class of OneLogin's Python Toolkit. diff --git a/src/onelogin/saml2/xml_templates.py b/src/onelogin/saml2/xml_templates.py index ec4f6260..306b1afe 100644 --- a/src/onelogin/saml2/xml_templates.py +++ b/src/onelogin/saml2/xml_templates.py @@ -2,7 +2,7 @@ """ OneLogin_Saml2_Auth class -Copyright (c) 2010-2018 OneLogin, Inc. +Copyright (c) 2010-2021 OneLogin, Inc. MIT License Main class of OneLogin's Python Toolkit. diff --git a/src/onelogin/saml2/xml_utils.py b/src/onelogin/saml2/xml_utils.py index d966e615..8cbd434b 100644 --- a/src/onelogin/saml2/xml_utils.py +++ b/src/onelogin/saml2/xml_utils.py @@ -2,7 +2,7 @@ """ OneLogin_Saml2_XML class -Copyright (c) 2010-2018 OneLogin, Inc. +Copyright (c) 2010-2021 OneLogin, Inc. MIT License Auxiliary class of OneLogin's Python Toolkit. @@ -11,9 +11,9 @@ from os.path import join, dirname from lxml import etree -from defusedxml.lxml import tostring, fromstring from onelogin.saml2 import compat from onelogin.saml2.constants import OneLogin_Saml2_Constants +from onelogin.saml2.xmlparser import tostring, fromstring for prefix, url in OneLogin_Saml2_Constants.NSMAP.items(): @@ -63,9 +63,9 @@ def to_etree(xml): if isinstance(xml, OneLogin_Saml2_XML._element_class): return xml if isinstance(xml, OneLogin_Saml2_XML._bytes_class): - return OneLogin_Saml2_XML._parse_etree(xml, forbid_dtd=True) + return OneLogin_Saml2_XML._parse_etree(xml, forbid_dtd=True, forbid_entities=True) if isinstance(xml, OneLogin_Saml2_XML._text_class): - return OneLogin_Saml2_XML._parse_etree(compat.to_bytes(xml), forbid_dtd=True) + return OneLogin_Saml2_XML._parse_etree(compat.to_bytes(xml), forbid_dtd=True, forbid_entities=True) raise ValueError('unsupported type %r' % type(xml)) @@ -172,5 +172,6 @@ def extract_tag_text(xml, tagname): @staticmethod def element_text(node): + # Double check, the LXML Parser already removes comments etree.strip_tags(node, etree.Comment) return node.text diff --git a/src/onelogin/saml2/xmlparser.py b/src/onelogin/saml2/xmlparser.py new file mode 100644 index 00000000..33010ac5 --- /dev/null +++ b/src/onelogin/saml2/xmlparser.py @@ -0,0 +1,147 @@ +# -*- coding: utf-8 -*- + +# Based on the lxml example from defusedxml +# +# Copyright (c) 2013 by Christian Heimes +# Licensed to PSF under a Contributor Agreement. +# See https://www.python.org/psf/license for licensing details. +"""lxml.etree protection""" + +from __future__ import print_function, absolute_import + +import threading + +from lxml import etree as _etree + +from defusedxml.lxml import DTDForbidden, EntitiesForbidden, NotSupportedError + +LXML3 = _etree.LXML_VERSION[0] >= 3 + +__origin__ = "lxml.etree" + +tostring = _etree.tostring + + +class RestrictedElement(_etree.ElementBase): + """A restricted Element class that filters out instances of some classes + """ + + __slots__ = () + blacklist = (_etree._Entity, _etree._ProcessingInstruction, _etree._Comment) + + def _filter(self, iterator): + blacklist = self.blacklist + for child in iterator: + if isinstance(child, blacklist): + continue + yield child + + def __iter__(self): + iterator = super(RestrictedElement, self).__iter__() + return self._filter(iterator) + + def iterchildren(self, tag=None, reversed=False): + iterator = super(RestrictedElement, self).iterchildren(tag=tag, reversed=reversed) + return self._filter(iterator) + + def iter(self, tag=None, *tags): + iterator = super(RestrictedElement, self).iter(tag=tag, *tags) + return self._filter(iterator) + + def iterdescendants(self, tag=None, *tags): + iterator = super(RestrictedElement, self).iterdescendants(tag=tag, *tags) + return self._filter(iterator) + + def itersiblings(self, tag=None, preceding=False): + iterator = super(RestrictedElement, self).itersiblings(tag=tag, preceding=preceding) + return self._filter(iterator) + + def getchildren(self): + iterator = super(RestrictedElement, self).__iter__() + return list(self._filter(iterator)) + + def getiterator(self, tag=None): + iterator = super(RestrictedElement, self).getiterator(tag) + return self._filter(iterator) + + +class GlobalParserTLS(threading.local): + """Thread local context for custom parser instances + """ + + parser_config = { + "resolve_entities": False, + 'remove_comments': True, + 'no_network': True, + 'remove_pis': True, + 'huge_tree': False + } + + element_class = RestrictedElement + + def createDefaultParser(self): + parser = _etree.XMLParser(**self.parser_config) + element_class = self.element_class + if self.element_class is not None: + lookup = _etree.ElementDefaultClassLookup(element=element_class) + parser.set_element_class_lookup(lookup) + return parser + + def setDefaultParser(self, parser): + self._default_parser = parser + + def getDefaultParser(self): + parser = getattr(self, "_default_parser", None) + if parser is None: + parser = self.createDefaultParser() + self.setDefaultParser(parser) + return parser + + +_parser_tls = GlobalParserTLS() +getDefaultParser = _parser_tls.getDefaultParser + + +def check_docinfo(elementtree, forbid_dtd=False, forbid_entities=True): + """Check docinfo of an element tree for DTD and entity declarations + The check for entity declarations needs lxml 3 or newer. lxml 2.x does + not support dtd.iterentities(). + """ + docinfo = elementtree.docinfo + if docinfo.doctype: + if forbid_dtd: + raise DTDForbidden(docinfo.doctype, docinfo.system_url, docinfo.public_id) + if forbid_entities and not LXML3: + # lxml < 3 has no iterentities() + raise NotSupportedError("Unable to check for entity declarations " "in lxml 2.x") + + if forbid_entities: + for dtd in docinfo.internalDTD, docinfo.externalDTD: + if dtd is None: + continue + for entity in dtd.iterentities(): + raise EntitiesForbidden(entity.name, entity.content, None, None, None, None) + + +def parse(source, parser=None, base_url=None, forbid_dtd=True, forbid_entities=True): + if parser is None: + parser = getDefaultParser() + elementtree = _etree.parse(source, parser, base_url=base_url) + check_docinfo(elementtree, forbid_dtd, forbid_entities) + return elementtree + + +def fromstring(text, parser=None, base_url=None, forbid_dtd=True, forbid_entities=True): + if parser is None: + parser = getDefaultParser() + rootelement = _etree.fromstring(text, parser, base_url=base_url) + elementtree = rootelement.getroottree() + check_docinfo(elementtree, forbid_dtd, forbid_entities) + return rootelement + + +XML = fromstring + + +def iterparse(*args, **kwargs): + raise NotSupportedError("iterparse not available") diff --git a/tests/src/OneLogin/saml2_tests/auth_test.py b/tests/src/OneLogin/saml2_tests/auth_test.py index 2ccd3994..ea8cf1d3 100644 --- a/tests/src/OneLogin/saml2_tests/auth_test.py +++ b/tests/src/OneLogin/saml2_tests/auth_test.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2010-2018 OneLogin, Inc. +# Copyright (c) 2010-2021 OneLogin, Inc. # MIT License from base64 import b64decode, b64encode diff --git a/tests/src/OneLogin/saml2_tests/authn_request_test.py b/tests/src/OneLogin/saml2_tests/authn_request_test.py index 3f1262f2..b23c633a 100644 --- a/tests/src/OneLogin/saml2_tests/authn_request_test.py +++ b/tests/src/OneLogin/saml2_tests/authn_request_test.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2010-2018 OneLogin, Inc. +# Copyright (c) 2010-2021 OneLogin, Inc. # MIT License import json diff --git a/tests/src/OneLogin/saml2_tests/error_test.py b/tests/src/OneLogin/saml2_tests/error_test.py index cd7546b7..9a36a283 100644 --- a/tests/src/OneLogin/saml2_tests/error_test.py +++ b/tests/src/OneLogin/saml2_tests/error_test.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2010-2018 OneLogin, Inc. +# Copyright (c) 2010-2021 OneLogin, Inc. # MIT License import unittest diff --git a/tests/src/OneLogin/saml2_tests/idp_metadata_parser_test.py b/tests/src/OneLogin/saml2_tests/idp_metadata_parser_test.py index f4859244..4aa653b5 100644 --- a/tests/src/OneLogin/saml2_tests/idp_metadata_parser_test.py +++ b/tests/src/OneLogin/saml2_tests/idp_metadata_parser_test.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2010-2018 OneLogin, Inc. +# Copyright (c) 2010-2021 OneLogin, Inc. # MIT License try: diff --git a/tests/src/OneLogin/saml2_tests/logout_request_test.py b/tests/src/OneLogin/saml2_tests/logout_request_test.py index e1510c4d..c18eaee4 100644 --- a/tests/src/OneLogin/saml2_tests/logout_request_test.py +++ b/tests/src/OneLogin/saml2_tests/logout_request_test.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2010-2018 OneLogin, Inc. +# Copyright (c) 2010-2021 OneLogin, Inc. # MIT License import json diff --git a/tests/src/OneLogin/saml2_tests/logout_response_test.py b/tests/src/OneLogin/saml2_tests/logout_response_test.py index 90872f10..e6f31b0f 100644 --- a/tests/src/OneLogin/saml2_tests/logout_response_test.py +++ b/tests/src/OneLogin/saml2_tests/logout_response_test.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2010-2018 OneLogin, Inc. +# Copyright (c) 2010-2021 OneLogin, Inc. # MIT License import json diff --git a/tests/src/OneLogin/saml2_tests/metadata_test.py b/tests/src/OneLogin/saml2_tests/metadata_test.py index 58afb5b9..0a12a6d4 100644 --- a/tests/src/OneLogin/saml2_tests/metadata_test.py +++ b/tests/src/OneLogin/saml2_tests/metadata_test.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2010-2018 OneLogin, Inc. +# Copyright (c) 2010-2021 OneLogin, Inc. # MIT License import json diff --git a/tests/src/OneLogin/saml2_tests/response_test.py b/tests/src/OneLogin/saml2_tests/response_test.py index 3ace583e..5110f6e7 100644 --- a/tests/src/OneLogin/saml2_tests/response_test.py +++ b/tests/src/OneLogin/saml2_tests/response_test.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2010-2018 OneLogin, Inc. +# Copyright (c) 2010-2021 OneLogin, Inc. # MIT License from base64 import b64decode diff --git a/tests/src/OneLogin/saml2_tests/settings_test.py b/tests/src/OneLogin/saml2_tests/settings_test.py index ddf8c531..4cb271f5 100644 --- a/tests/src/OneLogin/saml2_tests/settings_test.py +++ b/tests/src/OneLogin/saml2_tests/settings_test.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2010-2018 OneLogin, Inc. +# Copyright (c) 2010-2021 OneLogin, Inc. # MIT License import json diff --git a/tests/src/OneLogin/saml2_tests/signed_response_test.py b/tests/src/OneLogin/saml2_tests/signed_response_test.py index 45afb505..b820e6c7 100644 --- a/tests/src/OneLogin/saml2_tests/signed_response_test.py +++ b/tests/src/OneLogin/saml2_tests/signed_response_test.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2010-2018 OneLogin, Inc. +# Copyright (c) 2010-2021 OneLogin, Inc. # MIT License import json diff --git a/tests/src/OneLogin/saml2_tests/utils_test.py b/tests/src/OneLogin/saml2_tests/utils_test.py index c332a5e8..87112b0c 100644 --- a/tests/src/OneLogin/saml2_tests/utils_test.py +++ b/tests/src/OneLogin/saml2_tests/utils_test.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2010-2018 OneLogin, Inc. +# Copyright (c) 2010-2021 OneLogin, Inc. # MIT License from base64 import b64decode @@ -8,13 +8,13 @@ from lxml import etree from os.path import dirname, join, exists import unittest -from defusedxml.lxml import fromstring from xml.dom.minidom import parseString from onelogin.saml2 import compat from onelogin.saml2.constants import OneLogin_Saml2_Constants from onelogin.saml2.settings import OneLogin_Saml2_Settings from onelogin.saml2.utils import OneLogin_Saml2_Utils +from onelogin.saml2.xmlparser import fromstring class OneLogin_Saml2_Utils_Test(unittest.TestCase): diff --git a/tests/src/OneLogin/saml2_tests/xml_utils_test.py b/tests/src/OneLogin/saml2_tests/xml_utils_test.py index 507575f1..c9e01a61 100644 --- a/tests/src/OneLogin/saml2_tests/xml_utils_test.py +++ b/tests/src/OneLogin/saml2_tests/xml_utils_test.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2010-2018 OneLogin, Inc. +# Copyright (c) 2010-2021 OneLogin, Inc. # MIT License import json From 3ccf9ab5a0bb2669afa30a8d6ee601f56c97fedc Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Thu, 14 Jan 2021 11:29:15 +0100 Subject: [PATCH 064/205] Release 1.10.0 --- changelog.md | 13 +++++++++++++ setup.py | 4 ++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index b5e5da52..d83c1b3b 100644 --- a/changelog.md +++ b/changelog.md @@ -1,4 +1,17 @@ # python3-saml changelog +### 1.10.0 (Jan 14, 2021) +* Added custom lxml parser based on the one defined at xmldefused. Parser will ignore comments and processing instructions and by default have deactivated huge_tree, DTD and access to external documents +* Destination URL Comparison is now case-insensitive for netloc +* Support single-label-domains as valid. New security parameter allowSingleLabelDomains +* Added get_idp_sso_url, get_idp_slo_url and get_idp_slo_response_url methods to the Settings class and use it in the toolkit +* [#212](https://github.com/onelogin/python3-saml/pull/212) Overridability enhancements. Made classes overridable by subclassing. Use of classmethods instead staticmethods +* Add get_friendlyname_attributes support +* Remove external lib method get_ext_lib_path. Add set_cert_path in order to allow set the cert path in a different folder than the toolkit +* Add sha256 instead sha1 algorithm for sign/digest as recommended value on documentation and settings +* [#178](https://github.com/onelogin/python3-saml/pull/178) Support for adding idp.crt from filesystem +* Add samlUserdata to demo-flask session +* Fix autoreloading in demo-tornado + ### 1.9.0 (Nov 20, 2019) * Allow any number of decimal places for seconds on SAML datetimes * Fix failOnAuthnContextMismatch code diff --git a/setup.py b/setup.py index 3053b82c..5efa23f4 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ setup( name='python3-saml', - version='1.9.0', + version='1.10.0', description='Onelogin Python Toolkit. Add SAML support to your Python software using this library', classifiers=[ 'Development Status :: 5 - Production/Stable', @@ -38,7 +38,7 @@ install_requires=[ 'isodate>=0.5.0', 'lxml>=3.3.5', - 'xmlsec>=0.6.0', + 'xmlsec>=1.0.5', 'defusedxml==0.6.0' ], dependency_links=['http://github.com/mehcode/python-xmlsec/tarball/master'], From 615bb73452ddd714827f74efbbb0d1a94292f989 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Thu, 14 Jan 2021 11:39:45 +0100 Subject: [PATCH 065/205] Add reference to python 3.8 and 3.9 support --- .travis.yml | 2 ++ setup.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index 43d4b210..b690a1d0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,8 @@ python: - '3.5' - '3.6' - '3.7' + - '3.8' + - '3.9' matrix: include: diff --git a/setup.py b/setup.py index 5efa23f4..f97f0189 100644 --- a/setup.py +++ b/setup.py @@ -21,6 +21,8 @@ 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', ], author='OneLogin', author_email='support@onelogin.com', From d5304e09d7cf8980fddadc2fdb164e3b7d2d305a Mon Sep 17 00:00:00 2001 From: "Volm, David" Date: Fri, 15 Jan 2021 11:04:51 -0600 Subject: [PATCH 066/205] Commented out X-Forwarded bit in `views.py` --- demo_pyramid/demo_pyramid/views.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/demo_pyramid/demo_pyramid/views.py b/demo_pyramid/demo_pyramid/views.py index a213e367..6529d67b 100644 --- a/demo_pyramid/demo_pyramid/views.py +++ b/demo_pyramid/demo_pyramid/views.py @@ -15,12 +15,13 @@ def init_saml_auth(req): def prepare_pyramid_request(request): - # If server is behind proxys or balancers use the HTTP_X_FORWARDED fields - - if 'X-Forwarded-Proto' in request.headers: - request.scheme = request.headers['X-Forwarded-Proto'] - if 'X-Forwarded-Port' in request.headers: - request.server_port = int(request.headers['X-Forwarded-Port']) + ## Uncomment this portion to set the request.scheme and request.server_port + ## based on the supplied `X-Forwarded` headers + # + # if 'X-Forwarded-Proto' in request.headers: + # request.scheme = request.headers['X-Forwarded-Proto'] + # if 'X-Forwarded-Port' in request.headers: + # request.server_port = int(request.headers['X-Forwarded-Port']) return { 'https': 'on' if request.scheme == 'https' else 'off', From 3fa8f72fe70ec53fe2996cd58425247408407b51 Mon Sep 17 00:00:00 2001 From: "Volm, David" Date: Fri, 15 Jan 2021 11:08:39 -0600 Subject: [PATCH 067/205] More description for X-Forwarded header in comments. --- demo_pyramid/demo_pyramid/views.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/demo_pyramid/demo_pyramid/views.py b/demo_pyramid/demo_pyramid/views.py index 6529d67b..93ec55eb 100644 --- a/demo_pyramid/demo_pyramid/views.py +++ b/demo_pyramid/demo_pyramid/views.py @@ -16,7 +16,8 @@ def init_saml_auth(req): def prepare_pyramid_request(request): ## Uncomment this portion to set the request.scheme and request.server_port - ## based on the supplied `X-Forwarded` headers + ## based on the supplied `X-Forwarded` headers. + ## Useful for running behind reverse proxies or balancers. # # if 'X-Forwarded-Proto' in request.headers: # request.scheme = request.headers['X-Forwarded-Proto'] From 2db92842dc65a318ddd605443b3aa75a808813c9 Mon Sep 17 00:00:00 2001 From: nltommynl Date: Tue, 26 Jan 2021 17:09:47 +0100 Subject: [PATCH 068/205] Update logout_request.py fix for incorrect slo url. --- src/onelogin/saml2/logout_request.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/onelogin/saml2/logout_request.py b/src/onelogin/saml2/logout_request.py index caf0701f..3ed3fb15 100644 --- a/src/onelogin/saml2/logout_request.py +++ b/src/onelogin/saml2/logout_request.py @@ -109,7 +109,7 @@ def __init__(self, settings, request=None, name_id=None, session_index=None, nq= { 'id': self.id, 'issue_instant': issue_instant, - 'single_logout_url': self.__settings.get_idp_slo_response_url(), + 'single_logout_url': self.__settings.get_idp_slo_url(), 'entity_id': sp_data['entityId'], 'name_id': name_id_obj, 'session_index': session_index_str, From 982560576664175be7018cda8428b6731aabe31e Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Wed, 27 Jan 2021 11:45:47 +0100 Subject: [PATCH 069/205] Release 1.10.1 --- changelog.md | 3 +++ demo_pyramid/demo_pyramid/views.py | 6 +++--- setup.py | 6 +++--- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/changelog.md b/changelog.md index d83c1b3b..4114cc04 100644 --- a/changelog.md +++ b/changelog.md @@ -1,4 +1,7 @@ # python3-saml changelog +### 1.10.1 (Jan 27, 2021) +* Fix bug on LogoutRequest class, get_idp_slo_response_url was used instead get_idp_slo_url + ### 1.10.0 (Jan 14, 2021) * Added custom lxml parser based on the one defined at xmldefused. Parser will ignore comments and processing instructions and by default have deactivated huge_tree, DTD and access to external documents * Destination URL Comparison is now case-insensitive for netloc diff --git a/demo_pyramid/demo_pyramid/views.py b/demo_pyramid/demo_pyramid/views.py index 93ec55eb..6dab4edc 100644 --- a/demo_pyramid/demo_pyramid/views.py +++ b/demo_pyramid/demo_pyramid/views.py @@ -15,9 +15,9 @@ def init_saml_auth(req): def prepare_pyramid_request(request): - ## Uncomment this portion to set the request.scheme and request.server_port - ## based on the supplied `X-Forwarded` headers. - ## Useful for running behind reverse proxies or balancers. + # Uncomment this portion to set the request.scheme and request.server_port + # based on the supplied `X-Forwarded` headers. + # Useful for running behind reverse proxies or balancers. # # if 'X-Forwarded-Proto' in request.headers: # request.scheme = request.headers['X-Forwarded-Proto'] diff --git a/setup.py b/setup.py index f97f0189..9537c8d4 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ setup( name='python3-saml', - version='1.10.0', + version='1.10.1', description='Onelogin Python Toolkit. Add SAML support to your Python software using this library', classifiers=[ 'Development Status :: 5 - Production/Stable', @@ -21,8 +21,8 @@ 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', ], author='OneLogin', author_email='support@onelogin.com', From 4b6c4b1f2ed3f6eab70ff4391e595b808ace168c Mon Sep 17 00:00:00 2001 From: cdrx Date: Wed, 27 Jan 2021 11:17:29 +0000 Subject: [PATCH 070/205] Remove the dependency on defusedxml --- README.md | 1 - setup.py | 3 +-- src/onelogin/saml2/xmlparser.py | 41 +++++++++++++++++++++++++++++++-- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index c5528a2f..6d198601 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,6 @@ Installation * [xmlsec](https://pypi.python.org/pypi/xmlsec) Python bindings for the XML Security Library. * [isodate](https://pypi.python.org/pypi/isodate) An ISO 8601 date/time/ duration parser and formatter - * [defusedxml](https://pypi.python.org/pypi/defusedxml) XML bomb protection for Python stdlib modules Review the ``setup.py`` file to know the version of the library that ``python3-saml`` is using diff --git a/setup.py b/setup.py index 9537c8d4..61ee6de2 100644 --- a/setup.py +++ b/setup.py @@ -40,8 +40,7 @@ install_requires=[ 'isodate>=0.5.0', 'lxml>=3.3.5', - 'xmlsec>=1.0.5', - 'defusedxml==0.6.0' + 'xmlsec>=1.0.5' ], dependency_links=['http://github.com/mehcode/python-xmlsec/tarball/master'], extras_require={ diff --git a/src/onelogin/saml2/xmlparser.py b/src/onelogin/saml2/xmlparser.py index 33010ac5..4bf8df3d 100644 --- a/src/onelogin/saml2/xmlparser.py +++ b/src/onelogin/saml2/xmlparser.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # Based on the lxml example from defusedxml +# DTDForbidden, EntitiesForbidden, NotSupportedError are clones of the classes defined at defusedxml # # Copyright (c) 2013 by Christian Heimes # Licensed to PSF under a Contributor Agreement. @@ -13,8 +14,6 @@ from lxml import etree as _etree -from defusedxml.lxml import DTDForbidden, EntitiesForbidden, NotSupportedError - LXML3 = _etree.LXML_VERSION[0] >= 3 __origin__ = "lxml.etree" @@ -22,6 +21,44 @@ tostring = _etree.tostring +class DTDForbidden(ValueError): + """Document type definition is forbidden + """ + + def __init__(self, name, sysid, pubid): + super(DTDForbidden, self).__init__() + self.name = name + self.sysid = sysid + self.pubid = pubid + + def __str__(self): + tpl = "DTDForbidden(name='{}', system_id={!r}, public_id={!r})" + return tpl.format(self.name, self.sysid, self.pubid) + + +class EntitiesForbidden(ValueError): + """Entity definition is forbidden + """ + + def __init__(self, name, value, base, sysid, pubid, notation_name): + super(EntitiesForbidden, self).__init__() + self.name = name + self.value = value + self.base = base + self.sysid = sysid + self.pubid = pubid + self.notation_name = notation_name + + def __str__(self): + tpl = "EntitiesForbidden(name='{}', system_id={!r}, public_id={!r})" + return tpl.format(self.name, self.sysid, self.pubid) + + +class NotSupportedError(ValueError): + """The operation is not supported + """ + + class RestrictedElement(_etree.ElementBase): """A restricted Element class that filters out instances of some classes """ From c735bfb52bb475fff3691c8cd59646ba289cbdf5 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Thu, 28 Jan 2021 01:02:23 +0100 Subject: [PATCH 071/205] Update dev dependency due lack of python support 3.8 and 3.9 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 61ee6de2..452f1b9e 100644 --- a/setup.py +++ b/setup.py @@ -46,7 +46,7 @@ extras_require={ 'test': ( 'coverage>=4.5.2', - 'freezegun==0.3.11', + 'freezegun>=0.3.11, <=1.1.0', 'pylint==1.9.4', 'flake8==3.6.0', 'coveralls==1.5.1', From 22734cd4d2c51b37b128fbfabc422aad48174563 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Thu, 28 Jan 2021 10:37:56 +0100 Subject: [PATCH 072/205] Update dev dependency due lack of python support 3.8 (flake8) --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 452f1b9e..a999c1fa 100644 --- a/setup.py +++ b/setup.py @@ -48,7 +48,7 @@ 'coverage>=4.5.2', 'freezegun>=0.3.11, <=1.1.0', 'pylint==1.9.4', - 'flake8==3.6.0', + 'flake8>=3.6.0', 'coveralls==1.5.1', ), }, From be1c1c8ae7d472518acbcfb0fcf2b48a9c5a7365 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Thu, 28 Jan 2021 13:41:51 +0100 Subject: [PATCH 073/205] Fix pep8 --- demo-django/demo/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo-django/demo/views.py b/demo-django/demo/views.py index c02d226e..418ab0c8 100644 --- a/demo-django/demo/views.py +++ b/demo-django/demo/views.py @@ -87,7 +87,7 @@ def index(request): if 'RelayState' in req['post_data'] and OneLogin_Saml2_Utils.get_self_url(req) != req['post_data']['RelayState']: return HttpResponseRedirect(auth.redirect_to(req['post_data']['RelayState'])) elif auth.get_settings().is_debug_active(): - error_reason = auth.get_last_error_reason() + error_reason = auth.get_last_error_reason() elif 'sls' in req['get_data']: request_id = None if 'LogoutRequestID' in request.session: From 2f78c44c107024b9c0e982c7d5e986533a1efd87 Mon Sep 17 00:00:00 2001 From: Alexander Schrijver Date: Wed, 13 May 2020 16:45:07 +0200 Subject: [PATCH 074/205] Add the abilitity to change the AttributeConsumingService index in the metadata file. --- src/onelogin/saml2/metadata.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/onelogin/saml2/metadata.py b/src/onelogin/saml2/metadata.py index 89e0af8f..bd839c58 100644 --- a/src/onelogin/saml2/metadata.py +++ b/src/onelogin/saml2/metadata.py @@ -162,7 +162,7 @@ def builder(cls, sp, authnsign=False, wsign=False, valid_until=None, cache_durat requested_attribute_data.append(requested_attribute) - str_attribute_consuming_service = """ + str_attribute_consuming_service = """ %(service_name)s %(attr_cs_desc)s%(requested_attribute_str)s @@ -170,6 +170,7 @@ def builder(cls, sp, authnsign=False, wsign=False, valid_until=None, cache_durat { 'service_name': sp['attributeConsumingService']['serviceName'], 'attr_cs_desc': attr_cs_desc_str, + 'attribute_consuming_service_index': sp['attributeConsumingService'].get('index', '1'), 'requested_attribute_str': '\n'.join(requested_attribute_data) } From 7ed8932d36d550f6c994a0abf6fb2abf53035501 Mon Sep 17 00:00:00 2001 From: Alexander Schrijver Date: Mon, 11 May 2020 11:16:58 +0200 Subject: [PATCH 075/205] Add the ability to set AttributeConsumingServiceIndex in the authn request. --- src/onelogin/saml2/authn_request.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/onelogin/saml2/authn_request.py b/src/onelogin/saml2/authn_request.py index 48ad9d2a..4af455d2 100644 --- a/src/onelogin/saml2/authn_request.py +++ b/src/onelogin/saml2/authn_request.py @@ -108,7 +108,7 @@ def __init__(self, settings, force_authn=False, is_passive=False, set_nameid_pol attr_consuming_service_str = '' if 'attributeConsumingService' in sp_data and sp_data['attributeConsumingService']: - attr_consuming_service_str = "\n AttributeConsumingServiceIndex=\"1\"" + attr_consuming_service_str = "\n AttributeConsumingServiceIndex=\"%s\"" % sp_data['attributeConsumingService'].get('index', '1') request = OneLogin_Saml2_Templates.AUTHN_REQUEST % \ { From 332ea51b92fe6240855a4113fa0fd37da81b77ce Mon Sep 17 00:00:00 2001 From: Alexander Schrijver Date: Fri, 29 Jan 2021 16:19:09 +0100 Subject: [PATCH 076/205] Add a comment describing the attributeConsumingService 'index' configuration option. --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index c5528a2f..1001c4b8 100644 --- a/README.md +++ b/README.md @@ -257,6 +257,11 @@ This is the ``settings.json`` file: // attributeConsumingService. nameFormat, attributeValue and // friendlyName can be ommited "attributeConsumingService": { + // OPTIONAL: only specifiy if SP requires this. + // index is an integer which identifies the attributeConsumingService used + // to the SP. OneLogin toolkit supports configuring only one attributeConsumingService + // but in certain cases the SP requires a different value. Defaults to '1'. + // "index": '1', "serviceName": "SP test", "serviceDescription": "Test Service", "requestedAttributes": [ From 7d4351811efa2968ca5048e29701e67a99d090fe Mon Sep 17 00:00:00 2001 From: Alexander Schrijver Date: Fri, 5 Feb 2021 14:22:24 +0100 Subject: [PATCH 077/205] Move storing the response data into its own method in the Auth class This makes it easier to incorporate other response methods into the code. --- src/onelogin/saml2/auth.py | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/onelogin/saml2/auth.py b/src/onelogin/saml2/auth.py index a67e2071..33de8724 100644 --- a/src/onelogin/saml2/auth.py +++ b/src/onelogin/saml2/auth.py @@ -94,6 +94,21 @@ def set_strict(self, value): assert isinstance(value, bool) self.__settings.set_strict(value) + def store_valid_response(self, response): + self.__attributes = response.get_attributes() + self.__friendlyname_attributes = response.get_friendlyname_attributes() + self.__nameid = response.get_nameid() + self.__nameid_format = response.get_nameid_format() + self.__nameid_nq = response.get_nameid_nq() + self.__nameid_spnq = response.get_nameid_spnq() + self.__session_index = response.get_session_index() + self.__session_expiration = response.get_session_not_on_or_after() + self.__last_message_id = response.get_id() + self.__last_assertion_id = response.get_assertion_id() + self.__last_authn_contexts = response.get_authn_contexts() + self.__authenticated = True + self.__last_assertion_not_on_or_after = response.get_assertion_not_on_or_after() + def process_response(self, request_id=None): """ Process the SAML Response sent by the IdP. @@ -112,20 +127,7 @@ def process_response(self, request_id=None): self.__last_response = response.get_xml_document() if response.is_valid(self.__request_data, request_id): - self.__attributes = response.get_attributes() - self.__friendlyname_attributes = response.get_friendlyname_attributes() - self.__nameid = response.get_nameid() - self.__nameid_format = response.get_nameid_format() - self.__nameid_nq = response.get_nameid_nq() - self.__nameid_spnq = response.get_nameid_spnq() - self.__session_index = response.get_session_index() - self.__session_expiration = response.get_session_not_on_or_after() - self.__last_message_id = response.get_id() - self.__last_assertion_id = response.get_assertion_id() - self.__last_authn_contexts = response.get_authn_contexts() - self.__authenticated = True - self.__last_assertion_not_on_or_after = response.get_assertion_not_on_or_after() - + self.store_valid_response(response) else: self.__errors.append('invalid_response') self.__error_reason = response.get_error() From ded055793d7a6ab7dbe0fc74b67a70a6a88bcbb7 Mon Sep 17 00:00:00 2001 From: John-Scott Atlakson <24574+jsma@users.noreply.github.com> Date: Tue, 9 Feb 2021 14:57:40 -0800 Subject: [PATCH 078/205] Update README.md Update self-sign certificate example to use ``sp.key`` since that is the hard-coded filename that must be used. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6d198601..622db674 100644 --- a/README.md +++ b/README.md @@ -162,7 +162,7 @@ publish that X.509 certificate on Service Provider metadata. If you want to create self-signed certs, you can do it at the https://www.samltool.com/self_signed_certs.php service, or using the command: ```bash -openssl req -new -x509 -days 3652 -nodes -out sp.crt -keyout saml.key +openssl req -new -x509 -days 3652 -nodes -out sp.crt -keyout sp.key ``` #### demo-flask #### From 9cc63eb2aae97e9c82d11cc2229c37c5cc39f2b1 Mon Sep 17 00:00:00 2001 From: Alexander Schrijver Date: Mon, 11 May 2020 11:18:11 +0200 Subject: [PATCH 079/205] Add the ability to change the ProtocolBinding in the authn request. --- src/onelogin/saml2/authn_request.py | 1 + src/onelogin/saml2/xml_templates.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/onelogin/saml2/authn_request.py b/src/onelogin/saml2/authn_request.py index 48ad9d2a..c0ced249 100644 --- a/src/onelogin/saml2/authn_request.py +++ b/src/onelogin/saml2/authn_request.py @@ -124,6 +124,7 @@ def __init__(self, settings, force_authn=False, is_passive=False, set_nameid_pol 'nameid_policy_str': nameid_policy_str, 'requested_authn_context_str': requested_authn_context_str, 'attr_consuming_service_str': attr_consuming_service_str, + 'acs_binding': sp_data['assertionConsumerService'].get('binding', 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST') } self.__authn_request = request diff --git a/src/onelogin/saml2/xml_templates.py b/src/onelogin/saml2/xml_templates.py index 306b1afe..5575d116 100644 --- a/src/onelogin/saml2/xml_templates.py +++ b/src/onelogin/saml2/xml_templates.py @@ -27,7 +27,7 @@ class OneLogin_Saml2_Templates(object): Version="2.0"%(provider_name)s%(force_authn_str)s%(is_passive_str)s IssueInstant="%(issue_instant)s" Destination="%(destination)s" - ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" + ProtocolBinding="%(acs_binding)s" AssertionConsumerServiceURL="%(assertion_url)s"%(attr_consuming_service_str)s> %(entity_id)s%(subject_str)s%(nameid_policy_str)s %(requested_authn_context_str)s From 88f66a858044c9818ad60368320ebd0ba677917f Mon Sep 17 00:00:00 2001 From: sheng zhang Date: Wed, 24 Feb 2021 14:45:03 -0800 Subject: [PATCH 080/205] Update README.md for issue https://github.com/onelogin/python3-saml/issues/249 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8a524de8..dc854bbb 100644 --- a/README.md +++ b/README.md @@ -689,7 +689,7 @@ if not errors: else: print('Not authenticated') else: - print("Error when processing SAML Response: %s" % (', '.join(errors))) + print("Error when processing SAML Response: %s %s" % (', '.join(errors), auth.get_last_error_reason())) ``` The SAML response is processed and then checked that there are no errors. It also verifies that the user is authenticated and stored the userdata in session. @@ -748,7 +748,7 @@ if len(errors) == 0: else: print("Sucessfully Logged out") else: - print("Error when processing SLO: %s" % (', '.join(errors))) + print("Error when processing SLO: %s %s" % (', '.join(errors), auth.get_last_error_reason())) ``` If the SLS endpoints receives a Logout Response, the response is validated and the session could be closed, using the callback. From 1fbb515ba34255a684ac29125fdc38030446746a Mon Sep 17 00:00:00 2001 From: "Mahoney, John" Date: Mon, 10 May 2021 16:12:19 -0400 Subject: [PATCH 081/205] Added timeout kwarg to metadata retrieval --- README.md | 8 +++++++- src/onelogin/saml2/idp_metadata_parser.py | 18 ++++++++++++------ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index dc854bbb..614e6e21 100644 --- a/README.md +++ b/README.md @@ -520,12 +520,18 @@ The method above requires a little extra work to manually specify attributes abo There's an easier method -- use a metadata exchange. Metadata is just an XML file that defines the capabilities of both the IdP and the SP application. It also contains the X.509 public key certificates which add to the trusted relationship. The IdP administrator can also configure custom settings for an SP based on the metadata. -Using ````parse_remote```` IdP metadata can be obtained and added to the settings withouth further ado. +Using ````parse_remote```` IdP metadata can be obtained and added to the settings without further ado. `` idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote('https://example.com/auth/saml2/idp/metadata') `` +You can specify a timeout in seconds for metadata retrieval, if not it is not guaranteed that the request will complete + +`` +idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote('https://example.com/auth/saml2/idp/metadata', timeout=5) +`` + If the Metadata contains several entities, the relevant ``EntityDescriptor`` can be specified when retrieving the settings from the ``IdpMetadataParser`` by its ``entityId`` value: ``idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote(https://example.com/metadatas, entity_id='idp_entity_id')`` diff --git a/src/onelogin/saml2/idp_metadata_parser.py b/src/onelogin/saml2/idp_metadata_parser.py index 2d7eec2c..5da7989a 100644 --- a/src/onelogin/saml2/idp_metadata_parser.py +++ b/src/onelogin/saml2/idp_metadata_parser.py @@ -26,7 +26,7 @@ class OneLogin_Saml2_IdPMetadataParser(object): """ @classmethod - def get_metadata(cls, url, validate_cert=True): + def get_metadata(cls, url, validate_cert=True, timeout=None): """ Gets the metadata XML from the provided URL :param url: Url where the XML of the Identity Provider Metadata is published. @@ -35,18 +35,21 @@ def get_metadata(cls, url, validate_cert=True): :param validate_cert: If the url uses https schema, that flag enables or not the verification of the associated certificate. :type validate_cert: bool + :param timeout: Timeout in seconds to wait for metadata response + :type timeout: int + :returns: metadata XML :rtype: string """ valid = False if validate_cert: - response = urllib2.urlopen(url) + response = urllib2.urlopen(url, timeout=timeout) else: ctx = ssl.create_default_context() ctx.check_hostname = False ctx.verify_mode = ssl.CERT_NONE - response = urllib2.urlopen(url, context=ctx) + response = urllib2.urlopen(url, context=ctx, timeout=timeout) xml = response.read() if xml: @@ -59,12 +62,12 @@ def get_metadata(cls, url, validate_cert=True): pass if not valid: - raise Exception('Not valid IdP XML found from URL: %s' % (url)) + raise Exception('Not valid IdP XML found from URL: %s' % (url,)) return xml @classmethod - def parse_remote(cls, url, validate_cert=True, entity_id=None, **kwargs): + def parse_remote(cls, url, validate_cert=True, entity_id=None, timeout=None, **kwargs): """ Gets the metadata XML from the provided URL and parse it, returning a dict with extracted data :param url: Url where the XML of the Identity Provider Metadata is published. @@ -77,10 +80,13 @@ def parse_remote(cls, url, validate_cert=True, entity_id=None, **kwargs): that contains multiple EntityDescriptor. :type entity_id: string + :param timeout: Timeout in seconds to wait for metadata response + :type timeout: int + :returns: settings dict with extracted data :rtype: dict """ - idp_metadata = cls.get_metadata(url, validate_cert) + idp_metadata = cls.get_metadata(url, validate_cert, timeout) return cls.parse(idp_metadata, entity_id=entity_id, **kwargs) @classmethod From b14e438cdb5bf51be6df34d3f686598178da1d96 Mon Sep 17 00:00:00 2001 From: "Mahoney, John" Date: Mon, 10 May 2021 16:25:20 -0400 Subject: [PATCH 082/205] Fixed readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 614e6e21..c48f2c55 100644 --- a/README.md +++ b/README.md @@ -526,7 +526,7 @@ Using ````parse_remote```` IdP metadata can be obtained and added to the setting idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote('https://example.com/auth/saml2/idp/metadata') `` -You can specify a timeout in seconds for metadata retrieval, if not it is not guaranteed that the request will complete +You can specify a timeout in seconds for metadata retrieval, without it is not guaranteed that the request will complete `` idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote('https://example.com/auth/saml2/idp/metadata', timeout=5) From 293bcde73b12b7374f120752680c05a399b76774 Mon Sep 17 00:00:00 2001 From: Eugene Toder Date: Sat, 27 Mar 2021 17:41:59 -0400 Subject: [PATCH 083/205] Add an option to use query string for validation When validating request or response signature in process_slo() we currently rebuild query string from 'get_data' elements. This requires URL encoding components of the string. Unfortunately, some IdPs (Azure AD, ADFS) use lower-case encoding. To handle this, one needs to pass lowercase_urlencoding=True. This complicates code that needs to support different IdPs. Instead, if 'query_string' is passed, take parts from it directly. This avoids the need to URL encode them. This is similar to the `retrieveParametersFromServer` argument in the PHP version. This feature is disabled by default. Pass validate_signature_from_qs=True to enable it. --- README.md | 11 ++++++----- src/onelogin/saml2/auth.py | 36 ++++++++++++++++++++++++++---------- 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index dc854bbb..bb67791d 100644 --- a/README.md +++ b/README.md @@ -562,9 +562,10 @@ req = { # Advanced request options "https": "", - "lowercase_urlencoding": "", "request_uri": "", - "query_string": "" + "query_string": "", + "validate_signature_from_qs": False, + "lowercase_urlencoding": False } ``` @@ -596,12 +597,12 @@ An explanation of some advanced request parameters: * `https` - Defaults to ``off``. Set this to ``on`` if you receive responses over HTTPS. -* `lowercase_urlencoding` - Defaults to `false`. ADFS users should set this to `true`. - -* `request_uri` - The path where your SAML server recieves requests. Set this if requests are not recieved at the server's root. +* `request_uri` - The path where your SAML server receives requests. Set this if requests are not received at the server's root. * `query_string` - Set this with additional query parameters that should be passed to the request endpoint. +* `validate_signature_from_qs` - If `True`, use `query_string` to validate request and response signatures. Otherwise, use `get_data`. Defaults to `False`. Note that when using `get_data`, query parameters need to be url-encoded for validation. By default we use upper-case url-encoding. Some IdPs, notably Microsoft AD, use lower-case url-encoding, which makes signature validation to fail. To fix this issue, either pass `query_string` and set `validate_signature_from_qs` to `True`, which works for all IdPs, or set `lowercase_urlencoding` to `True`, which only works for AD. + #### Initiate SSO #### diff --git a/src/onelogin/saml2/auth.py b/src/onelogin/saml2/auth.py index 33de8724..cfaee5fd 100644 --- a/src/onelogin/saml2/auth.py +++ b/src/onelogin/saml2/auth.py @@ -528,6 +528,22 @@ def add_response_signature(self, response_data, sign_algorithm=OneLogin_Saml2_Co """ return self.__build_signature(response_data, 'SAMLResponse', sign_algorithm) + @staticmethod + def __build_sign_query_from_qs(query_string, saml_type): + """ + Build sign query from query string + + :param query_string: The query string + :type query_string: str + + :param saml_type: The target URL the user should be redirected to + :type saml_type: string SAMLRequest | SAMLResponse + """ + args = ('%s=' % saml_type, 'RelayState=', 'SigAlg=') + parts = query_string.split('&') + # Join in the order of arguments rather than the original order of parts. + return '&'.join(part for arg in args for part in parts if part.startswith(arg)) + @staticmethod def __build_sign_query(saml_data, relay_state, algorithm, saml_type, lowercase_urlencoding=False): """ @@ -660,16 +676,16 @@ def __validate_signature(self, data, saml_type, raise_exceptions=False): if isinstance(sign_alg, bytes): sign_alg = sign_alg.decode('utf8') - lowercase_urlencoding = False - if 'lowercase_urlencoding' in self.__request_data.keys(): - lowercase_urlencoding = self.__request_data['lowercase_urlencoding'] - - signed_query = self.__build_sign_query(data[saml_type], - data.get('RelayState', None), - sign_alg, - saml_type, - lowercase_urlencoding - ) + query_string = self.__request_data.get('query_string') + if query_string and self.__request_data.get('validate_signature_from_qs'): + signed_query = self.__build_sign_query_from_qs(query_string, saml_type) + else: + lowercase_urlencoding = self.__request_data.get('lowercase_urlencoding', False) + signed_query = self.__build_sign_query(data[saml_type], + data.get('RelayState'), + sign_alg, + saml_type, + lowercase_urlencoding) if exists_multix509sign: for cert in idp_data['x509certMulti']['signing']: From 11cc4f57870a3ffe8109956285834fe47222305b Mon Sep 17 00:00:00 2001 From: "Mahoney, John" Date: Mon, 10 May 2021 23:56:49 -0400 Subject: [PATCH 084/205] Removed comma --- src/onelogin/saml2/idp_metadata_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/onelogin/saml2/idp_metadata_parser.py b/src/onelogin/saml2/idp_metadata_parser.py index 5da7989a..279d80c5 100644 --- a/src/onelogin/saml2/idp_metadata_parser.py +++ b/src/onelogin/saml2/idp_metadata_parser.py @@ -62,7 +62,7 @@ def get_metadata(cls, url, validate_cert=True, timeout=None): pass if not valid: - raise Exception('Not valid IdP XML found from URL: %s' % (url,)) + raise Exception('Not valid IdP XML found from URL: %s' % (url)) return xml From b04065170aac60d81920196c85dee843ad779d64 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Fri, 14 May 2021 04:32:59 +0200 Subject: [PATCH 085/205] Create Makefile. Create python-package.yml --- .github/workflows/python-package.yml | 35 ++++++++++++++++++++++++++++ .gitignore | 2 ++ Makefile | 35 ++++++++++++++++++++++++++++ setup.py | 3 ++- 4 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/python-package.yml create mode 100644 Makefile diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml new file mode 100644 index 00000000..ac505be2 --- /dev/null +++ b/.github/workflows/python-package.yml @@ -0,0 +1,35 @@ +# This workflow will install Python dependencies, run tests and lint with a variety of Python versions +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions + +name: Python package + +on: [push, pull_request] + +jobs: + test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: [2.7,3.5,3.6,3.7, 3.8,3.9] + + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + sudo apt-get update -qq + sudo apt-get install -qq swig python-dev libxml2-dev libxmlsec1-dev + make install-req + make install-test + + - name: Test + run: | + make pytest + make pycodestyle + make flake8 + diff --git a/.gitignore b/.gitignore index c16b943e..d449f63f 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ __pycache_ /*.eg *.egg-info /eggs +/.eggs /build /dist /venv @@ -21,6 +22,7 @@ __pycache_ .pypirc /.idea .mypy_cache/ +.pytest_cache *.key *.crt diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..7f3faddb --- /dev/null +++ b/Makefile @@ -0,0 +1,35 @@ +PIP=pip +FLAKE8=flake8 +PYTEST=pytest +PYCODESTYLE=pycodestyle +COVERAGE=coverage +COVERAGE_CONFIG=tests/coverage.rc +PEP8_CONFIG=tests/pep8.rc +MAIN_SOURCE=src/onelogin/saml2 +DEMOS=demo-django demo-flask +TESTS=tests/src/OneLogin/saml2_tests +SOURCES=$(MAIN_SOURCE) $(DEMO) $(TESTS) + +install-req: + $(PIP) install --upgrade 'setuptools<45.0.0' + $(PIP) install . + +install-test: + $(PIP) install -e ".[test]" + +pytest: + $(COVERAGE) run --source $(MAIN_SOURCE) --rcfile=$(COVERAGE_CONFIG) -m pytest + $(COVERAGE) report -m --rcfile=$(COVERAGE_CONFIG) + +pycodestyle: + $(PYCODESTYLE) --ignore=E501,E731,W504 $(SOURCES) --config=$(PEP8_CONFIG) + +flake8: + $(FLAKE8) --ignore=E501,E731,W504 $(SOURCES) + +clean: + rm -rf .pytest_cache/ + rm -rf .eggs/ + find . -type d -name "__pycache__" -exec rm -r {} + + find . -type d -name "*.egg-info" -exec rm -r {} + + rm .coverage diff --git a/setup.py b/setup.py index a999c1fa..cff7b7c7 100644 --- a/setup.py +++ b/setup.py @@ -49,7 +49,8 @@ 'freezegun>=0.3.11, <=1.1.0', 'pylint==1.9.4', 'flake8>=3.6.0', - 'coveralls==1.5.1', + 'coveralls>=1.11.1', + 'pytest>=4.6', ), }, keywords='saml saml2 xmlsec django flask pyramid python3', From 277b642da278e484b0b983eea38ee1051feff13e Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Wed, 26 May 2021 16:31:33 +0200 Subject: [PATCH 086/205] Add warning about the use of OneLogin_Saml2_IdPMetadataParser class --- README.md | 7 +++++++ src/onelogin/saml2/idp_metadata_parser.py | 3 +++ 2 files changed, 10 insertions(+) diff --git a/README.md b/README.md index 56f279a5..752fcd43 100644 --- a/README.md +++ b/README.md @@ -522,6 +522,13 @@ There's an easier method -- use a metadata exchange. Metadata is just an XML fi Using ````parse_remote```` IdP metadata can be obtained and added to the settings without further ado. +Take in mind that the OneLogin_Saml2_IdPMetadataParser class does not validate in any way the URL that is introduced in order to be parsed. + +Usually the same administrator that handles the Service Provider also sets the URL to the IdP, which should be a trusted resource. + +But there are other scenarios, like a SAAS app where the administrator of the app delegates this functionality to other users. In this case, extra precaution should be taken in order to validate such URL inputs and avoid attacks like SSRF. + + `` idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote('https://example.com/auth/saml2/idp/metadata') `` diff --git a/src/onelogin/saml2/idp_metadata_parser.py b/src/onelogin/saml2/idp_metadata_parser.py index 279d80c5..e341aebc 100644 --- a/src/onelogin/saml2/idp_metadata_parser.py +++ b/src/onelogin/saml2/idp_metadata_parser.py @@ -23,6 +23,9 @@ class OneLogin_Saml2_IdPMetadataParser(object): """ A class that contain methods related to obtaining and parsing metadata from IdP + + This class does not validate in any way the URL that is introduced, + make sure to validate it properly before use it in a get_metadata method. """ @classmethod From 5cef5089bac191a2412e66e3ac8e9cd51c63c97a Mon Sep 17 00:00:00 2001 From: "Mahoney, John" Date: Fri, 4 Jun 2021 11:13:32 -0400 Subject: [PATCH 087/205] Fixed friendlyname --- src/onelogin/saml2/response.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/onelogin/saml2/response.py b/src/onelogin/saml2/response.py index 04264919..df0f85bb 100644 --- a/src/onelogin/saml2/response.py +++ b/src/onelogin/saml2/response.py @@ -613,12 +613,6 @@ def get_friendlyname_attributes(self): for attribute_node in attribute_nodes: attr_friendlyname = attribute_node.get('FriendlyName') if attr_friendlyname: - if attr_friendlyname in attributes.keys(): - raise OneLogin_Saml2_ValidationError( - 'Found an Attribute element with duplicated FriendlyName', - OneLogin_Saml2_ValidationError.DUPLICATED_ATTRIBUTE_NAME_FOUND - ) - values = [] for attr in attribute_node.iterchildren('{%s}AttributeValue' % OneLogin_Saml2_Constants.NSMAP['saml']): attr_text = OneLogin_Saml2_XML.element_text(attr) @@ -636,7 +630,10 @@ def get_friendlyname_attributes(self): 'value': nameid.text } }) - attributes[attr_friendlyname] = values + if attr_friendlyname in attributes: + attributes[attr_friendlyname].extend(values) + else: + attributes[attr_friendlyname] = values return attributes def validate_num_assertions(self): From f88368aa11d8f9fa31b768557e3156e2679dc302 Mon Sep 17 00:00:00 2001 From: "Mahoney, John" Date: Fri, 4 Jun 2021 11:46:43 -0400 Subject: [PATCH 088/205] Added duplicate friendlyname attribute test --- .../response1_with_duplicate_friendlynames.xml.base64 | 1 + tests/src/OneLogin/saml2_tests/response_test.py | 10 ++++++++++ 2 files changed, 11 insertions(+) create mode 100644 tests/data/responses/response1_with_duplicate_friendlynames.xml.base64 diff --git a/tests/data/responses/response1_with_duplicate_friendlynames.xml.base64 b/tests/data/responses/response1_with_duplicate_friendlynames.xml.base64 new file mode 100644 index 00000000..18871d35 --- /dev/null +++ b/tests/data/responses/response1_with_duplicate_friendlynames.xml.base64 @@ -0,0 +1 @@ +PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIElEPSJHT1NBTUxSMTI5MDExNzQ1NzE3OTQiIFZlcnNpb249IjIuMCIgSXNzdWVJbnN0YW50PSIyMDEwLTExLTE4VDIxOjU3OjM3WiIgRGVzdGluYXRpb249IntyZWNpcGllbnR9Ij48c2FtbHA6U3RhdHVzPjxzYW1scDpTdGF0dXNDb2RlIFZhbHVlPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6c3RhdHVzOlN1Y2Nlc3MiLz48L3NhbWxwOlN0YXR1cz48c2FtbDpBc3NlcnRpb24geG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiBWZXJzaW9uPSIyLjAiIElEPSJwZng4ZmZiMzk4My1jYmY2LTkyYTEtZjJjNC02MTlhZTFiZTFjODYiIElzc3VlSW5zdGFudD0iMjAxMC0xMS0xOFQyMTo1NzozN1oiPjxzYW1sOklzc3Vlcj5odHRwczovL2FwcC5vbmVsb2dpbi5jb20vc2FtbC9tZXRhZGF0YS8xMzU5MDwvc2FtbDpJc3N1ZXI+PGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+PGRzOlNpZ25lZEluZm8+PGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48ZHM6U2lnbmF0dXJlTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3JzYS1zaGExIi8+PGRzOlJlZmVyZW5jZSBVUkk9IiNwZng4ZmZiMzk4My1jYmY2LTkyYTEtZjJjNC02MTlhZTFiZTFjODYiPjxkczpUcmFuc2Zvcm1zPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIvPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48L2RzOlRyYW5zZm9ybXM+PGRzOkRpZ2VzdE1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNzaGExIi8+PGRzOkRpZ2VzdFZhbHVlPmhndVFiQ0hhbmliYkRDN3EzWnp4ekhjUG9uST08L2RzOkRpZ2VzdFZhbHVlPjwvZHM6UmVmZXJlbmNlPjwvZHM6U2lnbmVkSW5mbz48ZHM6U2lnbmF0dXJlVmFsdWU+R2FuY0Q5dlJvaDlNYlQwMDJEeTc5dDFtNkk2WWZoVUtQZmJsa21wMnVkb2xYdWp2NmUxTVd2c1ZteE56dHNJR2x4QWEwcUtEaVNNekNORFpzazNqc3lzVWwxbkFLbkFnMTg1amZYanN6aHNkbVIrTTkxZHhrNmtmY0xVb3NPb2xvdmFkV0xQV3FuN1AzajgvNXh6cDlMcFJBM2d2QjQxODJSU2lyV0NCWFBRPTwvZHM6U2lnbmF0dXJlVmFsdWU+PGRzOktleUluZm8+PGRzOlg1MDlEYXRhPjxkczpYNTA5Q2VydGlmaWNhdGU+TUlJQ2dUQ0NBZW9DQ1FDYk9scldEZFg3RlRBTkJna3Foa2lHOXcwQkFRVUZBRENCaERFTE1Ba0dBMVVFQmhNQ1RrOHhHREFXQmdOVkJBZ1REMEZ1WkhKbFlYTWdVMjlzWW1WeVp6RU1NQW9HQTFVRUJ4TURSbTl2TVJBd0RnWURWUVFLRXdkVlRrbE9SVlJVTVJnd0ZnWURWUVFERXc5bVpXbGtaUzVsY214aGJtY3VibTh4SVRBZkJna3Foa2lHOXcwQkNRRVdFbUZ1WkhKbFlYTkFkVzVwYm1WMGRDNXViekFlRncwd056QTJNVFV4TWpBeE16VmFGdzB3TnpBNE1UUXhNakF4TXpWYU1JR0VNUXN3Q1FZRFZRUUdFd0pPVHpFWU1CWUdBMVVFQ0JNUFFXNWtjbVZoY3lCVGIyeGlaWEpuTVF3d0NnWURWUVFIRXdOR2IyOHhFREFPQmdOVkJBb1RCMVZPU1U1RlZGUXhHREFXQmdOVkJBTVREMlpsYVdSbExtVnliR0Z1Wnk1dWJ6RWhNQjhHQ1NxR1NJYjNEUUVKQVJZU1lXNWtjbVZoYzBCMWJtbHVaWFIwTG01dk1JR2ZNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0R05BRENCaVFLQmdRRGl2YmhSN1A1MTZ4L1MzQnFLeHVwUWUwTE9Ob2xpdXBpQk9lc0NPM1NIYkRybDMrcTlJYmZuZm1FMDRyTnVNY1BzSXhCMTYxVGREcEllc0xDbjdjOGFQSElTS090UGxBZVRaU25iOFFBdTdhUmpacTMrUGJyUDV1VzNUY2ZDR1B0S1R5dEhPZ2UvT2xKYm8wNzhkVmhYUTE0ZDFFRHdYSlcxclJYdVV0NEM4UUlEQVFBQk1BMEdDU3FHU0liM0RRRUJCUVVBQTRHQkFDRFZmcDg2SE9icVkrZThCVW9XUTkrVk1ReDFBU0RvaEJqd09zZzJXeWtVcVJYRitkTGZjVUg5ZFdSNjNDdFpJS0ZEYlN0Tm9tUG5RejduYksrb255Z3dCc3BWRWJuSHVVaWhacTNaVWRtdW1RcUN3NFV2cy8xVXZxM29yT28vV0pWaFR5dkxnRlZLMlFhclE0LzY3T1pmSGQ3UitQT0JYaG9waFNNdjFaT288L2RzOlg1MDlDZXJ0aWZpY2F0ZT48L2RzOlg1MDlEYXRhPjwvZHM6S2V5SW5mbz48L2RzOlNpZ25hdHVyZT48c2FtbDpTdWJqZWN0PjxzYW1sOk5hbWVJRCBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+c3VwcG9ydEBvbmVsb2dpbi5jb208L3NhbWw6TmFtZUlEPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uRGF0YSBOb3RPbk9yQWZ0ZXI9IjIwMTAtMTEtMThUMjI6MDI6MzdaIiBSZWNpcGllbnQ9IntyZWNpcGllbnR9Ii8+PC9zYW1sOlN1YmplY3RDb25maXJtYXRpb24+PC9zYW1sOlN1YmplY3Q+PHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTAtMTEtMThUMjE6NTI6MzdaIiBOb3RPbk9yQWZ0ZXI9IjIwMTAtMTEtMThUMjI6MDI6MzdaIj48c2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPjxzYW1sOkF1ZGllbmNlPnthdWRpZW5jZX08L3NhbWw6QXVkaWVuY2U+PC9zYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+PC9zYW1sOkNvbmRpdGlvbnM+PHNhbWw6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDEwLTExLTE4VDIxOjU3OjM3WiIgU2Vzc2lvbk5vdE9uT3JBZnRlcj0iMjAxMC0xMS0xOVQyMTo1NzozN1oiIFNlc3Npb25JbmRleD0iXzUzMWMzMmQyODNiZGZmN2UwNGU0ODdiY2RiYzRkZDhkIj48c2FtbDpBdXRobkNvbnRleHQ+PHNhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6UGFzc3dvcmQ8L3NhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+PC9zYW1sOkF1dGhuQ29udGV4dD48L3NhbWw6QXV0aG5TdGF0ZW1lbnQ+PHNhbWw6QXR0cmlidXRlU3RhdGVtZW50PjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJ1aWQiIEZyaWVuZGx5TmFtZT0idXNlcm5hbWUiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeHNpOnR5cGU9InhzOnN0cmluZyI+ZGVtbzwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJmcmllbmRseTEiIEZyaWVuZGx5TmFtZT0iZnJpZW5kbHl0ZXN0Ij48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhzaTp0eXBlPSJ4czpzdHJpbmciPmZyaWVuZGx5MTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJmcmllbmRseTIiIEZyaWVuZGx5TmFtZT0iZnJpZW5kbHl0ZXN0Ij48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhzaTp0eXBlPSJ4czpzdHJpbmciPmZyaWVuZGx5Mjwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJhbm90aGVyX3ZhbHVlIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhzaTp0eXBlPSJ4czpzdHJpbmciPnZhbHVlPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD48L3NhbWw6QXNzZXJ0aW9uPjwvc2FtbHA6UmVzcG9uc2U+ \ No newline at end of file diff --git a/tests/src/OneLogin/saml2_tests/response_test.py b/tests/src/OneLogin/saml2_tests/response_test.py index 5110f6e7..711cc5d6 100644 --- a/tests/src/OneLogin/saml2_tests/response_test.py +++ b/tests/src/OneLogin/saml2_tests/response_test.py @@ -701,6 +701,7 @@ def testGetFriendlyAttributes(self): Tests the get_friendlyname_attributes method of the OneLogin_Saml2_Response """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) + xml = self.file_contents(join(self.data_path, 'responses', 'response1.xml.base64')) response = OneLogin_Saml2_Response(settings, xml) self.assertEqual({}, response.get_friendlyname_attributes()) @@ -720,6 +721,15 @@ def testGetFriendlyAttributes(self): response_4 = OneLogin_Saml2_Response(settings, xml_4) self.assertEqual({}, response_4.get_friendlyname_attributes()) + expected_attributes = { + 'username': ['demo'], + 'friendlytest': ['friendly1', 'friendly2'] + } + xml_5 = self.file_contents(join(self.data_path, 'responses', + 'response1_with_duplicate_friendlynames.xml.base64')) + response_5 = OneLogin_Saml2_Response(settings, xml_5) + self.assertEqual(expected_attributes, response_5.get_friendlyname_attributes()) + def testGetNestedNameIDAttributes(self): """ Tests the getAttributes method of the OneLogin_Saml2_Response with nested From da3d8967cceb30e9701881e6b8d2ef51cea3423c Mon Sep 17 00:00:00 2001 From: "Mahoney, John" Date: Fri, 4 Jun 2021 11:52:19 -0400 Subject: [PATCH 089/205] Fixed PEP8 error --- src/onelogin/saml2/idp_metadata_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/onelogin/saml2/idp_metadata_parser.py b/src/onelogin/saml2/idp_metadata_parser.py index e341aebc..1b94e1a8 100644 --- a/src/onelogin/saml2/idp_metadata_parser.py +++ b/src/onelogin/saml2/idp_metadata_parser.py @@ -23,7 +23,7 @@ class OneLogin_Saml2_IdPMetadataParser(object): """ A class that contain methods related to obtaining and parsing metadata from IdP - + This class does not validate in any way the URL that is introduced, make sure to validate it properly before use it in a get_metadata method. """ From 06b443b01e89086fdcf7dc3cab42e41ee25b7b0a Mon Sep 17 00:00:00 2001 From: "Mahoney, John" Date: Mon, 7 Jun 2021 13:42:04 -0400 Subject: [PATCH 090/205] Added setting to settings file, updated tests --- src/onelogin/saml2/response.py | 50 ++++++------------- src/onelogin/saml2/settings.py | 3 ++ ...nse1_with_duplicate_attributes.xml.base64} | 2 +- tests/settings/settings11.json | 48 ++++++++++++++++++ .../src/OneLogin/saml2_tests/response_test.py | 35 +++++++++++-- 5 files changed, 99 insertions(+), 39 deletions(-) rename tests/data/responses/{response1_with_duplicate_friendlynames.xml.base64 => response1_with_duplicate_attributes.xml.base64} (89%) create mode 100644 tests/settings/settings11.json diff --git a/src/onelogin/saml2/response.py b/src/onelogin/saml2/response.py index df0f85bb..c5ca4b86 100644 --- a/src/onelogin/saml2/response.py +++ b/src/onelogin/saml2/response.py @@ -573,46 +573,28 @@ def get_attributes(self): Gets the Attributes from the AttributeStatement element. EncryptedAttributes are not supported """ - attributes = {} - attribute_nodes = self.__query_assertion('/saml:AttributeStatement/saml:Attribute') - for attribute_node in attribute_nodes: - attr_name = attribute_node.get('Name') - if attr_name in attributes.keys(): - raise OneLogin_Saml2_ValidationError( - 'Found an Attribute element with duplicated Name', - OneLogin_Saml2_ValidationError.DUPLICATED_ATTRIBUTE_NAME_FOUND - ) - - values = [] - for attr in attribute_node.iterchildren('{%s}AttributeValue' % OneLogin_Saml2_Constants.NSMAP['saml']): - attr_text = OneLogin_Saml2_XML.element_text(attr) - if attr_text: - attr_text = attr_text.strip() - if attr_text: - values.append(attr_text) - - # Parse any nested NameID children - for nameid in attr.iterchildren('{%s}NameID' % OneLogin_Saml2_Constants.NSMAP['saml']): - values.append({ - 'NameID': { - 'Format': nameid.get('Format'), - 'NameQualifier': nameid.get('NameQualifier'), - 'value': nameid.text - } - }) - attributes[attr_name] = values - return attributes + return self._get_attributes('Name') def get_friendlyname_attributes(self): """ Gets the Attributes from the AttributeStatement element indexed by FiendlyName. EncryptedAttributes are not supported """ + return self._get_attributes('FriendlyName') + + def _get_attributes(self, attr_name): + allow_duplicates = self.__settings.get_security_data().get('allowRepeatAttributeName', False) attributes = {} attribute_nodes = self.__query_assertion('/saml:AttributeStatement/saml:Attribute') for attribute_node in attribute_nodes: - attr_friendlyname = attribute_node.get('FriendlyName') - if attr_friendlyname: + attr_key = attribute_node.get(attr_name) + if attr_key: + if not allow_duplicates and attr_key in attributes: + raise OneLogin_Saml2_ValidationError( + f'Found an Attribute element with duplicated {attr_name}', + OneLogin_Saml2_ValidationError.DUPLICATED_ATTRIBUTE_NAME_FOUND + ) + values = [] for attr in attribute_node.iterchildren('{%s}AttributeValue' % OneLogin_Saml2_Constants.NSMAP['saml']): attr_text = OneLogin_Saml2_XML.element_text(attr) @@ -630,10 +612,10 @@ def get_friendlyname_attributes(self): 'value': nameid.text } }) - if attr_friendlyname in attributes: - attributes[attr_friendlyname].extend(values) + if attr_key in attributes: + attributes[attr_key].extend(values) else: - attributes[attr_friendlyname] = values + attributes[attr_key] = values return attributes def validate_num_assertions(self): diff --git a/src/onelogin/saml2/settings.py b/src/onelogin/saml2/settings.py index ab3dbe37..38e79041 100644 --- a/src/onelogin/saml2/settings.py +++ b/src/onelogin/saml2/settings.py @@ -315,6 +315,9 @@ def __add_default_values(self): # AttributeStatement required by default self.__security.setdefault('wantAttributeStatement', True) + # Disallow duplicate attribute names by default + self.__security.setdefault('allowRepeatAttributeName', False) + self.__idp.setdefault('x509cert', '') self.__idp.setdefault('certFingerprint', '') self.__idp.setdefault('certFingerprintAlgorithm', 'sha1') diff --git a/tests/data/responses/response1_with_duplicate_friendlynames.xml.base64 b/tests/data/responses/response1_with_duplicate_attributes.xml.base64 similarity index 89% rename from tests/data/responses/response1_with_duplicate_friendlynames.xml.base64 rename to tests/data/responses/response1_with_duplicate_attributes.xml.base64 index 18871d35..de113c3c 100644 --- a/tests/data/responses/response1_with_duplicate_friendlynames.xml.base64 +++ b/tests/data/responses/response1_with_duplicate_attributes.xml.base64 @@ -1 +1 @@ -PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIElEPSJHT1NBTUxSMTI5MDExNzQ1NzE3OTQiIFZlcnNpb249IjIuMCIgSXNzdWVJbnN0YW50PSIyMDEwLTExLTE4VDIxOjU3OjM3WiIgRGVzdGluYXRpb249IntyZWNpcGllbnR9Ij48c2FtbHA6U3RhdHVzPjxzYW1scDpTdGF0dXNDb2RlIFZhbHVlPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6c3RhdHVzOlN1Y2Nlc3MiLz48L3NhbWxwOlN0YXR1cz48c2FtbDpBc3NlcnRpb24geG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiBWZXJzaW9uPSIyLjAiIElEPSJwZng4ZmZiMzk4My1jYmY2LTkyYTEtZjJjNC02MTlhZTFiZTFjODYiIElzc3VlSW5zdGFudD0iMjAxMC0xMS0xOFQyMTo1NzozN1oiPjxzYW1sOklzc3Vlcj5odHRwczovL2FwcC5vbmVsb2dpbi5jb20vc2FtbC9tZXRhZGF0YS8xMzU5MDwvc2FtbDpJc3N1ZXI+PGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+PGRzOlNpZ25lZEluZm8+PGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48ZHM6U2lnbmF0dXJlTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3JzYS1zaGExIi8+PGRzOlJlZmVyZW5jZSBVUkk9IiNwZng4ZmZiMzk4My1jYmY2LTkyYTEtZjJjNC02MTlhZTFiZTFjODYiPjxkczpUcmFuc2Zvcm1zPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIvPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48L2RzOlRyYW5zZm9ybXM+PGRzOkRpZ2VzdE1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNzaGExIi8+PGRzOkRpZ2VzdFZhbHVlPmhndVFiQ0hhbmliYkRDN3EzWnp4ekhjUG9uST08L2RzOkRpZ2VzdFZhbHVlPjwvZHM6UmVmZXJlbmNlPjwvZHM6U2lnbmVkSW5mbz48ZHM6U2lnbmF0dXJlVmFsdWU+R2FuY0Q5dlJvaDlNYlQwMDJEeTc5dDFtNkk2WWZoVUtQZmJsa21wMnVkb2xYdWp2NmUxTVd2c1ZteE56dHNJR2x4QWEwcUtEaVNNekNORFpzazNqc3lzVWwxbkFLbkFnMTg1amZYanN6aHNkbVIrTTkxZHhrNmtmY0xVb3NPb2xvdmFkV0xQV3FuN1AzajgvNXh6cDlMcFJBM2d2QjQxODJSU2lyV0NCWFBRPTwvZHM6U2lnbmF0dXJlVmFsdWU+PGRzOktleUluZm8+PGRzOlg1MDlEYXRhPjxkczpYNTA5Q2VydGlmaWNhdGU+TUlJQ2dUQ0NBZW9DQ1FDYk9scldEZFg3RlRBTkJna3Foa2lHOXcwQkFRVUZBRENCaERFTE1Ba0dBMVVFQmhNQ1RrOHhHREFXQmdOVkJBZ1REMEZ1WkhKbFlYTWdVMjlzWW1WeVp6RU1NQW9HQTFVRUJ4TURSbTl2TVJBd0RnWURWUVFLRXdkVlRrbE9SVlJVTVJnd0ZnWURWUVFERXc5bVpXbGtaUzVsY214aGJtY3VibTh4SVRBZkJna3Foa2lHOXcwQkNRRVdFbUZ1WkhKbFlYTkFkVzVwYm1WMGRDNXViekFlRncwd056QTJNVFV4TWpBeE16VmFGdzB3TnpBNE1UUXhNakF4TXpWYU1JR0VNUXN3Q1FZRFZRUUdFd0pPVHpFWU1CWUdBMVVFQ0JNUFFXNWtjbVZoY3lCVGIyeGlaWEpuTVF3d0NnWURWUVFIRXdOR2IyOHhFREFPQmdOVkJBb1RCMVZPU1U1RlZGUXhHREFXQmdOVkJBTVREMlpsYVdSbExtVnliR0Z1Wnk1dWJ6RWhNQjhHQ1NxR1NJYjNEUUVKQVJZU1lXNWtjbVZoYzBCMWJtbHVaWFIwTG01dk1JR2ZNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0R05BRENCaVFLQmdRRGl2YmhSN1A1MTZ4L1MzQnFLeHVwUWUwTE9Ob2xpdXBpQk9lc0NPM1NIYkRybDMrcTlJYmZuZm1FMDRyTnVNY1BzSXhCMTYxVGREcEllc0xDbjdjOGFQSElTS090UGxBZVRaU25iOFFBdTdhUmpacTMrUGJyUDV1VzNUY2ZDR1B0S1R5dEhPZ2UvT2xKYm8wNzhkVmhYUTE0ZDFFRHdYSlcxclJYdVV0NEM4UUlEQVFBQk1BMEdDU3FHU0liM0RRRUJCUVVBQTRHQkFDRFZmcDg2SE9icVkrZThCVW9XUTkrVk1ReDFBU0RvaEJqd09zZzJXeWtVcVJYRitkTGZjVUg5ZFdSNjNDdFpJS0ZEYlN0Tm9tUG5RejduYksrb255Z3dCc3BWRWJuSHVVaWhacTNaVWRtdW1RcUN3NFV2cy8xVXZxM29yT28vV0pWaFR5dkxnRlZLMlFhclE0LzY3T1pmSGQ3UitQT0JYaG9waFNNdjFaT288L2RzOlg1MDlDZXJ0aWZpY2F0ZT48L2RzOlg1MDlEYXRhPjwvZHM6S2V5SW5mbz48L2RzOlNpZ25hdHVyZT48c2FtbDpTdWJqZWN0PjxzYW1sOk5hbWVJRCBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+c3VwcG9ydEBvbmVsb2dpbi5jb208L3NhbWw6TmFtZUlEPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uRGF0YSBOb3RPbk9yQWZ0ZXI9IjIwMTAtMTEtMThUMjI6MDI6MzdaIiBSZWNpcGllbnQ9IntyZWNpcGllbnR9Ii8+PC9zYW1sOlN1YmplY3RDb25maXJtYXRpb24+PC9zYW1sOlN1YmplY3Q+PHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTAtMTEtMThUMjE6NTI6MzdaIiBOb3RPbk9yQWZ0ZXI9IjIwMTAtMTEtMThUMjI6MDI6MzdaIj48c2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPjxzYW1sOkF1ZGllbmNlPnthdWRpZW5jZX08L3NhbWw6QXVkaWVuY2U+PC9zYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+PC9zYW1sOkNvbmRpdGlvbnM+PHNhbWw6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDEwLTExLTE4VDIxOjU3OjM3WiIgU2Vzc2lvbk5vdE9uT3JBZnRlcj0iMjAxMC0xMS0xOVQyMTo1NzozN1oiIFNlc3Npb25JbmRleD0iXzUzMWMzMmQyODNiZGZmN2UwNGU0ODdiY2RiYzRkZDhkIj48c2FtbDpBdXRobkNvbnRleHQ+PHNhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6UGFzc3dvcmQ8L3NhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+PC9zYW1sOkF1dGhuQ29udGV4dD48L3NhbWw6QXV0aG5TdGF0ZW1lbnQ+PHNhbWw6QXR0cmlidXRlU3RhdGVtZW50PjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJ1aWQiIEZyaWVuZGx5TmFtZT0idXNlcm5hbWUiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeHNpOnR5cGU9InhzOnN0cmluZyI+ZGVtbzwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJmcmllbmRseTEiIEZyaWVuZGx5TmFtZT0iZnJpZW5kbHl0ZXN0Ij48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhzaTp0eXBlPSJ4czpzdHJpbmciPmZyaWVuZGx5MTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJmcmllbmRseTIiIEZyaWVuZGx5TmFtZT0iZnJpZW5kbHl0ZXN0Ij48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhzaTp0eXBlPSJ4czpzdHJpbmciPmZyaWVuZGx5Mjwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJhbm90aGVyX3ZhbHVlIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhzaTp0eXBlPSJ4czpzdHJpbmciPnZhbHVlPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD48L3NhbWw6QXNzZXJ0aW9uPjwvc2FtbHA6UmVzcG9uc2U+ \ No newline at end of file +PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIElEPSJHT1NBTUxSMTI5MDExNzQ1NzE3OTQiIFZlcnNpb249IjIuMCIgSXNzdWVJbnN0YW50PSIyMDEwLTExLTE4VDIxOjU3OjM3WiIgRGVzdGluYXRpb249IntyZWNpcGllbnR9Ij48c2FtbHA6U3RhdHVzPjxzYW1scDpTdGF0dXNDb2RlIFZhbHVlPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6c3RhdHVzOlN1Y2Nlc3MiLz48L3NhbWxwOlN0YXR1cz48c2FtbDpBc3NlcnRpb24geG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiBWZXJzaW9uPSIyLjAiIElEPSJwZng4ZmZiMzk4My1jYmY2LTkyYTEtZjJjNC02MTlhZTFiZTFjODYiIElzc3VlSW5zdGFudD0iMjAxMC0xMS0xOFQyMTo1NzozN1oiPjxzYW1sOklzc3Vlcj5odHRwczovL2FwcC5vbmVsb2dpbi5jb20vc2FtbC9tZXRhZGF0YS8xMzU5MDwvc2FtbDpJc3N1ZXI+PGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+PGRzOlNpZ25lZEluZm8+PGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48ZHM6U2lnbmF0dXJlTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3JzYS1zaGExIi8+PGRzOlJlZmVyZW5jZSBVUkk9IiNwZng4ZmZiMzk4My1jYmY2LTkyYTEtZjJjNC02MTlhZTFiZTFjODYiPjxkczpUcmFuc2Zvcm1zPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIvPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48L2RzOlRyYW5zZm9ybXM+PGRzOkRpZ2VzdE1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNzaGExIi8+PGRzOkRpZ2VzdFZhbHVlPmhndVFiQ0hhbmliYkRDN3EzWnp4ekhjUG9uST08L2RzOkRpZ2VzdFZhbHVlPjwvZHM6UmVmZXJlbmNlPjwvZHM6U2lnbmVkSW5mbz48ZHM6U2lnbmF0dXJlVmFsdWU+R2FuY0Q5dlJvaDlNYlQwMDJEeTc5dDFtNkk2WWZoVUtQZmJsa21wMnVkb2xYdWp2NmUxTVd2c1ZteE56dHNJR2x4QWEwcUtEaVNNekNORFpzazNqc3lzVWwxbkFLbkFnMTg1amZYanN6aHNkbVIrTTkxZHhrNmtmY0xVb3NPb2xvdmFkV0xQV3FuN1AzajgvNXh6cDlMcFJBM2d2QjQxODJSU2lyV0NCWFBRPTwvZHM6U2lnbmF0dXJlVmFsdWU+PGRzOktleUluZm8+PGRzOlg1MDlEYXRhPjxkczpYNTA5Q2VydGlmaWNhdGU+TUlJQ2dUQ0NBZW9DQ1FDYk9scldEZFg3RlRBTkJna3Foa2lHOXcwQkFRVUZBRENCaERFTE1Ba0dBMVVFQmhNQ1RrOHhHREFXQmdOVkJBZ1REMEZ1WkhKbFlYTWdVMjlzWW1WeVp6RU1NQW9HQTFVRUJ4TURSbTl2TVJBd0RnWURWUVFLRXdkVlRrbE9SVlJVTVJnd0ZnWURWUVFERXc5bVpXbGtaUzVsY214aGJtY3VibTh4SVRBZkJna3Foa2lHOXcwQkNRRVdFbUZ1WkhKbFlYTkFkVzVwYm1WMGRDNXViekFlRncwd056QTJNVFV4TWpBeE16VmFGdzB3TnpBNE1UUXhNakF4TXpWYU1JR0VNUXN3Q1FZRFZRUUdFd0pPVHpFWU1CWUdBMVVFQ0JNUFFXNWtjbVZoY3lCVGIyeGlaWEpuTVF3d0NnWURWUVFIRXdOR2IyOHhFREFPQmdOVkJBb1RCMVZPU1U1RlZGUXhHREFXQmdOVkJBTVREMlpsYVdSbExtVnliR0Z1Wnk1dWJ6RWhNQjhHQ1NxR1NJYjNEUUVKQVJZU1lXNWtjbVZoYzBCMWJtbHVaWFIwTG01dk1JR2ZNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0R05BRENCaVFLQmdRRGl2YmhSN1A1MTZ4L1MzQnFLeHVwUWUwTE9Ob2xpdXBpQk9lc0NPM1NIYkRybDMrcTlJYmZuZm1FMDRyTnVNY1BzSXhCMTYxVGREcEllc0xDbjdjOGFQSElTS090UGxBZVRaU25iOFFBdTdhUmpacTMrUGJyUDV1VzNUY2ZDR1B0S1R5dEhPZ2UvT2xKYm8wNzhkVmhYUTE0ZDFFRHdYSlcxclJYdVV0NEM4UUlEQVFBQk1BMEdDU3FHU0liM0RRRUJCUVVBQTRHQkFDRFZmcDg2SE9icVkrZThCVW9XUTkrVk1ReDFBU0RvaEJqd09zZzJXeWtVcVJYRitkTGZjVUg5ZFdSNjNDdFpJS0ZEYlN0Tm9tUG5RejduYksrb255Z3dCc3BWRWJuSHVVaWhacTNaVWRtdW1RcUN3NFV2cy8xVXZxM29yT28vV0pWaFR5dkxnRlZLMlFhclE0LzY3T1pmSGQ3UitQT0JYaG9waFNNdjFaT288L2RzOlg1MDlDZXJ0aWZpY2F0ZT48L2RzOlg1MDlEYXRhPjwvZHM6S2V5SW5mbz48L2RzOlNpZ25hdHVyZT48c2FtbDpTdWJqZWN0PjxzYW1sOk5hbWVJRCBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+c3VwcG9ydEBvbmVsb2dpbi5jb208L3NhbWw6TmFtZUlEPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uRGF0YSBOb3RPbk9yQWZ0ZXI9IjIwMTAtMTEtMThUMjI6MDI6MzdaIiBSZWNpcGllbnQ9IntyZWNpcGllbnR9Ii8+PC9zYW1sOlN1YmplY3RDb25maXJtYXRpb24+PC9zYW1sOlN1YmplY3Q+PHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTAtMTEtMThUMjE6NTI6MzdaIiBOb3RPbk9yQWZ0ZXI9IjIwMTAtMTEtMThUMjI6MDI6MzdaIj48c2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPjxzYW1sOkF1ZGllbmNlPnthdWRpZW5jZX08L3NhbWw6QXVkaWVuY2U+PC9zYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+PC9zYW1sOkNvbmRpdGlvbnM+PHNhbWw6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDEwLTExLTE4VDIxOjU3OjM3WiIgU2Vzc2lvbk5vdE9uT3JBZnRlcj0iMjAxMC0xMS0xOVQyMTo1NzozN1oiIFNlc3Npb25JbmRleD0iXzUzMWMzMmQyODNiZGZmN2UwNGU0ODdiY2RiYzRkZDhkIj48c2FtbDpBdXRobkNvbnRleHQ+PHNhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6UGFzc3dvcmQ8L3NhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+PC9zYW1sOkF1dGhuQ29udGV4dD48L3NhbWw6QXV0aG5TdGF0ZW1lbnQ+PHNhbWw6QXR0cmlidXRlU3RhdGVtZW50PjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJ1aWQiIEZyaWVuZGx5TmFtZT0idXNlcm5hbWUiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeHNpOnR5cGU9InhzOnN0cmluZyI+ZGVtbzwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJmcmllbmRseTEiIEZyaWVuZGx5TmFtZT0iZnJpZW5kbHl0ZXN0Ij48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhzaTp0eXBlPSJ4czpzdHJpbmciPmZyaWVuZGx5MTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJmcmllbmRseTIiIEZyaWVuZGx5TmFtZT0iZnJpZW5kbHl0ZXN0Ij48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhzaTp0eXBlPSJ4czpzdHJpbmciPmZyaWVuZGx5Mjwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJhbm90aGVyX3ZhbHVlIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhzaTp0eXBlPSJ4czpzdHJpbmciPnZhbHVlPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9ImR1cGxpY2F0ZV9uYW1lIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhzaTp0eXBlPSJ4czpzdHJpbmciPm5hbWUxPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9ImR1cGxpY2F0ZV9uYW1lIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhzaTp0eXBlPSJ4czpzdHJpbmciPm5hbWUyPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD48L3NhbWw6QXNzZXJ0aW9uPjwvc2FtbHA6UmVzcG9uc2U+ \ No newline at end of file diff --git a/tests/settings/settings11.json b/tests/settings/settings11.json new file mode 100644 index 00000000..a039b32d --- /dev/null +++ b/tests/settings/settings11.json @@ -0,0 +1,48 @@ +{ + "strict": false, + "debug": false, + "custom_base_path": "../../../tests/data/customPath/", + "sp": { + "entityId": "http://stuff.com/endpoints/metadata.php", + "assertionConsumerService": { + "url": "http://stuff.com/endpoints/endpoints/acs.php" + }, + "singleLogoutService": { + "url": "http://stuff.com/endpoints/endpoints/sls.php" + }, + "NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" + }, + "idp": { + "entityId": "http://idp.example.com/", + "singleSignOnService": { + "url": "http://idp.example.com/SSOService.php" + }, + "singleLogoutService": { + "url": "http://idp.example.com/SingleLogoutService.php" + }, + "x509cert": "MIICgTCCAeoCCQCbOlrWDdX7FTANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMCTk8xGDAWBgNVBAgTD0FuZHJlYXMgU29sYmVyZzEMMAoGA1UEBxMDRm9vMRAwDgYDVQQKEwdVTklORVRUMRgwFgYDVQQDEw9mZWlkZS5lcmxhbmcubm8xITAfBgkqhkiG9w0BCQEWEmFuZHJlYXNAdW5pbmV0dC5ubzAeFw0wNzA2MTUxMjAxMzVaFw0wNzA4MTQxMjAxMzVaMIGEMQswCQYDVQQGEwJOTzEYMBYGA1UECBMPQW5kcmVhcyBTb2xiZXJnMQwwCgYDVQQHEwNGb28xEDAOBgNVBAoTB1VOSU5FVFQxGDAWBgNVBAMTD2ZlaWRlLmVybGFuZy5ubzEhMB8GCSqGSIb3DQEJARYSYW5kcmVhc0B1bmluZXR0Lm5vMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDivbhR7P516x/S3BqKxupQe0LONoliupiBOesCO3SHbDrl3+q9IbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHISKOtPlAeTZSnb8QAu7aRjZq3+PbrP5uW3TcfCGPtKTytHOge/OlJbo078dVhXQ14d1EDwXJW1rRXuUt4C8QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACDVfp86HObqY+e8BUoWQ9+VMQx1ASDohBjwOsg2WykUqRXF+dLfcUH9dWR63CtZIKFDbStNomPnQz7nbK+onygwBspVEbnHuUihZq3ZUdmumQqCw4Uvs/1Uvq3orOo/WJVhTyvLgFVK2QarQ4/67OZfHd7R+POBXhophSMv1ZOo" + }, + "security": { + "authnRequestsSigned": false, + "wantAssertionsSigned": false, + "signMetadata": false, + "allowRepeatAttributeName": true + }, + "contactPerson": { + "technical": { + "givenName": "technical_name", + "emailAddress": "technical@example.com" + }, + "support": { + "givenName": "support_name", + "emailAddress": "support@example.com" + } + }, + "organization": { + "en-US": { + "name": "sp_test", + "displayname": "SP test", + "url": "http://sp.example.com" + } + } +} diff --git a/tests/src/OneLogin/saml2_tests/response_test.py b/tests/src/OneLogin/saml2_tests/response_test.py index 711cc5d6..c8fee9d5 100644 --- a/tests/src/OneLogin/saml2_tests/response_test.py +++ b/tests/src/OneLogin/saml2_tests/response_test.py @@ -14,6 +14,7 @@ from xml.dom.minidom import parseString from onelogin.saml2 import compat +from onelogin.saml2.errors import OneLogin_Saml2_ValidationError from onelogin.saml2.response import OneLogin_Saml2_Response from onelogin.saml2.settings import OneLogin_Saml2_Settings from onelogin.saml2.utils import OneLogin_Saml2_Utils @@ -696,6 +697,24 @@ def testGetAttributes(self): response_3 = OneLogin_Saml2_Response(settings, xml_3) self.assertEqual({}, response_3.get_attributes()) + # Test retrieving duplicate attributes + xml_4 = self.file_contents(join(self.data_path, 'responses', + 'response1_with_duplicate_attributes.xml.base64')) + response_4 = OneLogin_Saml2_Response(settings, xml_4) + with self.assertRaises(OneLogin_Saml2_ValidationError) as duplicate_name_exc: + response_4.get_attributes() + self.assertIn('Found an Attribute element with duplicated Name', str(duplicate_name_exc.exception)) + + settings = OneLogin_Saml2_Settings(self.loadSettingsJSON('settings11.json')) + expected_attributes = {'another_value': ['value'], + 'duplicate_name': ['name1', 'name2'], + 'friendly1': ['friendly1'], + 'friendly2': ['friendly2'], + 'uid': ['demo']} + + response_5 = OneLogin_Saml2_Response(settings, xml_4) + self.assertEqual(expected_attributes, response_5.get_attributes()) + def testGetFriendlyAttributes(self): """ Tests the get_friendlyname_attributes method of the OneLogin_Saml2_Response @@ -721,14 +740,22 @@ def testGetFriendlyAttributes(self): response_4 = OneLogin_Saml2_Response(settings, xml_4) self.assertEqual({}, response_4.get_friendlyname_attributes()) + # Test retrieving duplicate attributes + xml_5 = self.file_contents(join(self.data_path, 'responses', + 'response1_with_duplicate_attributes.xml.base64')) + response_5 = OneLogin_Saml2_Response(settings, xml_5) + with self.assertRaises(OneLogin_Saml2_ValidationError) as duplicate_name_exc: + response_5.get_friendlyname_attributes() + self.assertIn('Found an Attribute element with duplicated FriendlyName', str(duplicate_name_exc.exception)) + + settings = OneLogin_Saml2_Settings(self.loadSettingsJSON('settings11.json')) expected_attributes = { 'username': ['demo'], 'friendlytest': ['friendly1', 'friendly2'] } - xml_5 = self.file_contents(join(self.data_path, 'responses', - 'response1_with_duplicate_friendlynames.xml.base64')) - response_5 = OneLogin_Saml2_Response(settings, xml_5) - self.assertEqual(expected_attributes, response_5.get_friendlyname_attributes()) + + response_6 = OneLogin_Saml2_Response(settings, xml_5) + self.assertEqual(expected_attributes, response_6.get_friendlyname_attributes()) def testGetNestedNameIDAttributes(self): """ From 50cbbf77448c20a482b60e342796fc18845a908c Mon Sep 17 00:00:00 2001 From: "Mahoney, John" Date: Mon, 7 Jun 2021 13:44:59 -0400 Subject: [PATCH 091/205] Changed to support Python2 Syntax --- src/onelogin/saml2/response.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/onelogin/saml2/response.py b/src/onelogin/saml2/response.py index c5ca4b86..d69242d3 100644 --- a/src/onelogin/saml2/response.py +++ b/src/onelogin/saml2/response.py @@ -591,7 +591,7 @@ def _get_attributes(self, attr_name): if attr_key: if not allow_duplicates and attr_key in attributes: raise OneLogin_Saml2_ValidationError( - f'Found an Attribute element with duplicated {attr_name}', + 'Found an Attribute element with duplicated ' + attr_name, OneLogin_Saml2_ValidationError.DUPLICATED_ATTRIBUTE_NAME_FOUND ) From b794a89916e8e0aaed3d1b9c6b149964a2e98320 Mon Sep 17 00:00:00 2001 From: "Mahoney, John" Date: Mon, 7 Jun 2021 17:27:41 -0400 Subject: [PATCH 092/205] Added to readme --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 752fcd43..6a237212 100644 --- a/README.md +++ b/README.md @@ -451,7 +451,11 @@ In addition to the required settings data (idp, sp), extra settings can be defin // 'http://www.w3.org/2001/04/xmlenc#sha256' // 'http://www.w3.org/2001/04/xmldsig-more#sha384' // 'http://www.w3.org/2001/04/xmlenc#sha512' - 'digestAlgorithm': "http://www.w3.org/2001/04/xmlenc#sha256" + 'digestAlgorithm': "http://www.w3.org/2001/04/xmlenc#sha256", + + // Specify if you want the SP to view assertions with duplicated Name or FriendlyName attributes to be valid + // Defaults to false if not specified + 'allowRepeatAttributeName': false }, // Contact information template, it is recommended to suply a From a6878640b408e892f53364a41494d5e494c8cb14 Mon Sep 17 00:00:00 2001 From: "Mahoney, John" Date: Tue, 15 Jun 2021 22:04:39 -0400 Subject: [PATCH 093/205] Added test for nested nameid attributes in FriendlyName attribute retrieval --- ...ponse_with_nested_nameid_values.xml.base64 | 72 +------------------ .../src/OneLogin/saml2_tests/response_test.py | 11 +++ 2 files changed, 12 insertions(+), 71 deletions(-) diff --git a/tests/data/responses/response_with_nested_nameid_values.xml.base64 b/tests/data/responses/response_with_nested_nameid_values.xml.base64 index 3092c3cf..3b99e137 100644 --- a/tests/data/responses/response_with_nested_nameid_values.xml.base64 +++ b/tests/data/responses/response_with_nested_nameid_values.xml.base64 @@ -1,71 +1 @@ -PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDph -c3NlcnRpb24iIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9j -b2wiIElEPSJHT1NBTUxSMTI5MDExNzQ1NzE3OTQiIFZlcnNpb249IjIuMCIgSXNzdWVJbnN0YW50 -PSIyMDEwLTExLTE4VDIxOjU3OjM3WiIgRGVzdGluYXRpb249IntyZWNpcGllbnR9Ij4KICA8c2Ft -bHA6U3RhdHVzPgogICAgPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0 -YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPjwvc2FtbHA6U3RhdHVzPgogIDxzYW1sOkFzc2Vy -dGlvbiB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhz -aT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIFZlcnNpb249IjIu -MCIgSUQ9InBmeGE0NjU3NGRmLWIzYjAtYTA2YS0yM2M4LTYzNjQxMzE5ODc3MiIgSXNzdWVJbnN0 -YW50PSIyMDEwLTExLTE4VDIxOjU3OjM3WiI+CiAgICA8c2FtbDpJc3N1ZXI+aHR0cHM6Ly9hcHAu -b25lbG9naW4uY29tL3NhbWwvbWV0YWRhdGEvMTM1OTA8L3NhbWw6SXNzdWVyPgogICAgPGRzOlNp -Z25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+CiAg -ICAgIDxkczpTaWduZWRJbmZvPgogICAgICAgIDxkczpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFs -Z29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+CiAgICAg -ICAgPGRzOlNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAv -MDkveG1sZHNpZyNyc2Etc2hhMSIvPgogICAgICAgIDxkczpSZWZlcmVuY2UgVVJJPSIjcGZ4YTQ2 -NTc0ZGYtYjNiMC1hMDZhLTIzYzgtNjM2NDEzMTk4NzcyIj4KICAgICAgICAgIDxkczpUcmFuc2Zv -cm1zPgogICAgICAgICAgICA8ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5v -cmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz4KICAgICAgICAgICAgPGRz -OlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1j -MTRuIyIvPgogICAgICAgICAgPC9kczpUcmFuc2Zvcm1zPgogICAgICAgICAgPGRzOkRpZ2VzdE1l -dGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNzaGExIi8+ -CiAgICAgICAgICA8ZHM6RGlnZXN0VmFsdWU+cEpRN01TL2VrNEtSUldHbXYvSDQzUmVIWU1zPTwv -ZHM6RGlnZXN0VmFsdWU+CiAgICAgICAgPC9kczpSZWZlcmVuY2U+CiAgICAgIDwvZHM6U2lnbmVk -SW5mbz4KICAgICAgPGRzOlNpZ25hdHVyZVZhbHVlPnlpdmVLY1BkRHB1RE5qNnNoclEzQUJ3ci9j -QTNDcnlEMnBoRy94TFpzektXeFU1L21sYUt0OGV3YlpPZEtLdnRPczJwSEJ5NUR1YTNrOTRBRit6 -eEd5ZWw1Z09vd21veVhKcitBT3Ira1BPMHZsaTFWOG8zaFBQVVp3UmdTWDZROXBTMUNxUWdoS2lF -YXNSeXlscXFKVWFQWXptT3pPRTgvWGxNa3dpV21PMD08L2RzOlNpZ25hdHVyZVZhbHVlPgogICAg -ICA8ZHM6S2V5SW5mbz4KICAgICAgICA8ZHM6WDUwOURhdGE+CiAgICAgICAgICA8ZHM6WDUwOUNl -cnRpZmljYXRlPk1JSUJyVENDQWFHZ0F3SUJBZ0lCQVRBREJnRUFNR2N4Q3pBSkJnTlZCQVlUQWxW -VE1STXdFUVlEVlFRSURBcERZV3hwWm05eWJtbGhNUlV3RXdZRFZRUUhEQXhUWVc1MFlTQk5iMjVw -WTJFeEVUQVBCZ05WQkFvTUNFOXVaVXh2WjJsdU1Sa3dGd1lEVlFRRERCQmhjSEF1YjI1bGJHOW5h -VzR1WTI5dE1CNFhEVEV3TURNd09UQTVOVGcwTlZvWERURTFNRE13T1RBNU5UZzBOVm93WnpFTE1B -a0dBMVVFQmhNQ1ZWTXhFekFSQmdOVkJBZ01Da05oYkdsbWIzSnVhV0V4RlRBVEJnTlZCQWNNREZO -aGJuUmhJRTF2Ym1sallURVJNQThHQTFVRUNnd0lUMjVsVEc5bmFXNHhHVEFYQmdOVkJBTU1FR0Z3 -Y0M1dmJtVnNiMmRwYmk1amIyMHdnWjh3RFFZSktvWklodmNOQVFFQkJRQURnWTBBTUlHSkFvR0JB -T2pTdTFmalB5OGQ1dzRReUwxK3pkNGhJdzFNa2tmZjRXWS9UTEc4T1prVTVZVFNXbW1IUEQ1a3ZZ -SDV1b1hTLzZxUTgxcVhwUjJ3VjhDVG93WkpVTGcwOWRkUmRSbjhRc3FqMUZ5T0M1c2xFM3kyYloy -b0Z1YTcyb2YvNDlmcHVqbkZUNktuUTYxQ0JNcWxEb1RRcU9UNjJ2R0o4blA2TVpXdkE2c3hxdWQ1 -QWdNQkFBRXdBd1lCQUFNQkFBPT08L2RzOlg1MDlDZXJ0aWZpY2F0ZT4KICAgICAgICA8L2RzOlg1 -MDlEYXRhPgogICAgICA8L2RzOktleUluZm8+CiAgICA8L2RzOlNpZ25hdHVyZT4KICAgIDxzYW1s -OlN1YmplY3Q+CiAgICAgIDxzYW1sOk5hbWVJRCBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpT -QU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+c3VwcG9ydEBvbmVsb2dpbi5jb208 -L3NhbWw6TmFtZUlEPgogICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJu -Om9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+CiAgICAgICAgPHNhbWw6U3ViamVj -dENvbmZpcm1hdGlvbkRhdGEgTm90T25PckFmdGVyPSIyMDEwLTExLTE4VDIyOjAyOjM3WiIgUmVj -aXBpZW50PSJ7cmVjaXBpZW50fSIvPjwvc2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uPgogICAgPC9z -YW1sOlN1YmplY3Q+CiAgICA8c2FtbDpDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAxMC0xMS0xOFQy -MTo1MjozN1oiIE5vdE9uT3JBZnRlcj0iMjAxMC0xMS0xOFQyMjowMjozN1oiPgogICAgICA8c2Ft -bDpBdWRpZW5jZVJlc3RyaWN0aW9uPgogICAgICAgIDxzYW1sOkF1ZGllbmNlPnthdWRpZW5jZX08 -L3NhbWw6QXVkaWVuY2U+CiAgICAgIDwvc2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPgogICAgPC9z -YW1sOkNvbmRpdGlvbnM+CiAgICA8c2FtbDpBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIw -MTAtMTEtMThUMjE6NTc6MzdaIiBTZXNzaW9uTm90T25PckFmdGVyPSIyMDEwLTExLTE5VDIxOjU3 -OjM3WiIgU2Vzc2lvbkluZGV4PSJfNTMxYzMyZDI4M2JkZmY3ZTA0ZTQ4N2JjZGJjNGRkOGQiPgog -ICAgICA8c2FtbDpBdXRobkNvbnRleHQ+CiAgICAgICAgPHNhbWw6QXV0aG5Db250ZXh0Q2xhc3NS -ZWY+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6UGFzc3dvcmQ8L3NhbWw6 -QXV0aG5Db250ZXh0Q2xhc3NSZWY+CiAgICAgIDwvc2FtbDpBdXRobkNvbnRleHQ+CiAgICA8L3Nh -bWw6QXV0aG5TdGF0ZW1lbnQ+CiAgICA8c2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+CiAgICAgIDxz -YW1sOkF0dHJpYnV0ZSBOYW1lPSJ1aWQiPgogICAgICAgIDxzYW1sOkF0dHJpYnV0ZVZhbHVlIHht -bG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgeG1sbnM6eHNpPSJodHRw -Oi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeHNpOnR5cGU9InhzOnN0cmlu -ZyI+ZGVtbzwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT4KICAgICAgPC9zYW1sOkF0dHJpYnV0ZT4KICAg -ICAgPHNhbWw6QXR0cmlidXRlIE5hbWU9ImFub3RoZXJfdmFsdWUiPgogICAgICAgIDxzYW1sOkF0 -dHJpYnV0ZVZhbHVlIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIg -eG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeHNp -OnR5cGU9InhzOnN0cmluZyI+CiAgICAgICAgICAgIDxzYW1sOk5hbWVJRCBGb3JtYXQ9InVybjpv -YXNpczpuYW1lczp0YzpTQU1MOjIuMDpuYW1laWQtZm9ybWF0OnBlcnNpc3RlbnQiIE5hbWVRdWFs -aWZpZXI9Imh0dHBzOi8vaWRwSUQiIFNQTmFtZVF1YWxpZmllcj0iaHR0cHM6Ly9zcElEIj52YWx1 -ZTwvc2FtbDpOYW1lSUQ+CiAgICAgICAgPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPgogICAgICA8L3Nh -bWw6QXR0cmlidXRlPgogICAgPC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4KICA8L3NhbWw6QXNz -ZXJ0aW9uPgo8L3NhbWxwOlJlc3BvbnNlPgo= +PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIElEPSJHT1NBTUxSMTI5MDExNzQ1NzE3OTQiIFZlcnNpb249IjIuMCIgSXNzdWVJbnN0YW50PSIyMDEwLTExLTE4VDIxOjU3OjM3WiIgRGVzdGluYXRpb249IntyZWNpcGllbnR9Ij4NCiAgPHNhbWxwOlN0YXR1cz4NCiAgICA8c2FtbHA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+PC9zYW1scDpTdGF0dXM+DQogIDxzYW1sOkFzc2VydGlvbiB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIFZlcnNpb249IjIuMCIgSUQ9InBmeGE0NjU3NGRmLWIzYjAtYTA2YS0yM2M4LTYzNjQxMzE5ODc3MiIgSXNzdWVJbnN0YW50PSIyMDEwLTExLTE4VDIxOjU3OjM3WiI+DQogICAgPHNhbWw6SXNzdWVyPmh0dHBzOi8vYXBwLm9uZWxvZ2luLmNvbS9zYW1sL21ldGFkYXRhLzEzNTkwPC9zYW1sOklzc3Vlcj4NCiAgICA8ZHM6U2lnbmF0dXJlIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj4NCiAgICAgIDxkczpTaWduZWRJbmZvPg0KICAgICAgICA8ZHM6Q2Fub25pY2FsaXphdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPg0KICAgICAgICA8ZHM6U2lnbmF0dXJlTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3JzYS1zaGExIi8+DQogICAgICAgIDxkczpSZWZlcmVuY2UgVVJJPSIjcGZ4YTQ2NTc0ZGYtYjNiMC1hMDZhLTIzYzgtNjM2NDEzMTk4NzcyIj4NCiAgICAgICAgICA8ZHM6VHJhbnNmb3Jtcz4NCiAgICAgICAgICAgIDxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIvPg0KICAgICAgICAgICAgPGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPg0KICAgICAgICAgIDwvZHM6VHJhbnNmb3Jtcz4NCiAgICAgICAgICA8ZHM6RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3NoYTEiLz4NCiAgICAgICAgICA8ZHM6RGlnZXN0VmFsdWU+cEpRN01TL2VrNEtSUldHbXYvSDQzUmVIWU1zPTwvZHM6RGlnZXN0VmFsdWU+DQogICAgICAgIDwvZHM6UmVmZXJlbmNlPg0KICAgICAgPC9kczpTaWduZWRJbmZvPg0KICAgICAgPGRzOlNpZ25hdHVyZVZhbHVlPnlpdmVLY1BkRHB1RE5qNnNoclEzQUJ3ci9jQTNDcnlEMnBoRy94TFpzektXeFU1L21sYUt0OGV3YlpPZEtLdnRPczJwSEJ5NUR1YTNrOTRBRit6eEd5ZWw1Z09vd21veVhKcitBT3Ira1BPMHZsaTFWOG8zaFBQVVp3UmdTWDZROXBTMUNxUWdoS2lFYXNSeXlscXFKVWFQWXptT3pPRTgvWGxNa3dpV21PMD08L2RzOlNpZ25hdHVyZVZhbHVlPg0KICAgICAgPGRzOktleUluZm8+DQogICAgICAgIDxkczpYNTA5RGF0YT4NCiAgICAgICAgICA8ZHM6WDUwOUNlcnRpZmljYXRlPk1JSUJyVENDQWFHZ0F3SUJBZ0lCQVRBREJnRUFNR2N4Q3pBSkJnTlZCQVlUQWxWVE1STXdFUVlEVlFRSURBcERZV3hwWm05eWJtbGhNUlV3RXdZRFZRUUhEQXhUWVc1MFlTQk5iMjVwWTJFeEVUQVBCZ05WQkFvTUNFOXVaVXh2WjJsdU1Sa3dGd1lEVlFRRERCQmhjSEF1YjI1bGJHOW5hVzR1WTI5dE1CNFhEVEV3TURNd09UQTVOVGcwTlZvWERURTFNRE13T1RBNU5UZzBOVm93WnpFTE1Ba0dBMVVFQmhNQ1ZWTXhFekFSQmdOVkJBZ01Da05oYkdsbWIzSnVhV0V4RlRBVEJnTlZCQWNNREZOaGJuUmhJRTF2Ym1sallURVJNQThHQTFVRUNnd0lUMjVsVEc5bmFXNHhHVEFYQmdOVkJBTU1FR0Z3Y0M1dmJtVnNiMmRwYmk1amIyMHdnWjh3RFFZSktvWklodmNOQVFFQkJRQURnWTBBTUlHSkFvR0JBT2pTdTFmalB5OGQ1dzRReUwxK3pkNGhJdzFNa2tmZjRXWS9UTEc4T1prVTVZVFNXbW1IUEQ1a3ZZSDV1b1hTLzZxUTgxcVhwUjJ3VjhDVG93WkpVTGcwOWRkUmRSbjhRc3FqMUZ5T0M1c2xFM3kyYloyb0Z1YTcyb2YvNDlmcHVqbkZUNktuUTYxQ0JNcWxEb1RRcU9UNjJ2R0o4blA2TVpXdkE2c3hxdWQ1QWdNQkFBRXdBd1lCQUFNQkFBPT08L2RzOlg1MDlDZXJ0aWZpY2F0ZT4NCiAgICAgICAgPC9kczpYNTA5RGF0YT4NCiAgICAgIDwvZHM6S2V5SW5mbz4NCiAgICA8L2RzOlNpZ25hdHVyZT4NCiAgICA8c2FtbDpTdWJqZWN0Pg0KICAgICAgPHNhbWw6TmFtZUlEIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4xOm5hbWVpZC1mb3JtYXQ6ZW1haWxBZGRyZXNzIj5zdXBwb3J0QG9uZWxvZ2luLmNvbTwvc2FtbDpOYW1lSUQ+DQogICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+DQogICAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjAxMC0xMS0xOFQyMjowMjozN1oiIFJlY2lwaWVudD0ie3JlY2lwaWVudH0iLz48L3NhbWw6U3ViamVjdENvbmZpcm1hdGlvbj4NCiAgICA8L3NhbWw6U3ViamVjdD4NCiAgICA8c2FtbDpDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAxMC0xMS0xOFQyMTo1MjozN1oiIE5vdE9uT3JBZnRlcj0iMjAxMC0xMS0xOFQyMjowMjozN1oiPg0KICAgICAgPHNhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj4NCiAgICAgICAgPHNhbWw6QXVkaWVuY2U+e2F1ZGllbmNlfTwvc2FtbDpBdWRpZW5jZT4NCiAgICAgIDwvc2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPg0KICAgIDwvc2FtbDpDb25kaXRpb25zPg0KICAgIDxzYW1sOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAxMC0xMS0xOFQyMTo1NzozN1oiIFNlc3Npb25Ob3RPbk9yQWZ0ZXI9IjIwMTAtMTEtMTlUMjE6NTc6MzdaIiBTZXNzaW9uSW5kZXg9Il81MzFjMzJkMjgzYmRmZjdlMDRlNDg3YmNkYmM0ZGQ4ZCI+DQogICAgICA8c2FtbDpBdXRobkNvbnRleHQ+DQogICAgICAgIDxzYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkPC9zYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPg0KICAgICAgPC9zYW1sOkF1dGhuQ29udGV4dD4NCiAgICA8L3NhbWw6QXV0aG5TdGF0ZW1lbnQ+DQogICAgPHNhbWw6QXR0cmlidXRlU3RhdGVtZW50Pg0KICAgICAgPHNhbWw6QXR0cmlidXRlIE5hbWU9InVpZCI+DQogICAgICAgIDxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeHNpOnR5cGU9InhzOnN0cmluZyI+ZGVtbzwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT4NCiAgICAgIDwvc2FtbDpBdHRyaWJ1dGU+DQogICAgICA8c2FtbDpBdHRyaWJ1dGUgTmFtZT0iYW5vdGhlcl92YWx1ZSIgRnJpZW5kbHlOYW1lPSJhbm90aGVyX2ZyaWVuZGx5X3ZhbHVlIj4NCiAgICAgICAgPHNhbWw6QXR0cmlidXRlVmFsdWUgeG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4c2k6dHlwZT0ieHM6c3RyaW5nIj4NCiAgICAgICAgICAgIDxzYW1sOk5hbWVJRCBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpuYW1laWQtZm9ybWF0OnBlcnNpc3RlbnQiIE5hbWVRdWFsaWZpZXI9Imh0dHBzOi8vaWRwSUQiIFNQTmFtZVF1YWxpZmllcj0iaHR0cHM6Ly9zcElEIj52YWx1ZTwvc2FtbDpOYW1lSUQ+DQogICAgICAgIDwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT4NCiAgICAgIDwvc2FtbDpBdHRyaWJ1dGU+DQogICAgPC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4NCiAgPC9zYW1sOkFzc2VydGlvbj4NCjwvc2FtbHA6UmVzcG9uc2U+ \ No newline at end of file diff --git a/tests/src/OneLogin/saml2_tests/response_test.py b/tests/src/OneLogin/saml2_tests/response_test.py index c8fee9d5..a51f8e60 100644 --- a/tests/src/OneLogin/saml2_tests/response_test.py +++ b/tests/src/OneLogin/saml2_tests/response_test.py @@ -777,6 +777,17 @@ def testGetNestedNameIDAttributes(self): } self.assertEqual(expected_attributes, response.get_attributes()) + expected_attributes = { + 'another_friendly_value': [{ + 'NameID': { + 'Format': 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent', + 'NameQualifier': 'https://idpID', + 'value': 'value' + } + }] + } + self.assertEqual(expected_attributes, response.get_friendlyname_attributes()) + def testOnlyRetrieveAssertionWithIDThatMatchesSignatureReference(self): """ Tests the get_nameid method of the OneLogin_Saml2_Response From 5eaad0a3c6cb06769b0d21cdf1f294aac2f364f9 Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Mon, 28 Jun 2021 13:57:37 +0200 Subject: [PATCH 094/205] Fix misleading comment with fingerprint hash weaker than a certificate verification The reasoning of a fingerprint hash weaker than providing a certificate like a CA is wrong. A X509 signature of a certificate always uses a Hash like SHA1, SHA256, etc, which is then signed. E.g. openssl1.1 x509 -text -in sp-test.pem Signature Algorithm: ecdsa-with-SHA256 So these are as vulnerable to collision attacks as fingeprints. Depending on the implementation of the fingerprint, there are other for not using them. E.g. some implementation ignore other problem with a certificate like validity or missing EKUs. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6a237212..3bbf2a1f 100644 --- a/README.md +++ b/README.md @@ -323,8 +323,8 @@ This is the ``settings.json`` file: /* * Instead of using the whole X.509cert you can use a fingerprint in order to * validate a SAMLResponse (but you still need the X.509cert to validate LogoutRequest and LogoutResponse using the HTTP-Redirect binding). - * But take in mind that the fingerprint, is a hash, so at the end is open to a collision attack that can end on a signature validation bypass, - * that why we don't recommend it use for production environments. + * But take in mind that the algortithm for the fingerprint should be as strong as the algorithm in a normal certificate signature + * (e.g. SHA256 or strong) * * (openssl x509 -noout -fingerprint -in "idp.crt" to generate it, * or add for example the -sha256 , -sha384 or -sha512 parameter) From 53ee46fe2632c5f137fa6d1df72403cbd703b9be Mon Sep 17 00:00:00 2001 From: Chris Volny Date: Wed, 30 Jun 2021 16:48:46 -0400 Subject: [PATCH 095/205] make the redirect scheme matcher case-insensitive. --- src/onelogin/saml2/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/onelogin/saml2/utils.py b/src/onelogin/saml2/utils.py index 1290033e..db88bd34 100644 --- a/src/onelogin/saml2/utils.py +++ b/src/onelogin/saml2/utils.py @@ -210,7 +210,7 @@ def redirect(url, parameters={}, request_data={}): url = '%s%s' % (OneLogin_Saml2_Utils.get_self_url_host(request_data), url) # Verify that the URL is to a http or https site. - if re.search('^https?://', url) is None: + if re.search('^https?://', url, flags=re.IGNORECASE) is None: raise OneLogin_Saml2_Error( 'Redirect to invalid URL: ' + url, OneLogin_Saml2_Error.REDIRECT_INVALID_URL From e7f6c67eee9eb25ab7746af96ab00ad0a3fd145e Mon Sep 17 00:00:00 2001 From: Zhaofeng Li Date: Sat, 3 Jul 2021 18:35:58 -0700 Subject: [PATCH 096/205] tests: Update expiry dates for responses --- tests/data/responses/invalids/encrypted_attrs.xml.base64 | 2 +- tests/data/responses/invalids/invalid_audience.xml.base64 | 2 +- .../data/responses/invalids/invalid_issuer_assertion.xml.base64 | 2 +- tests/data/responses/invalids/invalid_issuer_message.xml.base64 | 2 +- tests/data/responses/invalids/invalid_sessionindex.xml.base64 | 2 +- .../invalids/invalid_subjectconfirmation_inresponse.xml.base64 | 2 +- .../invalids/invalid_subjectconfirmation_noa.xml.base64 | 2 +- .../invalids/invalid_subjectconfirmation_recipient.xml.base64 | 2 +- tests/data/responses/invalids/no_nameid.xml.base64 | 2 +- .../responses/invalids/no_subjectconfirmation_data.xml.base64 | 2 +- .../responses/invalids/no_subjectconfirmation_method.xml.base64 | 2 +- .../data/responses/invalids/response_encrypted_attrs.xml.base64 | 2 +- tests/data/responses/no_audience.xml.base64 | 2 +- .../data/responses/unsigned_response_with_miliseconds.xm.base64 | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/data/responses/invalids/encrypted_attrs.xml.base64 b/tests/data/responses/invalids/encrypted_attrs.xml.base64 index f452075c..a170970a 100644 --- a/tests/data/responses/invalids/encrypted_attrs.xml.base64 +++ b/tests/data/responses/invalids/encrypted_attrs.xml.base64 @@ -1 +1 @@ -PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9InBmeGMzMmFlZDY3LTgyMGYtNDI5Ni0wYzIwLTIwNWExMGRkNTc4NyIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIiBEZXN0aW5hdGlvbj0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCI+DQogIDxzYW1sOklzc3Vlcj5odHRwOi8vaWRwLmV4YW1wbGUuY29tLzwvc2FtbDpJc3N1ZXI+DQogIDxzYW1scDpTdGF0dXM+DQogICAgPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPg0KICA8L3NhbWxwOlN0YXR1cz4NCiAgPHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9InBmeDc4NDE5OTFjLWM3M2YtNDAzNS1lMmVlLWMxNzBjMGUxZDNlNCIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIj4NCiAgICA8c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS88L3NhbWw6SXNzdWVyPiAgICANCiAgICA8c2FtbDpTdWJqZWN0Pg0KICAgICAgPHNhbWw6TmFtZUlEIFNQTmFtZVF1YWxpZmllcj0iaGVsbG8uY29tIiBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+c29tZW9uZUBleGFtcGxlLmNvbTwvc2FtbDpOYW1lSUQ+DQogICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+DQogICAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjAyMC0wNi0xN1QxNDo1OToxNFoiIFJlY2lwaWVudD0iaGh0dHA6Ly9zdHVmZi5jb20vZW5kcG9pbnRzL21ldGFkYXRhLnBocCIgSW5SZXNwb25zZVRvPSJfNTdiY2JmNzAtN2IxZi0wMTJlLWM4MjEtNzgyYmNiMTNiYjM4Ii8+DQogICAgICA8L3NhbWw6U3ViamVjdENvbmZpcm1hdGlvbj4NCiAgICA8L3NhbWw6U3ViamVjdD4NCiAgICA8c2FtbDpDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAxMC0wNi0xN1QxNDo1Mzo0NFoiIE5vdE9uT3JBZnRlcj0iMjAyMS0wNi0xN1QxNDo1OToxNFoiPg0KICAgICAgPHNhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj4NCiAgICAgICAgPHNhbWw6QXVkaWVuY2U+aHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvbWV0YWRhdGEucGhwPC9zYW1sOkF1ZGllbmNlPg0KICAgICAgPC9zYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+DQogICAgPC9zYW1sOkNvbmRpdGlvbnM+DQogICAgPHNhbWw6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDExLTA2LTE3VDE0OjU0OjA3WiIgU2Vzc2lvbk5vdE9uT3JBZnRlcj0iMjAyMC0wNi0xN1QyMjo1NDoxNFoiIFNlc3Npb25JbmRleD0iXzUxYmUzNzk2NWZlYjU1NzlkODAzMTQxMDc2OTM2ZGMyZTlkMWQ5OGViZiI+DQogICAgICA8c2FtbDpBdXRobkNvbnRleHQ+DQogICAgICAgIDxzYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkPC9zYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPg0KICAgICAgPC9zYW1sOkF1dGhuQ29udGV4dD4NCiAgICA8L3NhbWw6QXV0aG5TdGF0ZW1lbnQ+DQogICAgPHNhbWw6QXR0cmlidXRlU3RhdGVtZW50Pg0KIDxzYW1sOkVuY3J5cHRlZEF0dHJpYnV0ZSB4bWxuczpzYW1sPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj4NCiAgICAgICAgICA8eGVuYzpFbmNyeXB0ZWREYXRhIHhtbG5zOnhlbmM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jIyIgVHlwZT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjRWxlbWVudCIgSWQ9Il9GMzk2MjVBRjY4QjRGQzA3OENDNzU4MkQyOEQwNUQ5QyI+DQogICAgICAgICAgICA8eGVuYzpFbmNyeXB0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjYWVzMjU2LWNiYyIvPg0KICAgICAgICAgICAgPGRzOktleUluZm8geG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPg0KICAgICAgICAgICAgICA8eGVuYzpFbmNyeXB0ZWRLZXk+DQogICAgICAgICAgICAgICAgPHhlbmM6RW5jcnlwdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jI3JzYS1vYWVwLW1nZjFwIi8+DQogICAgICAgICAgICAgICAgPGRzOktleUluZm8geG1sbnM6ZHNpZz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+DQogICAgICAgICAgICAgICAgICA8ZHM6S2V5TmFtZT42MjM1NWZiZDFmNjI0NTAzYzVjOTY3NzQwMmVjY2EwMGVmMWY2Mjc3PC9kczpLZXlOYW1lPg0KICAgICAgICAgICAgICAgIDwvZHM6S2V5SW5mbz4NCiAgICAgICAgICAgICAgICA8eGVuYzpDaXBoZXJEYXRhPg0KICAgICAgICAgICAgICAgICAgPHhlbmM6Q2lwaGVyVmFsdWU+SzBtQkx4Zkx6aUtWVUtFQU9ZZTdENnVWU0NQeTh2eVdWaDNSZWNuUEVTKzhRa0FoT3VSU3VFL0xRcEZyMGh1SS9pQ0V5OXBkZTFRZ2pZREx0akhjdWpLaTJ4R3FXNmprWFcvRXVLb21xV1BQQTJ4WXMxZnBCMXN1NGFYVU9RQjZPSjcwL29EY09zeTgzNGdoRmFCV2lsRThmcXlEQlVCdlcrMkl2YU1VWmFid04vczltVmtXek0zcjMwdGxraExLN2lPcmJHQWxkSUh3RlU1ejdQUFI2Uk8zWTNmSXhqSFU0ME9uTHNKYzN4SXFkTEgzZlhwQzBrZ2k1VXNwTGRxMTRlNU9vWGpMb1BHM0JPM3p3T0FJSjhYTkJXWTV1UW9mNktyS2JjdnRaU1kwZk12UFloWWZOanRSRnk4eTQ5b3ZMOWZ3akNSVERsVDUrYUhxc0NUQnJ3PT08L3hlbmM6Q2lwaGVyVmFsdWU+DQogICAgICAgICAgICAgICAgPC94ZW5jOkNpcGhlckRhdGE+DQogICAgICAgICAgICAgIDwveGVuYzpFbmNyeXB0ZWRLZXk+DQogICAgICAgICAgICA8L2RzOktleUluZm8+DQogICAgICAgICAgICA8eGVuYzpDaXBoZXJEYXRhPg0KICAgICAgICAgICAgICA8eGVuYzpDaXBoZXJWYWx1ZT5aekN1NmF4R2dBWVpIVmY3N05YOGFwWktCL0dKRGV1VjZiRkJ5QlMwQUlnaVhrdkRVQW1MQ3BhYlRBV0JNK3l6MTlvbEE2cnJ5dU9mcjgyZXYyYnpQTlVSdm00U1l4YWh2dUw0UGlibjV3Smt5MEJsNTRWcW1jVStBcWowZEF2T2dxRzF5M1g0d085bjliUnNUdjY5MjFtMGVxUkFGcGg4a0s4TDloaXJLMUJ4WUJZajJSeUZDb0ZEUHhWWjV3eXJhM3E0cW1FNC9FTFFwRlA2bWZVOExYYjB1b1dKVWpHVWVsUzJBYTdiWmlzOHpFcHdvdjRDd3RsTmpsdFFpaDRtdjd0dENBZllxY1FJRnpCVEIrREFhMCtYZ2d4Q0xjZEIzK21RaVJjRUNCZndISEo3Z1JtbnVCRWdlV1QzQ0dLYTNOYjdHTVhPZnV4RktGNXBJZWhXZ28za2ROUUxhbG9yOFJWVzZJOFAvSThmUTMzRmUrTnNIVm5KM3p3U0EvL2E8L3hlbmM6Q2lwaGVyVmFsdWU+DQogICAgICAgICAgICA8L3hlbmM6Q2lwaGVyRGF0YT4NCiAgICAgICAgICA8L3hlbmM6RW5jcnlwdGVkRGF0YT4NCiAgICAgICAgPC9zYW1sOkVuY3J5cHRlZEF0dHJpYnV0ZT4NCiAgICA8L3NhbWw6QXR0cmlidXRlU3RhdGVtZW50Pg0KICA8L3NhbWw6QXNzZXJ0aW9uPg0KPC9zYW1scDpSZXNwb25zZT4= +PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9InBmeGMzMmFlZDY3LTgyMGYtNDI5Ni0wYzIwLTIwNWExMGRkNTc4NyIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIiBEZXN0aW5hdGlvbj0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCI+DQogIDxzYW1sOklzc3Vlcj5odHRwOi8vaWRwLmV4YW1wbGUuY29tLzwvc2FtbDpJc3N1ZXI+DQogIDxzYW1scDpTdGF0dXM+DQogICAgPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPg0KICA8L3NhbWxwOlN0YXR1cz4NCiAgPHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9InBmeDc4NDE5OTFjLWM3M2YtNDAzNS1lMmVlLWMxNzBjMGUxZDNlNCIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIj4NCiAgICA8c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS88L3NhbWw6SXNzdWVyPiAgICANCiAgICA8c2FtbDpTdWJqZWN0Pg0KICAgICAgPHNhbWw6TmFtZUlEIFNQTmFtZVF1YWxpZmllcj0iaGVsbG8uY29tIiBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+c29tZW9uZUBleGFtcGxlLmNvbTwvc2FtbDpOYW1lSUQ+DQogICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+DQogICAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjAyMC0wNi0xN1QxNDo1OToxNFoiIFJlY2lwaWVudD0iaGh0dHA6Ly9zdHVmZi5jb20vZW5kcG9pbnRzL21ldGFkYXRhLnBocCIgSW5SZXNwb25zZVRvPSJfNTdiY2JmNzAtN2IxZi0wMTJlLWM4MjEtNzgyYmNiMTNiYjM4Ii8+DQogICAgICA8L3NhbWw6U3ViamVjdENvbmZpcm1hdGlvbj4NCiAgICA8L3NhbWw6U3ViamVjdD4NCiAgICA8c2FtbDpDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAxMC0wNi0xN1QxNDo1Mzo0NFoiIE5vdE9uT3JBZnRlcj0iMjA5OS0wNi0xN1QxNDo1OToxNFoiPg0KICAgICAgPHNhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj4NCiAgICAgICAgPHNhbWw6QXVkaWVuY2U+aHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvbWV0YWRhdGEucGhwPC9zYW1sOkF1ZGllbmNlPg0KICAgICAgPC9zYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+DQogICAgPC9zYW1sOkNvbmRpdGlvbnM+DQogICAgPHNhbWw6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDExLTA2LTE3VDE0OjU0OjA3WiIgU2Vzc2lvbk5vdE9uT3JBZnRlcj0iMjAyMC0wNi0xN1QyMjo1NDoxNFoiIFNlc3Npb25JbmRleD0iXzUxYmUzNzk2NWZlYjU1NzlkODAzMTQxMDc2OTM2ZGMyZTlkMWQ5OGViZiI+DQogICAgICA8c2FtbDpBdXRobkNvbnRleHQ+DQogICAgICAgIDxzYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkPC9zYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPg0KICAgICAgPC9zYW1sOkF1dGhuQ29udGV4dD4NCiAgICA8L3NhbWw6QXV0aG5TdGF0ZW1lbnQ+DQogICAgPHNhbWw6QXR0cmlidXRlU3RhdGVtZW50Pg0KIDxzYW1sOkVuY3J5cHRlZEF0dHJpYnV0ZSB4bWxuczpzYW1sPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj4NCiAgICAgICAgICA8eGVuYzpFbmNyeXB0ZWREYXRhIHhtbG5zOnhlbmM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jIyIgVHlwZT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjRWxlbWVudCIgSWQ9Il9GMzk2MjVBRjY4QjRGQzA3OENDNzU4MkQyOEQwNUQ5QyI+DQogICAgICAgICAgICA8eGVuYzpFbmNyeXB0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjYWVzMjU2LWNiYyIvPg0KICAgICAgICAgICAgPGRzOktleUluZm8geG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPg0KICAgICAgICAgICAgICA8eGVuYzpFbmNyeXB0ZWRLZXk+DQogICAgICAgICAgICAgICAgPHhlbmM6RW5jcnlwdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jI3JzYS1vYWVwLW1nZjFwIi8+DQogICAgICAgICAgICAgICAgPGRzOktleUluZm8geG1sbnM6ZHNpZz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+DQogICAgICAgICAgICAgICAgICA8ZHM6S2V5TmFtZT42MjM1NWZiZDFmNjI0NTAzYzVjOTY3NzQwMmVjY2EwMGVmMWY2Mjc3PC9kczpLZXlOYW1lPg0KICAgICAgICAgICAgICAgIDwvZHM6S2V5SW5mbz4NCiAgICAgICAgICAgICAgICA8eGVuYzpDaXBoZXJEYXRhPg0KICAgICAgICAgICAgICAgICAgPHhlbmM6Q2lwaGVyVmFsdWU+SzBtQkx4Zkx6aUtWVUtFQU9ZZTdENnVWU0NQeTh2eVdWaDNSZWNuUEVTKzhRa0FoT3VSU3VFL0xRcEZyMGh1SS9pQ0V5OXBkZTFRZ2pZREx0akhjdWpLaTJ4R3FXNmprWFcvRXVLb21xV1BQQTJ4WXMxZnBCMXN1NGFYVU9RQjZPSjcwL29EY09zeTgzNGdoRmFCV2lsRThmcXlEQlVCdlcrMkl2YU1VWmFid04vczltVmtXek0zcjMwdGxraExLN2lPcmJHQWxkSUh3RlU1ejdQUFI2Uk8zWTNmSXhqSFU0ME9uTHNKYzN4SXFkTEgzZlhwQzBrZ2k1VXNwTGRxMTRlNU9vWGpMb1BHM0JPM3p3T0FJSjhYTkJXWTV1UW9mNktyS2JjdnRaU1kwZk12UFloWWZOanRSRnk4eTQ5b3ZMOWZ3akNSVERsVDUrYUhxc0NUQnJ3PT08L3hlbmM6Q2lwaGVyVmFsdWU+DQogICAgICAgICAgICAgICAgPC94ZW5jOkNpcGhlckRhdGE+DQogICAgICAgICAgICAgIDwveGVuYzpFbmNyeXB0ZWRLZXk+DQogICAgICAgICAgICA8L2RzOktleUluZm8+DQogICAgICAgICAgICA8eGVuYzpDaXBoZXJEYXRhPg0KICAgICAgICAgICAgICA8eGVuYzpDaXBoZXJWYWx1ZT5aekN1NmF4R2dBWVpIVmY3N05YOGFwWktCL0dKRGV1VjZiRkJ5QlMwQUlnaVhrdkRVQW1MQ3BhYlRBV0JNK3l6MTlvbEE2cnJ5dU9mcjgyZXYyYnpQTlVSdm00U1l4YWh2dUw0UGlibjV3Smt5MEJsNTRWcW1jVStBcWowZEF2T2dxRzF5M1g0d085bjliUnNUdjY5MjFtMGVxUkFGcGg4a0s4TDloaXJLMUJ4WUJZajJSeUZDb0ZEUHhWWjV3eXJhM3E0cW1FNC9FTFFwRlA2bWZVOExYYjB1b1dKVWpHVWVsUzJBYTdiWmlzOHpFcHdvdjRDd3RsTmpsdFFpaDRtdjd0dENBZllxY1FJRnpCVEIrREFhMCtYZ2d4Q0xjZEIzK21RaVJjRUNCZndISEo3Z1JtbnVCRWdlV1QzQ0dLYTNOYjdHTVhPZnV4RktGNXBJZWhXZ28za2ROUUxhbG9yOFJWVzZJOFAvSThmUTMzRmUrTnNIVm5KM3p3U0EvL2E8L3hlbmM6Q2lwaGVyVmFsdWU+DQogICAgICAgICAgICA8L3hlbmM6Q2lwaGVyRGF0YT4NCiAgICAgICAgICA8L3hlbmM6RW5jcnlwdGVkRGF0YT4NCiAgICAgICAgPC9zYW1sOkVuY3J5cHRlZEF0dHJpYnV0ZT4NCiAgICA8L3NhbWw6QXR0cmlidXRlU3RhdGVtZW50Pg0KICA8L3NhbWw6QXNzZXJ0aW9uPg0KPC9zYW1scDpSZXNwb25zZT4= \ No newline at end of file diff --git a/tests/data/responses/invalids/invalid_audience.xml.base64 b/tests/data/responses/invalids/invalid_audience.xml.base64 index a2575836..787c8a79 100644 --- a/tests/data/responses/invalids/invalid_audience.xml.base64 +++ b/tests/data/responses/invalids/invalid_audience.xml.base64 @@ -1 +1 @@ -PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9InBmeGMzMmFlZDY3LTgyMGYtNDI5Ni0wYzIwLTIwNWExMGRkNTc4NyIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIiBEZXN0aW5hdGlvbj0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCI+DQogIDxzYW1sOklzc3Vlcj5odHRwOi8vaWRwLmV4YW1wbGUuY29tLzwvc2FtbDpJc3N1ZXI+DQogIDxzYW1scDpTdGF0dXM+DQogICAgPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPg0KICA8L3NhbWxwOlN0YXR1cz4NCiAgPHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9InBmeDc4NDE5OTFjLWM3M2YtNDAzNS1lMmVlLWMxNzBjMGUxZDNlNCIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIj4NCiAgICA8c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS88L3NhbWw6SXNzdWVyPiAgICANCiAgICA8c2FtbDpTdWJqZWN0Pg0KICAgICAgPHNhbWw6TmFtZUlEIFNQTmFtZVF1YWxpZmllcj0iaGVsbG8uY29tIiBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+c29tZW9uZUBleGFtcGxlLmNvbTwvc2FtbDpOYW1lSUQ+DQogICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+DQogICAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjAyMC0wNi0xN1QxNDo1OToxNFoiIFJlY2lwaWVudD0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCIvPg0KICAgICAgPC9zYW1sOlN1YmplY3RDb25maXJtYXRpb24+DQogICAgPC9zYW1sOlN1YmplY3Q+DQogICAgPHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTAtMDYtMTdUMTQ6NTM6NDRaIiBOb3RPbk9yQWZ0ZXI9IjIwMjEtMDYtMTdUMTQ6NTk6MTRaIj4NCiAgICAgIDxzYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+DQogICAgICAgIDxzYW1sOkF1ZGllbmNlPmh0dHA6Ly9pbnZhbGlkLmF1ZGllbmNlLmNvbTwvc2FtbDpBdWRpZW5jZT4NCiAgICAgIDwvc2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPg0KICAgIDwvc2FtbDpDb25kaXRpb25zPg0KICAgIDxzYW1sOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAxMS0wNi0xN1QxNDo1NDowN1oiIFNlc3Npb25Ob3RPbk9yQWZ0ZXI9IjIwMjEtMDYtMTdUMjI6NTQ6MTRaIiBTZXNzaW9uSW5kZXg9Il81MWJlMzc5NjVmZWI1NTc5ZDgwMzE0MTA3NjkzNmRjMmU5ZDFkOThlYmYiPg0KICAgICAgPHNhbWw6QXV0aG5Db250ZXh0Pg0KICAgICAgICA8c2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZDwvc2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj4NCiAgICAgIDwvc2FtbDpBdXRobkNvbnRleHQ+DQogICAgPC9zYW1sOkF1dGhuU3RhdGVtZW50Pg0KICAgIDxzYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4NCiAgICAgIDxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJtYWlsIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj4NCiAgICAgICAgPHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+c29tZW9uZUBleGFtcGxlLmNvbTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT4NCiAgICAgIDwvc2FtbDpBdHRyaWJ1dGU+DQogICAgPC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4NCiAgPC9zYW1sOkFzc2VydGlvbj4NCjwvc2FtbHA6UmVzcG9uc2U+ +PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9InBmeGMzMmFlZDY3LTgyMGYtNDI5Ni0wYzIwLTIwNWExMGRkNTc4NyIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIiBEZXN0aW5hdGlvbj0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCI+DQogIDxzYW1sOklzc3Vlcj5odHRwOi8vaWRwLmV4YW1wbGUuY29tLzwvc2FtbDpJc3N1ZXI+DQogIDxzYW1scDpTdGF0dXM+DQogICAgPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPg0KICA8L3NhbWxwOlN0YXR1cz4NCiAgPHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9InBmeDc4NDE5OTFjLWM3M2YtNDAzNS1lMmVlLWMxNzBjMGUxZDNlNCIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIj4NCiAgICA8c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS88L3NhbWw6SXNzdWVyPiAgICANCiAgICA8c2FtbDpTdWJqZWN0Pg0KICAgICAgPHNhbWw6TmFtZUlEIFNQTmFtZVF1YWxpZmllcj0iaGVsbG8uY29tIiBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+c29tZW9uZUBleGFtcGxlLmNvbTwvc2FtbDpOYW1lSUQ+DQogICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+DQogICAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjAyMC0wNi0xN1QxNDo1OToxNFoiIFJlY2lwaWVudD0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCIvPg0KICAgICAgPC9zYW1sOlN1YmplY3RDb25maXJtYXRpb24+DQogICAgPC9zYW1sOlN1YmplY3Q+DQogICAgPHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTAtMDYtMTdUMTQ6NTM6NDRaIiBOb3RPbk9yQWZ0ZXI9IjIwOTktMDYtMTdUMTQ6NTk6MTRaIj4NCiAgICAgIDxzYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+DQogICAgICAgIDxzYW1sOkF1ZGllbmNlPmh0dHA6Ly9pbnZhbGlkLmF1ZGllbmNlLmNvbTwvc2FtbDpBdWRpZW5jZT4NCiAgICAgIDwvc2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPg0KICAgIDwvc2FtbDpDb25kaXRpb25zPg0KICAgIDxzYW1sOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAxMS0wNi0xN1QxNDo1NDowN1oiIFNlc3Npb25Ob3RPbk9yQWZ0ZXI9IjIwOTktMDYtMTdUMjI6NTQ6MTRaIiBTZXNzaW9uSW5kZXg9Il81MWJlMzc5NjVmZWI1NTc5ZDgwMzE0MTA3NjkzNmRjMmU5ZDFkOThlYmYiPg0KICAgICAgPHNhbWw6QXV0aG5Db250ZXh0Pg0KICAgICAgICA8c2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZDwvc2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj4NCiAgICAgIDwvc2FtbDpBdXRobkNvbnRleHQ+DQogICAgPC9zYW1sOkF1dGhuU3RhdGVtZW50Pg0KICAgIDxzYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4NCiAgICAgIDxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJtYWlsIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj4NCiAgICAgICAgPHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+c29tZW9uZUBleGFtcGxlLmNvbTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT4NCiAgICAgIDwvc2FtbDpBdHRyaWJ1dGU+DQogICAgPC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4NCiAgPC9zYW1sOkFzc2VydGlvbj4NCjwvc2FtbHA6UmVzcG9uc2U+ \ No newline at end of file diff --git a/tests/data/responses/invalids/invalid_issuer_assertion.xml.base64 b/tests/data/responses/invalids/invalid_issuer_assertion.xml.base64 index 07748ece..426ea5c2 100644 --- a/tests/data/responses/invalids/invalid_issuer_assertion.xml.base64 +++ b/tests/data/responses/invalids/invalid_issuer_assertion.xml.base64 @@ -1 +1 @@ -PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9InBmeGMzMmFlZDY3LTgyMGYtNDI5Ni0wYzIwLTIwNWExMGRkNTc4NyIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIiBEZXN0aW5hdGlvbj0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCI+DQogIDxzYW1sOklzc3Vlcj5odHRwOi8vaWRwLmV4YW1wbGUuY29tLzwvc2FtbDpJc3N1ZXI+DQogIDxzYW1scDpTdGF0dXM+DQogICAgPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPg0KICA8L3NhbWxwOlN0YXR1cz4NCiAgPHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9InBmeDc4NDE5OTFjLWM3M2YtNDAzNS1lMmVlLWMxNzBjMGUxZDNlNCIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIj4NCiAgICA8c2FtbDpJc3N1ZXI+aHR0cDovL2ludmFsaWQuaXNzdWVyLmV4YW1wbGUuY29tLzwvc2FtbDpJc3N1ZXI+ICAgIA0KICAgIDxzYW1sOlN1YmplY3Q+DQogICAgICA8c2FtbDpOYW1lSUQgU1BOYW1lUXVhbGlmaWVyPSJoZWxsby5jb20iIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4xOm5hbWVpZC1mb3JtYXQ6ZW1haWxBZGRyZXNzIj5zb21lb25lQGV4YW1wbGUuY29tPC9zYW1sOk5hbWVJRD4NCiAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj4NCiAgICAgICAgPHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgTm90T25PckFmdGVyPSIyMDIwLTA2LTE3VDE0OjU5OjE0WiIgUmVjaXBpZW50PSJodHRwOi8vc3R1ZmYuY29tL2VuZHBvaW50cy9lbmRwb2ludHMvYWNzLnBocCIgSW5SZXNwb25zZVRvPSJfNTdiY2JmNzAtN2IxZi0wMTJlLWM4MjEtNzgyYmNiMTNiYjM4Ii8+DQogICAgICA8L3NhbWw6U3ViamVjdENvbmZpcm1hdGlvbj4NCiAgICA8L3NhbWw6U3ViamVjdD4NCiAgICA8c2FtbDpDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAxMC0wNi0xN1QxNDo1Mzo0NFoiIE5vdE9uT3JBZnRlcj0iMjAyMS0wNi0xN1QxNDo1OToxNFoiPg0KICAgICAgPHNhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj4NCiAgICAgICAgPHNhbWw6QXVkaWVuY2U+aHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvbWV0YWRhdGEucGhwPC9zYW1sOkF1ZGllbmNlPg0KICAgICAgPC9zYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+DQogICAgPC9zYW1sOkNvbmRpdGlvbnM+DQogICAgPHNhbWw6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDExLTA2LTE3VDE0OjU0OjA3WiIgU2Vzc2lvbk5vdE9uT3JBZnRlcj0iMjAyMS0wNi0xN1QyMjo1NDoxNFoiIFNlc3Npb25JbmRleD0iXzUxYmUzNzk2NWZlYjU1NzlkODAzMTQxMDc2OTM2ZGMyZTlkMWQ5OGViZiI+DQogICAgICA8c2FtbDpBdXRobkNvbnRleHQ+DQogICAgICAgIDxzYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkPC9zYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPg0KICAgICAgPC9zYW1sOkF1dGhuQ29udGV4dD4NCiAgICA8L3NhbWw6QXV0aG5TdGF0ZW1lbnQ+DQogICAgPHNhbWw6QXR0cmlidXRlU3RhdGVtZW50Pg0KICAgICAgPHNhbWw6QXR0cmlidXRlIE5hbWU9Im1haWwiIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiPg0KICAgICAgICA8c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj5zb21lb25lQGV4YW1wbGUuY29tPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPg0KICAgICAgPC9zYW1sOkF0dHJpYnV0ZT4NCiAgICA8L3NhbWw6QXR0cmlidXRlU3RhdGVtZW50Pg0KICA8L3NhbWw6QXNzZXJ0aW9uPg0KPC9zYW1scDpSZXNwb25zZT4= +PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9InBmeGMzMmFlZDY3LTgyMGYtNDI5Ni0wYzIwLTIwNWExMGRkNTc4NyIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIiBEZXN0aW5hdGlvbj0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCI+DQogIDxzYW1sOklzc3Vlcj5odHRwOi8vaWRwLmV4YW1wbGUuY29tLzwvc2FtbDpJc3N1ZXI+DQogIDxzYW1scDpTdGF0dXM+DQogICAgPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPg0KICA8L3NhbWxwOlN0YXR1cz4NCiAgPHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9InBmeDc4NDE5OTFjLWM3M2YtNDAzNS1lMmVlLWMxNzBjMGUxZDNlNCIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIj4NCiAgICA8c2FtbDpJc3N1ZXI+aHR0cDovL2ludmFsaWQuaXNzdWVyLmV4YW1wbGUuY29tLzwvc2FtbDpJc3N1ZXI+ICAgIA0KICAgIDxzYW1sOlN1YmplY3Q+DQogICAgICA8c2FtbDpOYW1lSUQgU1BOYW1lUXVhbGlmaWVyPSJoZWxsby5jb20iIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4xOm5hbWVpZC1mb3JtYXQ6ZW1haWxBZGRyZXNzIj5zb21lb25lQGV4YW1wbGUuY29tPC9zYW1sOk5hbWVJRD4NCiAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj4NCiAgICAgICAgPHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgTm90T25PckFmdGVyPSIyMDIwLTA2LTE3VDE0OjU5OjE0WiIgUmVjaXBpZW50PSJodHRwOi8vc3R1ZmYuY29tL2VuZHBvaW50cy9lbmRwb2ludHMvYWNzLnBocCIgSW5SZXNwb25zZVRvPSJfNTdiY2JmNzAtN2IxZi0wMTJlLWM4MjEtNzgyYmNiMTNiYjM4Ii8+DQogICAgICA8L3NhbWw6U3ViamVjdENvbmZpcm1hdGlvbj4NCiAgICA8L3NhbWw6U3ViamVjdD4NCiAgICA8c2FtbDpDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAxMC0wNi0xN1QxNDo1Mzo0NFoiIE5vdE9uT3JBZnRlcj0iMjA5OS0wNi0xN1QxNDo1OToxNFoiPg0KICAgICAgPHNhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj4NCiAgICAgICAgPHNhbWw6QXVkaWVuY2U+aHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvbWV0YWRhdGEucGhwPC9zYW1sOkF1ZGllbmNlPg0KICAgICAgPC9zYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+DQogICAgPC9zYW1sOkNvbmRpdGlvbnM+DQogICAgPHNhbWw6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDExLTA2LTE3VDE0OjU0OjA3WiIgU2Vzc2lvbk5vdE9uT3JBZnRlcj0iMjA5OS0wNi0xN1QyMjo1NDoxNFoiIFNlc3Npb25JbmRleD0iXzUxYmUzNzk2NWZlYjU1NzlkODAzMTQxMDc2OTM2ZGMyZTlkMWQ5OGViZiI+DQogICAgICA8c2FtbDpBdXRobkNvbnRleHQ+DQogICAgICAgIDxzYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkPC9zYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPg0KICAgICAgPC9zYW1sOkF1dGhuQ29udGV4dD4NCiAgICA8L3NhbWw6QXV0aG5TdGF0ZW1lbnQ+DQogICAgPHNhbWw6QXR0cmlidXRlU3RhdGVtZW50Pg0KICAgICAgPHNhbWw6QXR0cmlidXRlIE5hbWU9Im1haWwiIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiPg0KICAgICAgICA8c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj5zb21lb25lQGV4YW1wbGUuY29tPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPg0KICAgICAgPC9zYW1sOkF0dHJpYnV0ZT4NCiAgICA8L3NhbWw6QXR0cmlidXRlU3RhdGVtZW50Pg0KICA8L3NhbWw6QXNzZXJ0aW9uPg0KPC9zYW1scDpSZXNwb25zZT4= \ No newline at end of file diff --git a/tests/data/responses/invalids/invalid_issuer_message.xml.base64 b/tests/data/responses/invalids/invalid_issuer_message.xml.base64 index f1f49fe5..0c06a25e 100644 --- a/tests/data/responses/invalids/invalid_issuer_message.xml.base64 +++ b/tests/data/responses/invalids/invalid_issuer_message.xml.base64 @@ -1 +1 @@ -PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9InBmeGMzMmFlZDY3LTgyMGYtNDI5Ni0wYzIwLTIwNWExMGRkNTc4NyIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIiBEZXN0aW5hdGlvbj0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCI+DQogIDxzYW1sOklzc3Vlcj5odHRwOi8vaW52YWxpZC5pc3Nlci5leGFtcGxlLmNvbS88L3NhbWw6SXNzdWVyPg0KICA8c2FtbHA6U3RhdHVzPg0KICAgIDxzYW1scDpTdGF0dXNDb2RlIFZhbHVlPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6c3RhdHVzOlN1Y2Nlc3MiLz4NCiAgPC9zYW1scDpTdGF0dXM+DQogIDxzYW1sOkFzc2VydGlvbiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIElEPSJwZng3ODQxOTkxYy1jNzNmLTQwMzUtZTJlZS1jMTcwYzBlMWQzZTQiIFZlcnNpb249IjIuMCIgSXNzdWVJbnN0YW50PSIyMDExLTA2LTE3VDE0OjU0OjE0WiI+DQogICAgPHNhbWw6SXNzdWVyPmh0dHA6Ly9pZHAuZXhhbXBsZS5jb20vPC9zYW1sOklzc3Vlcj4gICAgDQogICAgPHNhbWw6U3ViamVjdD4NCiAgICAgIDxzYW1sOk5hbWVJRCBTUE5hbWVRdWFsaWZpZXI9ImhlbGxvLmNvbSIgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjE6bmFtZWlkLWZvcm1hdDplbWFpbEFkZHJlc3MiPnNvbWVvbmVAZXhhbXBsZS5jb208L3NhbWw6TmFtZUlEPg0KICAgICAgPHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbiBNZXRob2Q9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjbTpiZWFyZXIiPg0KICAgICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uRGF0YSBOb3RPbk9yQWZ0ZXI9IjIwMjAtMDYtMTdUMTQ6NTk6MTRaIiBSZWNpcGllbnQ9Imh0dHA6Ly9zdHVmZi5jb20vZW5kcG9pbnRzL2VuZHBvaW50cy9hY3MucGhwIiBJblJlc3BvbnNlVG89Il81N2JjYmY3MC03YjFmLTAxMmUtYzgyMS03ODJiY2IxM2JiMzgiLz4NCiAgICAgIDwvc2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uPg0KICAgIDwvc2FtbDpTdWJqZWN0Pg0KICAgIDxzYW1sOkNvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDEwLTA2LTE3VDE0OjUzOjQ0WiIgTm90T25PckFmdGVyPSIyMDIxLTA2LTE3VDE0OjU5OjE0WiI+DQogICAgICA8c2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPg0KICAgICAgICA8c2FtbDpBdWRpZW5jZT5odHRwOi8vc3R1ZmYuY29tL2VuZHBvaW50cy9tZXRhZGF0YS5waHA8L3NhbWw6QXVkaWVuY2U+DQogICAgICA8L3NhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj4NCiAgICA8L3NhbWw6Q29uZGl0aW9ucz4NCiAgICA8c2FtbDpBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MDdaIiBTZXNzaW9uTm90T25PckFmdGVyPSIyMDIxLTA2LTE3VDIyOjU0OjE0WiIgU2Vzc2lvbkluZGV4PSJfNTFiZTM3OTY1ZmViNTU3OWQ4MDMxNDEwNzY5MzZkYzJlOWQxZDk4ZWJmIj4NCiAgICAgIDxzYW1sOkF1dGhuQ29udGV4dD4NCiAgICAgICAgPHNhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6UGFzc3dvcmQ8L3NhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+DQogICAgICA8L3NhbWw6QXV0aG5Db250ZXh0Pg0KICAgIDwvc2FtbDpBdXRoblN0YXRlbWVudD4NCiAgICA8c2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+DQogICAgICA8c2FtbDpBdHRyaWJ1dGUgTmFtZT0ibWFpbCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+DQogICAgICAgIDxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPnNvbWVvbmVAZXhhbXBsZS5jb208L3NhbWw6QXR0cmlidXRlVmFsdWU+DQogICAgICA8L3NhbWw6QXR0cmlidXRlPg0KICAgIDwvc2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+DQogIDwvc2FtbDpBc3NlcnRpb24+DQo8L3NhbWxwOlJlc3BvbnNlPg0KICA= +PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9InBmeGMzMmFlZDY3LTgyMGYtNDI5Ni0wYzIwLTIwNWExMGRkNTc4NyIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIiBEZXN0aW5hdGlvbj0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCI+DQogIDxzYW1sOklzc3Vlcj5odHRwOi8vaW52YWxpZC5pc3Nlci5leGFtcGxlLmNvbS88L3NhbWw6SXNzdWVyPg0KICA8c2FtbHA6U3RhdHVzPg0KICAgIDxzYW1scDpTdGF0dXNDb2RlIFZhbHVlPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6c3RhdHVzOlN1Y2Nlc3MiLz4NCiAgPC9zYW1scDpTdGF0dXM+DQogIDxzYW1sOkFzc2VydGlvbiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIElEPSJwZng3ODQxOTkxYy1jNzNmLTQwMzUtZTJlZS1jMTcwYzBlMWQzZTQiIFZlcnNpb249IjIuMCIgSXNzdWVJbnN0YW50PSIyMDExLTA2LTE3VDE0OjU0OjE0WiI+DQogICAgPHNhbWw6SXNzdWVyPmh0dHA6Ly9pZHAuZXhhbXBsZS5jb20vPC9zYW1sOklzc3Vlcj4gICAgDQogICAgPHNhbWw6U3ViamVjdD4NCiAgICAgIDxzYW1sOk5hbWVJRCBTUE5hbWVRdWFsaWZpZXI9ImhlbGxvLmNvbSIgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjE6bmFtZWlkLWZvcm1hdDplbWFpbEFkZHJlc3MiPnNvbWVvbmVAZXhhbXBsZS5jb208L3NhbWw6TmFtZUlEPg0KICAgICAgPHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbiBNZXRob2Q9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjbTpiZWFyZXIiPg0KICAgICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uRGF0YSBOb3RPbk9yQWZ0ZXI9IjIwMjAtMDYtMTdUMTQ6NTk6MTRaIiBSZWNpcGllbnQ9Imh0dHA6Ly9zdHVmZi5jb20vZW5kcG9pbnRzL2VuZHBvaW50cy9hY3MucGhwIiBJblJlc3BvbnNlVG89Il81N2JjYmY3MC03YjFmLTAxMmUtYzgyMS03ODJiY2IxM2JiMzgiLz4NCiAgICAgIDwvc2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uPg0KICAgIDwvc2FtbDpTdWJqZWN0Pg0KICAgIDxzYW1sOkNvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDEwLTA2LTE3VDE0OjUzOjQ0WiIgTm90T25PckFmdGVyPSIyMDk5LTA2LTE3VDE0OjU5OjE0WiI+DQogICAgICA8c2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPg0KICAgICAgICA8c2FtbDpBdWRpZW5jZT5odHRwOi8vc3R1ZmYuY29tL2VuZHBvaW50cy9tZXRhZGF0YS5waHA8L3NhbWw6QXVkaWVuY2U+DQogICAgICA8L3NhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj4NCiAgICA8L3NhbWw6Q29uZGl0aW9ucz4NCiAgICA8c2FtbDpBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MDdaIiBTZXNzaW9uTm90T25PckFmdGVyPSIyMDk5LTA2LTE3VDIyOjU0OjE0WiIgU2Vzc2lvbkluZGV4PSJfNTFiZTM3OTY1ZmViNTU3OWQ4MDMxNDEwNzY5MzZkYzJlOWQxZDk4ZWJmIj4NCiAgICAgIDxzYW1sOkF1dGhuQ29udGV4dD4NCiAgICAgICAgPHNhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6UGFzc3dvcmQ8L3NhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+DQogICAgICA8L3NhbWw6QXV0aG5Db250ZXh0Pg0KICAgIDwvc2FtbDpBdXRoblN0YXRlbWVudD4NCiAgICA8c2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+DQogICAgICA8c2FtbDpBdHRyaWJ1dGUgTmFtZT0ibWFpbCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+DQogICAgICAgIDxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPnNvbWVvbmVAZXhhbXBsZS5jb208L3NhbWw6QXR0cmlidXRlVmFsdWU+DQogICAgICA8L3NhbWw6QXR0cmlidXRlPg0KICAgIDwvc2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+DQogIDwvc2FtbDpBc3NlcnRpb24+DQo8L3NhbWxwOlJlc3BvbnNlPg0KICA= \ No newline at end of file diff --git a/tests/data/responses/invalids/invalid_sessionindex.xml.base64 b/tests/data/responses/invalids/invalid_sessionindex.xml.base64 index cc5a581c..f2e4c4c6 100644 --- a/tests/data/responses/invalids/invalid_sessionindex.xml.base64 +++ b/tests/data/responses/invalids/invalid_sessionindex.xml.base64 @@ -1 +1 @@ -PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9InBmeGMzMmFlZDY3LTgyMGYtNDI5Ni0wYzIwLTIwNWExMGRkNTc4NyIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIiBEZXN0aW5hdGlvbj0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCI+DQogIDxzYW1sOklzc3Vlcj5odHRwOi8vaWRwLmV4YW1wbGUuY29tLzwvc2FtbDpJc3N1ZXI+DQogIDxzYW1scDpTdGF0dXM+DQogICAgPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPg0KICA8L3NhbWxwOlN0YXR1cz4NCiAgPHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9InBmeDc4NDE5OTFjLWM3M2YtNDAzNS1lMmVlLWMxNzBjMGUxZDNlNCIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIj4NCiAgICA8c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS88L3NhbWw6SXNzdWVyPiAgICANCiAgICA8c2FtbDpTdWJqZWN0Pg0KICAgICAgPHNhbWw6TmFtZUlEIFNQTmFtZVF1YWxpZmllcj0iaGVsbG8uY29tIiBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+c29tZW9uZUBleGFtcGxlLmNvbTwvc2FtbDpOYW1lSUQ+DQogICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+DQogICAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjAyMC0wNi0xN1QxNDo1OToxNFoiIFJlY2lwaWVudD0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCIvPg0KICAgICAgPC9zYW1sOlN1YmplY3RDb25maXJtYXRpb24+DQogICAgPC9zYW1sOlN1YmplY3Q+DQogICAgPHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTAtMDYtMTdUMTQ6NTM6NDRaIiBOb3RPbk9yQWZ0ZXI9IjIwMjEtMDYtMTdUMTQ6NTk6MTRaIj4NCiAgICAgIDxzYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+DQogICAgICAgIDxzYW1sOkF1ZGllbmNlPmh0dHA6Ly9zdHVmZi5jb20vZW5kcG9pbnRzL21ldGFkYXRhLnBocDwvc2FtbDpBdWRpZW5jZT4NCiAgICAgIDwvc2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPg0KICAgIDwvc2FtbDpDb25kaXRpb25zPg0KICAgIDxzYW1sOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAxMS0wNi0xN1QxNDo1NDowN1oiIFNlc3Npb25Ob3RPbk9yQWZ0ZXI9IjIwMTMtMDYtMTdUMjI6NTQ6MTRaIiBTZXNzaW9uSW5kZXg9Il81MWJlMzc5NjVmZWI1NTc5ZDgwMzE0MTA3NjkzNmRjMmU5ZDFkOThlYmYiPg0KICAgICAgPHNhbWw6QXV0aG5Db250ZXh0Pg0KICAgICAgICA8c2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZDwvc2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj4NCiAgICAgIDwvc2FtbDpBdXRobkNvbnRleHQ+DQogICAgPC9zYW1sOkF1dGhuU3RhdGVtZW50Pg0KICAgIDxzYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4NCiAgICAgIDxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJtYWlsIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj4NCiAgICAgICAgPHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+c29tZW9uZUBleGFtcGxlLmNvbTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT4NCiAgICAgIDwvc2FtbDpBdHRyaWJ1dGU+DQogICAgPC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4NCiAgPC9zYW1sOkFzc2VydGlvbj4NCjwvc2FtbHA6UmVzcG9uc2U+ +PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9InBmeGMzMmFlZDY3LTgyMGYtNDI5Ni0wYzIwLTIwNWExMGRkNTc4NyIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIiBEZXN0aW5hdGlvbj0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCI+DQogIDxzYW1sOklzc3Vlcj5odHRwOi8vaWRwLmV4YW1wbGUuY29tLzwvc2FtbDpJc3N1ZXI+DQogIDxzYW1scDpTdGF0dXM+DQogICAgPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPg0KICA8L3NhbWxwOlN0YXR1cz4NCiAgPHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9InBmeDc4NDE5OTFjLWM3M2YtNDAzNS1lMmVlLWMxNzBjMGUxZDNlNCIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIj4NCiAgICA8c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS88L3NhbWw6SXNzdWVyPiAgICANCiAgICA8c2FtbDpTdWJqZWN0Pg0KICAgICAgPHNhbWw6TmFtZUlEIFNQTmFtZVF1YWxpZmllcj0iaGVsbG8uY29tIiBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+c29tZW9uZUBleGFtcGxlLmNvbTwvc2FtbDpOYW1lSUQ+DQogICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+DQogICAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjAyMC0wNi0xN1QxNDo1OToxNFoiIFJlY2lwaWVudD0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCIvPg0KICAgICAgPC9zYW1sOlN1YmplY3RDb25maXJtYXRpb24+DQogICAgPC9zYW1sOlN1YmplY3Q+DQogICAgPHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTAtMDYtMTdUMTQ6NTM6NDRaIiBOb3RPbk9yQWZ0ZXI9IjIwOTktMDYtMTdUMTQ6NTk6MTRaIj4NCiAgICAgIDxzYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+DQogICAgICAgIDxzYW1sOkF1ZGllbmNlPmh0dHA6Ly9zdHVmZi5jb20vZW5kcG9pbnRzL21ldGFkYXRhLnBocDwvc2FtbDpBdWRpZW5jZT4NCiAgICAgIDwvc2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPg0KICAgIDwvc2FtbDpDb25kaXRpb25zPg0KICAgIDxzYW1sOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAxMS0wNi0xN1QxNDo1NDowN1oiIFNlc3Npb25Ob3RPbk9yQWZ0ZXI9IjIwMTMtMDYtMTdUMjI6NTQ6MTRaIiBTZXNzaW9uSW5kZXg9Il81MWJlMzc5NjVmZWI1NTc5ZDgwMzE0MTA3NjkzNmRjMmU5ZDFkOThlYmYiPg0KICAgICAgPHNhbWw6QXV0aG5Db250ZXh0Pg0KICAgICAgICA8c2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZDwvc2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj4NCiAgICAgIDwvc2FtbDpBdXRobkNvbnRleHQ+DQogICAgPC9zYW1sOkF1dGhuU3RhdGVtZW50Pg0KICAgIDxzYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4NCiAgICAgIDxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJtYWlsIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj4NCiAgICAgICAgPHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+c29tZW9uZUBleGFtcGxlLmNvbTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT4NCiAgICAgIDwvc2FtbDpBdHRyaWJ1dGU+DQogICAgPC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4NCiAgPC9zYW1sOkFzc2VydGlvbj4NCjwvc2FtbHA6UmVzcG9uc2U+ \ No newline at end of file diff --git a/tests/data/responses/invalids/invalid_subjectconfirmation_inresponse.xml.base64 b/tests/data/responses/invalids/invalid_subjectconfirmation_inresponse.xml.base64 index 2ce545f8..b6a4e2eb 100644 --- a/tests/data/responses/invalids/invalid_subjectconfirmation_inresponse.xml.base64 +++ b/tests/data/responses/invalids/invalid_subjectconfirmation_inresponse.xml.base64 @@ -1 +1 @@ -PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9InBmeGMzMmFlZDY3LTgyMGYtNDI5Ni0wYzIwLTIwNWExMGRkNTc4NyIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIiBEZXN0aW5hdGlvbj0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCI+DQogIDxzYW1sOklzc3Vlcj5odHRwOi8vaWRwLmV4YW1wbGUuY29tLzwvc2FtbDpJc3N1ZXI+DQogIDxzYW1scDpTdGF0dXM+DQogICAgPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPg0KICA8L3NhbWxwOlN0YXR1cz4NCiAgPHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9InBmeDc4NDE5OTFjLWM3M2YtNDAzNS1lMmVlLWMxNzBjMGUxZDNlNCIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIj4NCiAgICA8c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS88L3NhbWw6SXNzdWVyPiAgICANCiAgICA8c2FtbDpTdWJqZWN0Pg0KICAgICAgPHNhbWw6TmFtZUlEIFNQTmFtZVF1YWxpZmllcj0iaGVsbG8uY29tIiBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+c29tZW9uZUBleGFtcGxlLmNvbTwvc2FtbDpOYW1lSUQ+DQogICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+DQogICAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjAyMC0wNi0xN1QxNDo1OToxNFoiIFJlY2lwaWVudD0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iaW52YWxpZF9pbnJlc3BvbnNlIi8+DQogICAgICA8L3NhbWw6U3ViamVjdENvbmZpcm1hdGlvbj4NCiAgICA8L3NhbWw6U3ViamVjdD4NCiAgICA8c2FtbDpDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAxMC0wNi0xN1QxNDo1Mzo0NFoiIE5vdE9uT3JBZnRlcj0iMjAyMS0wNi0xN1QxNDo1OToxNFoiPg0KICAgICAgPHNhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj4NCiAgICAgICAgPHNhbWw6QXVkaWVuY2U+aHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvbWV0YWRhdGEucGhwPC9zYW1sOkF1ZGllbmNlPg0KICAgICAgPC9zYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+DQogICAgPC9zYW1sOkNvbmRpdGlvbnM+DQogICAgPHNhbWw6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDExLTA2LTE3VDE0OjU0OjA3WiIgU2Vzc2lvbk5vdE9uT3JBZnRlcj0iMjAyMS0wNi0xN1QyMjo1NDoxNFoiIFNlc3Npb25JbmRleD0iXzUxYmUzNzk2NWZlYjU1NzlkODAzMTQxMDc2OTM2ZGMyZTlkMWQ5OGViZiI+DQogICAgICA8c2FtbDpBdXRobkNvbnRleHQ+DQogICAgICAgIDxzYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkPC9zYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPg0KICAgICAgPC9zYW1sOkF1dGhuQ29udGV4dD4NCiAgICA8L3NhbWw6QXV0aG5TdGF0ZW1lbnQ+DQogICAgPHNhbWw6QXR0cmlidXRlU3RhdGVtZW50Pg0KICAgICAgPHNhbWw6QXR0cmlidXRlIE5hbWU9Im1haWwiIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiPg0KICAgICAgICA8c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj5zb21lb25lQGV4YW1wbGUuY29tPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPg0KICAgICAgPC9zYW1sOkF0dHJpYnV0ZT4NCiAgICA8L3NhbWw6QXR0cmlidXRlU3RhdGVtZW50Pg0KICA8L3NhbWw6QXNzZXJ0aW9uPg0KPC9zYW1scDpSZXNwb25zZT4= +PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9InBmeGMzMmFlZDY3LTgyMGYtNDI5Ni0wYzIwLTIwNWExMGRkNTc4NyIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIiBEZXN0aW5hdGlvbj0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCI+DQogIDxzYW1sOklzc3Vlcj5odHRwOi8vaWRwLmV4YW1wbGUuY29tLzwvc2FtbDpJc3N1ZXI+DQogIDxzYW1scDpTdGF0dXM+DQogICAgPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPg0KICA8L3NhbWxwOlN0YXR1cz4NCiAgPHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9InBmeDc4NDE5OTFjLWM3M2YtNDAzNS1lMmVlLWMxNzBjMGUxZDNlNCIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIj4NCiAgICA8c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS88L3NhbWw6SXNzdWVyPiAgICANCiAgICA8c2FtbDpTdWJqZWN0Pg0KICAgICAgPHNhbWw6TmFtZUlEIFNQTmFtZVF1YWxpZmllcj0iaGVsbG8uY29tIiBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+c29tZW9uZUBleGFtcGxlLmNvbTwvc2FtbDpOYW1lSUQ+DQogICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+DQogICAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjAyMC0wNi0xN1QxNDo1OToxNFoiIFJlY2lwaWVudD0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iaW52YWxpZF9pbnJlc3BvbnNlIi8+DQogICAgICA8L3NhbWw6U3ViamVjdENvbmZpcm1hdGlvbj4NCiAgICA8L3NhbWw6U3ViamVjdD4NCiAgICA8c2FtbDpDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAxMC0wNi0xN1QxNDo1Mzo0NFoiIE5vdE9uT3JBZnRlcj0iMjA5OS0wNi0xN1QxNDo1OToxNFoiPg0KICAgICAgPHNhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj4NCiAgICAgICAgPHNhbWw6QXVkaWVuY2U+aHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvbWV0YWRhdGEucGhwPC9zYW1sOkF1ZGllbmNlPg0KICAgICAgPC9zYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+DQogICAgPC9zYW1sOkNvbmRpdGlvbnM+DQogICAgPHNhbWw6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDExLTA2LTE3VDE0OjU0OjA3WiIgU2Vzc2lvbk5vdE9uT3JBZnRlcj0iMjA5OS0wNi0xN1QyMjo1NDoxNFoiIFNlc3Npb25JbmRleD0iXzUxYmUzNzk2NWZlYjU1NzlkODAzMTQxMDc2OTM2ZGMyZTlkMWQ5OGViZiI+DQogICAgICA8c2FtbDpBdXRobkNvbnRleHQ+DQogICAgICAgIDxzYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkPC9zYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPg0KICAgICAgPC9zYW1sOkF1dGhuQ29udGV4dD4NCiAgICA8L3NhbWw6QXV0aG5TdGF0ZW1lbnQ+DQogICAgPHNhbWw6QXR0cmlidXRlU3RhdGVtZW50Pg0KICAgICAgPHNhbWw6QXR0cmlidXRlIE5hbWU9Im1haWwiIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiPg0KICAgICAgICA8c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj5zb21lb25lQGV4YW1wbGUuY29tPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPg0KICAgICAgPC9zYW1sOkF0dHJpYnV0ZT4NCiAgICA8L3NhbWw6QXR0cmlidXRlU3RhdGVtZW50Pg0KICA8L3NhbWw6QXNzZXJ0aW9uPg0KPC9zYW1scDpSZXNwb25zZT4= \ No newline at end of file diff --git a/tests/data/responses/invalids/invalid_subjectconfirmation_noa.xml.base64 b/tests/data/responses/invalids/invalid_subjectconfirmation_noa.xml.base64 index 6d4b70aa..4f922132 100644 --- a/tests/data/responses/invalids/invalid_subjectconfirmation_noa.xml.base64 +++ b/tests/data/responses/invalids/invalid_subjectconfirmation_noa.xml.base64 @@ -1 +1 @@ -PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9InBmeGMzMmFlZDY3LTgyMGYtNDI5Ni0wYzIwLTIwNWExMGRkNTc4NyIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIiBEZXN0aW5hdGlvbj0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCI+DQogIDxzYW1sOklzc3Vlcj5odHRwOi8vaWRwLmV4YW1wbGUuY29tLzwvc2FtbDpJc3N1ZXI+DQogIDxzYW1scDpTdGF0dXM+DQogICAgPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPg0KICA8L3NhbWxwOlN0YXR1cz4NCiAgPHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9InBmeDc4NDE5OTFjLWM3M2YtNDAzNS1lMmVlLWMxNzBjMGUxZDNlNCIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIj4NCiAgICA8c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS88L3NhbWw6SXNzdWVyPiAgICANCiAgICA8c2FtbDpTdWJqZWN0Pg0KICAgICAgPHNhbWw6TmFtZUlEIFNQTmFtZVF1YWxpZmllcj0iaGVsbG8uY29tIiBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+c29tZW9uZUBleGFtcGxlLmNvbTwvc2FtbDpOYW1lSUQ+DQogICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+DQogICAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjAxMC0wNi0xN1QxNDo1OToxNFoiIFJlY2lwaWVudD0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCIvPg0KICAgICAgPC9zYW1sOlN1YmplY3RDb25maXJtYXRpb24+DQogICAgPC9zYW1sOlN1YmplY3Q+DQogICAgPHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTAtMDYtMTdUMTQ6NTM6NDRaIiBOb3RPbk9yQWZ0ZXI9IjIwMjEtMDYtMTdUMTQ6NTk6MTRaIj4NCiAgICAgIDxzYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+DQogICAgICAgIDxzYW1sOkF1ZGllbmNlPmh0dHA6Ly9zdHVmZi5jb20vZW5kcG9pbnRzL21ldGFkYXRhLnBocDwvc2FtbDpBdWRpZW5jZT4NCiAgICAgIDwvc2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPg0KICAgIDwvc2FtbDpDb25kaXRpb25zPg0KICAgIDxzYW1sOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAxMS0wNi0xN1QxNDo1NDowN1oiIFNlc3Npb25Ob3RPbk9yQWZ0ZXI9IjIwMjEtMDYtMTdUMjI6NTQ6MTRaIiBTZXNzaW9uSW5kZXg9Il81MWJlMzc5NjVmZWI1NTc5ZDgwMzE0MTA3NjkzNmRjMmU5ZDFkOThlYmYiPg0KICAgICAgPHNhbWw6QXV0aG5Db250ZXh0Pg0KICAgICAgICA8c2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZDwvc2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj4NCiAgICAgIDwvc2FtbDpBdXRobkNvbnRleHQ+DQogICAgPC9zYW1sOkF1dGhuU3RhdGVtZW50Pg0KICAgIDxzYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4NCiAgICAgIDxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJtYWlsIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj4NCiAgICAgICAgPHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+c29tZW9uZUBleGFtcGxlLmNvbTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT4NCiAgICAgIDwvc2FtbDpBdHRyaWJ1dGU+DQogICAgPC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4NCiAgPC9zYW1sOkFzc2VydGlvbj4NCjwvc2FtbHA6UmVzcG9uc2U+ +PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9InBmeGMzMmFlZDY3LTgyMGYtNDI5Ni0wYzIwLTIwNWExMGRkNTc4NyIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIiBEZXN0aW5hdGlvbj0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCI+DQogIDxzYW1sOklzc3Vlcj5odHRwOi8vaWRwLmV4YW1wbGUuY29tLzwvc2FtbDpJc3N1ZXI+DQogIDxzYW1scDpTdGF0dXM+DQogICAgPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPg0KICA8L3NhbWxwOlN0YXR1cz4NCiAgPHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9InBmeDc4NDE5OTFjLWM3M2YtNDAzNS1lMmVlLWMxNzBjMGUxZDNlNCIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIj4NCiAgICA8c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS88L3NhbWw6SXNzdWVyPiAgICANCiAgICA8c2FtbDpTdWJqZWN0Pg0KICAgICAgPHNhbWw6TmFtZUlEIFNQTmFtZVF1YWxpZmllcj0iaGVsbG8uY29tIiBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+c29tZW9uZUBleGFtcGxlLmNvbTwvc2FtbDpOYW1lSUQ+DQogICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+DQogICAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjAxMC0wNi0xN1QxNDo1OToxNFoiIFJlY2lwaWVudD0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCIvPg0KICAgICAgPC9zYW1sOlN1YmplY3RDb25maXJtYXRpb24+DQogICAgPC9zYW1sOlN1YmplY3Q+DQogICAgPHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTAtMDYtMTdUMTQ6NTM6NDRaIiBOb3RPbk9yQWZ0ZXI9IjIwOTktMDYtMTdUMTQ6NTk6MTRaIj4NCiAgICAgIDxzYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+DQogICAgICAgIDxzYW1sOkF1ZGllbmNlPmh0dHA6Ly9zdHVmZi5jb20vZW5kcG9pbnRzL21ldGFkYXRhLnBocDwvc2FtbDpBdWRpZW5jZT4NCiAgICAgIDwvc2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPg0KICAgIDwvc2FtbDpDb25kaXRpb25zPg0KICAgIDxzYW1sOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAxMS0wNi0xN1QxNDo1NDowN1oiIFNlc3Npb25Ob3RPbk9yQWZ0ZXI9IjIwOTktMDYtMTdUMjI6NTQ6MTRaIiBTZXNzaW9uSW5kZXg9Il81MWJlMzc5NjVmZWI1NTc5ZDgwMzE0MTA3NjkzNmRjMmU5ZDFkOThlYmYiPg0KICAgICAgPHNhbWw6QXV0aG5Db250ZXh0Pg0KICAgICAgICA8c2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZDwvc2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj4NCiAgICAgIDwvc2FtbDpBdXRobkNvbnRleHQ+DQogICAgPC9zYW1sOkF1dGhuU3RhdGVtZW50Pg0KICAgIDxzYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4NCiAgICAgIDxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJtYWlsIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj4NCiAgICAgICAgPHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+c29tZW9uZUBleGFtcGxlLmNvbTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT4NCiAgICAgIDwvc2FtbDpBdHRyaWJ1dGU+DQogICAgPC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4NCiAgPC9zYW1sOkFzc2VydGlvbj4NCjwvc2FtbHA6UmVzcG9uc2U+ \ No newline at end of file diff --git a/tests/data/responses/invalids/invalid_subjectconfirmation_recipient.xml.base64 b/tests/data/responses/invalids/invalid_subjectconfirmation_recipient.xml.base64 index 1bf1d25a..30c55eb2 100644 --- a/tests/data/responses/invalids/invalid_subjectconfirmation_recipient.xml.base64 +++ b/tests/data/responses/invalids/invalid_subjectconfirmation_recipient.xml.base64 @@ -1 +1 @@ -PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9InBmeGMzMmFlZDY3LTgyMGYtNDI5Ni0wYzIwLTIwNWExMGRkNTc4NyIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIiBEZXN0aW5hdGlvbj0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCI+DQogIDxzYW1sOklzc3Vlcj5odHRwOi8vaWRwLmV4YW1wbGUuY29tLzwvc2FtbDpJc3N1ZXI+DQogIDxzYW1scDpTdGF0dXM+DQogICAgPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPg0KICA8L3NhbWxwOlN0YXR1cz4NCiAgPHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9InBmeDc4NDE5OTFjLWM3M2YtNDAzNS1lMmVlLWMxNzBjMGUxZDNlNCIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIj4NCiAgICA8c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS88L3NhbWw6SXNzdWVyPiAgICANCiAgICA8c2FtbDpTdWJqZWN0Pg0KICAgICAgPHNhbWw6TmFtZUlEIFNQTmFtZVF1YWxpZmllcj0iaGVsbG8uY29tIiBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+c29tZW9uZUBleGFtcGxlLmNvbTwvc2FtbDpOYW1lSUQ+DQogICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+DQogICAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjAyMC0wNi0xN1QxNDo1OToxNFoiIFJlY2lwaWVudD0iaHR0cDovL2ludmFsaWQucmVjaXBlbnQuZXhhbXBsZS5jb20iIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCIvPg0KICAgICAgPC9zYW1sOlN1YmplY3RDb25maXJtYXRpb24+DQogICAgPC9zYW1sOlN1YmplY3Q+DQogICAgPHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTAtMDYtMTdUMTQ6NTM6NDRaIiBOb3RPbk9yQWZ0ZXI9IjIwMjEtMDYtMTdUMTQ6NTk6MTRaIj4NCiAgICAgIDxzYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+DQogICAgICAgIDxzYW1sOkF1ZGllbmNlPmh0dHA6Ly9zdHVmZi5jb20vZW5kcG9pbnRzL21ldGFkYXRhLnBocDwvc2FtbDpBdWRpZW5jZT4NCiAgICAgIDwvc2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPg0KICAgIDwvc2FtbDpDb25kaXRpb25zPg0KICAgIDxzYW1sOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAxMS0wNi0xN1QxNDo1NDowN1oiIFNlc3Npb25Ob3RPbk9yQWZ0ZXI9IjIwMjEtMDYtMTdUMjI6NTQ6MTRaIiBTZXNzaW9uSW5kZXg9Il81MWJlMzc5NjVmZWI1NTc5ZDgwMzE0MTA3NjkzNmRjMmU5ZDFkOThlYmYiPg0KICAgICAgPHNhbWw6QXV0aG5Db250ZXh0Pg0KICAgICAgICA8c2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZDwvc2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj4NCiAgICAgIDwvc2FtbDpBdXRobkNvbnRleHQ+DQogICAgPC9zYW1sOkF1dGhuU3RhdGVtZW50Pg0KICAgIDxzYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4NCiAgICAgIDxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJtYWlsIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj4NCiAgICAgICAgPHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+c29tZW9uZUBleGFtcGxlLmNvbTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT4NCiAgICAgIDwvc2FtbDpBdHRyaWJ1dGU+DQogICAgPC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4NCiAgPC9zYW1sOkFzc2VydGlvbj4NCjwvc2FtbHA6UmVzcG9uc2U+ +PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9InBmeGMzMmFlZDY3LTgyMGYtNDI5Ni0wYzIwLTIwNWExMGRkNTc4NyIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIiBEZXN0aW5hdGlvbj0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCI+DQogIDxzYW1sOklzc3Vlcj5odHRwOi8vaWRwLmV4YW1wbGUuY29tLzwvc2FtbDpJc3N1ZXI+DQogIDxzYW1scDpTdGF0dXM+DQogICAgPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPg0KICA8L3NhbWxwOlN0YXR1cz4NCiAgPHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9InBmeDc4NDE5OTFjLWM3M2YtNDAzNS1lMmVlLWMxNzBjMGUxZDNlNCIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIj4NCiAgICA8c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS88L3NhbWw6SXNzdWVyPiAgICANCiAgICA8c2FtbDpTdWJqZWN0Pg0KICAgICAgPHNhbWw6TmFtZUlEIFNQTmFtZVF1YWxpZmllcj0iaGVsbG8uY29tIiBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+c29tZW9uZUBleGFtcGxlLmNvbTwvc2FtbDpOYW1lSUQ+DQogICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+DQogICAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjAyMC0wNi0xN1QxNDo1OToxNFoiIFJlY2lwaWVudD0iaHR0cDovL2ludmFsaWQucmVjaXBlbnQuZXhhbXBsZS5jb20iIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCIvPg0KICAgICAgPC9zYW1sOlN1YmplY3RDb25maXJtYXRpb24+DQogICAgPC9zYW1sOlN1YmplY3Q+DQogICAgPHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTAtMDYtMTdUMTQ6NTM6NDRaIiBOb3RPbk9yQWZ0ZXI9IjIwOTktMDYtMTdUMTQ6NTk6MTRaIj4NCiAgICAgIDxzYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+DQogICAgICAgIDxzYW1sOkF1ZGllbmNlPmh0dHA6Ly9zdHVmZi5jb20vZW5kcG9pbnRzL21ldGFkYXRhLnBocDwvc2FtbDpBdWRpZW5jZT4NCiAgICAgIDwvc2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPg0KICAgIDwvc2FtbDpDb25kaXRpb25zPg0KICAgIDxzYW1sOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAxMS0wNi0xN1QxNDo1NDowN1oiIFNlc3Npb25Ob3RPbk9yQWZ0ZXI9IjIwOTktMDYtMTdUMjI6NTQ6MTRaIiBTZXNzaW9uSW5kZXg9Il81MWJlMzc5NjVmZWI1NTc5ZDgwMzE0MTA3NjkzNmRjMmU5ZDFkOThlYmYiPg0KICAgICAgPHNhbWw6QXV0aG5Db250ZXh0Pg0KICAgICAgICA8c2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZDwvc2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj4NCiAgICAgIDwvc2FtbDpBdXRobkNvbnRleHQ+DQogICAgPC9zYW1sOkF1dGhuU3RhdGVtZW50Pg0KICAgIDxzYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4NCiAgICAgIDxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJtYWlsIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj4NCiAgICAgICAgPHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+c29tZW9uZUBleGFtcGxlLmNvbTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT4NCiAgICAgIDwvc2FtbDpBdHRyaWJ1dGU+DQogICAgPC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4NCiAgPC9zYW1sOkFzc2VydGlvbj4NCjwvc2FtbHA6UmVzcG9uc2U+ \ No newline at end of file diff --git a/tests/data/responses/invalids/no_nameid.xml.base64 b/tests/data/responses/invalids/no_nameid.xml.base64 index 3c2b6d9b..db8879b2 100644 --- a/tests/data/responses/invalids/no_nameid.xml.base64 +++ b/tests/data/responses/invalids/no_nameid.xml.base64 @@ -1 +1 @@ -PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9InBmeGMzMmFlZDY3LTgyMGYtNDI5Ni0wYzIwLTIwNWExMGRkNTc4NyIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIiBEZXN0aW5hdGlvbj0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCI+DQogIDxzYW1sOklzc3Vlcj5odHRwOi8vaWRwLmV4YW1wbGUuY29tLzwvc2FtbDpJc3N1ZXI+DQogIDxzYW1scDpTdGF0dXM+DQogICAgPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPg0KICA8L3NhbWxwOlN0YXR1cz4NCiAgPHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9InBmeDc4NDE5OTFjLWM3M2YtNDAzNS1lMmVlLWMxNzBjMGUxZDNlNCIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIj4NCiAgICA8c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS88L3NhbWw6SXNzdWVyPiAgICANCiAgICA8c2FtbDpTdWJqZWN0Pg0KICAgICAgPHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbiBNZXRob2Q9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjbTpiZWFyZXIiPg0KICAgICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uRGF0YSBOb3RPbk9yQWZ0ZXI9IjIwMjAtMDYtMTdUMTQ6NTk6MTRaIiBSZWNpcGllbnQ9Imh0dHA6Ly9zdHVmZi5jb20vZW5kcG9pbnRzL2VuZHBvaW50cy9hY3MucGhwIiBJblJlc3BvbnNlVG89Il81N2JjYmY3MC03YjFmLTAxMmUtYzgyMS03ODJiY2IxM2JiMzgiLz4NCiAgICAgIDwvc2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uPg0KICAgIDwvc2FtbDpTdWJqZWN0Pg0KICAgIDxzYW1sOkNvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDEwLTA2LTE3VDE0OjUzOjQ0WiIgTm90T25PckFmdGVyPSIyMDIxLTA2LTE3VDE0OjU5OjE0WiI+DQogICAgICA8c2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPg0KICAgICAgICA8c2FtbDpBdWRpZW5jZT5odHRwOi8vc3R1ZmYuY29tL2VuZHBvaW50cy9tZXRhZGF0YS5waHA8L3NhbWw6QXVkaWVuY2U+DQogICAgICA8L3NhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj4NCiAgICA8L3NhbWw6Q29uZGl0aW9ucz4NCiAgICA8c2FtbDpBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MDdaIiBTZXNzaW9uTm90T25PckFmdGVyPSIyMDIxLTA2LTE3VDIyOjU0OjE0WiIgU2Vzc2lvbkluZGV4PSJfNTFiZTM3OTY1ZmViNTU3OWQ4MDMxNDEwNzY5MzZkYzJlOWQxZDk4ZWJmIj4NCiAgICAgIDxzYW1sOkF1dGhuQ29udGV4dD4NCiAgICAgICAgPHNhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6UGFzc3dvcmQ8L3NhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+DQogICAgICA8L3NhbWw6QXV0aG5Db250ZXh0Pg0KICAgIDwvc2FtbDpBdXRoblN0YXRlbWVudD4NCiAgICA8c2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+DQogICAgICA8c2FtbDpBdHRyaWJ1dGUgTmFtZT0ibWFpbCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+DQogICAgICAgIDxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPnNvbWVvbmVAZXhhbXBsZS5jb208L3NhbWw6QXR0cmlidXRlVmFsdWU+DQogICAgICA8L3NhbWw6QXR0cmlidXRlPg0KICAgIDwvc2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+DQogIDwvc2FtbDpBc3NlcnRpb24+DQo8L3NhbWxwOlJlc3BvbnNlPg== +PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9InBmeGMzMmFlZDY3LTgyMGYtNDI5Ni0wYzIwLTIwNWExMGRkNTc4NyIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIiBEZXN0aW5hdGlvbj0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCI+DQogIDxzYW1sOklzc3Vlcj5odHRwOi8vaWRwLmV4YW1wbGUuY29tLzwvc2FtbDpJc3N1ZXI+DQogIDxzYW1scDpTdGF0dXM+DQogICAgPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPg0KICA8L3NhbWxwOlN0YXR1cz4NCiAgPHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9InBmeDc4NDE5OTFjLWM3M2YtNDAzNS1lMmVlLWMxNzBjMGUxZDNlNCIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIj4NCiAgICA8c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS88L3NhbWw6SXNzdWVyPiAgICANCiAgICA8c2FtbDpTdWJqZWN0Pg0KICAgICAgPHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbiBNZXRob2Q9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjbTpiZWFyZXIiPg0KICAgICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uRGF0YSBOb3RPbk9yQWZ0ZXI9IjIwMjAtMDYtMTdUMTQ6NTk6MTRaIiBSZWNpcGllbnQ9Imh0dHA6Ly9zdHVmZi5jb20vZW5kcG9pbnRzL2VuZHBvaW50cy9hY3MucGhwIiBJblJlc3BvbnNlVG89Il81N2JjYmY3MC03YjFmLTAxMmUtYzgyMS03ODJiY2IxM2JiMzgiLz4NCiAgICAgIDwvc2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uPg0KICAgIDwvc2FtbDpTdWJqZWN0Pg0KICAgIDxzYW1sOkNvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDEwLTA2LTE3VDE0OjUzOjQ0WiIgTm90T25PckFmdGVyPSIyMDk5LTA2LTE3VDE0OjU5OjE0WiI+DQogICAgICA8c2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPg0KICAgICAgICA8c2FtbDpBdWRpZW5jZT5odHRwOi8vc3R1ZmYuY29tL2VuZHBvaW50cy9tZXRhZGF0YS5waHA8L3NhbWw6QXVkaWVuY2U+DQogICAgICA8L3NhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj4NCiAgICA8L3NhbWw6Q29uZGl0aW9ucz4NCiAgICA8c2FtbDpBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MDdaIiBTZXNzaW9uTm90T25PckFmdGVyPSIyMDk5LTA2LTE3VDIyOjU0OjE0WiIgU2Vzc2lvbkluZGV4PSJfNTFiZTM3OTY1ZmViNTU3OWQ4MDMxNDEwNzY5MzZkYzJlOWQxZDk4ZWJmIj4NCiAgICAgIDxzYW1sOkF1dGhuQ29udGV4dD4NCiAgICAgICAgPHNhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6UGFzc3dvcmQ8L3NhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+DQogICAgICA8L3NhbWw6QXV0aG5Db250ZXh0Pg0KICAgIDwvc2FtbDpBdXRoblN0YXRlbWVudD4NCiAgICA8c2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+DQogICAgICA8c2FtbDpBdHRyaWJ1dGUgTmFtZT0ibWFpbCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+DQogICAgICAgIDxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPnNvbWVvbmVAZXhhbXBsZS5jb208L3NhbWw6QXR0cmlidXRlVmFsdWU+DQogICAgICA8L3NhbWw6QXR0cmlidXRlPg0KICAgIDwvc2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+DQogIDwvc2FtbDpBc3NlcnRpb24+DQo8L3NhbWxwOlJlc3BvbnNlPg== \ No newline at end of file diff --git a/tests/data/responses/invalids/no_subjectconfirmation_data.xml.base64 b/tests/data/responses/invalids/no_subjectconfirmation_data.xml.base64 index 3c92f561..bc28d907 100644 --- a/tests/data/responses/invalids/no_subjectconfirmation_data.xml.base64 +++ b/tests/data/responses/invalids/no_subjectconfirmation_data.xml.base64 @@ -1 +1 @@ -PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9InBmeGMzMmFlZDY3LTgyMGYtNDI5Ni0wYzIwLTIwNWExMGRkNTc4NyIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIiBEZXN0aW5hdGlvbj0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCI+DQogIDxzYW1sOklzc3Vlcj5odHRwOi8vaWRwLmV4YW1wbGUuY29tLzwvc2FtbDpJc3N1ZXI+DQogIDxzYW1scDpTdGF0dXM+DQogICAgPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPg0KICA8L3NhbWxwOlN0YXR1cz4NCiAgPHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9InBmeDc4NDE5OTFjLWM3M2YtNDAzNS1lMmVlLWMxNzBjMGUxZDNlNCIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIj4NCiAgICA8c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS88L3NhbWw6SXNzdWVyPiAgICANCiAgICA8c2FtbDpTdWJqZWN0Pg0KICAgICAgPHNhbWw6TmFtZUlEIFNQTmFtZVF1YWxpZmllcj0iaGVsbG8uY29tIiBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+c29tZW9uZUBleGFtcGxlLmNvbTwvc2FtbDpOYW1lSUQ+DQogICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+ICAgICAgICANCiAgICAgIDwvc2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uPg0KICAgIDwvc2FtbDpTdWJqZWN0Pg0KICAgIDxzYW1sOkNvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDEwLTA2LTE3VDE0OjUzOjQ0WiIgTm90T25PckFmdGVyPSIyMDIxLTA2LTE3VDE0OjU5OjE0WiI+DQogICAgICA8c2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPg0KICAgICAgICA8c2FtbDpBdWRpZW5jZT5odHRwOi8vc3R1ZmYuY29tL2VuZHBvaW50cy9tZXRhZGF0YS5waHA8L3NhbWw6QXVkaWVuY2U+DQogICAgICA8L3NhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj4NCiAgICA8L3NhbWw6Q29uZGl0aW9ucz4NCiAgICA8c2FtbDpBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MDdaIiBTZXNzaW9uTm90T25PckFmdGVyPSIyMDIxLTA2LTE3VDIyOjU0OjE0WiIgU2Vzc2lvbkluZGV4PSJfNTFiZTM3OTY1ZmViNTU3OWQ4MDMxNDEwNzY5MzZkYzJlOWQxZDk4ZWJmIj4NCiAgICAgIDxzYW1sOkF1dGhuQ29udGV4dD4NCiAgICAgICAgPHNhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6UGFzc3dvcmQ8L3NhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+DQogICAgICA8L3NhbWw6QXV0aG5Db250ZXh0Pg0KICAgIDwvc2FtbDpBdXRoblN0YXRlbWVudD4NCiAgICA8c2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+DQogICAgICA8c2FtbDpBdHRyaWJ1dGUgTmFtZT0ibWFpbCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+DQogICAgICAgIDxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPnNvbWVvbmVAZXhhbXBsZS5jb208L3NhbWw6QXR0cmlidXRlVmFsdWU+DQogICAgICA8L3NhbWw6QXR0cmlidXRlPg0KICAgIDwvc2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+DQogIDwvc2FtbDpBc3NlcnRpb24+DQo8L3NhbWxwOlJlc3BvbnNlPg== +PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9InBmeGMzMmFlZDY3LTgyMGYtNDI5Ni0wYzIwLTIwNWExMGRkNTc4NyIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIiBEZXN0aW5hdGlvbj0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCI+DQogIDxzYW1sOklzc3Vlcj5odHRwOi8vaWRwLmV4YW1wbGUuY29tLzwvc2FtbDpJc3N1ZXI+DQogIDxzYW1scDpTdGF0dXM+DQogICAgPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPg0KICA8L3NhbWxwOlN0YXR1cz4NCiAgPHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9InBmeDc4NDE5OTFjLWM3M2YtNDAzNS1lMmVlLWMxNzBjMGUxZDNlNCIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIj4NCiAgICA8c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS88L3NhbWw6SXNzdWVyPiAgICANCiAgICA8c2FtbDpTdWJqZWN0Pg0KICAgICAgPHNhbWw6TmFtZUlEIFNQTmFtZVF1YWxpZmllcj0iaGVsbG8uY29tIiBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+c29tZW9uZUBleGFtcGxlLmNvbTwvc2FtbDpOYW1lSUQ+DQogICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+ICAgICAgICANCiAgICAgIDwvc2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uPg0KICAgIDwvc2FtbDpTdWJqZWN0Pg0KICAgIDxzYW1sOkNvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDEwLTA2LTE3VDE0OjUzOjQ0WiIgTm90T25PckFmdGVyPSIyMDk5LTA2LTE3VDE0OjU5OjE0WiI+DQogICAgICA8c2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPg0KICAgICAgICA8c2FtbDpBdWRpZW5jZT5odHRwOi8vc3R1ZmYuY29tL2VuZHBvaW50cy9tZXRhZGF0YS5waHA8L3NhbWw6QXVkaWVuY2U+DQogICAgICA8L3NhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj4NCiAgICA8L3NhbWw6Q29uZGl0aW9ucz4NCiAgICA8c2FtbDpBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MDdaIiBTZXNzaW9uTm90T25PckFmdGVyPSIyMDk5LTA2LTE3VDIyOjU0OjE0WiIgU2Vzc2lvbkluZGV4PSJfNTFiZTM3OTY1ZmViNTU3OWQ4MDMxNDEwNzY5MzZkYzJlOWQxZDk4ZWJmIj4NCiAgICAgIDxzYW1sOkF1dGhuQ29udGV4dD4NCiAgICAgICAgPHNhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6UGFzc3dvcmQ8L3NhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+DQogICAgICA8L3NhbWw6QXV0aG5Db250ZXh0Pg0KICAgIDwvc2FtbDpBdXRoblN0YXRlbWVudD4NCiAgICA8c2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+DQogICAgICA8c2FtbDpBdHRyaWJ1dGUgTmFtZT0ibWFpbCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+DQogICAgICAgIDxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPnNvbWVvbmVAZXhhbXBsZS5jb208L3NhbWw6QXR0cmlidXRlVmFsdWU+DQogICAgICA8L3NhbWw6QXR0cmlidXRlPg0KICAgIDwvc2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+DQogIDwvc2FtbDpBc3NlcnRpb24+DQo8L3NhbWxwOlJlc3BvbnNlPg== \ No newline at end of file diff --git a/tests/data/responses/invalids/no_subjectconfirmation_method.xml.base64 b/tests/data/responses/invalids/no_subjectconfirmation_method.xml.base64 index 07ce153a..37b69370 100644 --- a/tests/data/responses/invalids/no_subjectconfirmation_method.xml.base64 +++ b/tests/data/responses/invalids/no_subjectconfirmation_method.xml.base64 @@ -1 +1 @@ -PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9InBmeGMzMmFlZDY3LTgyMGYtNDI5Ni0wYzIwLTIwNWExMGRkNTc4NyIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIiBEZXN0aW5hdGlvbj0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCI+DQogIDxzYW1sOklzc3Vlcj5odHRwOi8vaWRwLmV4YW1wbGUuY29tLzwvc2FtbDpJc3N1ZXI+DQogIDxzYW1scDpTdGF0dXM+DQogICAgPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPg0KICA8L3NhbWxwOlN0YXR1cz4NCiAgPHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9InBmeDc4NDE5OTFjLWM3M2YtNDAzNS1lMmVlLWMxNzBjMGUxZDNlNCIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIj4NCiAgICA8c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS88L3NhbWw6SXNzdWVyPiAgICANCiAgICA8c2FtbDpTdWJqZWN0Pg0KICAgICAgPHNhbWw6TmFtZUlEIFNQTmFtZVF1YWxpZmllcj0iaGVsbG8uY29tIiBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+c29tZW9uZUBleGFtcGxlLmNvbTwvc2FtbDpOYW1lSUQ+DQogICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmhvbGRlci1vZi1rZXkiPg0KICAgICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uRGF0YSBOb3RPbk9yQWZ0ZXI9IjIwMjAtMDYtMTdUMTQ6NTk6MTRaIiBSZWNpcGllbnQ9Imh0dHA6Ly9zdHVmZi5jb20vZW5kcG9pbnRzL2VuZHBvaW50cy9hY3MucGhwIiBJblJlc3BvbnNlVG89Il81N2JjYmY3MC03YjFmLTAxMmUtYzgyMS03ODJiY2IxM2JiMzgiLz4NCiAgICAgIDwvc2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uPg0KICAgIDwvc2FtbDpTdWJqZWN0Pg0KICAgIDxzYW1sOkNvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDEwLTA2LTE3VDE0OjUzOjQ0WiIgTm90T25PckFmdGVyPSIyMDIxLTA2LTE3VDE0OjU5OjE0WiI+DQogICAgICA8c2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPg0KICAgICAgICA8c2FtbDpBdWRpZW5jZT5odHRwOi8vc3R1ZmYuY29tL2VuZHBvaW50cy9tZXRhZGF0YS5waHA8L3NhbWw6QXVkaWVuY2U+DQogICAgICA8L3NhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj4NCiAgICA8L3NhbWw6Q29uZGl0aW9ucz4NCiAgICA8c2FtbDpBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MDdaIiBTZXNzaW9uTm90T25PckFmdGVyPSIyMDIxLTA2LTE3VDIyOjU0OjE0WiIgU2Vzc2lvbkluZGV4PSJfNTFiZTM3OTY1ZmViNTU3OWQ4MDMxNDEwNzY5MzZkYzJlOWQxZDk4ZWJmIj4NCiAgICAgIDxzYW1sOkF1dGhuQ29udGV4dD4NCiAgICAgICAgPHNhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6UGFzc3dvcmQ8L3NhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+DQogICAgICA8L3NhbWw6QXV0aG5Db250ZXh0Pg0KICAgIDwvc2FtbDpBdXRoblN0YXRlbWVudD4NCiAgICA8c2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+DQogICAgICA8c2FtbDpBdHRyaWJ1dGUgTmFtZT0ibWFpbCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+DQogICAgICAgIDxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPnNvbWVvbmVAZXhhbXBsZS5jb208L3NhbWw6QXR0cmlidXRlVmFsdWU+DQogICAgICA8L3NhbWw6QXR0cmlidXRlPg0KICAgIDwvc2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+DQogIDwvc2FtbDpBc3NlcnRpb24+DQo8L3NhbWxwOlJlc3BvbnNlPg== +PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9InBmeGMzMmFlZDY3LTgyMGYtNDI5Ni0wYzIwLTIwNWExMGRkNTc4NyIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIiBEZXN0aW5hdGlvbj0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCI+DQogIDxzYW1sOklzc3Vlcj5odHRwOi8vaWRwLmV4YW1wbGUuY29tLzwvc2FtbDpJc3N1ZXI+DQogIDxzYW1scDpTdGF0dXM+DQogICAgPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPg0KICA8L3NhbWxwOlN0YXR1cz4NCiAgPHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9InBmeDc4NDE5OTFjLWM3M2YtNDAzNS1lMmVlLWMxNzBjMGUxZDNlNCIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIj4NCiAgICA8c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS88L3NhbWw6SXNzdWVyPiAgICANCiAgICA8c2FtbDpTdWJqZWN0Pg0KICAgICAgPHNhbWw6TmFtZUlEIFNQTmFtZVF1YWxpZmllcj0iaGVsbG8uY29tIiBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+c29tZW9uZUBleGFtcGxlLmNvbTwvc2FtbDpOYW1lSUQ+DQogICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmhvbGRlci1vZi1rZXkiPg0KICAgICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uRGF0YSBOb3RPbk9yQWZ0ZXI9IjIwMjAtMDYtMTdUMTQ6NTk6MTRaIiBSZWNpcGllbnQ9Imh0dHA6Ly9zdHVmZi5jb20vZW5kcG9pbnRzL2VuZHBvaW50cy9hY3MucGhwIiBJblJlc3BvbnNlVG89Il81N2JjYmY3MC03YjFmLTAxMmUtYzgyMS03ODJiY2IxM2JiMzgiLz4NCiAgICAgIDwvc2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uPg0KICAgIDwvc2FtbDpTdWJqZWN0Pg0KICAgIDxzYW1sOkNvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDEwLTA2LTE3VDE0OjUzOjQ0WiIgTm90T25PckFmdGVyPSIyMDk5LTA2LTE3VDE0OjU5OjE0WiI+DQogICAgICA8c2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPg0KICAgICAgICA8c2FtbDpBdWRpZW5jZT5odHRwOi8vc3R1ZmYuY29tL2VuZHBvaW50cy9tZXRhZGF0YS5waHA8L3NhbWw6QXVkaWVuY2U+DQogICAgICA8L3NhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj4NCiAgICA8L3NhbWw6Q29uZGl0aW9ucz4NCiAgICA8c2FtbDpBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MDdaIiBTZXNzaW9uTm90T25PckFmdGVyPSIyMDk5LTA2LTE3VDIyOjU0OjE0WiIgU2Vzc2lvbkluZGV4PSJfNTFiZTM3OTY1ZmViNTU3OWQ4MDMxNDEwNzY5MzZkYzJlOWQxZDk4ZWJmIj4NCiAgICAgIDxzYW1sOkF1dGhuQ29udGV4dD4NCiAgICAgICAgPHNhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6UGFzc3dvcmQ8L3NhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+DQogICAgICA8L3NhbWw6QXV0aG5Db250ZXh0Pg0KICAgIDwvc2FtbDpBdXRoblN0YXRlbWVudD4NCiAgICA8c2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+DQogICAgICA8c2FtbDpBdHRyaWJ1dGUgTmFtZT0ibWFpbCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+DQogICAgICAgIDxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPnNvbWVvbmVAZXhhbXBsZS5jb208L3NhbWw6QXR0cmlidXRlVmFsdWU+DQogICAgICA8L3NhbWw6QXR0cmlidXRlPg0KICAgIDwvc2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+DQogIDwvc2FtbDpBc3NlcnRpb24+DQo8L3NhbWxwOlJlc3BvbnNlPg== \ No newline at end of file diff --git a/tests/data/responses/invalids/response_encrypted_attrs.xml.base64 b/tests/data/responses/invalids/response_encrypted_attrs.xml.base64 index 32449899..ad6392c5 100644 --- a/tests/data/responses/invalids/response_encrypted_attrs.xml.base64 +++ b/tests/data/responses/invalids/response_encrypted_attrs.xml.base64 @@ -1 +1 @@ -PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9InBmeGMzMmFlZDY3LTgyMGYtNDI5Ni0wYzIwLTIwNWExMGRkNTc4NyIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIiBEZXN0aW5hdGlvbj0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCI+DQogIDxzYW1sOklzc3Vlcj5odHRwOi8vaWRwLmV4YW1wbGUuY29tLzwvc2FtbDpJc3N1ZXI+DQogIDxzYW1scDpTdGF0dXM+DQogICAgPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPg0KICA8L3NhbWxwOlN0YXR1cz4NCiAgPHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9InBmeDc4NDE5OTFjLWM3M2YtNDAzNS1lMmVlLWMxNzBjMGUxZDNlNCIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIj4NCiAgICA8c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS88L3NhbWw6SXNzdWVyPiAgICANCiAgICA8c2FtbDpTdWJqZWN0Pg0KICAgICAgPHNhbWw6TmFtZUlEIFNQTmFtZVF1YWxpZmllcj0iaGVsbG8uY29tIiBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+c29tZW9uZUBleGFtcGxlLmNvbTwvc2FtbDpOYW1lSUQ+DQogICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+DQogICAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjAyMC0wNi0xN1QxNDo1OToxNFoiIFJlY2lwaWVudD0iaGh0dHA6Ly9zdHVmZi5jb20vZW5kcG9pbnRzL21ldGFkYXRhLnBocCIgSW5SZXNwb25zZVRvPSJfNTdiY2JmNzAtN2IxZi0wMTJlLWM4MjEtNzgyYmNiMTNiYjM4Ii8+DQogICAgICA8L3NhbWw6U3ViamVjdENvbmZpcm1hdGlvbj4NCiAgICA8L3NhbWw6U3ViamVjdD4NCiAgICA8c2FtbDpDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAxMC0wNi0xN1QxNDo1Mzo0NFoiIE5vdE9uT3JBZnRlcj0iMjAyMS0wNi0xN1QxNDo1OToxNFoiPg0KICAgICAgPHNhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj4NCiAgICAgICAgPHNhbWw6QXVkaWVuY2U+aHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvbWV0YWRhdGEucGhwPC9zYW1sOkF1ZGllbmNlPg0KICAgICAgPC9zYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+DQogICAgPC9zYW1sOkNvbmRpdGlvbnM+DQogICAgPHNhbWw6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDExLTA2LTE3VDE0OjU0OjA3WiIgU2Vzc2lvbk5vdE9uT3JBZnRlcj0iMjAyMS0wNi0xN1QyMjo1NDoxNFoiIFNlc3Npb25JbmRleD0iXzUxYmUzNzk2NWZlYjU1NzlkODAzMTQxMDc2OTM2ZGMyZTlkMWQ5OGViZiI+DQogICAgICA8c2FtbDpBdXRobkNvbnRleHQ+DQogICAgICAgIDxzYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkPC9zYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPg0KICAgICAgPC9zYW1sOkF1dGhuQ29udGV4dD4NCiAgICA8L3NhbWw6QXV0aG5TdGF0ZW1lbnQ+DQogICAgPHNhbWw6QXR0cmlidXRlU3RhdGVtZW50Pg0KIDxzYW1sOkVuY3J5cHRlZEF0dHJpYnV0ZSB4bWxuczpzYW1sPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj4NCiAgICAgICAgICA8eGVuYzpFbmNyeXB0ZWREYXRhIHhtbG5zOnhlbmM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jIyIgVHlwZT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjRWxlbWVudCIgSWQ9Il9GMzk2MjVBRjY4QjRGQzA3OENDNzU4MkQyOEQwNUQ5QyI+DQogICAgICAgICAgICA8eGVuYzpFbmNyeXB0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjYWVzMjU2LWNiYyIvPg0KICAgICAgICAgICAgPGRzOktleUluZm8geG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPg0KICAgICAgICAgICAgICA8eGVuYzpFbmNyeXB0ZWRLZXk+DQogICAgICAgICAgICAgICAgPHhlbmM6RW5jcnlwdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jI3JzYS1vYWVwLW1nZjFwIi8+DQogICAgICAgICAgICAgICAgPGRzOktleUluZm8geG1sbnM6ZHNpZz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+DQogICAgICAgICAgICAgICAgICA8ZHM6S2V5TmFtZT42MjM1NWZiZDFmNjI0NTAzYzVjOTY3NzQwMmVjY2EwMGVmMWY2Mjc3PC9kczpLZXlOYW1lPg0KICAgICAgICAgICAgICAgIDwvZHM6S2V5SW5mbz4NCiAgICAgICAgICAgICAgICA8eGVuYzpDaXBoZXJEYXRhPg0KICAgICAgICAgICAgICAgICAgPHhlbmM6Q2lwaGVyVmFsdWU+SzBtQkx4Zkx6aUtWVUtFQU9ZZTdENnVWU0NQeTh2eVdWaDNSZWNuUEVTKzhRa0FoT3VSU3VFL0xRcEZyMGh1SS9pQ0V5OXBkZTFRZ2pZREx0akhjdWpLaTJ4R3FXNmprWFcvRXVLb21xV1BQQTJ4WXMxZnBCMXN1NGFYVU9RQjZPSjcwL29EY09zeTgzNGdoRmFCV2lsRThmcXlEQlVCdlcrMkl2YU1VWmFid04vczltVmtXek0zcjMwdGxraExLN2lPcmJHQWxkSUh3RlU1ejdQUFI2Uk8zWTNmSXhqSFU0ME9uTHNKYzN4SXFkTEgzZlhwQzBrZ2k1VXNwTGRxMTRlNU9vWGpMb1BHM0JPM3p3T0FJSjhYTkJXWTV1UW9mNktyS2JjdnRaU1kwZk12UFloWWZOanRSRnk4eTQ5b3ZMOWZ3akNSVERsVDUrYUhxc0NUQnJ3PT08L3hlbmM6Q2lwaGVyVmFsdWU+DQogICAgICAgICAgICAgICAgPC94ZW5jOkNpcGhlckRhdGE+DQogICAgICAgICAgICAgIDwveGVuYzpFbmNyeXB0ZWRLZXk+DQogICAgICAgICAgICA8L2RzOktleUluZm8+DQogICAgICAgICAgICA8eGVuYzpDaXBoZXJEYXRhPg0KICAgICAgICAgICAgICA8eGVuYzpDaXBoZXJWYWx1ZT5aekN1NmF4R2dBWVpIVmY3N05YOGFwWktCL0dKRGV1VjZiRkJ5QlMwQUlnaVhrdkRVQW1MQ3BhYlRBV0JNK3l6MTlvbEE2cnJ5dU9mcjgyZXYyYnpQTlVSdm00U1l4YWh2dUw0UGlibjV3Smt5MEJsNTRWcW1jVStBcWowZEF2T2dxRzF5M1g0d085bjliUnNUdjY5MjFtMGVxUkFGcGg4a0s4TDloaXJLMUJ4WUJZajJSeUZDb0ZEUHhWWjV3eXJhM3E0cW1FNC9FTFFwRlA2bWZVOExYYjB1b1dKVWpHVWVsUzJBYTdiWmlzOHpFcHdvdjRDd3RsTmpsdFFpaDRtdjd0dENBZllxY1FJRnpCVEIrREFhMCtYZ2d4Q0xjZEIzK21RaVJjRUNCZndISEo3Z1JtbnVCRWdlV1QzQ0dLYTNOYjdHTVhPZnV4RktGNXBJZWhXZ28za2ROUUxhbG9yOFJWVzZJOFAvSThmUTMzRmUrTnNIVm5KM3p3U0EvL2E8L3hlbmM6Q2lwaGVyVmFsdWU+DQogICAgICAgICAgICA8L3hlbmM6Q2lwaGVyRGF0YT4NCiAgICAgICAgICA8L3hlbmM6RW5jcnlwdGVkRGF0YT4NCiAgICAgICAgPC9zYW1sOkVuY3J5cHRlZEF0dHJpYnV0ZT4NCiAgICA8L3NhbWw6QXR0cmlidXRlU3RhdGVtZW50Pg0KICA8L3NhbWw6QXNzZXJ0aW9uPg0KPC9zYW1scDpSZXNwb25zZT4= +PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9InBmeGMzMmFlZDY3LTgyMGYtNDI5Ni0wYzIwLTIwNWExMGRkNTc4NyIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIiBEZXN0aW5hdGlvbj0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCI+DQogIDxzYW1sOklzc3Vlcj5odHRwOi8vaWRwLmV4YW1wbGUuY29tLzwvc2FtbDpJc3N1ZXI+DQogIDxzYW1scDpTdGF0dXM+DQogICAgPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPg0KICA8L3NhbWxwOlN0YXR1cz4NCiAgPHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9InBmeDc4NDE5OTFjLWM3M2YtNDAzNS1lMmVlLWMxNzBjMGUxZDNlNCIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIj4NCiAgICA8c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS88L3NhbWw6SXNzdWVyPiAgICANCiAgICA8c2FtbDpTdWJqZWN0Pg0KICAgICAgPHNhbWw6TmFtZUlEIFNQTmFtZVF1YWxpZmllcj0iaGVsbG8uY29tIiBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+c29tZW9uZUBleGFtcGxlLmNvbTwvc2FtbDpOYW1lSUQ+DQogICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+DQogICAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjAyMC0wNi0xN1QxNDo1OToxNFoiIFJlY2lwaWVudD0iaGh0dHA6Ly9zdHVmZi5jb20vZW5kcG9pbnRzL21ldGFkYXRhLnBocCIgSW5SZXNwb25zZVRvPSJfNTdiY2JmNzAtN2IxZi0wMTJlLWM4MjEtNzgyYmNiMTNiYjM4Ii8+DQogICAgICA8L3NhbWw6U3ViamVjdENvbmZpcm1hdGlvbj4NCiAgICA8L3NhbWw6U3ViamVjdD4NCiAgICA8c2FtbDpDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAxMC0wNi0xN1QxNDo1Mzo0NFoiIE5vdE9uT3JBZnRlcj0iMjA5OS0wNi0xN1QxNDo1OToxNFoiPg0KICAgICAgPHNhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj4NCiAgICAgICAgPHNhbWw6QXVkaWVuY2U+aHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvbWV0YWRhdGEucGhwPC9zYW1sOkF1ZGllbmNlPg0KICAgICAgPC9zYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+DQogICAgPC9zYW1sOkNvbmRpdGlvbnM+DQogICAgPHNhbWw6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDExLTA2LTE3VDE0OjU0OjA3WiIgU2Vzc2lvbk5vdE9uT3JBZnRlcj0iMjA5OS0wNi0xN1QyMjo1NDoxNFoiIFNlc3Npb25JbmRleD0iXzUxYmUzNzk2NWZlYjU1NzlkODAzMTQxMDc2OTM2ZGMyZTlkMWQ5OGViZiI+DQogICAgICA8c2FtbDpBdXRobkNvbnRleHQ+DQogICAgICAgIDxzYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkPC9zYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPg0KICAgICAgPC9zYW1sOkF1dGhuQ29udGV4dD4NCiAgICA8L3NhbWw6QXV0aG5TdGF0ZW1lbnQ+DQogICAgPHNhbWw6QXR0cmlidXRlU3RhdGVtZW50Pg0KIDxzYW1sOkVuY3J5cHRlZEF0dHJpYnV0ZSB4bWxuczpzYW1sPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj4NCiAgICAgICAgICA8eGVuYzpFbmNyeXB0ZWREYXRhIHhtbG5zOnhlbmM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jIyIgVHlwZT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjRWxlbWVudCIgSWQ9Il9GMzk2MjVBRjY4QjRGQzA3OENDNzU4MkQyOEQwNUQ5QyI+DQogICAgICAgICAgICA8eGVuYzpFbmNyeXB0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjYWVzMjU2LWNiYyIvPg0KICAgICAgICAgICAgPGRzOktleUluZm8geG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPg0KICAgICAgICAgICAgICA8eGVuYzpFbmNyeXB0ZWRLZXk+DQogICAgICAgICAgICAgICAgPHhlbmM6RW5jcnlwdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jI3JzYS1vYWVwLW1nZjFwIi8+DQogICAgICAgICAgICAgICAgPGRzOktleUluZm8geG1sbnM6ZHNpZz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+DQogICAgICAgICAgICAgICAgICA8ZHM6S2V5TmFtZT42MjM1NWZiZDFmNjI0NTAzYzVjOTY3NzQwMmVjY2EwMGVmMWY2Mjc3PC9kczpLZXlOYW1lPg0KICAgICAgICAgICAgICAgIDwvZHM6S2V5SW5mbz4NCiAgICAgICAgICAgICAgICA8eGVuYzpDaXBoZXJEYXRhPg0KICAgICAgICAgICAgICAgICAgPHhlbmM6Q2lwaGVyVmFsdWU+SzBtQkx4Zkx6aUtWVUtFQU9ZZTdENnVWU0NQeTh2eVdWaDNSZWNuUEVTKzhRa0FoT3VSU3VFL0xRcEZyMGh1SS9pQ0V5OXBkZTFRZ2pZREx0akhjdWpLaTJ4R3FXNmprWFcvRXVLb21xV1BQQTJ4WXMxZnBCMXN1NGFYVU9RQjZPSjcwL29EY09zeTgzNGdoRmFCV2lsRThmcXlEQlVCdlcrMkl2YU1VWmFid04vczltVmtXek0zcjMwdGxraExLN2lPcmJHQWxkSUh3RlU1ejdQUFI2Uk8zWTNmSXhqSFU0ME9uTHNKYzN4SXFkTEgzZlhwQzBrZ2k1VXNwTGRxMTRlNU9vWGpMb1BHM0JPM3p3T0FJSjhYTkJXWTV1UW9mNktyS2JjdnRaU1kwZk12UFloWWZOanRSRnk4eTQ5b3ZMOWZ3akNSVERsVDUrYUhxc0NUQnJ3PT08L3hlbmM6Q2lwaGVyVmFsdWU+DQogICAgICAgICAgICAgICAgPC94ZW5jOkNpcGhlckRhdGE+DQogICAgICAgICAgICAgIDwveGVuYzpFbmNyeXB0ZWRLZXk+DQogICAgICAgICAgICA8L2RzOktleUluZm8+DQogICAgICAgICAgICA8eGVuYzpDaXBoZXJEYXRhPg0KICAgICAgICAgICAgICA8eGVuYzpDaXBoZXJWYWx1ZT5aekN1NmF4R2dBWVpIVmY3N05YOGFwWktCL0dKRGV1VjZiRkJ5QlMwQUlnaVhrdkRVQW1MQ3BhYlRBV0JNK3l6MTlvbEE2cnJ5dU9mcjgyZXYyYnpQTlVSdm00U1l4YWh2dUw0UGlibjV3Smt5MEJsNTRWcW1jVStBcWowZEF2T2dxRzF5M1g0d085bjliUnNUdjY5MjFtMGVxUkFGcGg4a0s4TDloaXJLMUJ4WUJZajJSeUZDb0ZEUHhWWjV3eXJhM3E0cW1FNC9FTFFwRlA2bWZVOExYYjB1b1dKVWpHVWVsUzJBYTdiWmlzOHpFcHdvdjRDd3RsTmpsdFFpaDRtdjd0dENBZllxY1FJRnpCVEIrREFhMCtYZ2d4Q0xjZEIzK21RaVJjRUNCZndISEo3Z1JtbnVCRWdlV1QzQ0dLYTNOYjdHTVhPZnV4RktGNXBJZWhXZ28za2ROUUxhbG9yOFJWVzZJOFAvSThmUTMzRmUrTnNIVm5KM3p3U0EvL2E8L3hlbmM6Q2lwaGVyVmFsdWU+DQogICAgICAgICAgICA8L3hlbmM6Q2lwaGVyRGF0YT4NCiAgICAgICAgICA8L3hlbmM6RW5jcnlwdGVkRGF0YT4NCiAgICAgICAgPC9zYW1sOkVuY3J5cHRlZEF0dHJpYnV0ZT4NCiAgICA8L3NhbWw6QXR0cmlidXRlU3RhdGVtZW50Pg0KICA8L3NhbWw6QXNzZXJ0aW9uPg0KPC9zYW1scDpSZXNwb25zZT4= \ No newline at end of file diff --git a/tests/data/responses/no_audience.xml.base64 b/tests/data/responses/no_audience.xml.base64 index 6ac34337..25550e3b 100644 --- a/tests/data/responses/no_audience.xml.base64 +++ b/tests/data/responses/no_audience.xml.base64 @@ -1 +1 @@ -PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9InBmeGMzMmFlZDY3LTgyMGYtNDI5Ni0wYzIwLTIwNWExMGRkNTc4NyIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIiBEZXN0aW5hdGlvbj0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCI+DQogIDxzYW1sOklzc3Vlcj5odHRwOi8vaWRwLmV4YW1wbGUuY29tLzwvc2FtbDpJc3N1ZXI+DQogIDxzYW1scDpTdGF0dXM+DQogICAgPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPg0KICA8L3NhbWxwOlN0YXR1cz4NCiAgPHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9InBmeDc4NDE5OTFjLWM3M2YtNDAzNS1lMmVlLWMxNzBjMGUxZDNlNCIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIj4NCiAgICA8c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS88L3NhbWw6SXNzdWVyPiAgICANCiAgICA8c2FtbDpTdWJqZWN0Pg0KICAgICAgPHNhbWw6TmFtZUlEIFNQTmFtZVF1YWxpZmllcj0iaGVsbG8uY29tIiBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+c29tZW9uZUBleGFtcGxlLmNvbTwvc2FtbDpOYW1lSUQ+DQogICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+DQogICAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjAyMC0wNi0xN1QxNDo1OToxNFoiIFJlY2lwaWVudD0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCIvPg0KICAgICAgPC9zYW1sOlN1YmplY3RDb25maXJtYXRpb24+DQogICAgPC9zYW1sOlN1YmplY3Q+DQogICAgPHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTAtMDYtMTdUMTQ6NTM6NDRaIiBOb3RPbk9yQWZ0ZXI9IjIwMjEtMDYtMTdUMTQ6NTk6MTRaIj4NCiAgICA8L3NhbWw6Q29uZGl0aW9ucz4NCiAgICA8c2FtbDpBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MDdaIiBTZXNzaW9uTm90T25PckFmdGVyPSIyMDIxLTA2LTE3VDIyOjU0OjE0WiIgU2Vzc2lvbkluZGV4PSJfNTFiZTM3OTY1ZmViNTU3OWQ4MDMxNDEwNzY5MzZkYzJlOWQxZDk4ZWJmIj4NCiAgICAgIDxzYW1sOkF1dGhuQ29udGV4dD4NCiAgICAgICAgPHNhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6UGFzc3dvcmQ8L3NhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+DQogICAgICA8L3NhbWw6QXV0aG5Db250ZXh0Pg0KICAgIDwvc2FtbDpBdXRoblN0YXRlbWVudD4NCiAgICA8c2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+DQogICAgICA8c2FtbDpBdHRyaWJ1dGUgTmFtZT0ibWFpbCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+DQogICAgICAgIDxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPnNvbWVvbmVAZXhhbXBsZS5jb208L3NhbWw6QXR0cmlidXRlVmFsdWU+DQogICAgICA8L3NhbWw6QXR0cmlidXRlPg0KICAgIDwvc2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+DQogIDwvc2FtbDpBc3NlcnRpb24+DQo8L3NhbWxwOlJlc3BvbnNlPg== +PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9InBmeGMzMmFlZDY3LTgyMGYtNDI5Ni0wYzIwLTIwNWExMGRkNTc4NyIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIiBEZXN0aW5hdGlvbj0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCI+DQogIDxzYW1sOklzc3Vlcj5odHRwOi8vaWRwLmV4YW1wbGUuY29tLzwvc2FtbDpJc3N1ZXI+DQogIDxzYW1scDpTdGF0dXM+DQogICAgPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPg0KICA8L3NhbWxwOlN0YXR1cz4NCiAgPHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9InBmeDc4NDE5OTFjLWM3M2YtNDAzNS1lMmVlLWMxNzBjMGUxZDNlNCIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTRaIj4NCiAgICA8c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS88L3NhbWw6SXNzdWVyPiAgICANCiAgICA8c2FtbDpTdWJqZWN0Pg0KICAgICAgPHNhbWw6TmFtZUlEIFNQTmFtZVF1YWxpZmllcj0iaGVsbG8uY29tIiBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+c29tZW9uZUBleGFtcGxlLmNvbTwvc2FtbDpOYW1lSUQ+DQogICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+DQogICAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjAyMC0wNi0xN1QxNDo1OToxNFoiIFJlY2lwaWVudD0iaHR0cDovL3N0dWZmLmNvbS9lbmRwb2ludHMvZW5kcG9pbnRzL2Fjcy5waHAiIEluUmVzcG9uc2VUbz0iXzU3YmNiZjcwLTdiMWYtMDEyZS1jODIxLTc4MmJjYjEzYmIzOCIvPg0KICAgICAgPC9zYW1sOlN1YmplY3RDb25maXJtYXRpb24+DQogICAgPC9zYW1sOlN1YmplY3Q+DQogICAgPHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTAtMDYtMTdUMTQ6NTM6NDRaIiBOb3RPbk9yQWZ0ZXI9IjIwOTktMDYtMTdUMTQ6NTk6MTRaIj4NCiAgICA8L3NhbWw6Q29uZGl0aW9ucz4NCiAgICA8c2FtbDpBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MDdaIiBTZXNzaW9uTm90T25PckFmdGVyPSIyMDk5LTA2LTE3VDIyOjU0OjE0WiIgU2Vzc2lvbkluZGV4PSJfNTFiZTM3OTY1ZmViNTU3OWQ4MDMxNDEwNzY5MzZkYzJlOWQxZDk4ZWJmIj4NCiAgICAgIDxzYW1sOkF1dGhuQ29udGV4dD4NCiAgICAgICAgPHNhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6UGFzc3dvcmQ8L3NhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+DQogICAgICA8L3NhbWw6QXV0aG5Db250ZXh0Pg0KICAgIDwvc2FtbDpBdXRoblN0YXRlbWVudD4NCiAgICA8c2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+DQogICAgICA8c2FtbDpBdHRyaWJ1dGUgTmFtZT0ibWFpbCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+DQogICAgICAgIDxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPnNvbWVvbmVAZXhhbXBsZS5jb208L3NhbWw6QXR0cmlidXRlVmFsdWU+DQogICAgICA8L3NhbWw6QXR0cmlidXRlPg0KICAgIDwvc2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+DQogIDwvc2FtbDpBc3NlcnRpb24+DQo8L3NhbWxwOlJlc3BvbnNlPg== \ No newline at end of file diff --git a/tests/data/responses/unsigned_response_with_miliseconds.xm.base64 b/tests/data/responses/unsigned_response_with_miliseconds.xm.base64 index 76522b7e..1722cbf9 100644 --- a/tests/data/responses/unsigned_response_with_miliseconds.xm.base64 +++ b/tests/data/responses/unsigned_response_with_miliseconds.xm.base64 @@ -1 +1 @@ -PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9InBmeGMzMmFlZDY3LTgyMGYtNDI5Ni0wYzIwLTIwNWExMGRkNTc4NyIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTQuMTIwWiIgRGVzdGluYXRpb249Imh0dHA6Ly9zdHVmZi5jb20vZW5kcG9pbnRzL2VuZHBvaW50cy9hY3MucGhwIiBJblJlc3BvbnNlVG89Il81N2JjYmY3MC03YjFmLTAxMmUtYzgyMS03ODJiY2IxM2JiMzgiPg0KICA8c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS88L3NhbWw6SXNzdWVyPg0KICA8c2FtbHA6U3RhdHVzPg0KICAgIDxzYW1scDpTdGF0dXNDb2RlIFZhbHVlPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6c3RhdHVzOlN1Y2Nlc3MiLz4NCiAgPC9zYW1scDpTdGF0dXM+DQogIDxzYW1sOkFzc2VydGlvbiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIElEPSJwZng3ODQxOTkxYy1jNzNmLTQwMzUtZTJlZS1jMTcwYzBlMWQzZTQiIFZlcnNpb249IjIuMCIgSXNzdWVJbnN0YW50PSIyMDExLTA2LTE3VDE0OjU0OjE0LjEyMFoiPg0KICAgIDxzYW1sOklzc3Vlcj5odHRwOi8vaWRwLmV4YW1wbGUuY29tLzwvc2FtbDpJc3N1ZXI+ICAgIA0KICAgIDxzYW1sOlN1YmplY3Q+DQogICAgICA8c2FtbDpOYW1lSUQgU1BOYW1lUXVhbGlmaWVyPSJoZWxsby5jb20iIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4xOm5hbWVpZC1mb3JtYXQ6ZW1haWxBZGRyZXNzIj5zb21lb25lQGV4YW1wbGUuY29tPC9zYW1sOk5hbWVJRD4NCiAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj4NCiAgICAgICAgPHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgTm90T25PckFmdGVyPSIyMDIwLTA2LTE3VDE0OjU5OjE0WiIgUmVjaXBpZW50PSJodHRwOi8vc3R1ZmYuY29tL2VuZHBvaW50cy9lbmRwb2ludHMvYWNzLnBocCIgSW5SZXNwb25zZVRvPSJfNTdiY2JmNzAtN2IxZi0wMTJlLWM4MjEtNzgyYmNiMTNiYjM4Ii8+DQogICAgICA8L3NhbWw6U3ViamVjdENvbmZpcm1hdGlvbj4NCiAgICA8L3NhbWw6U3ViamVjdD4NCiAgICA8c2FtbDpDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAxMC0wNi0xN1QxNDo1Mzo0NC4xNzNaIiBOb3RPbk9yQWZ0ZXI9IjIwMjEtMDYtMTdUMTQ6NTk6MTQuMjM1WiI+DQogICAgICA8c2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPg0KICAgICAgICA8c2FtbDpBdWRpZW5jZT5odHRwOi8vc3R1ZmYuY29tL2VuZHBvaW50cy9tZXRhZGF0YS5waHA8L3NhbWw6QXVkaWVuY2U+DQogICAgICA8L3NhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj4NCiAgICA8L3NhbWw6Q29uZGl0aW9ucz4NCiAgICA8c2FtbDpBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MDcuMTIwWiIgU2Vzc2lvbk5vdE9uT3JBZnRlcj0iMjAyMS0wNi0xN1QyMjo1NDoxNC4xMjBaIiBTZXNzaW9uSW5kZXg9Il81MWJlMzc5NjVmZWI1NTc5ZDgwMzE0MTA3NjkzNmRjMmU5ZDFkOThlYmYiPg0KICAgICAgPHNhbWw6QXV0aG5Db250ZXh0Pg0KICAgICAgICA8c2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZDwvc2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj4NCiAgICAgIDwvc2FtbDpBdXRobkNvbnRleHQ+DQogICAgPC9zYW1sOkF1dGhuU3RhdGVtZW50Pg0KICAgIDxzYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4NCiAgICAgIDxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJtYWlsIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj4NCiAgICAgICAgPHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+c29tZW9uZUBleGFtcGxlLmNvbTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT4NCiAgICAgIDwvc2FtbDpBdHRyaWJ1dGU+DQogICAgPC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4NCiAgPC9zYW1sOkFzc2VydGlvbj4NCjwvc2FtbHA6UmVzcG9uc2U+ \ No newline at end of file +PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9InBmeGMzMmFlZDY3LTgyMGYtNDI5Ni0wYzIwLTIwNWExMGRkNTc4NyIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MTQuMTIwWiIgRGVzdGluYXRpb249Imh0dHA6Ly9zdHVmZi5jb20vZW5kcG9pbnRzL2VuZHBvaW50cy9hY3MucGhwIiBJblJlc3BvbnNlVG89Il81N2JjYmY3MC03YjFmLTAxMmUtYzgyMS03ODJiY2IxM2JiMzgiPg0KICA8c2FtbDpJc3N1ZXI+aHR0cDovL2lkcC5leGFtcGxlLmNvbS88L3NhbWw6SXNzdWVyPg0KICA8c2FtbHA6U3RhdHVzPg0KICAgIDxzYW1scDpTdGF0dXNDb2RlIFZhbHVlPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6c3RhdHVzOlN1Y2Nlc3MiLz4NCiAgPC9zYW1scDpTdGF0dXM+DQogIDxzYW1sOkFzc2VydGlvbiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIElEPSJwZng3ODQxOTkxYy1jNzNmLTQwMzUtZTJlZS1jMTcwYzBlMWQzZTQiIFZlcnNpb249IjIuMCIgSXNzdWVJbnN0YW50PSIyMDExLTA2LTE3VDE0OjU0OjE0LjEyMFoiPg0KICAgIDxzYW1sOklzc3Vlcj5odHRwOi8vaWRwLmV4YW1wbGUuY29tLzwvc2FtbDpJc3N1ZXI+ICAgIA0KICAgIDxzYW1sOlN1YmplY3Q+DQogICAgICA8c2FtbDpOYW1lSUQgU1BOYW1lUXVhbGlmaWVyPSJoZWxsby5jb20iIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4xOm5hbWVpZC1mb3JtYXQ6ZW1haWxBZGRyZXNzIj5zb21lb25lQGV4YW1wbGUuY29tPC9zYW1sOk5hbWVJRD4NCiAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj4NCiAgICAgICAgPHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgTm90T25PckFmdGVyPSIyMDIwLTA2LTE3VDE0OjU5OjE0WiIgUmVjaXBpZW50PSJodHRwOi8vc3R1ZmYuY29tL2VuZHBvaW50cy9lbmRwb2ludHMvYWNzLnBocCIgSW5SZXNwb25zZVRvPSJfNTdiY2JmNzAtN2IxZi0wMTJlLWM4MjEtNzgyYmNiMTNiYjM4Ii8+DQogICAgICA8L3NhbWw6U3ViamVjdENvbmZpcm1hdGlvbj4NCiAgICA8L3NhbWw6U3ViamVjdD4NCiAgICA8c2FtbDpDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAxMC0wNi0xN1QxNDo1Mzo0NC4xNzNaIiBOb3RPbk9yQWZ0ZXI9IjIwOTktMDYtMTdUMTQ6NTk6MTQuMjM1WiI+DQogICAgICA8c2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPg0KICAgICAgICA8c2FtbDpBdWRpZW5jZT5odHRwOi8vc3R1ZmYuY29tL2VuZHBvaW50cy9tZXRhZGF0YS5waHA8L3NhbWw6QXVkaWVuY2U+DQogICAgICA8L3NhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj4NCiAgICA8L3NhbWw6Q29uZGl0aW9ucz4NCiAgICA8c2FtbDpBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTEtMDYtMTdUMTQ6NTQ6MDcuMTIwWiIgU2Vzc2lvbk5vdE9uT3JBZnRlcj0iMjA5OS0wNi0xN1QyMjo1NDoxNC4xMjBaIiBTZXNzaW9uSW5kZXg9Il81MWJlMzc5NjVmZWI1NTc5ZDgwMzE0MTA3NjkzNmRjMmU5ZDFkOThlYmYiPg0KICAgICAgPHNhbWw6QXV0aG5Db250ZXh0Pg0KICAgICAgICA8c2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZDwvc2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj4NCiAgICAgIDwvc2FtbDpBdXRobkNvbnRleHQ+DQogICAgPC9zYW1sOkF1dGhuU3RhdGVtZW50Pg0KICAgIDxzYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4NCiAgICAgIDxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJtYWlsIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj4NCiAgICAgICAgPHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+c29tZW9uZUBleGFtcGxlLmNvbTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT4NCiAgICAgIDwvc2FtbDpBdHRyaWJ1dGU+DQogICAgPC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4NCiAgPC9zYW1sOkFzc2VydGlvbj4NCjwvc2FtbHA6UmVzcG9uc2U+ \ No newline at end of file From aa1481eae7f315e24d9f96dfd2ee0aca90f27d4f Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Tue, 20 Jul 2021 15:08:55 +0300 Subject: [PATCH 097/205] Replace double-underscored names with single underscores This will avoid Python's class-name-prefix mangling of attributes and methods while still retaining the signal that they're meant for private use of the library. This makes it easier to e.g. subclass classes should one need to, and to access data from outside (while being mindful of the fact that you're accessing notionally private data). Only one change to a test was required (and that test was indeed accessing a private field from outside...). --- src/onelogin/saml2/auth.py | 280 +++++++++--------- src/onelogin/saml2/authn_request.py | 22 +- src/onelogin/saml2/logout_request.py | 40 +-- src/onelogin/saml2/logout_response.py | 42 +-- src/onelogin/saml2/metadata.py | 6 +- src/onelogin/saml2/response.py | 92 +++--- src/onelogin/saml2/settings.py | 246 +++++++-------- .../saml2_tests/authn_request_test.py | 2 +- 8 files changed, 365 insertions(+), 365 deletions(-) diff --git a/src/onelogin/saml2/auth.py b/src/onelogin/saml2/auth.py index cfaee5fd..c284fe46 100644 --- a/src/onelogin/saml2/auth.py +++ b/src/onelogin/saml2/auth.py @@ -52,29 +52,29 @@ def __init__(self, request_data, old_settings=None, custom_base_path=None): :param custom_base_path: Optional. Path where are stored the settings file and the cert folder :type custom_base_path: string """ - self.__request_data = request_data + self._request_data = request_data if isinstance(old_settings, OneLogin_Saml2_Settings): - self.__settings = old_settings + self._settings = old_settings else: - self.__settings = OneLogin_Saml2_Settings(old_settings, custom_base_path) - self.__attributes = dict() - self.__friendlyname_attributes = dict() - self.__nameid = None - self.__nameid_format = None - self.__nameid_nq = None - self.__nameid_spnq = None - self.__session_index = None - self.__session_expiration = None - self.__authenticated = False - self.__errors = [] - self.__error_reason = None - self.__last_request_id = None - self.__last_message_id = None - self.__last_assertion_id = None - self.__last_authn_contexts = [] - self.__last_request = None - self.__last_response = None - self.__last_assertion_not_on_or_after = None + self._settings = OneLogin_Saml2_Settings(old_settings, custom_base_path) + self._attributes = dict() + self._friendlyname_attributes = dict() + self._nameid = None + self._nameid_format = None + self._nameid_nq = None + self._nameid_spnq = None + self._session_index = None + self._session_expiration = None + self._authenticated = False + self._errors = [] + self._error_reason = None + self._last_request_id = None + self._last_message_id = None + self._last_assertion_id = None + self._last_authn_contexts = [] + self._last_request = None + self._last_response = None + self._last_assertion_not_on_or_after = None def get_settings(self): """ @@ -82,7 +82,7 @@ def get_settings(self): :return: Setting info :rtype: OneLogin_Saml2_Setting object """ - return self.__settings + return self._settings def set_strict(self, value): """ @@ -92,22 +92,22 @@ def set_strict(self, value): :type value: bool """ assert isinstance(value, bool) - self.__settings.set_strict(value) + self._settings.set_strict(value) def store_valid_response(self, response): - self.__attributes = response.get_attributes() - self.__friendlyname_attributes = response.get_friendlyname_attributes() - self.__nameid = response.get_nameid() - self.__nameid_format = response.get_nameid_format() - self.__nameid_nq = response.get_nameid_nq() - self.__nameid_spnq = response.get_nameid_spnq() - self.__session_index = response.get_session_index() - self.__session_expiration = response.get_session_not_on_or_after() - self.__last_message_id = response.get_id() - self.__last_assertion_id = response.get_assertion_id() - self.__last_authn_contexts = response.get_authn_contexts() - self.__authenticated = True - self.__last_assertion_not_on_or_after = response.get_assertion_not_on_or_after() + self._attributes = response.get_attributes() + self._friendlyname_attributes = response.get_friendlyname_attributes() + self._nameid = response.get_nameid() + self._nameid_format = response.get_nameid_format() + self._nameid_nq = response.get_nameid_nq() + self._nameid_spnq = response.get_nameid_spnq() + self._session_index = response.get_session_index() + self._session_expiration = response.get_session_not_on_or_after() + self._last_message_id = response.get_id() + self._last_assertion_id = response.get_assertion_id() + self._last_authn_contexts = response.get_authn_contexts() + self._authenticated = True + self._last_assertion_not_on_or_after = response.get_assertion_not_on_or_after() def process_response(self, request_id=None): """ @@ -118,22 +118,22 @@ def process_response(self, request_id=None): :raises: OneLogin_Saml2_Error.SAML_RESPONSE_NOT_FOUND, when a POST with a SAMLResponse is not found """ - self.__errors = [] - self.__error_reason = None + self._errors = [] + self._error_reason = None - if 'post_data' in self.__request_data and 'SAMLResponse' in self.__request_data['post_data']: + if 'post_data' in self._request_data and 'SAMLResponse' in self._request_data['post_data']: # AuthnResponse -- HTTP_POST Binding - response = self.response_class(self.__settings, self.__request_data['post_data']['SAMLResponse']) - self.__last_response = response.get_xml_document() + response = self.response_class(self._settings, self._request_data['post_data']['SAMLResponse']) + self._last_response = response.get_xml_document() - if response.is_valid(self.__request_data, request_id): + if response.is_valid(self._request_data, request_id): self.store_valid_response(response) else: - self.__errors.append('invalid_response') - self.__error_reason = response.get_error() + self._errors.append('invalid_response') + self._error_reason = response.get_error() else: - self.__errors.append('invalid_binding') + self._errors.append('invalid_binding') raise OneLogin_Saml2_Error( 'SAML Response not found, Only supported HTTP_POST Binding', OneLogin_Saml2_Error.SAML_RESPONSE_NOT_FOUND @@ -151,57 +151,57 @@ def process_slo(self, keep_local_session=False, request_id=None, delete_session_ :returns: Redirection url """ - self.__errors = [] - self.__error_reason = None + self._errors = [] + self._error_reason = None - get_data = 'get_data' in self.__request_data and self.__request_data['get_data'] + get_data = 'get_data' in self._request_data and self._request_data['get_data'] if get_data and 'SAMLResponse' in get_data: - logout_response = self.logout_response_class(self.__settings, get_data['SAMLResponse']) - self.__last_response = logout_response.get_xml() + logout_response = self.logout_response_class(self._settings, get_data['SAMLResponse']) + self._last_response = logout_response.get_xml() if not self.validate_response_signature(get_data): - self.__errors.append('invalid_logout_response_signature') - self.__errors.append('Signature validation failed. Logout Response rejected') - elif not logout_response.is_valid(self.__request_data, request_id): - self.__errors.append('invalid_logout_response') - self.__error_reason = logout_response.get_error() + self._errors.append('invalid_logout_response_signature') + self._errors.append('Signature validation failed. Logout Response rejected') + elif not logout_response.is_valid(self._request_data, request_id): + self._errors.append('invalid_logout_response') + self._error_reason = logout_response.get_error() elif logout_response.get_status() != OneLogin_Saml2_Constants.STATUS_SUCCESS: - self.__errors.append('logout_not_success') + self._errors.append('logout_not_success') else: - self.__last_message_id = logout_response.id + self._last_message_id = logout_response.id if not keep_local_session: OneLogin_Saml2_Utils.delete_local_session(delete_session_cb) elif get_data and 'SAMLRequest' in get_data: - logout_request = self.logout_request_class(self.__settings, get_data['SAMLRequest']) - self.__last_request = logout_request.get_xml() + logout_request = self.logout_request_class(self._settings, get_data['SAMLRequest']) + self._last_request = logout_request.get_xml() if not self.validate_request_signature(get_data): - self.__errors.append("invalid_logout_request_signature") - self.__errors.append('Signature validation failed. Logout Request rejected') - elif not logout_request.is_valid(self.__request_data): - self.__errors.append('invalid_logout_request') - self.__error_reason = logout_request.get_error() + self._errors.append("invalid_logout_request_signature") + self._errors.append('Signature validation failed. Logout Request rejected') + elif not logout_request.is_valid(self._request_data): + self._errors.append('invalid_logout_request') + self._error_reason = logout_request.get_error() else: if not keep_local_session: OneLogin_Saml2_Utils.delete_local_session(delete_session_cb) in_response_to = logout_request.id - self.__last_message_id = logout_request.id - response_builder = self.logout_response_class(self.__settings) + self._last_message_id = logout_request.id + response_builder = self.logout_response_class(self._settings) response_builder.build(in_response_to) - self.__last_response = response_builder.get_xml() + self._last_response = response_builder.get_xml() logout_response = response_builder.get_response() parameters = {'SAMLResponse': logout_response} - if 'RelayState' in self.__request_data['get_data']: - parameters['RelayState'] = self.__request_data['get_data']['RelayState'] + if 'RelayState' in self._request_data['get_data']: + parameters['RelayState'] = self._request_data['get_data']['RelayState'] - security = self.__settings.get_security_data() + security = self._settings.get_security_data() if security['logoutResponseSigned']: self.add_response_signature(parameters, security['signatureAlgorithm']) return self.redirect_to(self.get_slo_response_url(), parameters) else: - self.__errors.append('invalid_binding') + self._errors.append('invalid_binding') raise OneLogin_Saml2_Error( 'SAML LogoutRequest/LogoutResponse not found. Only supported HTTP_REDIRECT Binding', OneLogin_Saml2_Error.SAML_LOGOUTMESSAGE_NOT_FOUND @@ -218,9 +218,9 @@ def redirect_to(self, url=None, parameters={}): :returns: Redirection URL """ - if url is None and 'RelayState' in self.__request_data['get_data']: - url = self.__request_data['get_data']['RelayState'] - return OneLogin_Saml2_Utils.redirect(url, parameters, request_data=self.__request_data) + if url is None and 'RelayState' in self._request_data['get_data']: + url = self._request_data['get_data']['RelayState'] + return OneLogin_Saml2_Utils.redirect(url, parameters, request_data=self._request_data) def is_authenticated(self): """ @@ -229,7 +229,7 @@ def is_authenticated(self): :returns: True if is authenticated, False if not :rtype: bool """ - return self.__authenticated + return self._authenticated def get_attributes(self): """ @@ -238,7 +238,7 @@ def get_attributes(self): :returns: SAML attributes :rtype: dict """ - return self.__attributes + return self._attributes def get_friendlyname_attributes(self): """ @@ -247,7 +247,7 @@ def get_friendlyname_attributes(self): :returns: SAML attributes :rtype: dict """ - return self.__friendlyname_attributes + return self._friendlyname_attributes def get_nameid(self): """ @@ -256,7 +256,7 @@ def get_nameid(self): :returns: NameID :rtype: string|None """ - return self.__nameid + return self._nameid def get_nameid_format(self): """ @@ -265,7 +265,7 @@ def get_nameid_format(self): :returns: NameID Format :rtype: string|None """ - return self.__nameid_format + return self._nameid_format def get_nameid_nq(self): """ @@ -274,7 +274,7 @@ def get_nameid_nq(self): :returns: NameID NameQualifier :rtype: string|None """ - return self.__nameid_nq + return self._nameid_nq def get_nameid_spnq(self): """ @@ -283,7 +283,7 @@ def get_nameid_spnq(self): :returns: NameID SP NameQualifier :rtype: string|None """ - return self.__nameid_spnq + return self._nameid_spnq def get_session_index(self): """ @@ -291,7 +291,7 @@ def get_session_index(self): :returns: The SessionIndex of the assertion :rtype: string """ - return self.__session_index + return self._session_index def get_session_expiration(self): """ @@ -299,14 +299,14 @@ def get_session_expiration(self): :returns: The SessionNotOnOrAfter of the assertion :rtype: unix/posix timestamp|None """ - return self.__session_expiration + return self._session_expiration def get_last_assertion_not_on_or_after(self): """ The NotOnOrAfter value of the valid SubjectConfirmationData node (if any) of the last assertion processed """ - return self.__last_assertion_not_on_or_after + return self._last_assertion_not_on_or_after def get_errors(self): """ @@ -315,7 +315,7 @@ def get_errors(self): :returns: List of errors :rtype: list """ - return self.__errors + return self._errors def get_last_error_reason(self): """ @@ -324,7 +324,7 @@ def get_last_error_reason(self): :returns: Reason of the last error :rtype: None | string """ - return self.__error_reason + return self._error_reason def get_attribute(self, name): """ @@ -337,7 +337,7 @@ def get_attribute(self, name): :rtype: list """ assert isinstance(name, compat.str_type) - return self.__attributes.get(name) + return self._attributes.get(name) def get_friendlyname_attribute(self, friendlyname): """ @@ -350,35 +350,35 @@ def get_friendlyname_attribute(self, friendlyname): :rtype: list """ assert isinstance(friendlyname, compat.str_type) - return self.__friendlyname_attributes.get(friendlyname) + return self._friendlyname_attributes.get(friendlyname) def get_last_request_id(self): """ :returns: The ID of the last Request SAML message generated. :rtype: string """ - return self.__last_request_id + return self._last_request_id def get_last_message_id(self): """ :returns: The ID of the last Response SAML message processed. :rtype: string """ - return self.__last_message_id + return self._last_message_id def get_last_assertion_id(self): """ :returns: The ID of the last assertion processed. :rtype: string """ - return self.__last_assertion_id + return self._last_assertion_id def get_last_authn_contexts(self): """ :returns: The list of authentication contexts sent in the last SAML Response. :rtype: list """ - return self.__last_authn_contexts + return self._last_authn_contexts def login(self, return_to=None, force_authn=False, is_passive=False, set_nameid_policy=True, name_id_value_req=None): """ @@ -402,9 +402,9 @@ def login(self, return_to=None, force_authn=False, is_passive=False, set_nameid_ :returns: Redirection URL :rtype: string """ - authn_request = self.authn_request_class(self.__settings, force_authn, is_passive, set_nameid_policy, name_id_value_req) - self.__last_request = authn_request.get_xml() - self.__last_request_id = authn_request.get_id() + authn_request = self.authn_request_class(self._settings, force_authn, is_passive, set_nameid_policy, name_id_value_req) + self._last_request = authn_request.get_xml() + self._last_request_id = authn_request.get_id() saml_request = authn_request.get_request() parameters = {'SAMLRequest': saml_request} @@ -412,9 +412,9 @@ def login(self, return_to=None, force_authn=False, is_passive=False, set_nameid_ if return_to is not None: parameters['RelayState'] = return_to else: - parameters['RelayState'] = OneLogin_Saml2_Utils.get_self_url_no_query(self.__request_data) + parameters['RelayState'] = OneLogin_Saml2_Utils.get_self_url_no_query(self._request_data) - security = self.__settings.get_security_data() + security = self._settings.get_security_data() if security.get('authnRequestsSigned', False): self.add_request_signature(parameters, security['signatureAlgorithm']) return self.redirect_to(self.get_sso_url(), parameters) @@ -450,30 +450,30 @@ def logout(self, return_to=None, name_id=None, session_index=None, nq=None, name OneLogin_Saml2_Error.SAML_SINGLE_LOGOUT_NOT_SUPPORTED ) - if name_id is None and self.__nameid is not None: - name_id = self.__nameid + if name_id is None and self._nameid is not None: + name_id = self._nameid - if name_id_format is None and self.__nameid_format is not None: - name_id_format = self.__nameid_format + if name_id_format is None and self._nameid_format is not None: + name_id_format = self._nameid_format logout_request = self.logout_request_class( - self.__settings, + self._settings, name_id=name_id, session_index=session_index, nq=nq, name_id_format=name_id_format, spnq=spnq ) - self.__last_request = logout_request.get_xml() - self.__last_request_id = logout_request.id + self._last_request = logout_request.get_xml() + self._last_request_id = logout_request.id parameters = {'SAMLRequest': logout_request.get_request()} if return_to is not None: parameters['RelayState'] = return_to else: - parameters['RelayState'] = OneLogin_Saml2_Utils.get_self_url_no_query(self.__request_data) + parameters['RelayState'] = OneLogin_Saml2_Utils.get_self_url_no_query(self._request_data) - security = self.__settings.get_security_data() + security = self._settings.get_security_data() if security.get('logoutRequestSigned', False): self.add_request_signature(parameters, security['signatureAlgorithm']) return self.redirect_to(slo_url, parameters) @@ -485,7 +485,7 @@ def get_sso_url(self): :returns: An URL, the SSO endpoint of the IdP :rtype: string """ - return self.__settings.get_idp_sso_url() + return self._settings.get_idp_sso_url() def get_slo_url(self): """ @@ -494,7 +494,7 @@ def get_slo_url(self): :returns: An URL, the SLO endpoint of the IdP :rtype: string """ - return self.__settings.get_idp_slo_url() + return self._settings.get_idp_slo_url() def get_slo_response_url(self): """ @@ -503,7 +503,7 @@ def get_slo_response_url(self): :returns: an URL, the SLO return endpoint of the IdP :rtype: string """ - return self.__settings.get_idp_slo_response_url() + return self._settings.get_idp_slo_response_url() def add_request_signature(self, request_data, sign_algorithm=OneLogin_Saml2_Constants.RSA_SHA1): """ @@ -515,7 +515,7 @@ def add_request_signature(self, request_data, sign_algorithm=OneLogin_Saml2_Cons :param sign_algorithm: Signature algorithm method :type sign_algorithm: string """ - return self.__build_signature(request_data, 'SAMLRequest', sign_algorithm) + return self._build_signature(request_data, 'SAMLRequest', sign_algorithm) def add_response_signature(self, response_data, sign_algorithm=OneLogin_Saml2_Constants.RSA_SHA1): """ @@ -526,10 +526,10 @@ def add_response_signature(self, response_data, sign_algorithm=OneLogin_Saml2_Co :param sign_algorithm: Signature algorithm method :type sign_algorithm: string """ - return self.__build_signature(response_data, 'SAMLResponse', sign_algorithm) + return self._build_signature(response_data, 'SAMLResponse', sign_algorithm) @staticmethod - def __build_sign_query_from_qs(query_string, saml_type): + def _build_sign_query_from_qs(query_string, saml_type): """ Build sign query from query string @@ -545,7 +545,7 @@ def __build_sign_query_from_qs(query_string, saml_type): return '&'.join(part for arg in args for part in parts if part.startswith(arg)) @staticmethod - def __build_sign_query(saml_data, relay_state, algorithm, saml_type, lowercase_urlencoding=False): + def _build_sign_query(saml_data, relay_state, algorithm, saml_type, lowercase_urlencoding=False): """ Build sign query @@ -570,7 +570,7 @@ def __build_sign_query(saml_data, relay_state, algorithm, saml_type, lowercase_u sign_data.append('SigAlg=%s' % OneLogin_Saml2_Utils.escape_url(algorithm, lowercase_urlencoding)) return '&'.join(sign_data) - def __build_signature(self, data, saml_type, sign_algorithm=OneLogin_Saml2_Constants.RSA_SHA1): + def _build_signature(self, data, saml_type, sign_algorithm=OneLogin_Saml2_Constants.RSA_SHA1): """ Builds the Signature :param data: The Request data @@ -591,10 +591,10 @@ def __build_signature(self, data, saml_type, sign_algorithm=OneLogin_Saml2_Const OneLogin_Saml2_Error.PRIVATE_KEY_NOT_FOUND ) - msg = self.__build_sign_query(data[saml_type], - data.get('RelayState', None), - sign_algorithm, - saml_type) + msg = self._build_sign_query(data[saml_type], + data.get('RelayState', None), + sign_algorithm, + saml_type) sign_algorithm_transform_map = { OneLogin_Saml2_Constants.DSA_SHA1: xmlsec.Transform.DSA_SHA1, @@ -605,7 +605,7 @@ def __build_signature(self, data, saml_type, sign_algorithm=OneLogin_Saml2_Const } sign_algorithm_transform = sign_algorithm_transform_map.get(sign_algorithm, xmlsec.Transform.RSA_SHA1) - signature = OneLogin_Saml2_Utils.sign_binary(msg, key, sign_algorithm_transform, self.__settings.is_debug_active()) + signature = OneLogin_Saml2_Utils.sign_binary(msg, key, sign_algorithm_transform, self._settings.is_debug_active()) data['Signature'] = OneLogin_Saml2_Utils.b64encode(signature) data['SigAlg'] = sign_algorithm @@ -618,7 +618,7 @@ def validate_request_signature(self, request_data): """ - return self.__validate_signature(request_data, 'SAMLRequest') + return self._validate_signature(request_data, 'SAMLRequest') def validate_response_signature(self, request_data): """ @@ -629,9 +629,9 @@ def validate_response_signature(self, request_data): """ - return self.__validate_signature(request_data, 'SAMLResponse') + return self._validate_signature(request_data, 'SAMLResponse') - def __validate_signature(self, data, saml_type, raise_exceptions=False): + def _validate_signature(self, data, saml_type, raise_exceptions=False): """ Validate Signature @@ -650,7 +650,7 @@ def __validate_signature(self, data, saml_type, raise_exceptions=False): try: signature = data.get('Signature', None) if signature is None: - if self.__settings.is_strict() and self.__settings.get_security_data().get('wantMessagesSigned', False): + if self._settings.is_strict() and self._settings.get_security_data().get('wantMessagesSigned', False): raise OneLogin_Saml2_ValidationError( 'The %s is not signed. Rejected.' % saml_type, OneLogin_Saml2_ValidationError.NO_SIGNED_MESSAGE @@ -666,7 +666,7 @@ def __validate_signature(self, data, saml_type, raise_exceptions=False): if not (exists_x509cert or exists_multix509sign): error_msg = 'In order to validate the sign on the %s, the x509cert of the IdP is required' % saml_type - self.__errors.append(error_msg) + self._errors.append(error_msg) raise OneLogin_Saml2_Error( error_msg, OneLogin_Saml2_Error.CERT_NOT_FOUND @@ -676,16 +676,16 @@ def __validate_signature(self, data, saml_type, raise_exceptions=False): if isinstance(sign_alg, bytes): sign_alg = sign_alg.decode('utf8') - query_string = self.__request_data.get('query_string') - if query_string and self.__request_data.get('validate_signature_from_qs'): - signed_query = self.__build_sign_query_from_qs(query_string, saml_type) + query_string = self._request_data.get('query_string') + if query_string and self._request_data.get('validate_signature_from_qs'): + signed_query = self._build_sign_query_from_qs(query_string, saml_type) else: - lowercase_urlencoding = self.__request_data.get('lowercase_urlencoding', False) - signed_query = self.__build_sign_query(data[saml_type], - data.get('RelayState'), - sign_alg, - saml_type, - lowercase_urlencoding) + lowercase_urlencoding = self._request_data.get('lowercase_urlencoding', False) + signed_query = self._build_sign_query(data[saml_type], + data.get('RelayState'), + sign_alg, + saml_type, + lowercase_urlencoding) if exists_multix509sign: for cert in idp_data['x509certMulti']['signing']: @@ -705,14 +705,14 @@ def __validate_signature(self, data, saml_type, raise_exceptions=False): OneLogin_Saml2_Utils.b64decode(signature), cert, sign_alg, - self.__settings.is_debug_active()): + self._settings.is_debug_active()): raise OneLogin_Saml2_ValidationError( 'Signature validation failed. %s rejected' % saml_type, OneLogin_Saml2_ValidationError.INVALID_SIGNATURE ) return True except Exception as e: - self.__error_reason = str(e) + self._error_reason = str(e) if raise_exceptions: raise e return False @@ -725,11 +725,11 @@ def get_last_response_xml(self, pretty_print_if_possible=False): :rtype: string|None """ response = None - if self.__last_response is not None: - if isinstance(self.__last_response, compat.str_type): - response = self.__last_response + if self._last_response is not None: + if isinstance(self._last_response, compat.str_type): + response = self._last_response else: - response = tostring(self.__last_response, encoding='unicode', pretty_print=pretty_print_if_possible) + response = tostring(self._last_response, encoding='unicode', pretty_print=pretty_print_if_possible) return response def get_last_request_xml(self): @@ -738,4 +738,4 @@ def get_last_request_xml(self): :returns: SAML request XML :rtype: string|None """ - return self.__last_request or None + return self._last_request or None diff --git a/src/onelogin/saml2/authn_request.py b/src/onelogin/saml2/authn_request.py index cdbf2c44..0aa1623d 100644 --- a/src/onelogin/saml2/authn_request.py +++ b/src/onelogin/saml2/authn_request.py @@ -41,13 +41,13 @@ def __init__(self, settings, force_authn=False, is_passive=False, set_nameid_pol :param name_id_value_req: Optional argument. Indicates to the IdP the subject that should be authenticated :type name_id_value_req: string """ - self.__settings = settings + self._settings = settings - sp_data = self.__settings.get_sp_data() - idp_data = self.__settings.get_idp_data() - security = self.__settings.get_security_data() + sp_data = self._settings.get_sp_data() + idp_data = self._settings.get_idp_data() + security = self._settings.get_security_data() - self.__id = self._generate_request_id() + self._id = self._generate_request_id() issue_instant = OneLogin_Saml2_Utils.parse_time_to_SAML(OneLogin_Saml2_Utils.now()) destination = idp_data['singleSignOnService']['url'] @@ -112,7 +112,7 @@ def __init__(self, settings, force_authn=False, is_passive=False, set_nameid_pol request = OneLogin_Saml2_Templates.AUTHN_REQUEST % \ { - 'id': self.__id, + 'id': self._id, 'provider_name': provider_name_str, 'force_authn_str': force_authn_str, 'is_passive_str': is_passive_str, @@ -127,7 +127,7 @@ def __init__(self, settings, force_authn=False, is_passive=False, set_nameid_pol 'acs_binding': sp_data['assertionConsumerService'].get('binding', 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST') } - self.__authn_request = request + self._authn_request = request def _generate_request_id(self): """ @@ -144,9 +144,9 @@ def get_request(self, deflate=True): :rtype: str object """ if deflate: - request = OneLogin_Saml2_Utils.deflate_and_base64_encode(self.__authn_request) + request = OneLogin_Saml2_Utils.deflate_and_base64_encode(self._authn_request) else: - request = OneLogin_Saml2_Utils.b64encode(self.__authn_request) + request = OneLogin_Saml2_Utils.b64encode(self._authn_request) return request def get_id(self): @@ -155,7 +155,7 @@ def get_id(self): :return: AuthNRequest ID :rtype: string """ - return self.__id + return self._id def get_xml(self): """ @@ -163,4 +163,4 @@ def get_xml(self): :return: XML request body :rtype: string """ - return self.__authn_request + return self._authn_request diff --git a/src/onelogin/saml2/logout_request.py b/src/onelogin/saml2/logout_request.py index 3ed3fb15..84cdd86b 100644 --- a/src/onelogin/saml2/logout_request.py +++ b/src/onelogin/saml2/logout_request.py @@ -50,14 +50,14 @@ def __init__(self, settings, request=None, name_id=None, session_index=None, nq= :param spnq: SP Name Qualifier :type: string """ - self.__settings = settings - self.__error = None + self._settings = settings + self._error = None self.id = None if request is None: - sp_data = self.__settings.get_sp_data() - idp_data = self.__settings.get_idp_data() - security = self.__settings.get_security_data() + sp_data = self._settings.get_sp_data() + idp_data = self._settings.get_idp_data() + security = self._settings.get_security_data() self.id = self._generate_request_id() @@ -71,7 +71,7 @@ def __init__(self, settings, request=None, name_id=None, session_index=None, nq= if exists_multix509enc: cert = idp_data['x509certMulti']['encryption'][0] else: - cert = self.__settings.get_idp_cert() + cert = self._settings.get_idp_cert() if name_id is not None: if not name_id_format and sp_data['NameIDFormat'] != OneLogin_Saml2_Constants.NAMEID_UNSPECIFIED: @@ -109,7 +109,7 @@ def __init__(self, settings, request=None, name_id=None, session_index=None, nq= { 'id': self.id, 'issue_instant': issue_instant, - 'single_logout_url': self.__settings.get_idp_slo_url(), + 'single_logout_url': self._settings.get_idp_slo_url(), 'entity_id': sp_data['entityId'], 'name_id': name_id_obj, 'session_index': session_index_str, @@ -118,7 +118,7 @@ def __init__(self, settings, request=None, name_id=None, session_index=None, nq= logout_request = OneLogin_Saml2_Utils.decode_base64_and_inflate(request, ignore_zip=True) self.id = self.get_id(logout_request) - self.__logout_request = compat.to_string(logout_request) + self._logout_request = compat.to_string(logout_request) def get_request(self, deflate=True): """ @@ -129,9 +129,9 @@ def get_request(self, deflate=True): :rtype: str object """ if deflate: - request = OneLogin_Saml2_Utils.deflate_and_base64_encode(self.__logout_request) + request = OneLogin_Saml2_Utils.deflate_and_base64_encode(self._logout_request) else: - request = OneLogin_Saml2_Utils.b64encode(self.__logout_request) + request = OneLogin_Saml2_Utils.b64encode(self._logout_request) return request def get_xml(self): @@ -141,7 +141,7 @@ def get_xml(self): :return: XML request body :rtype: string """ - return self.__logout_request + return self._logout_request @classmethod def get_id(cls, request): @@ -279,24 +279,24 @@ def is_valid(self, request_data, raise_exceptions=False): :return: If the Logout Request is or not valid :rtype: boolean """ - self.__error = None + self._error = None try: - root = OneLogin_Saml2_XML.to_etree(self.__logout_request) + root = OneLogin_Saml2_XML.to_etree(self._logout_request) - idp_data = self.__settings.get_idp_data() + idp_data = self._settings.get_idp_data() idp_entity_id = idp_data['entityId'] get_data = ('get_data' in request_data and request_data['get_data']) or dict() - if self.__settings.is_strict(): - res = OneLogin_Saml2_XML.validate_xml(root, 'saml-schema-protocol-2.0.xsd', self.__settings.is_debug_active()) + if self._settings.is_strict(): + res = OneLogin_Saml2_XML.validate_xml(root, 'saml-schema-protocol-2.0.xsd', self._settings.is_debug_active()) if isinstance(res, str): raise OneLogin_Saml2_ValidationError( 'Invalid SAML Logout Request. Not match the saml-schema-protocol-2.0.xsd', OneLogin_Saml2_ValidationError.INVALID_XML_FORMAT ) - security = self.__settings.get_security_data() + security = self._settings.get_security_data() current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data) @@ -345,8 +345,8 @@ def is_valid(self, request_data, raise_exceptions=False): return True except Exception as err: # pylint: disable=R0801 - self.__error = str(err) - debug = self.__settings.is_debug_active() + self._error = str(err) + debug = self._settings.is_debug_active() if debug: print(err) if raise_exceptions: @@ -357,7 +357,7 @@ def get_error(self): """ After executing a validation process, if it fails this method returns the cause """ - return self.__error + return self._error def _generate_request_id(self): """ diff --git a/src/onelogin/saml2/logout_response.py b/src/onelogin/saml2/logout_response.py index bd942200..e9368e8a 100644 --- a/src/onelogin/saml2/logout_response.py +++ b/src/onelogin/saml2/logout_response.py @@ -33,13 +33,13 @@ def __init__(self, settings, response=None): * (string) response. An UUEncoded SAML Logout response from the IdP. """ - self.__settings = settings - self.__error = None + self._settings = settings + self._error = None self.id = None if response is not None: - self.__logout_response = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(response, ignore_zip=True)) - self.document = OneLogin_Saml2_XML.to_etree(self.__logout_response) + self._logout_response = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(response, ignore_zip=True)) + self.document = OneLogin_Saml2_XML.to_etree(self._logout_response) self.id = self.document.get('ID', None) def get_issuer(self): @@ -49,7 +49,7 @@ def get_issuer(self): :rtype: string """ issuer = None - issuer_nodes = self.__query('/samlp:LogoutResponse/saml:Issuer') + issuer_nodes = self._query('/samlp:LogoutResponse/saml:Issuer') if len(issuer_nodes) == 1: issuer = OneLogin_Saml2_XML.element_text(issuer_nodes[0]) return issuer @@ -60,7 +60,7 @@ def get_status(self): :return: The Status :rtype: string """ - entries = self.__query('/samlp:LogoutResponse/samlp:Status/samlp:StatusCode') + entries = self._query('/samlp:LogoutResponse/samlp:Status/samlp:StatusCode') if len(entries) == 0: return None status = entries[0].attrib['Value'] @@ -78,21 +78,21 @@ def is_valid(self, request_data, request_id=None, raise_exceptions=False): :return: Returns if the SAML LogoutResponse is or not valid :rtype: boolean """ - self.__error = None + self._error = None try: - idp_data = self.__settings.get_idp_data() + idp_data = self._settings.get_idp_data() idp_entity_id = idp_data['entityId'] get_data = request_data['get_data'] - if self.__settings.is_strict(): - res = OneLogin_Saml2_XML.validate_xml(self.document, 'saml-schema-protocol-2.0.xsd', self.__settings.is_debug_active()) + if self._settings.is_strict(): + res = OneLogin_Saml2_XML.validate_xml(self.document, 'saml-schema-protocol-2.0.xsd', self._settings.is_debug_active()) if isinstance(res, str): raise OneLogin_Saml2_ValidationError( 'Invalid SAML Logout Request. Not match the saml-schema-protocol-2.0.xsd', OneLogin_Saml2_ValidationError.INVALID_XML_FORMAT ) - security = self.__settings.get_security_data() + security = self._settings.get_security_data() # Check if the InResponseTo of the Logout Response matches the ID of the Logout Request (requestId) if provided in_response_to = self.get_in_response_to() @@ -134,15 +134,15 @@ def is_valid(self, request_data, request_id=None, raise_exceptions=False): return True # pylint: disable=R0801 except Exception as err: - self.__error = str(err) - debug = self.__settings.is_debug_active() + self._error = str(err) + debug = self._settings.is_debug_active() if debug: print(err) if raise_exceptions: raise return False - def __query(self, query): + def _query(self, query): """ Extracts a node from the Etree (Logout Response Message) :param query: Xpath Expression @@ -158,7 +158,7 @@ def build(self, in_response_to): :param in_response_to: InResponseTo value for the Logout Response. :type in_response_to: string """ - sp_data = self.__settings.get_sp_data() + sp_data = self._settings.get_sp_data() self.id = self._generate_request_id() @@ -168,13 +168,13 @@ def build(self, in_response_to): { 'id': self.id, 'issue_instant': issue_instant, - 'destination': self.__settings.get_idp_slo_response_url(), + 'destination': self._settings.get_idp_slo_response_url(), 'in_response_to': in_response_to, 'entity_id': sp_data['entityId'], 'status': "urn:oasis:names:tc:SAML:2.0:status:Success" } - self.__logout_response = logout_response + self._logout_response = logout_response def get_in_response_to(self): """ @@ -193,16 +193,16 @@ def get_response(self, deflate=True): :rtype: string """ if deflate: - response = OneLogin_Saml2_Utils.deflate_and_base64_encode(self.__logout_response) + response = OneLogin_Saml2_Utils.deflate_and_base64_encode(self._logout_response) else: - response = OneLogin_Saml2_Utils.b64encode(self.__logout_response) + response = OneLogin_Saml2_Utils.b64encode(self._logout_response) return response def get_error(self): """ After executing a validation process, if it fails this method returns the cause """ - return self.__error + return self._error def get_xml(self): """ @@ -211,7 +211,7 @@ def get_xml(self): :return: XML response body :rtype: string """ - return self.__logout_response + return self._logout_response def _generate_request_id(self): """ diff --git a/src/onelogin/saml2/metadata.py b/src/onelogin/saml2/metadata.py index bd839c58..34c5f2c1 100644 --- a/src/onelogin/saml2/metadata.py +++ b/src/onelogin/saml2/metadata.py @@ -218,7 +218,7 @@ def sign_metadata(metadata, key, cert, sign_algorithm=OneLogin_Saml2_Constants.R return OneLogin_Saml2_Utils.add_sign(metadata, key, cert, False, sign_algorithm, digest_algorithm) @staticmethod - def __add_x509_key_descriptors(root, cert, signing): + def _add_x509_key_descriptors(root, cert, signing): key_descriptor = OneLogin_Saml2_XML.make_child(root, '{%s}KeyDescriptor' % OneLogin_Saml2_Constants.NS_MD) root.remove(key_descriptor) root.insert(0, key_descriptor) @@ -261,6 +261,6 @@ def add_x509_key_descriptors(cls, metadata, cert=None, add_encryption=True): raise Exception('Malformed metadata.') if add_encryption: - cls.__add_x509_key_descriptors(sp_sso_descriptor, cert, False) - cls.__add_x509_key_descriptors(sp_sso_descriptor, cert, True) + cls._add_x509_key_descriptors(sp_sso_descriptor, cert, False) + cls._add_x509_key_descriptors(sp_sso_descriptor, cert, True) return OneLogin_Saml2_XML.to_string(root) diff --git a/src/onelogin/saml2/response.py b/src/onelogin/saml2/response.py index d69242d3..7d372ab6 100644 --- a/src/onelogin/saml2/response.py +++ b/src/onelogin/saml2/response.py @@ -33,8 +33,8 @@ def __init__(self, settings, response): :param response: The base64 encoded, XML string containing the samlp:Response :type response: string """ - self.__settings = settings - self.__error = None + self._settings = settings + self._error = None self.response = OneLogin_Saml2_Utils.b64decode(response) self.document = OneLogin_Saml2_XML.to_etree(self.response) self.decrypted_document = None @@ -42,11 +42,11 @@ def __init__(self, settings, response): self.valid_scd_not_on_or_after = None # Quick check for the presence of EncryptedAssertion - encrypted_assertion_nodes = self.__query('/samlp:Response/saml:EncryptedAssertion') + encrypted_assertion_nodes = self._query('/samlp:Response/saml:EncryptedAssertion') if encrypted_assertion_nodes: decrypted_document = deepcopy(self.document) self.encrypted = True - self.decrypted_document = self.__decrypt_assertion(decrypted_document) + self.decrypted_document = self._decrypt_assertion(decrypted_document) def is_valid(self, request_data, request_id=None, raise_exceptions=False): """ @@ -64,7 +64,7 @@ def is_valid(self, request_data, request_id=None, raise_exceptions=False): :returns: True if the SAML Response is valid, False if not :rtype: bool """ - self.__error = None + self._error = None try: # Checks SAML version if self.document.get('Version', None) != '2.0': @@ -90,9 +90,9 @@ def is_valid(self, request_data, request_id=None, raise_exceptions=False): OneLogin_Saml2_ValidationError.WRONG_NUMBER_OF_ASSERTIONS ) - idp_data = self.__settings.get_idp_data() + idp_data = self._settings.get_idp_data() idp_entity_id = idp_data['entityId'] - sp_data = self.__settings.get_sp_data() + sp_data = self._settings.get_sp_data() sp_entity_id = sp_data['entityId'] signed_elements = self.process_signed_elements() @@ -100,9 +100,9 @@ def is_valid(self, request_data, request_id=None, raise_exceptions=False): has_signed_response = '{%s}Response' % OneLogin_Saml2_Constants.NS_SAMLP in signed_elements has_signed_assertion = '{%s}Assertion' % OneLogin_Saml2_Constants.NS_SAML in signed_elements - if self.__settings.is_strict(): + if self._settings.is_strict(): no_valid_xml_msg = 'Invalid SAML Response. Not match the saml-schema-protocol-2.0.xsd' - res = OneLogin_Saml2_XML.validate_xml(self.document, 'saml-schema-protocol-2.0.xsd', self.__settings.is_debug_active()) + res = OneLogin_Saml2_XML.validate_xml(self.document, 'saml-schema-protocol-2.0.xsd', self._settings.is_debug_active()) if isinstance(res, str): raise OneLogin_Saml2_ValidationError( no_valid_xml_msg, @@ -111,14 +111,14 @@ def is_valid(self, request_data, request_id=None, raise_exceptions=False): # If encrypted, check also the decrypted document if self.encrypted: - res = OneLogin_Saml2_XML.validate_xml(self.decrypted_document, 'saml-schema-protocol-2.0.xsd', self.__settings.is_debug_active()) + res = OneLogin_Saml2_XML.validate_xml(self.decrypted_document, 'saml-schema-protocol-2.0.xsd', self._settings.is_debug_active()) if isinstance(res, str): raise OneLogin_Saml2_ValidationError( no_valid_xml_msg, OneLogin_Saml2_ValidationError.INVALID_XML_FORMAT ) - security = self.__settings.get_security_data() + security = self._settings.get_security_data() current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data) # Check if the InResponseTo of the Response matchs the ID of the AuthNRequest (requestId) if provided @@ -137,7 +137,7 @@ def is_valid(self, request_data, request_id=None, raise_exceptions=False): ) if security['wantNameIdEncrypted']: - encrypted_nameid_nodes = self.__query_assertion('/saml:Subject/saml:EncryptedID/xenc:EncryptedData') + encrypted_nameid_nodes = self._query_assertion('/saml:Subject/saml:EncryptedID/xenc:EncryptedData') if len(encrypted_nameid_nodes) != 1: raise OneLogin_Saml2_ValidationError( 'The NameID of the Response is not encrypted and the SP require it', @@ -174,14 +174,14 @@ def is_valid(self, request_data, request_id=None, raise_exceptions=False): ) # Checks that there is at least one AttributeStatement if required - attribute_statement_nodes = self.__query_assertion('/saml:AttributeStatement') + attribute_statement_nodes = self._query_assertion('/saml:AttributeStatement') if security.get('wantAttributeStatement', True) and not attribute_statement_nodes: raise OneLogin_Saml2_ValidationError( 'There is no AttributeStatement on the Response', OneLogin_Saml2_ValidationError.NO_ATTRIBUTESTATEMENT ) - encrypted_attributes_nodes = self.__query_assertion('/saml:AttributeStatement/saml:EncryptedAttribute') + encrypted_attributes_nodes = self._query_assertion('/saml:AttributeStatement/saml:EncryptedAttribute') if encrypted_attributes_nodes: raise OneLogin_Saml2_ValidationError( 'There is an EncryptedAttribute in the Response and this SP not support them', @@ -236,7 +236,7 @@ def is_valid(self, request_data, request_id=None, raise_exceptions=False): # Checks the SubjectConfirmation, at least one SubjectConfirmation must be valid any_subject_confirmation = False - subject_confirmation_nodes = self.__query_assertion('/saml:Subject/saml:SubjectConfirmation') + subject_confirmation_nodes = self._query_assertion('/saml:Subject/saml:SubjectConfirmation') for scn in subject_confirmation_nodes: method = scn.get('Method', None) @@ -293,7 +293,7 @@ def is_valid(self, request_data, request_id=None, raise_exceptions=False): OneLogin_Saml2_ValidationError.NO_SIGNATURE_FOUND ) else: - cert = self.__settings.get_idp_cert() + cert = self._settings.get_idp_cert() fingerprint = idp_data.get('certFingerprint', None) if fingerprint: fingerprint = OneLogin_Saml2_Utils.format_finger_print(fingerprint) @@ -319,8 +319,8 @@ def is_valid(self, request_data, request_id=None, raise_exceptions=False): return True except Exception as err: - self.__error = str(err) - debug = self.__settings.is_debug_active() + self._error = str(err) + debug = self._settings.is_debug_active() if debug: print(err) if raise_exceptions: @@ -351,7 +351,7 @@ def check_one_condition(self): """ Checks that the samlp:Response/saml:Assertion/saml:Conditions element exists and is unique. """ - condition_nodes = self.__query_assertion('/saml:Conditions') + condition_nodes = self._query_assertion('/saml:Conditions') if len(condition_nodes) == 1: return True else: @@ -361,7 +361,7 @@ def check_one_authnstatement(self): """ Checks that the samlp:Response/saml:Assertion/saml:AuthnStatement element exists and is unique. """ - authnstatement_nodes = self.__query_assertion('/saml:AuthnStatement') + authnstatement_nodes = self._query_assertion('/saml:AuthnStatement') if len(authnstatement_nodes) == 1: return True else: @@ -374,7 +374,7 @@ def get_audiences(self): :returns: The valid audiences for the SAML Response :rtype: list """ - audience_nodes = self.__query_assertion('/saml:Conditions/saml:AudienceRestriction/saml:Audience') + audience_nodes = self._query_assertion('/saml:Conditions/saml:AudienceRestriction/saml:Audience') return [OneLogin_Saml2_XML.element_text(node) for node in audience_nodes if OneLogin_Saml2_XML.element_text(node) is not None] def get_authn_contexts(self): @@ -384,7 +384,7 @@ def get_authn_contexts(self): :returns: The authentication classes for the SAML Response :rtype: list """ - authn_context_nodes = self.__query_assertion('/saml:AuthnStatement/saml:AuthnContext/saml:AuthnContextClassRef') + authn_context_nodes = self._query_assertion('/saml:AuthnStatement/saml:AuthnContext/saml:AuthnContextClassRef') return [OneLogin_Saml2_XML.element_text(node) for node in authn_context_nodes] def get_in_response_to(self): @@ -416,7 +416,7 @@ def get_issuers(self): OneLogin_Saml2_ValidationError.ISSUER_MULTIPLE_IN_RESPONSE ) - assertion_issuer_nodes = self.__query_assertion('/saml:Issuer') + assertion_issuer_nodes = self._query_assertion('/saml:Issuer') if len(assertion_issuer_nodes) == 1: issuer_value = OneLogin_Saml2_XML.element_text(assertion_issuer_nodes[0]) if issuer_value: @@ -439,18 +439,18 @@ def get_nameid_data(self): nameid = None nameid_data = {} - encrypted_id_data_nodes = self.__query_assertion('/saml:Subject/saml:EncryptedID/xenc:EncryptedData') + encrypted_id_data_nodes = self._query_assertion('/saml:Subject/saml:EncryptedID/xenc:EncryptedData') if encrypted_id_data_nodes: encrypted_data = encrypted_id_data_nodes[0] - key = self.__settings.get_sp_key() + key = self._settings.get_sp_key() nameid = OneLogin_Saml2_Utils.decrypt_element(encrypted_data, key) else: - nameid_nodes = self.__query_assertion('/saml:Subject/saml:NameID') + nameid_nodes = self._query_assertion('/saml:Subject/saml:NameID') if nameid_nodes: nameid = nameid_nodes[0] - is_strict = self.__settings.is_strict() - want_nameid = self.__settings.get_security_data().get('wantNameId', True) + is_strict = self._settings.is_strict() + want_nameid = self._settings.get_security_data().get('wantNameId', True) if nameid is None: if is_strict and want_nameid: raise OneLogin_Saml2_ValidationError( @@ -469,7 +469,7 @@ def get_nameid_data(self): value = nameid.get(attr, None) if value: if is_strict and attr == 'SPNameQualifier': - sp_data = self.__settings.get_sp_data() + sp_data = self._settings.get_sp_data() sp_entity_id = sp_data.get('entityId', '') if sp_entity_id != value: raise OneLogin_Saml2_ValidationError( @@ -541,7 +541,7 @@ def get_session_not_on_or_after(self): :rtype: time|None """ not_on_or_after = None - authn_statement_nodes = self.__query_assertion('/saml:AuthnStatement[@SessionNotOnOrAfter]') + authn_statement_nodes = self._query_assertion('/saml:AuthnStatement[@SessionNotOnOrAfter]') if authn_statement_nodes: not_on_or_after = OneLogin_Saml2_Utils.parse_SAML_to_time(authn_statement_nodes[0].get('SessionNotOnOrAfter')) return not_on_or_after @@ -563,7 +563,7 @@ def get_session_index(self): :rtype: string|None """ session_index = None - authn_statement_nodes = self.__query_assertion('/saml:AuthnStatement[@SessionIndex]') + authn_statement_nodes = self._query_assertion('/saml:AuthnStatement[@SessionIndex]') if authn_statement_nodes: session_index = authn_statement_nodes[0].get('SessionIndex') return session_index @@ -583,9 +583,9 @@ def get_friendlyname_attributes(self): return self._get_attributes('FriendlyName') def _get_attributes(self, attr_name): - allow_duplicates = self.__settings.get_security_data().get('allowRepeatAttributeName', False) + allow_duplicates = self._settings.get_security_data().get('allowRepeatAttributeName', False) attributes = {} - attribute_nodes = self.__query_assertion('/saml:AttributeStatement/saml:Attribute') + attribute_nodes = self._query_assertion('/saml:AttributeStatement/saml:Attribute') for attribute_node in attribute_nodes: attr_key = attribute_node.get(attr_name) if attr_key: @@ -645,7 +645,7 @@ def process_signed_elements(self): :returns: The signed elements tag names :rtype: list """ - sign_nodes = self.__query('//ds:Signature') + sign_nodes = self._query('//ds:Signature') signed_elements = [] verified_seis = [] @@ -737,7 +737,7 @@ def validate_signed_elements(self, signed_elements): ) if assertion_tag in signed_elements: - expected_signature_nodes = self.__query(OneLogin_Saml2_Utils.ASSERTION_SIGNATURE_XPATH) + expected_signature_nodes = self._query(OneLogin_Saml2_Utils.ASSERTION_SIGNATURE_XPATH) if len(expected_signature_nodes) != 1: raise OneLogin_Saml2_ValidationError( 'Unexpected number of Assertion signatures found. SAML Response rejected.', @@ -754,7 +754,7 @@ def validate_timestamps(self): :returns: True if the condition is valid, False otherwise :rtype: bool """ - conditions_nodes = self.__query_assertion('/saml:Conditions') + conditions_nodes = self._query_assertion('/saml:Conditions') for conditions_node in conditions_nodes: nb_attr = conditions_node.get('NotBefore') @@ -771,7 +771,7 @@ def validate_timestamps(self): ) return True - def __query_assertion(self, xpath_expr): + def _query_assertion(self, xpath_expr): """ Extracts nodes that match the query from the Assertion @@ -785,13 +785,13 @@ def __query_assertion(self, xpath_expr): assertion_expr = '/saml:Assertion' signature_expr = '/ds:Signature/ds:SignedInfo/ds:Reference' signed_assertion_query = '/samlp:Response' + assertion_expr + signature_expr - assertion_reference_nodes = self.__query(signed_assertion_query) + assertion_reference_nodes = self._query(signed_assertion_query) tagid = None if not assertion_reference_nodes: # Check if the message is signed signed_message_query = '/samlp:Response' + signature_expr - message_reference_nodes = self.__query(signed_message_query) + message_reference_nodes = self._query(signed_message_query) if message_reference_nodes: message_id = message_reference_nodes[0].get('URI') final_query = "/samlp:Response[@ID=$tagid]/" @@ -804,9 +804,9 @@ def __query_assertion(self, xpath_expr): final_query = '/samlp:Response' + assertion_expr + "[@ID=$tagid]" tagid = assertion_id[1:] final_query += xpath_expr - return self.__query(final_query, tagid) + return self._query(final_query, tagid) - def __query(self, query, tagid=None): + def _query(self, query, tagid=None): """ Extracts nodes that match the query from the Response @@ -825,7 +825,7 @@ def __query(self, query, tagid=None): document = self.document return OneLogin_Saml2_XML.query(document, query, None, tagid) - def __decrypt_assertion(self, xml): + def _decrypt_assertion(self, xml): """ Decrypts the Assertion @@ -835,8 +835,8 @@ def __decrypt_assertion(self, xml): :returns: Decrypted Assertion :rtype: Element """ - key = self.__settings.get_sp_key() - debug = self.__settings.is_debug_active() + key = self._settings.get_sp_key() + debug = self._settings.is_debug_active() if not key: raise OneLogin_Saml2_Error( @@ -885,7 +885,7 @@ def get_error(self): """ After executing a validation process, if it fails this method returns the cause """ - return self.__error + return self._error def get_xml_document(self): """ @@ -916,4 +916,4 @@ def get_assertion_id(self): 'SAML Response must contain 1 assertion', OneLogin_Saml2_ValidationError.WRONG_NUMBER_OF_ASSERTIONS ) - return self.__query_assertion('')[0].get('ID', None) + return self._query_assertion('')[0].get('ID', None) diff --git a/src/onelogin/saml2/settings.py b/src/onelogin/saml2/settings.py index 38e79041..3163995e 100644 --- a/src/onelogin/saml2/settings.py +++ b/src/onelogin/saml2/settings.py @@ -98,37 +98,37 @@ def __init__(self, settings=None, custom_base_path=None, sp_validation_only=Fals :param sp_validation_only: Avoid the IdP validation :type sp_validation_only: boolean """ - self.__sp_validation_only = sp_validation_only - self.__paths = {} - self.__strict = True - self.__debug = False - self.__sp = {} - self.__idp = {} - self.__security = {} - self.__contacts = {} - self.__organization = {} - self.__errors = [] - - self.__load_paths(base_path=custom_base_path) - self.__update_paths(settings) + self._sp_validation_only = sp_validation_only + self._paths = {} + self._strict = True + self._debug = False + self._sp = {} + self._idp = {} + self._security = {} + self._contacts = {} + self._organization = {} + self._errors = [] + + self._load_paths(base_path=custom_base_path) + self._update_paths(settings) if settings is None: try: - valid = self.__load_settings_from_file() + valid = self._load_settings_from_file() except Exception as e: raise e if not valid: raise OneLogin_Saml2_Error( 'Invalid dict settings at the file: %s', OneLogin_Saml2_Error.SETTINGS_INVALID, - ','.join(self.__errors) + ','.join(self._errors) ) elif isinstance(settings, dict): - if not self.__load_settings_from_dict(settings): + if not self._load_settings_from_dict(settings): raise OneLogin_Saml2_Error( 'Invalid dict settings: %s', OneLogin_Saml2_Error.SETTINGS_INVALID, - ','.join(self.__errors) + ','.join(self._errors) ) else: raise OneLogin_Saml2_Error( @@ -137,14 +137,14 @@ def __init__(self, settings=None, custom_base_path=None, sp_validation_only=Fals ) self.format_idp_cert() - if 'x509certMulti' in self.__idp: + if 'x509certMulti' in self._idp: self.format_idp_cert_multi() self.format_sp_cert() - if 'x509certNew' in self.__sp: + if 'x509certNew' in self._sp: self.format_sp_cert_new() self.format_sp_key() - def __load_paths(self, base_path=None): + def _load_paths(self, base_path=None): """ Set the paths of the different folders """ @@ -152,13 +152,13 @@ def __load_paths(self, base_path=None): base_path = dirname(dirname(dirname(__file__))) if not base_path.endswith(sep): base_path += sep - self.__paths = { + self._paths = { 'base': base_path, 'cert': base_path + 'certs' + sep, 'lib': dirname(__file__) + sep } - def __update_paths(self, settings): + def _update_paths(self, settings): """ Set custom paths if necessary """ @@ -168,7 +168,7 @@ def __update_paths(self, settings): if 'custom_base_path' in settings: base_path = settings['custom_base_path'] base_path = join(dirname(__file__), base_path) - self.__load_paths(base_path) + self._load_paths(base_path) def get_base_path(self): """ @@ -177,7 +177,7 @@ def get_base_path(self): :return: The base toolkit folder path :rtype: string """ - return self.__paths['base'] + return self._paths['base'] def get_cert_path(self): """ @@ -186,13 +186,13 @@ def get_cert_path(self): :return: The cert folder path :rtype: string """ - return self.__paths['cert'] + return self._paths['cert'] def set_cert_path(self, path): """ Set a new cert path """ - self.__paths['cert'] = path + self._paths['cert'] = path def get_lib_path(self): """ @@ -201,7 +201,7 @@ def get_lib_path(self): :return: The library folder path :rtype: string """ - return self.__paths['lib'] + return self._paths['lib'] def get_schemas_path(self): """ @@ -210,9 +210,9 @@ def get_schemas_path(self): :return: The schema folder path :rtype: string """ - return self.__paths['lib'] + 'schemas/' + return self._paths['lib'] + 'schemas/' - def __load_settings_from_dict(self, settings): + def _load_settings_from_dict(self, settings): """ Loads settings info from a settings Dict @@ -224,22 +224,22 @@ def __load_settings_from_dict(self, settings): """ errors = self.check_settings(settings) if len(errors) == 0: - self.__errors = [] - self.__sp = settings['sp'] - self.__idp = settings.get('idp', {}) - self.__strict = settings.get('strict', True) - self.__debug = settings.get('debug', False) - self.__security = settings.get('security', {}) - self.__contacts = settings.get('contactPerson', {}) - self.__organization = settings.get('organization', {}) - - self.__add_default_values() + self._errors = [] + self._sp = settings['sp'] + self._idp = settings.get('idp', {}) + self._strict = settings.get('strict', True) + self._debug = settings.get('debug', False) + self._security = settings.get('security', {}) + self._contacts = settings.get('contactPerson', {}) + self._organization = settings.get('organization', {}) + + self._add_default_values() return True - self.__errors = errors + self._errors = errors return False - def __load_settings_from_file(self): + def _load_settings_from_file(self): """ Loads settings info from the settings json file @@ -265,69 +265,69 @@ def __load_settings_from_file(self): with open(advanced_filename, 'r') as json_data: settings.update(json.loads(json_data.read())) # Merge settings - return self.__load_settings_from_dict(settings) + return self._load_settings_from_dict(settings) - def __add_default_values(self): + def _add_default_values(self): """ Add default values if the settings info is not complete """ - self.__sp.setdefault('assertionConsumerService', {}) - self.__sp['assertionConsumerService'].setdefault('binding', OneLogin_Saml2_Constants.BINDING_HTTP_POST) + self._sp.setdefault('assertionConsumerService', {}) + self._sp['assertionConsumerService'].setdefault('binding', OneLogin_Saml2_Constants.BINDING_HTTP_POST) - self.__sp.setdefault('attributeConsumingService', {}) + self._sp.setdefault('attributeConsumingService', {}) - self.__sp.setdefault('singleLogoutService', {}) - self.__sp['singleLogoutService'].setdefault('binding', OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT) + self._sp.setdefault('singleLogoutService', {}) + self._sp['singleLogoutService'].setdefault('binding', OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT) - self.__idp.setdefault('singleLogoutService', {}) + self._idp.setdefault('singleLogoutService', {}) # Related to nameID - self.__sp.setdefault('NameIDFormat', OneLogin_Saml2_Constants.NAMEID_UNSPECIFIED) - self.__security.setdefault('nameIdEncrypted', False) + self._sp.setdefault('NameIDFormat', OneLogin_Saml2_Constants.NAMEID_UNSPECIFIED) + self._security.setdefault('nameIdEncrypted', False) # Metadata format - self.__security.setdefault('metadataValidUntil', None) # None means use default - self.__security.setdefault('metadataCacheDuration', None) # None means use default + self._security.setdefault('metadataValidUntil', None) # None means use default + self._security.setdefault('metadataCacheDuration', None) # None means use default # Sign provided - self.__security.setdefault('authnRequestsSigned', False) - self.__security.setdefault('logoutRequestSigned', False) - self.__security.setdefault('logoutResponseSigned', False) - self.__security.setdefault('signMetadata', False) + self._security.setdefault('authnRequestsSigned', False) + self._security.setdefault('logoutRequestSigned', False) + self._security.setdefault('logoutResponseSigned', False) + self._security.setdefault('signMetadata', False) # Sign expected - self.__security.setdefault('wantMessagesSigned', False) - self.__security.setdefault('wantAssertionsSigned', False) + self._security.setdefault('wantMessagesSigned', False) + self._security.setdefault('wantAssertionsSigned', False) # NameID element expected - self.__security.setdefault('wantNameId', True) + self._security.setdefault('wantNameId', True) # Encrypt expected - self.__security.setdefault('wantAssertionsEncrypted', False) - self.__security.setdefault('wantNameIdEncrypted', False) + self._security.setdefault('wantAssertionsEncrypted', False) + self._security.setdefault('wantNameIdEncrypted', False) # Signature Algorithm - self.__security.setdefault('signatureAlgorithm', OneLogin_Saml2_Constants.RSA_SHA1) + self._security.setdefault('signatureAlgorithm', OneLogin_Saml2_Constants.RSA_SHA1) # Digest Algorithm - self.__security.setdefault('digestAlgorithm', OneLogin_Saml2_Constants.SHA1) + self._security.setdefault('digestAlgorithm', OneLogin_Saml2_Constants.SHA1) # AttributeStatement required by default - self.__security.setdefault('wantAttributeStatement', True) + self._security.setdefault('wantAttributeStatement', True) # Disallow duplicate attribute names by default - self.__security.setdefault('allowRepeatAttributeName', False) + self._security.setdefault('allowRepeatAttributeName', False) - self.__idp.setdefault('x509cert', '') - self.__idp.setdefault('certFingerprint', '') - self.__idp.setdefault('certFingerprintAlgorithm', 'sha1') + self._idp.setdefault('x509cert', '') + self._idp.setdefault('certFingerprint', '') + self._idp.setdefault('certFingerprintAlgorithm', 'sha1') - self.__sp.setdefault('x509cert', '') - self.__sp.setdefault('privateKey', '') + self._sp.setdefault('x509cert', '') + self._sp.setdefault('privateKey', '') - self.__security.setdefault('requestedAuthnContext', True) - self.__security.setdefault('requestedAuthnContextComparison', 'exact') - self.__security.setdefault('failOnAuthnContextMismatch', False) + self._security.setdefault('requestedAuthnContext', True) + self._security.setdefault('requestedAuthnContextComparison', 'exact') + self._security.setdefault('failOnAuthnContextMismatch', False) def check_settings(self, settings): """ @@ -345,7 +345,7 @@ def check_settings(self, settings): if not isinstance(settings, dict) or len(settings) == 0: errors.append('invalid_syntax') else: - if not self.__sp_validation_only: + if not self._sp_validation_only: errors += self.check_idp_settings(settings) sp_errors = self.check_sp_settings(settings) errors += sp_errors @@ -425,9 +425,9 @@ def check_sp_settings(self, settings): errors.append('sp_not_found') else: allow_single_domain_urls = self._get_allow_single_label_domain(settings) - # check_sp_certs uses self.__sp so I add it - old_sp = self.__sp - self.__sp = settings['sp'] + # check_sp_certs uses self._sp so I add it + old_sp = self._sp + self._sp = settings['sp'] sp = settings['sp'] security = settings.get('security', {}) @@ -508,9 +508,9 @@ def check_sp_settings(self, settings): ('url' not in organization or len(organization['url']) == 0): errors.append('organization_not_enought_data') break - # Restores the value that had the self.__sp + # Restores the value that had the self._sp if 'old_sp' in locals(): - self.__sp = old_sp + self._sp = old_sp return errors @@ -562,8 +562,8 @@ def get_sp_key(self): :returns: SP private key :rtype: string or None """ - key = self.__sp.get('privateKey') - key_file_name = self.__paths['cert'] + 'sp.key' + key = self._sp.get('privateKey') + key_file_name = self._paths['cert'] + 'sp.key' if not key and exists(key_file_name): with open(key_file_name) as f: @@ -577,8 +577,8 @@ def get_sp_cert(self): :returns: SP public cert :rtype: string or None """ - cert = self.__sp.get('x509cert') - cert_file_name = self.__paths['cert'] + 'sp.crt' + cert = self._sp.get('x509cert') + cert_file_name = self._paths['cert'] + 'sp.crt' if not cert and exists(cert_file_name): with open(cert_file_name) as f: @@ -593,8 +593,8 @@ def get_sp_cert_new(self): :returns: SP public cert new :rtype: string or None """ - cert = self.__sp.get('x509certNew') - cert_file_name = self.__paths['cert'] + 'sp_new.crt' + cert = self._sp.get('x509certNew') + cert_file_name = self._paths['cert'] + 'sp_new.crt' if not cert and exists(cert_file_name): with open(cert_file_name) as f: @@ -608,7 +608,7 @@ def get_idp_cert(self): :returns: IdP public cert :rtype: string """ - cert = self.__idp.get('x509cert') + cert = self._idp.get('x509cert') cert_file_name = self.get_cert_path() + 'idp.crt' if not cert and exists(cert_file_name): with open(cert_file_name) as f: @@ -622,7 +622,7 @@ def get_idp_data(self): :returns: IdP info :rtype: dict """ - return self.__idp + return self._idp def get_sp_data(self): """ @@ -631,7 +631,7 @@ def get_sp_data(self): :returns: SP info :rtype: dict """ - return self.__sp + return self._sp def get_security_data(self): """ @@ -640,7 +640,7 @@ def get_security_data(self): :returns: Security info :rtype: dict """ - return self.__security + return self._security def get_contacts(self): """ @@ -649,7 +649,7 @@ def get_contacts(self): :returns: Contacts info :rtype: dict """ - return self.__contacts + return self._contacts def get_organization(self): """ @@ -658,7 +658,7 @@ def get_organization(self): :returns: Organization info :rtype: dict """ - return self.__organization + return self._organization def get_sp_metadata(self): """ @@ -667,14 +667,14 @@ def get_sp_metadata(self): :rtype: string """ metadata = self.metadata_class.builder( - self.__sp, self.__security['authnRequestsSigned'], - self.__security['wantAssertionsSigned'], - self.__security['metadataValidUntil'], - self.__security['metadataCacheDuration'], + self._sp, self._security['authnRequestsSigned'], + self._security['wantAssertionsSigned'], + self._security['metadataValidUntil'], + self._security['metadataCacheDuration'], self.get_contacts(), self.get_organization() ) - add_encryption = self.__security['wantNameIdEncrypted'] or self.__security['wantAssertionsEncrypted'] + add_encryption = self._security['wantNameIdEncrypted'] or self._security['wantAssertionsEncrypted'] cert_new = self.get_sp_cert_new() metadata = self.metadata_class.add_x509_key_descriptors(metadata, cert_new, add_encryption) @@ -683,8 +683,8 @@ def get_sp_metadata(self): metadata = self.metadata_class.add_x509_key_descriptors(metadata, cert, add_encryption) # Sign metadata - if 'signMetadata' in self.__security and self.__security['signMetadata'] is not False: - if self.__security['signMetadata'] is True: + if 'signMetadata' in self._security and self._security['signMetadata'] is not False: + if self._security['signMetadata'] is True: # Use the SP's normal key to sign the metadata: if not cert: raise OneLogin_Saml2_Error( @@ -700,16 +700,16 @@ def get_sp_metadata(self): ) else: # Use a custom key to sign the metadata: - if ('keyFileName' not in self.__security['signMetadata'] or - 'certFileName' not in self.__security['signMetadata']): + if ('keyFileName' not in self._security['signMetadata'] or + 'certFileName' not in self._security['signMetadata']): raise OneLogin_Saml2_Error( 'Invalid Setting: signMetadata value of the sp is not valid', OneLogin_Saml2_Error.SETTINGS_INVALID_SYNTAX ) - key_file_name = self.__security['signMetadata']['keyFileName'] - cert_file_name = self.__security['signMetadata']['certFileName'] - key_metadata_file = self.__paths['cert'] + key_file_name - cert_metadata_file = self.__paths['cert'] + cert_file_name + key_file_name = self._security['signMetadata']['keyFileName'] + cert_file_name = self._security['signMetadata']['certFileName'] + key_metadata_file = self._paths['cert'] + key_file_name + cert_metadata_file = self._paths['cert'] + cert_file_name try: with open(key_metadata_file, 'r') as f_metadata_key: @@ -731,8 +731,8 @@ def get_sp_metadata(self): cert_metadata_file ) - signature_algorithm = self.__security['signatureAlgorithm'] - digest_algorithm = self.__security['digestAlgorithm'] + signature_algorithm = self._security['signatureAlgorithm'] + digest_algorithm = self._security['digestAlgorithm'] metadata = self.metadata_class.sign_metadata(metadata, key_metadata, cert_metadata, signature_algorithm, digest_algorithm) @@ -755,7 +755,7 @@ def validate_metadata(self, xml): raise Exception('Empty string supplied as input') errors = [] - root = OneLogin_Saml2_XML.validate_xml(xml, 'saml-schema-metadata-2.0.xsd', self.__debug) + root = OneLogin_Saml2_XML.validate_xml(xml, 'saml-schema-metadata-2.0.xsd', self._debug) if isinstance(root, str): errors.append(root) else: @@ -781,38 +781,38 @@ def format_idp_cert(self): """ Formats the IdP cert. """ - self.__idp['x509cert'] = OneLogin_Saml2_Utils.format_cert(self.__idp['x509cert']) + self._idp['x509cert'] = OneLogin_Saml2_Utils.format_cert(self._idp['x509cert']) def format_idp_cert_multi(self): """ Formats the Multple IdP certs. """ - if 'x509certMulti' in self.__idp: - if 'signing' in self.__idp['x509certMulti']: - for idx in range(len(self.__idp['x509certMulti']['signing'])): - self.__idp['x509certMulti']['signing'][idx] = OneLogin_Saml2_Utils.format_cert(self.__idp['x509certMulti']['signing'][idx]) + if 'x509certMulti' in self._idp: + if 'signing' in self._idp['x509certMulti']: + for idx in range(len(self._idp['x509certMulti']['signing'])): + self._idp['x509certMulti']['signing'][idx] = OneLogin_Saml2_Utils.format_cert(self._idp['x509certMulti']['signing'][idx]) - if 'encryption' in self.__idp['x509certMulti']: - for idx in range(len(self.__idp['x509certMulti']['encryption'])): - self.__idp['x509certMulti']['encryption'][idx] = OneLogin_Saml2_Utils.format_cert(self.__idp['x509certMulti']['encryption'][idx]) + if 'encryption' in self._idp['x509certMulti']: + for idx in range(len(self._idp['x509certMulti']['encryption'])): + self._idp['x509certMulti']['encryption'][idx] = OneLogin_Saml2_Utils.format_cert(self._idp['x509certMulti']['encryption'][idx]) def format_sp_cert(self): """ Formats the SP cert. """ - self.__sp['x509cert'] = OneLogin_Saml2_Utils.format_cert(self.__sp['x509cert']) + self._sp['x509cert'] = OneLogin_Saml2_Utils.format_cert(self._sp['x509cert']) def format_sp_cert_new(self): """ Formats the SP cert. """ - self.__sp['x509certNew'] = OneLogin_Saml2_Utils.format_cert(self.__sp['x509certNew']) + self._sp['x509certNew'] = OneLogin_Saml2_Utils.format_cert(self._sp['x509certNew']) def format_sp_key(self): """ Formats the private key. """ - self.__sp['privateKey'] = OneLogin_Saml2_Utils.format_private_key(self.__sp['privateKey']) + self._sp['privateKey'] = OneLogin_Saml2_Utils.format_private_key(self._sp['privateKey']) def get_errors(self): """ @@ -821,7 +821,7 @@ def get_errors(self): :returns: Errors :rtype: list """ - return self.__errors + return self._errors def set_strict(self, value): """ @@ -832,7 +832,7 @@ def set_strict(self, value): """ assert isinstance(value, bool) - self.__strict = value + self._strict = value def is_strict(self): """ @@ -841,7 +841,7 @@ def is_strict(self): :returns: Strict parameter :rtype: boolean """ - return self.__strict + return self._strict def is_debug_active(self): """ @@ -850,7 +850,7 @@ def is_debug_active(self): :returns: Debug parameter :rtype: boolean """ - return self.__debug + return self._debug def _get_allow_single_label_domain(self, settings): security = settings.get('security', {}) diff --git a/tests/src/OneLogin/saml2_tests/authn_request_test.py b/tests/src/OneLogin/saml2_tests/authn_request_test.py index b23c633a..cbf225a1 100644 --- a/tests/src/OneLogin/saml2_tests/authn_request_test.py +++ b/tests/src/OneLogin/saml2_tests/authn_request_test.py @@ -52,7 +52,7 @@ def testCreateRequest(self): saml_settings = self.loadSettingsJSON() settings = OneLogin_Saml2_Settings(saml_settings) - settings._OneLogin_Saml2_Settings__organization = { + settings._organization = { u'en-US': { u'url': u'http://sp.example.com', u'name': u'sp_test' From 3bd411d9ad00a38d02cfa9e4f550ca011a18b514 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Fri, 23 Jul 2021 02:26:33 +0200 Subject: [PATCH 098/205] Release 1.11.0 --- changelog.md | 12 ++++++++++++ setup.py | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 4114cc04..4571ed31 100644 --- a/changelog.md +++ b/changelog.md @@ -1,4 +1,16 @@ # python3-saml changelog +### 1.11.0 (Jul 23, 2021) +* [#261](https://github.com/onelogin/python3-saml/pull/261) Allow duplicate named attributes, controlled by a new setting +* [#268](https://github.com/onelogin/python3-saml/pull/268) Make the redirect scheme matcher case-insensitive +* [#256](https://github.com/onelogin/python3-saml/pull/256) Improve signature validation process. Add an option to use query string for validation +* [#259](https://github.com/onelogin/python3-saml/pull/259) Add get metadata timeout +* [#246](https://github.com/onelogin/python3-saml/pull/246) Add the ability to change the ProtocolBinding in the authn request. +* [#248](https://github.com/onelogin/python3-saml/pull/248) Move storing the response data into its own method in the Auth class +* Remove the dependency on defusedxml +* [#241](https://github.com/onelogin/python3-saml/pull/241) Improve AttributeConsumingService support +* Update expired dates from test responses +* Migrate from Travis to Github Actions + ### 1.10.1 (Jan 27, 2021) * Fix bug on LogoutRequest class, get_idp_slo_response_url was used instead get_idp_slo_url diff --git a/setup.py b/setup.py index cff7b7c7..c783d224 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ setup( name='python3-saml', - version='1.10.1', + version='1.11.0', description='Onelogin Python Toolkit. Add SAML support to your Python software using this library', classifiers=[ 'Development Status :: 5 - Production/Stable', From f435584496977bb91e2f62aecf3fe22fa47095b9 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Fri, 23 Jul 2021 14:34:21 +0300 Subject: [PATCH 099/205] Deprecate server_port from request data dictionary `server_port` is unnecessary, since the HTTP Host header sent by the client already includes any non-standard port. In addition, when the Python application server is sitting behind a reverse proxy/TLS terminator, SERVER_PORT is likely to be wrong anyway (since it would be the server port of the non-reverse-proxied server). See https://github.com/onelogin/python3-saml/issues/273#issuecomment-885566427 --- README.md | 5 +- demo-django/demo/views.py | 1 - demo-flask/index.py | 4 +- demo-tornado/views.py | 3 +- demo_pyramid/demo_pyramid/views.py | 5 +- src/onelogin/saml2/utils.py | 49 +++++++------------ .../src/OneLogin/saml2_tests/response_test.py | 24 ++++++--- tests/src/OneLogin/saml2_tests/utils_test.py | 7 +-- 8 files changed, 41 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index 3bbf2a1f..05b5faf2 100644 --- a/README.md +++ b/README.md @@ -573,7 +573,6 @@ This parameter has the following scheme: req = { "http_host": "", "script_name": "", - "server_port": "", "get_data": "", "post_data": "", @@ -594,7 +593,6 @@ def prepare_from_django_request(request): return { 'http_host': request.META['HTTP_HOST'], 'script_name': request.META['PATH_INFO'], - 'server_port': request.META['SERVER_PORT'], 'get_data': request.GET.copy(), 'post_data': request.POST.copy() } @@ -602,8 +600,7 @@ def prepare_from_django_request(request): def prepare_from_flask_request(request): url_data = urlparse(request.url) return { - 'http_host': request.host, - 'server_port': url_data.port, + 'http_host': request.netloc, 'script_name': request.path, 'get_data': request.args.copy(), 'post_data': request.form.copy() diff --git a/demo-django/demo/views.py b/demo-django/demo/views.py index 418ab0c8..6003b821 100644 --- a/demo-django/demo/views.py +++ b/demo-django/demo/views.py @@ -20,7 +20,6 @@ def prepare_django_request(request): 'https': 'on' if request.is_secure() else 'off', 'http_host': request.META['HTTP_HOST'], 'script_name': request.META['PATH_INFO'], - 'server_port': request.META['SERVER_PORT'], 'get_data': request.GET.copy(), # Uncomment if using ADFS as IdP, https://github.com/onelogin/python-saml/pull/144 # 'lowercase_urlencoding': True, diff --git a/demo-flask/index.py b/demo-flask/index.py index b523cb92..8a251a0a 100644 --- a/demo-flask/index.py +++ b/demo-flask/index.py @@ -21,11 +21,9 @@ def init_saml_auth(req): def prepare_flask_request(request): # If server is behind proxys or balancers use the HTTP_X_FORWARDED fields - url_data = urlparse(request.url) return { 'https': 'on' if request.scheme == 'https' else 'off', - 'http_host': request.host, - 'server_port': url_data.port, + 'http_host': request.netloc, 'script_name': request.path, 'get_data': request.args.copy(), # Uncomment if using ADFS as IdP, https://github.com/onelogin/python-saml/pull/144 diff --git a/demo-tornado/views.py b/demo-tornado/views.py index 70cfea68..e5d062aa 100644 --- a/demo-tornado/views.py +++ b/demo-tornado/views.py @@ -157,9 +157,8 @@ def prepare_tornado_request(request): result = { 'https': 'on' if request == 'https' else 'off', - 'http_host': tornado.httputil.split_host_and_port(request.host)[0], + 'http_host': request.host, 'script_name': request.path, - 'server_port': tornado.httputil.split_host_and_port(request.host)[1], 'get_data': dataDict, 'post_data': dataDict, 'query_string': request.query diff --git a/demo_pyramid/demo_pyramid/views.py b/demo_pyramid/demo_pyramid/views.py index 6dab4edc..96434b8b 100644 --- a/demo_pyramid/demo_pyramid/views.py +++ b/demo_pyramid/demo_pyramid/views.py @@ -15,19 +15,16 @@ def init_saml_auth(req): def prepare_pyramid_request(request): - # Uncomment this portion to set the request.scheme and request.server_port + # Uncomment this portion to set the request.scheme # based on the supplied `X-Forwarded` headers. # Useful for running behind reverse proxies or balancers. # # if 'X-Forwarded-Proto' in request.headers: # request.scheme = request.headers['X-Forwarded-Proto'] - # if 'X-Forwarded-Port' in request.headers: - # request.server_port = int(request.headers['X-Forwarded-Port']) return { 'https': 'on' if request.scheme == 'https' else 'off', 'http_host': request.host, - 'server_port': request.server_port, 'script_name': request.path, 'get_data': request.GET.copy(), # Uncomment if using ADFS as IdP, https://github.com/onelogin/python-saml/pull/144 diff --git a/src/onelogin/saml2/utils.py b/src/onelogin/saml2/utils.py index db88bd34..51ab4e00 100644 --- a/src/onelogin/saml2/utils.py +++ b/src/onelogin/saml2/utils.py @@ -10,6 +10,7 @@ """ import base64 +import warnings from copy import deepcopy import calendar from datetime import datetime @@ -254,27 +255,25 @@ def get_self_url_host(request_data): :rtype: string """ current_host = OneLogin_Saml2_Utils.get_self_host(request_data) - port = '' - if OneLogin_Saml2_Utils.is_https(request_data): - protocol = 'https' - else: - protocol = 'http' - - if 'server_port' in request_data and request_data['server_port'] is not None: - port_number = str(request_data['server_port']) - port = ':' + port_number + protocol = 'https' if OneLogin_Saml2_Utils.is_https(request_data) else 'http' - if protocol == 'http' and port_number == '80': - port = '' - elif protocol == 'https' and port_number == '443': - port = '' + if request_data.get('server_port') is not None: + warnings.warn( + 'The server_port key in request data is deprecated. ' + 'The http_host key should include a port, if required.', + category=DeprecationWarning, + ) + port_suffix = ':%s' % request_data['server_port'] + if not current_host.endswith(port_suffix): + if not ((protocol == 'https' and port_suffix == ':443') or (protocol == 'http' and port_suffix == ':80')): + current_host += port_suffix - return '%s://%s%s' % (protocol, current_host, port) + return '%s://%s' % (protocol, current_host) @staticmethod def get_self_host(request_data): """ - Returns the current host. + Returns the current host (which may include a port number part). :param request_data: The request as a dict :type: dict @@ -283,22 +282,11 @@ def get_self_host(request_data): :rtype: string """ if 'http_host' in request_data: - current_host = request_data['http_host'] + return request_data['http_host'] elif 'server_name' in request_data: - current_host = request_data['server_name'] - else: - raise Exception('No hostname defined') - - if ':' in current_host: - current_host_data = current_host.split(':') - possible_port = current_host_data[-1] - try: - int(possible_port) - current_host = current_host_data[0] - except ValueError: - current_host = ':'.join(current_host_data) - - return current_host + warnings.warn("The server_name key in request data is undocumented & deprecated.", category=DeprecationWarning) + return request_data['server_name'] + raise Exception('No hostname defined') @staticmethod def is_https(request_data): @@ -312,6 +300,7 @@ def is_https(request_data): :rtype: boolean """ is_https = 'https' in request_data and request_data['https'] != 'off' + # TODO: this use of server_port should be removed too is_https = is_https or ('server_port' in request_data and str(request_data['server_port']) == '443') return is_https diff --git a/tests/src/OneLogin/saml2_tests/response_test.py b/tests/src/OneLogin/saml2_tests/response_test.py index a51f8e60..3f83bc2b 100644 --- a/tests/src/OneLogin/saml2_tests/response_test.py +++ b/tests/src/OneLogin/saml2_tests/response_test.py @@ -4,6 +4,7 @@ # MIT License from base64 import b64decode + from lxml import etree from datetime import datetime from datetime import timedelta @@ -1528,22 +1529,31 @@ def testIsInValidEncIssues(self): self.assertFalse(response_5.is_valid(request_data)) self.assertEqual('The NameID of the Response is not encrypted and the SP require it', response_5.get_error()) + def testIsInValidEncIssues_2(self): settings_info_2 = self.loadSettingsJSON('settings3.json') settings_info_2['strict'] = True settings_info_2['security']['wantNameIdEncrypted'] = True settings_2 = OneLogin_Saml2_Settings(settings_info_2) request_data = { - 'http_host': 'pytoolkit.com', - 'server_port': 8000, 'script_name': '', 'request_uri': '?acs', } - - message_2 = self.file_contents(join(self.data_path, 'responses', 'valid_encrypted_assertion_encrypted_nameid.xml.base64')) - response_6 = OneLogin_Saml2_Response(settings_2, message_2) - self.assertFalse(response_6.is_valid(request_data)) - self.assertEqual('The attributes have expired, based on the SessionNotOnOrAfter of the AttributeStatement of this Response', response_6.get_error()) + for separate_port in (False, True): + if separate_port: + request_data.update({ + 'http_host': 'pytoolkit.com', + 'server_port': 8000, + }) + else: + request_data.update({ + 'http_host': 'pytoolkit.com:8000', + }) + + message_2 = self.file_contents(join(self.data_path, 'responses', 'valid_encrypted_assertion_encrypted_nameid.xml.base64')) + response_6 = OneLogin_Saml2_Response(settings_2, message_2) + self.assertFalse(response_6.is_valid(request_data)) + self.assertEqual('The attributes have expired, based on the SessionNotOnOrAfter of the AttributeStatement of this Response', response_6.get_error()) def testIsInValidCert(self): """ diff --git a/tests/src/OneLogin/saml2_tests/utils_test.py b/tests/src/OneLogin/saml2_tests/utils_test.py index 87112b0c..54d9ae66 100644 --- a/tests/src/OneLogin/saml2_tests/utils_test.py +++ b/tests/src/OneLogin/saml2_tests/utils_test.py @@ -190,7 +190,7 @@ def testGetselfhost(self): request_data = { 'http_host': 'example.com:443' } - self.assertEqual('example.com', OneLogin_Saml2_Utils.get_self_host(request_data)) + self.assertEqual('example.com:443', OneLogin_Saml2_Utils.get_self_host(request_data)) request_data = { 'http_host': 'example.com:ok' @@ -211,11 +211,6 @@ def testisHTTPS(self): } self.assertTrue(OneLogin_Saml2_Utils.is_https(request_data)) - request_data = { - 'server_port': '80' - } - self.assertFalse(OneLogin_Saml2_Utils.is_https(request_data)) - request_data = { 'server_port': '443' } From 562ceedfefdc0e2b9335488cf537ddc3bd8c897d Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Sun, 25 Jul 2021 15:10:32 +0300 Subject: [PATCH 100/205] Add Pip cache to CI --- .github/workflows/python-package.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index ac505be2..38ac7f4c 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -19,14 +19,18 @@ jobs: uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - + - uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/setup.py') }} + restore-keys: | + ${{ runner.os }}-pip- - name: Install dependencies run: | sudo apt-get update -qq sudo apt-get install -qq swig python-dev libxml2-dev libxmlsec1-dev make install-req make install-test - - name: Test run: | make pytest From 69d132c52ba354bb4d9660df2a0606b91ca316bb Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Sun, 25 Jul 2021 15:14:41 +0300 Subject: [PATCH 101/205] Separate linting in CI --- .github/workflows/python-package.yml | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 38ac7f4c..1cc6b96a 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -32,8 +32,27 @@ jobs: make install-req make install-test - name: Test + run: make pytest + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: 3.9 + - uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/setup.py') }} + restore-keys: | + ${{ runner.os }}-pip- + - name: Install dependencies + run: | + sudo apt-get update -qq + sudo apt-get install -qq swig python-dev libxml2-dev libxmlsec1-dev + make install-req + make install-test + - name: Run linters run: | - make pytest make pycodestyle make flake8 - From f7f45a7b6e9a14cbb3ebd8771e945fe022886ccc Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Sun, 25 Jul 2021 15:17:16 +0300 Subject: [PATCH 102/205] Move flake8 ignores to setup.cfg --- Makefile | 2 +- setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 7f3faddb..30f8af09 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ pycodestyle: $(PYCODESTYLE) --ignore=E501,E731,W504 $(SOURCES) --config=$(PEP8_CONFIG) flake8: - $(FLAKE8) --ignore=E501,E731,W504 $(SOURCES) + $(FLAKE8) $(SOURCES) clean: rm -rf .pytest_cache/ diff --git a/setup.cfg b/setup.cfg index e9d41598..36fa9534 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [flake8] -ignore = E731,W504 +ignore = E731,W504,E501 max-complexity = 48 max-line-length = 1900 From 603f95132023a410e77fa8ea77f0a45138b4b28e Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Sun, 25 Jul 2021 15:17:56 +0300 Subject: [PATCH 103/205] Fix typos in makefile causing demos to be ignored by lint --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 30f8af09..45d73ecb 100644 --- a/Makefile +++ b/Makefile @@ -6,9 +6,9 @@ COVERAGE=coverage COVERAGE_CONFIG=tests/coverage.rc PEP8_CONFIG=tests/pep8.rc MAIN_SOURCE=src/onelogin/saml2 -DEMOS=demo-django demo-flask +DEMOS=demo-django demo-flask demo-tornado demo_pyramid TESTS=tests/src/OneLogin/saml2_tests -SOURCES=$(MAIN_SOURCE) $(DEMO) $(TESTS) +SOURCES=$(MAIN_SOURCE) $(DEMOS) $(TESTS) install-req: $(PIP) install --upgrade 'setuptools<45.0.0' From c84af25048554c8f13d94f9fa1544b30a51e78c1 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Fri, 13 Aug 2021 18:36:01 +0200 Subject: [PATCH 104/205] Release 1.12.0 --- changelog.md | 3 +++ setup.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 4571ed31..cc17cb4e 100644 --- a/changelog.md +++ b/changelog.md @@ -1,4 +1,7 @@ # python3-saml changelog +### 1.12.0 (Aug 13, 2021) +* [#276](https://github.com/onelogin/python3-saml/pull/276) Deprecate server_port from request data dictionary + ### 1.11.0 (Jul 23, 2021) * [#261](https://github.com/onelogin/python3-saml/pull/261) Allow duplicate named attributes, controlled by a new setting * [#268](https://github.com/onelogin/python3-saml/pull/268) Make the redirect scheme matcher case-insensitive diff --git a/setup.py b/setup.py index c783d224..4aad2c76 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ setup( name='python3-saml', - version='1.11.0', + version='1.12.0', description='Onelogin Python Toolkit. Add SAML support to your Python software using this library', classifiers=[ 'Development Status :: 5 - Production/Stable', From e77d015ba4ce077787fee321614dee86d78b583d Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Mon, 16 Aug 2021 14:13:32 +0200 Subject: [PATCH 105/205] Restore request.host Flask example hits error: AttributeError: 'Request' object has no attribute 'netloc' --- demo-flask/index.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo-flask/index.py b/demo-flask/index.py index 8a251a0a..5f43292a 100644 --- a/demo-flask/index.py +++ b/demo-flask/index.py @@ -23,7 +23,7 @@ def prepare_flask_request(request): # If server is behind proxys or balancers use the HTTP_X_FORWARDED fields return { 'https': 'on' if request.scheme == 'https' else 'off', - 'http_host': request.netloc, + 'http_host': request.host, 'script_name': request.path, 'get_data': request.args.copy(), # Uncomment if using ADFS as IdP, https://github.com/onelogin/python-saml/pull/144 From 8a47e705365035da0e97f96edd49bb6721035c85 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Mon, 16 Aug 2021 14:19:10 +0200 Subject: [PATCH 106/205] Fix pycodestyle --- demo-flask/index.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/demo-flask/index.py b/demo-flask/index.py index 5f43292a..a10ac8ce 100644 --- a/demo-flask/index.py +++ b/demo-flask/index.py @@ -3,8 +3,6 @@ from flask import (Flask, request, render_template, redirect, session, make_response) -from urllib.parse import urlparse - from onelogin.saml2.auth import OneLogin_Saml2_Auth from onelogin.saml2.utils import OneLogin_Saml2_Utils From c137dddc6f44c9c5ee3e8884e6d53b0376508126 Mon Sep 17 00:00:00 2001 From: Gunesh Pinar Date: Thu, 26 Aug 2021 23:19:37 -0700 Subject: [PATCH 107/205] Implement OneLogin_Saml2_Auth.get_last_assertion_issue_instant() --- README.md | 1 + src/onelogin/saml2/auth.py | 9 +++++++++ src/onelogin/saml2/response.py | 13 +++++++++++++ tests/src/OneLogin/saml2_tests/auth_test.py | 7 +++++-- 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 05b5faf2..3a5f39ff 100644 --- a/README.md +++ b/README.md @@ -970,6 +970,7 @@ Main class of OneLogin Python Toolkit * ***get_last_message_id*** The ID of the last Response SAML message processed. * ***get_last_assertion_id*** The ID of the last assertion processed. * ***get_last_assertion_not_on_or_after*** The ``NotOnOrAfter`` value of the valid ``SubjectConfirmationData`` node (if any) of the last assertion processed (is only calculated with strict = true) +* ***get_last_assertion_issue_instant*** The `IssueInstant` value of the last assertion processed. #### OneLogin_Saml2_Auth - authn_request.py #### diff --git a/src/onelogin/saml2/auth.py b/src/onelogin/saml2/auth.py index c284fe46..6c72932b 100644 --- a/src/onelogin/saml2/auth.py +++ b/src/onelogin/saml2/auth.py @@ -71,6 +71,7 @@ def __init__(self, request_data, old_settings=None, custom_base_path=None): self._last_request_id = None self._last_message_id = None self._last_assertion_id = None + self._last_assertion_issue_instant = None self._last_authn_contexts = [] self._last_request = None self._last_response = None @@ -105,6 +106,7 @@ def store_valid_response(self, response): self._session_expiration = response.get_session_not_on_or_after() self._last_message_id = response.get_id() self._last_assertion_id = response.get_assertion_id() + self._last_assertion_issue_instant = response.get_assertion_issue_instant() self._last_authn_contexts = response.get_authn_contexts() self._authenticated = True self._last_assertion_not_on_or_after = response.get_assertion_not_on_or_after() @@ -373,6 +375,13 @@ def get_last_assertion_id(self): """ return self._last_assertion_id + def get_last_assertion_issue_instant(self): + """ + :returns: The IssueInstant of the last assertion processed. + :rtype: unix/posix timestamp|None + """ + return self._last_assertion_issue_instant + def get_last_authn_contexts(self): """ :returns: The list of authentication contexts sent in the last SAML Response. diff --git a/src/onelogin/saml2/response.py b/src/onelogin/saml2/response.py index 7d372ab6..4ef0418c 100644 --- a/src/onelogin/saml2/response.py +++ b/src/onelogin/saml2/response.py @@ -917,3 +917,16 @@ def get_assertion_id(self): OneLogin_Saml2_ValidationError.WRONG_NUMBER_OF_ASSERTIONS ) return self._query_assertion('')[0].get('ID', None) + + def get_assertion_issue_instant(self): + """ + :returns: the IssueInstant of the assertion in the response + :rtype: unix/posix timestamp|None + """ + if not self.validate_num_assertions(): + raise OneLogin_Saml2_ValidationError( + 'SAML Response must contain 1 assertion', + OneLogin_Saml2_ValidationError.WRONG_NUMBER_OF_ASSERTIONS + ) + issue_instant = self._query_assertion('')[0].get('IssueInstant', None) + return OneLogin_Saml2_Utils.parse_SAML_to_time(issue_instant) diff --git a/tests/src/OneLogin/saml2_tests/auth_test.py b/tests/src/OneLogin/saml2_tests/auth_test.py index ea8cf1d3..87a57f19 100644 --- a/tests/src/OneLogin/saml2_tests/auth_test.py +++ b/tests/src/OneLogin/saml2_tests/auth_test.py @@ -1415,7 +1415,7 @@ def testGetLastLogoutResponse(self): def testGetInfoFromLastResponseReceived(self): """ - Tests the get_last_message_id, get_last_assertion_id and get_last_assertion_not_on_or_after + Tests the get_last_message_id, get_last_assertion_id, get_last_assertion_not_on_or_after and get_last_assertion_issue_instant of the OneLogin_Saml2_Auth class """ settings = self.loadSettingsJSON() @@ -1431,8 +1431,9 @@ def testGetInfoFromLastResponseReceived(self): self.assertEqual(auth.get_last_message_id(), 'pfx42be40bf-39c3-77f0-c6ae-8bf2e23a1a2e') self.assertEqual(auth.get_last_assertion_id(), 'pfx57dfda60-b211-4cda-0f63-6d5deb69e5bb') self.assertIsNone(auth.get_last_assertion_not_on_or_after()) + self.assertEqual(auth.get_last_assertion_issue_instant(), 1392773821) - # NotOnOrAfter is only calculated with strict = true + # NotOnOrAfter is only calculated with strict = true # If invalid, response id and assertion id are not obtained settings['strict'] = True @@ -1442,6 +1443,7 @@ def testGetInfoFromLastResponseReceived(self): self.assertIsNone(auth.get_last_message_id()) self.assertIsNone(auth.get_last_assertion_id()) self.assertIsNone(auth.get_last_assertion_not_on_or_after()) + self.assertIsNone(auth.get_last_assertion_issue_instant()) request_data['https'] = 'on' request_data['http_host'] = 'pitbulk.no-ip.org' @@ -1452,6 +1454,7 @@ def testGetInfoFromLastResponseReceived(self): self.assertEqual(auth.get_last_message_id(), 'pfx42be40bf-39c3-77f0-c6ae-8bf2e23a1a2e') self.assertEqual(auth.get_last_assertion_id(), 'pfx57dfda60-b211-4cda-0f63-6d5deb69e5bb') self.assertEqual(auth.get_last_assertion_not_on_or_after(), 2671081021) + self.assertEqual(auth.get_last_assertion_issue_instant(), 1392773821) def testGetIdFromLogoutRequest(self): """ From 149a4666e18a0f54794f4552de0da4a2753ff6a4 Mon Sep 17 00:00:00 2001 From: Gunesh Pinar Date: Thu, 26 Aug 2021 23:37:37 -0700 Subject: [PATCH 108/205] Revert Whitespace Change --- tests/src/OneLogin/saml2_tests/auth_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/src/OneLogin/saml2_tests/auth_test.py b/tests/src/OneLogin/saml2_tests/auth_test.py index 87a57f19..809ebdfb 100644 --- a/tests/src/OneLogin/saml2_tests/auth_test.py +++ b/tests/src/OneLogin/saml2_tests/auth_test.py @@ -1433,7 +1433,7 @@ def testGetInfoFromLastResponseReceived(self): self.assertIsNone(auth.get_last_assertion_not_on_or_after()) self.assertEqual(auth.get_last_assertion_issue_instant(), 1392773821) - # NotOnOrAfter is only calculated with strict = true + # NotOnOrAfter is only calculated with strict = true # If invalid, response id and assertion id are not obtained settings['strict'] = True From b6ffc5932d16dacf314123dcd653a92e1332f32e Mon Sep 17 00:00:00 2001 From: Gunesh Pinar Date: Mon, 4 Oct 2021 22:40:40 -0700 Subject: [PATCH 109/205] Implement get_last_response_in_response_to() --- README.md | 1 + src/onelogin/saml2/auth.py | 9 +++++++++ tests/src/OneLogin/saml2_tests/auth_test.py | 5 ++++- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3a5f39ff..dccc84f3 100644 --- a/README.md +++ b/README.md @@ -967,6 +967,7 @@ Main class of OneLogin Python Toolkit * ***set_strict*** Set the strict mode active/disable. * ***get_last_request_xml*** Returns the most recently-constructed/processed XML SAML request (``AuthNRequest``, ``LogoutRequest``) * ***get_last_response_xml*** Returns the most recently-constructed/processed XML SAML response (``SAMLResponse``, ``LogoutResponse``). If the SAMLResponse had an encrypted assertion, decrypts it. +* ***get_last_response_in_response_to*** The `InResponseTo` of the most recently processed SAML Response. * ***get_last_message_id*** The ID of the last Response SAML message processed. * ***get_last_assertion_id*** The ID of the last assertion processed. * ***get_last_assertion_not_on_or_after*** The ``NotOnOrAfter`` value of the valid ``SubjectConfirmationData`` node (if any) of the last assertion processed (is only calculated with strict = true) diff --git a/src/onelogin/saml2/auth.py b/src/onelogin/saml2/auth.py index 6c72932b..c097678b 100644 --- a/src/onelogin/saml2/auth.py +++ b/src/onelogin/saml2/auth.py @@ -75,6 +75,7 @@ def __init__(self, request_data, old_settings=None, custom_base_path=None): self._last_authn_contexts = [] self._last_request = None self._last_response = None + self._last_response_in_response_to = None self._last_assertion_not_on_or_after = None def get_settings(self): @@ -109,6 +110,7 @@ def store_valid_response(self, response): self._last_assertion_issue_instant = response.get_assertion_issue_instant() self._last_authn_contexts = response.get_authn_contexts() self._authenticated = True + self._last_response_in_response_to = response.get_in_response_to() self._last_assertion_not_on_or_after = response.get_assertion_not_on_or_after() def process_response(self, request_id=None): @@ -389,6 +391,13 @@ def get_last_authn_contexts(self): """ return self._last_authn_contexts + def get_last_response_in_response_to(self): + """ + :returns: InResponseTo attribute of the last Response SAML processed or None if it is not present. + :rtype: string + """ + return self._last_response_in_response_to + def login(self, return_to=None, force_authn=False, is_passive=False, set_nameid_policy=True, name_id_value_req=None): """ Initiates the SSO process. diff --git a/tests/src/OneLogin/saml2_tests/auth_test.py b/tests/src/OneLogin/saml2_tests/auth_test.py index 809ebdfb..0f979073 100644 --- a/tests/src/OneLogin/saml2_tests/auth_test.py +++ b/tests/src/OneLogin/saml2_tests/auth_test.py @@ -1415,7 +1415,7 @@ def testGetLastLogoutResponse(self): def testGetInfoFromLastResponseReceived(self): """ - Tests the get_last_message_id, get_last_assertion_id, get_last_assertion_not_on_or_after and get_last_assertion_issue_instant + Tests the get_last_response_in_response_to, get_last_message_id, get_last_assertion_id, get_last_assertion_not_on_or_after and get_last_assertion_issue_instant of the OneLogin_Saml2_Auth class """ settings = self.loadSettingsJSON() @@ -1428,6 +1428,7 @@ def testGetInfoFromLastResponseReceived(self): auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_response() + self.assertEqual(auth.get_last_response_in_response_to(), 'ONELOGIN_5fe9d6e499b2f0913206aab3f7191729049bb807') self.assertEqual(auth.get_last_message_id(), 'pfx42be40bf-39c3-77f0-c6ae-8bf2e23a1a2e') self.assertEqual(auth.get_last_assertion_id(), 'pfx57dfda60-b211-4cda-0f63-6d5deb69e5bb') self.assertIsNone(auth.get_last_assertion_not_on_or_after()) @@ -1440,6 +1441,7 @@ def testGetInfoFromLastResponseReceived(self): auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_response() self.assertNotEqual(len(auth.get_errors()), 0) + self.assertIsNone(auth.get_last_response_in_response_to()) self.assertIsNone(auth.get_last_message_id()) self.assertIsNone(auth.get_last_assertion_id()) self.assertIsNone(auth.get_last_assertion_not_on_or_after()) @@ -1451,6 +1453,7 @@ def testGetInfoFromLastResponseReceived(self): auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_response() self.assertEqual(len(auth.get_errors()), 0) + self.assertEqual(auth.get_last_response_in_response_to(), 'ONELOGIN_5fe9d6e499b2f0913206aab3f7191729049bb807') self.assertEqual(auth.get_last_message_id(), 'pfx42be40bf-39c3-77f0-c6ae-8bf2e23a1a2e') self.assertEqual(auth.get_last_assertion_id(), 'pfx57dfda60-b211-4cda-0f63-6d5deb69e5bb') self.assertEqual(auth.get_last_assertion_not_on_or_after(), 2671081021) From 138916d1cd5cc391b5f39c9f9eaef494a3fa8834 Mon Sep 17 00:00:00 2001 From: Gunesh Pinar Date: Mon, 4 Oct 2021 22:41:40 -0700 Subject: [PATCH 110/205] Clarify README description --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dccc84f3..2d132e9e 100644 --- a/README.md +++ b/README.md @@ -967,7 +967,7 @@ Main class of OneLogin Python Toolkit * ***set_strict*** Set the strict mode active/disable. * ***get_last_request_xml*** Returns the most recently-constructed/processed XML SAML request (``AuthNRequest``, ``LogoutRequest``) * ***get_last_response_xml*** Returns the most recently-constructed/processed XML SAML response (``SAMLResponse``, ``LogoutResponse``). If the SAMLResponse had an encrypted assertion, decrypts it. -* ***get_last_response_in_response_to*** The `InResponseTo` of the most recently processed SAML Response. +* ***get_last_response_in_response_to*** The `InResponseTo` ID of the most recently processed SAML Response. * ***get_last_message_id*** The ID of the last Response SAML message processed. * ***get_last_assertion_id*** The ID of the last assertion processed. * ***get_last_assertion_not_on_or_after*** The ``NotOnOrAfter`` value of the valid ``SubjectConfirmationData`` node (if any) of the last assertion processed (is only calculated with strict = true) From 4c4d54005b8dd3d8ffa630e45b4d217e8f01621e Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Mon, 18 Oct 2021 21:21:11 +0200 Subject: [PATCH 111/205] Warn about Open Redirect and Reply attacks --- README.md | 30 ++++++++++++++++++++++++++++++ demo-django/demo/views.py | 4 ++++ demo-flask/index.py | 4 ++++ demo-tornado/views.py | 4 ++++ demo_pyramid/demo_pyramid/views.py | 4 ++++ 5 files changed, 46 insertions(+) diff --git a/README.md b/README.md index 3a5f39ff..739ff670 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,36 @@ your environment is not secure and will be exposed to attacks. In production also we highly recommend to register on the settings the IdP certificate instead of using the fingerprint method. The fingerprint, is a hash, so at the end is open to a collision attack that can end on a signature validation bypass. Other SAML toolkits deprecated that mechanism, we maintain it for compatibility and also to be used on test environment. + +### Avoiding Open Redirect attacks ### + +Some implementations uses the RelayState parameter as a way to control the flow when SSO and SLO succeeded. So basically the +user is redirected to the value of the RelayState. + +If you are using Signature Validation on the HTTP-Redirect binding, you will have the RelayState value integrity covered, otherwise, and +on HTTP-POST binding, you can't trust the RelayState so before +executing the validation, you need to verify that its value belong +a trusted and expected URL. + +Read more about Open Redirect [CWE-601](https://cwe.mitre.org/data/definitions/601.html). + +### Avoiding Reply attacks ### + +A reply attack is basically try to reuse an intercepted valid SAML Message in order to impersonate a SAML action (SSO or SLO). + +SAML Messages have a limited timelife (NotBefore, NotOnOrAfter) that +make harder this kind of attacks, but they are still possible. + +In order to avoid them, the SP can keep a list of SAML Messages or Assertion IDs alredy valdidated and processed. Those values only need +to be stored the amount of time of the SAML Message life time, so +we don't need to store all processed message/assertion Ids, but the most recent ones. + +The OneLogin_Saml2_Auth class contains the [get_last_request_id](https://github.com/onelogin/python3-saml/blob/ab62b0d6f3e5ac2ae8e95ce3ed2f85389252a32d/src/onelogin/saml2/auth.py#L357), [get_last_message_id](https://github.com/onelogin/python3-saml/blob/ab62b0d6f3e5ac2ae8e95ce3ed2f85389252a32d/src/onelogin/saml2/auth.py#L364) and [get_last_assertion_id](https://github.com/onelogin/python3-saml/blob/ab62b0d6f3e5ac2ae8e95ce3ed2f85389252a32d/src/onelogin/saml2/auth.py#L371) methods to retrieve the IDs + +Checking that the ID of the current Message/Assertion does not exists in the lis of the ones already processed will prevent reply +attacks. + + Getting Started --------------- diff --git a/demo-django/demo/views.py b/demo-django/demo/views.py index 6003b821..b9acdd76 100644 --- a/demo-django/demo/views.py +++ b/demo-django/demo/views.py @@ -84,6 +84,8 @@ def index(request): request.session['samlNameIdSPNameQualifier'] = auth.get_nameid_spnq() request.session['samlSessionIndex'] = auth.get_session_index() if 'RelayState' in req['post_data'] and OneLogin_Saml2_Utils.get_self_url(req) != req['post_data']['RelayState']: + # To avoid 'Open Redirect' attacks, before execute the redirection confirm + # the value of the req['post_data']['RelayState'] is a trusted URL. return HttpResponseRedirect(auth.redirect_to(req['post_data']['RelayState'])) elif auth.get_settings().is_debug_active(): error_reason = auth.get_last_error_reason() @@ -96,6 +98,8 @@ def index(request): errors = auth.get_errors() if len(errors) == 0: if url is not None: + # To avoid 'Open Redirect' attacks, before execute the redirection confirm + # the value of the url is a trusted URL return HttpResponseRedirect(url) else: success_slo = True diff --git a/demo-flask/index.py b/demo-flask/index.py index a10ac8ce..3ad2a4e6 100644 --- a/demo-flask/index.py +++ b/demo-flask/index.py @@ -83,6 +83,8 @@ def index(): session['samlSessionIndex'] = auth.get_session_index() self_url = OneLogin_Saml2_Utils.get_self_url(req) if 'RelayState' in request.form and self_url != request.form['RelayState']: + # To avoid 'Open Redirect' attacks, before execute the redirection confirm + # the value of the request.form['RelayState'] is a trusted URL. return redirect(auth.redirect_to(request.form['RelayState'])) elif auth.get_settings().is_debug_active(): error_reason = auth.get_last_error_reason() @@ -95,6 +97,8 @@ def index(): errors = auth.get_errors() if len(errors) == 0: if url is not None: + # To avoid 'Open Redirect' attacks, before execute the redirection confirm + # the value of the url is a trusted URL. return redirect(url) else: success_slo = True diff --git a/demo-tornado/views.py b/demo-tornado/views.py index e5d062aa..70ef1565 100644 --- a/demo-tornado/views.py +++ b/demo-tornado/views.py @@ -46,6 +46,8 @@ def post(self): session['samlSessionIndex'] = auth.get_session_index() self_url = OneLogin_Saml2_Utils.get_self_url(req) if 'RelayState' in self.request.arguments and self_url != self.request.arguments['RelayState'][0].decode('utf-8'): + # To avoid 'Open Redirect' attacks, before execute the redirection confirm + # the value of the self.request.arguments['RelayState'][0] is a trusted URL. return self.redirect(self.request.arguments['RelayState'][0].decode('utf-8')) elif auth.get_settings().is_debug_active(): error_reason = auth.get_last_error_reason() @@ -104,6 +106,8 @@ def get(self): errors = auth.get_errors() if len(errors) == 0: if url is not None: + # To avoid 'Open Redirect' attacks, before execute the redirection confirm + # the value of the url is a trusted URL. return self.redirect(url) else: success_slo = True diff --git a/demo_pyramid/demo_pyramid/views.py b/demo_pyramid/demo_pyramid/views.py index 96434b8b..5b6be9dd 100644 --- a/demo_pyramid/demo_pyramid/views.py +++ b/demo_pyramid/demo_pyramid/views.py @@ -70,6 +70,8 @@ def index(request): session['samlSessionIndex'] = auth.get_session_index() self_url = OneLogin_Saml2_Utils.get_self_url(req) if 'RelayState' in request.POST and self_url != request.POST['RelayState']: + # To avoid 'Open Redirect' attacks, before execute the redirection confirm + # the value of the request.POST['RelayState'] is a trusted URL. return HTTPFound(auth.redirect_to(request.POST['RelayState'])) else: error_reason = auth.get_last_error_reason() @@ -79,6 +81,8 @@ def index(request): errors = auth.get_errors() if len(errors) == 0: if url is not None: + # To avoid 'Open Redirect' attacks, before execute the redirection confirm + # the value of the url is a trusted URL. return HTTPFound(url) else: success_slo = True From b4199c5364d4b4c00d9930d9e4dab655ecdfaf81 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Mon, 18 Oct 2021 21:29:09 +0200 Subject: [PATCH 112/205] Modify examples of README as well --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 739ff670..003beb63 100644 --- a/README.md +++ b/README.md @@ -727,6 +727,8 @@ if not errors: request.session['samlUserdata'] = auth.get_attributes() if 'RelayState' in req['post_data'] and OneLogin_Saml2_Utils.get_self_url(req) != req['post_data']['RelayState']: + # To avoid 'Open Redirect' attacks, before execute the redirection confirm + # the value of the req['post_data']['RelayState'] is a trusted URL. auth.redirect_to(req['post_data']['RelayState']) else: for attr_name in request.session['samlUserdata'].keys(): @@ -789,6 +791,8 @@ url = auth.process_slo(delete_session_cb=delete_session_callback) errors = auth.get_errors() if len(errors) == 0: if url is not None: + # To avoid 'Open Redirect' attacks, before execute the redirection confirm + # the value of the url is a trusted URL. return redirect(url) else: print("Sucessfully Logged out") @@ -916,6 +920,8 @@ elif 'acs' in request.args: # Assertion Consumer Service request.session['samlSessionIndex'] = auth.get_session_index() self_url = OneLogin_Saml2_Utils.get_self_url(req) if 'RelayState' in request.form and self_url != request.form['RelayState']: + # To avoid 'Open Redirect' attacks, before execute the redirection confirm + # the value of the request.form['RelayState'] is a trusted URL. return redirect(auth.redirect_to(request.form['RelayState'])) # Redirect if there is a relayState else: # If there is user data we save that to print it later. msg = '' @@ -927,6 +933,8 @@ elif 'sls' in request.args: # Single errors = auth.get_errors() # Retrieves possible validation errors if len(errors) == 0: if url is not None: + # To avoid 'Open Redirect' attacks, before execute the redirection confirm + # the value of the url is a trusted URL. return redirect(url) else: msg = "Sucessfully logged out" From 6f973d181a50530542a73628e4bd3166f05e9aac Mon Sep 17 00:00:00 2001 From: Mateusz Mandera Date: Mon, 8 Nov 2021 12:46:13 +0100 Subject: [PATCH 113/205] Support building a LogoutResponse with non-success status --- src/onelogin/saml2/logout_response.py | 22 ++++++++++--------- .../saml2_tests/logout_response_test.py | 20 +++++++++++++++++ 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/src/onelogin/saml2/logout_response.py b/src/onelogin/saml2/logout_response.py index e9368e8a..d1110a73 100644 --- a/src/onelogin/saml2/logout_response.py +++ b/src/onelogin/saml2/logout_response.py @@ -10,6 +10,7 @@ """ from onelogin.saml2 import compat +from onelogin.saml2.constants import OneLogin_Saml2_Constants from onelogin.saml2.utils import OneLogin_Saml2_Utils, OneLogin_Saml2_ValidationError from onelogin.saml2.xml_templates import OneLogin_Saml2_Templates from onelogin.saml2.xml_utils import OneLogin_Saml2_XML @@ -152,11 +153,13 @@ def _query(self, query): """ return OneLogin_Saml2_XML.query(self.document, query) - def build(self, in_response_to): + def build(self, in_response_to, status=OneLogin_Saml2_Constants.STATUS_SUCCESS): """ Creates a Logout Response object. :param in_response_to: InResponseTo value for the Logout Response. :type in_response_to: string + :param: status: The status of the response + :type: status: string """ sp_data = self._settings.get_sp_data() @@ -164,15 +167,14 @@ def build(self, in_response_to): issue_instant = OneLogin_Saml2_Utils.parse_time_to_SAML(OneLogin_Saml2_Utils.now()) - logout_response = OneLogin_Saml2_Templates.LOGOUT_RESPONSE % \ - { - 'id': self.id, - 'issue_instant': issue_instant, - 'destination': self._settings.get_idp_slo_response_url(), - 'in_response_to': in_response_to, - 'entity_id': sp_data['entityId'], - 'status': "urn:oasis:names:tc:SAML:2.0:status:Success" - } + logout_response = OneLogin_Saml2_Templates.LOGOUT_RESPONSE % { + "id": self.id, + "issue_instant": issue_instant, + "destination": self._settings.get_idp_slo_response_url(), + "in_response_to": in_response_to, + "entity_id": sp_data["entityId"], + "status": status, + } self._logout_response = logout_response diff --git a/tests/src/OneLogin/saml2_tests/logout_response_test.py b/tests/src/OneLogin/saml2_tests/logout_response_test.py index e6f31b0f..4b3da96a 100644 --- a/tests/src/OneLogin/saml2_tests/logout_response_test.py +++ b/tests/src/OneLogin/saml2_tests/logout_response_test.py @@ -401,3 +401,23 @@ def testGetXML(self): logout_response_processed = OneLogin_Saml2_Logout_Response(settings, OneLogin_Saml2_Utils.deflate_and_base64_encode(response)) self.assertEqual(response, logout_response_processed.get_xml()) + + def testBuildWithStatus(self): + """ + Tests the build method when called specifying a non-default status for the LogoutResponse. + """ + settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) + + response_builder = OneLogin_Saml2_Logout_Response(settings) + response_builder.build("InResponseValue", status=OneLogin_Saml2_Constants.STATUS_REQUESTER) + generated_encoded_response = response_builder.get_response() + + # Parse and verify the status of the response, as the receiver will do: + parsed_response = OneLogin_Saml2_Logout_Response(settings, generated_encoded_response) + expectedFragment = ( + ' \n' + ' \n' + ' \n' + ) + self.assertIn(expectedFragment, parsed_response.get_xml()) + self.assertEqual(parsed_response.get_status(), OneLogin_Saml2_Constants.STATUS_REQUESTER) From d2716a1642b55e45028d63c653304786973e2108 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Fri, 28 Jan 2022 12:58:45 +0100 Subject: [PATCH 114/205] Adding python 3.10 to CI. See #294 --- .github/workflows/python-package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 1cc6b96a..91d8f7b4 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -11,7 +11,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: [2.7,3.5,3.6,3.7, 3.8,3.9] + python-version: [2.7,3.5,3.6,3.7, 3.8,3.9,3.10.2] steps: - uses: actions/checkout@v2 From 9726d5c2e1e345023249e7caa7a2a2159e5a2558 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Fri, 28 Jan 2022 13:33:19 +0100 Subject: [PATCH 115/205] Upgrade dependencies --- setup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 4aad2c76..d6c5de51 100644 --- a/setup.py +++ b/setup.py @@ -38,9 +38,9 @@ }, test_suite='tests', install_requires=[ - 'isodate>=0.5.0', - 'lxml>=3.3.5', - 'xmlsec>=1.0.5' + 'lxml>=4.7.1', + 'isodate>=0.6.1', + 'xmlsec>=1.3.9' ], dependency_links=['http://github.com/mehcode/python-xmlsec/tarball/master'], extras_require={ From b5fdfc9b3c8722abbef020aa64a8c4d29d7288b2 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Fri, 28 Jan 2022 13:36:21 +0100 Subject: [PATCH 116/205] Remove 3.10 for now from CI --- .github/workflows/python-package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 91d8f7b4..1cc6b96a 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -11,7 +11,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: [2.7,3.5,3.6,3.7, 3.8,3.9,3.10.2] + python-version: [2.7,3.5,3.6,3.7, 3.8,3.9] steps: - uses: actions/checkout@v2 From a55cc9a379da03eff9f96a886f6164fd0792a62e Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Fri, 28 Jan 2022 18:00:18 +0100 Subject: [PATCH 117/205] Fixing lxml to 4.7.0, it seems 4.7.1 had conflicts when the signature inside a encrypted element is validated. See #292 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index d6c5de51..21108ec8 100644 --- a/setup.py +++ b/setup.py @@ -38,7 +38,7 @@ }, test_suite='tests', install_requires=[ - 'lxml>=4.7.1', + 'lxml==4.7.0', 'isodate>=0.6.1', 'xmlsec>=1.3.9' ], From 6e34b695f0c596f0e07fa6eb297032013e1fb0f0 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Fri, 28 Jan 2022 19:03:31 +0100 Subject: [PATCH 118/205] Set sha256 and rsa-sha256 as default algorithms to be used --- src/onelogin/saml2/auth.py | 8 ++++---- src/onelogin/saml2/metadata.py | 2 +- src/onelogin/saml2/settings.py | 4 ++-- src/onelogin/saml2/utils.py | 12 ++++++------ tests/src/OneLogin/saml2_tests/auth_test.py | 10 +++++----- tests/src/OneLogin/saml2_tests/metadata_test.py | 4 ++-- tests/src/OneLogin/saml2_tests/settings_test.py | 2 +- tests/src/OneLogin/saml2_tests/utils_test.py | 4 ++-- 8 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/onelogin/saml2/auth.py b/src/onelogin/saml2/auth.py index c097678b..d17c7d48 100644 --- a/src/onelogin/saml2/auth.py +++ b/src/onelogin/saml2/auth.py @@ -523,7 +523,7 @@ def get_slo_response_url(self): """ return self._settings.get_idp_slo_response_url() - def add_request_signature(self, request_data, sign_algorithm=OneLogin_Saml2_Constants.RSA_SHA1): + def add_request_signature(self, request_data, sign_algorithm=OneLogin_Saml2_Constants.RSA_SHA256): """ Builds the Signature of the SAML Request. @@ -535,7 +535,7 @@ def add_request_signature(self, request_data, sign_algorithm=OneLogin_Saml2_Cons """ return self._build_signature(request_data, 'SAMLRequest', sign_algorithm) - def add_response_signature(self, response_data, sign_algorithm=OneLogin_Saml2_Constants.RSA_SHA1): + def add_response_signature(self, response_data, sign_algorithm=OneLogin_Saml2_Constants.RSA_SHA256): """ Builds the Signature of the SAML Response. :param response_data: The Response parameters @@ -588,7 +588,7 @@ def _build_sign_query(saml_data, relay_state, algorithm, saml_type, lowercase_ur sign_data.append('SigAlg=%s' % OneLogin_Saml2_Utils.escape_url(algorithm, lowercase_urlencoding)) return '&'.join(sign_data) - def _build_signature(self, data, saml_type, sign_algorithm=OneLogin_Saml2_Constants.RSA_SHA1): + def _build_signature(self, data, saml_type, sign_algorithm=OneLogin_Saml2_Constants.RSA_SHA256): """ Builds the Signature :param data: The Request data @@ -621,7 +621,7 @@ def _build_signature(self, data, saml_type, sign_algorithm=OneLogin_Saml2_Consta OneLogin_Saml2_Constants.RSA_SHA384: xmlsec.Transform.RSA_SHA384, OneLogin_Saml2_Constants.RSA_SHA512: xmlsec.Transform.RSA_SHA512 } - sign_algorithm_transform = sign_algorithm_transform_map.get(sign_algorithm, xmlsec.Transform.RSA_SHA1) + sign_algorithm_transform = sign_algorithm_transform_map.get(sign_algorithm, xmlsec.Transform.RSA_SHA256) signature = OneLogin_Saml2_Utils.sign_binary(msg, key, sign_algorithm_transform, self._settings.is_debug_active()) data['Signature'] = OneLogin_Saml2_Utils.b64encode(signature) diff --git a/src/onelogin/saml2/metadata.py b/src/onelogin/saml2/metadata.py index 34c5f2c1..cccd7ce0 100644 --- a/src/onelogin/saml2/metadata.py +++ b/src/onelogin/saml2/metadata.py @@ -193,7 +193,7 @@ def builder(cls, sp, authnsign=False, wsign=False, valid_until=None, cache_durat return metadata @staticmethod - def sign_metadata(metadata, key, cert, sign_algorithm=OneLogin_Saml2_Constants.RSA_SHA1, digest_algorithm=OneLogin_Saml2_Constants.SHA1): + def sign_metadata(metadata, key, cert, sign_algorithm=OneLogin_Saml2_Constants.RSA_SHA256, digest_algorithm=OneLogin_Saml2_Constants.SHA256): """ Signs the metadata with the key/cert provided diff --git a/src/onelogin/saml2/settings.py b/src/onelogin/saml2/settings.py index 3163995e..c258758c 100644 --- a/src/onelogin/saml2/settings.py +++ b/src/onelogin/saml2/settings.py @@ -307,10 +307,10 @@ def _add_default_values(self): self._security.setdefault('wantNameIdEncrypted', False) # Signature Algorithm - self._security.setdefault('signatureAlgorithm', OneLogin_Saml2_Constants.RSA_SHA1) + self._security.setdefault('signatureAlgorithm', OneLogin_Saml2_Constants.RSA_SHA256) # Digest Algorithm - self._security.setdefault('digestAlgorithm', OneLogin_Saml2_Constants.SHA1) + self._security.setdefault('digestAlgorithm', OneLogin_Saml2_Constants.SHA256) # AttributeStatement required by default self._security.setdefault('wantAttributeStatement', True) diff --git a/src/onelogin/saml2/utils.py b/src/onelogin/saml2/utils.py index 51ab4e00..34176e25 100644 --- a/src/onelogin/saml2/utils.py +++ b/src/onelogin/saml2/utils.py @@ -697,7 +697,7 @@ def decrypt_element(encrypted_data, key, debug=False, inplace=False): return enc_ctx.decrypt(encrypted_data) @staticmethod - def add_sign(xml, key, cert, debug=False, sign_algorithm=OneLogin_Saml2_Constants.RSA_SHA1, digest_algorithm=OneLogin_Saml2_Constants.SHA1): + def add_sign(xml, key, cert, debug=False, sign_algorithm=OneLogin_Saml2_Constants.RSA_SHA256, digest_algorithm=OneLogin_Saml2_Constants.SHA256): """ Adds signature key and senders certificate to an element (Message or Assertion). @@ -735,7 +735,7 @@ def add_sign(xml, key, cert, debug=False, sign_algorithm=OneLogin_Saml2_Constant OneLogin_Saml2_Constants.RSA_SHA384: xmlsec.Transform.RSA_SHA384, OneLogin_Saml2_Constants.RSA_SHA512: xmlsec.Transform.RSA_SHA512 } - sign_algorithm_transform = sign_algorithm_transform_map.get(sign_algorithm, xmlsec.Transform.RSA_SHA1) + sign_algorithm_transform = sign_algorithm_transform_map.get(sign_algorithm, xmlsec.Transform.RSA_SHA256) signature = xmlsec.template.create(elem, xmlsec.Transform.EXCL_C14N, sign_algorithm_transform, ns='ds') @@ -770,7 +770,7 @@ def add_sign(xml, key, cert, debug=False, sign_algorithm=OneLogin_Saml2_Constant OneLogin_Saml2_Constants.SHA384: xmlsec.Transform.SHA384, OneLogin_Saml2_Constants.SHA512: xmlsec.Transform.SHA512 } - digest_algorithm_transform = digest_algorithm_transform_map.get(digest_algorithm, xmlsec.Transform.SHA1) + digest_algorithm_transform = digest_algorithm_transform_map.get(digest_algorithm, xmlsec.Transform.SHA256) ref = xmlsec.template.add_reference(signature, digest_algorithm_transform, uri=elem_id) xmlsec.template.add_transform(ref, xmlsec.Transform.ENVELOPED) @@ -983,7 +983,7 @@ def validate_node_sign(signature_node, elem, cert=None, fingerprint=None, finger return True @staticmethod - def sign_binary(msg, key, algorithm=xmlsec.Transform.RSA_SHA1, debug=False): + def sign_binary(msg, key, algorithm=xmlsec.Transform.RSA_SHA256, debug=False): """ Sign binary message @@ -1009,7 +1009,7 @@ def sign_binary(msg, key, algorithm=xmlsec.Transform.RSA_SHA1, debug=False): return dsig_ctx.sign_binary(compat.to_bytes(msg), algorithm) @staticmethod - def validate_binary_sign(signed_query, signature, cert=None, algorithm=OneLogin_Saml2_Constants.RSA_SHA1, debug=False): + def validate_binary_sign(signed_query, signature, cert=None, algorithm=OneLogin_Saml2_Constants.RSA_SHA256, debug=False): """ Validates signed binary data (Used to validate GET Signature). @@ -1041,7 +1041,7 @@ def validate_binary_sign(signed_query, signature, cert=None, algorithm=OneLogin_ OneLogin_Saml2_Constants.RSA_SHA384: xmlsec.Transform.RSA_SHA384, OneLogin_Saml2_Constants.RSA_SHA512: xmlsec.Transform.RSA_SHA512 } - sign_algorithm_transform = sign_algorithm_transform_map.get(algorithm, xmlsec.Transform.RSA_SHA1) + sign_algorithm_transform = sign_algorithm_transform_map.get(algorithm, xmlsec.Transform.RSA_SHA256) dsig_ctx.verify_binary(compat.to_bytes(signed_query), sign_algorithm_transform, diff --git a/tests/src/OneLogin/saml2_tests/auth_test.py b/tests/src/OneLogin/saml2_tests/auth_test.py index 0f979073..4a872c81 100644 --- a/tests/src/OneLogin/saml2_tests/auth_test.py +++ b/tests/src/OneLogin/saml2_tests/auth_test.py @@ -567,7 +567,7 @@ def testProcessSLORequestSignedResponse(self): self.assertIn('SigAlg', parsed_query) self.assertIn('Signature', parsed_query) self.assertIn('http://relaystate.com', parsed_query['RelayState']) - self.assertIn(OneLogin_Saml2_Constants.RSA_SHA1, parsed_query['SigAlg']) + self.assertIn(OneLogin_Saml2_Constants.RSA_SHA256, parsed_query['SigAlg']) def testLogin(self): """ @@ -624,7 +624,7 @@ def testLoginSigned(self): self.assertIn('SigAlg', parsed_query) self.assertIn('Signature', parsed_query) self.assertIn(return_to, parsed_query['RelayState']) - self.assertIn(OneLogin_Saml2_Constants.RSA_SHA1, parsed_query['SigAlg']) + self.assertIn(OneLogin_Saml2_Constants.RSA_SHA256, parsed_query['SigAlg']) def testLoginForceAuthN(self): """ @@ -824,7 +824,7 @@ def testLogoutSigned(self): self.assertIn('SigAlg', parsed_query) self.assertIn('Signature', parsed_query) self.assertIn(return_to, parsed_query['RelayState']) - self.assertIn(OneLogin_Saml2_Constants.RSA_SHA1, parsed_query['SigAlg']) + self.assertIn(OneLogin_Saml2_Constants.RSA_SHA256, parsed_query['SigAlg']) def testLogoutNoSLO(self): """ @@ -1088,7 +1088,7 @@ def testBuildRequestSignature(self): auth = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings) auth.add_request_signature(parameters) - valid_signature = 'Pb1EXAX5TyipSJ1SndEKZstLQTsT+1D00IZAhEepBM+OkAZQSToivu3njgJu47HZiZAqgXZFgloBuuWE/+GdcSsRYEMkEkiSDWTpUr25zKYLJDSg6GNo6iAHsKSuFt46Z54Xe/keYxYP03Hdy97EwuuSjBzzgRc5tmpV+KC7+a0=' + valid_signature = 'CqdIlbO6GieeJFV+PYqyqz1QVJunQXdZZl+ZyIby9O3/eMJM0XHi+TWReRrpgNxKkbmmvx5fp/t7mphbLiVYNMgGINEaaa/OfoaGwU9GM5YCVULA2t7qZBel1yrIXGMxijJizB7UPR2ZMo4G+Wdhx1zbmbB0GYM0A27w6YCe/+k=' self.assertEqual(valid_signature, parameters["Signature"]) settings['sp']['privateKey'] = '' @@ -1109,7 +1109,7 @@ def testBuildResponseSignature(self): parameters = {"SAMLResponse": message, 'RelayState': relay_state} auth.add_response_signature(parameters) - valid_signature = 'IcyWLRX6Dz3wHBfpcUaNLVDMGM3uo6z2Z11Gjq0/APPJaHboKGljffsgMVAGBml497yckq+eYKmmz+jpURV9yTj2sF9qfD6CwX2dEzSzMdRzB40X7pWyHgEJGIhs6BhaOt5oXEk4T+h3AczERqpVYFpL00yo7FNtyQkhZFpHFhM=' + valid_signature = 'fFGaOuO/2+ch/xlwU5o7iS6R+v2quWchLAtiDyQTxStFQZKY1NsBs/eYIin2Meq7oTl1Ks6tpT6JshH5OwhPh/08K7M2oa6FIKb99cjg+jIJ/WwpuJ5h9SH0XXP8y3RLhCxLIomHDsBOGQK8WvOlXFUg+9nvOaEMNi6raUWrGhA=' self.assertEqual(valid_signature, parameters['Signature']) settings['sp']['privateKey'] = '' diff --git a/tests/src/OneLogin/saml2_tests/metadata_test.py b/tests/src/OneLogin/saml2_tests/metadata_test.py index 0a12a6d4..c95ceead 100644 --- a/tests/src/OneLogin/saml2_tests/metadata_test.py +++ b/tests/src/OneLogin/saml2_tests/metadata_test.py @@ -222,8 +222,8 @@ def testSignMetadata(self): self.assertIn('urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified', signed_metadata) self.assertIn('\n', signed_metadata) - self.assertIn('', signed_metadata) - self.assertIn('', signed_metadata) + self.assertIn('', signed_metadata) + self.assertIn('', signed_metadata) self.assertIn('\n\n', signed_metadata) diff --git a/tests/src/OneLogin/saml2_tests/settings_test.py b/tests/src/OneLogin/saml2_tests/settings_test.py index 4cb271f5..16f59e26 100644 --- a/tests/src/OneLogin/saml2_tests/settings_test.py +++ b/tests/src/OneLogin/saml2_tests/settings_test.py @@ -593,7 +593,7 @@ def generateAndCheckMetadata(self, settings): self.assertIn('', metadata) self.assertIn('urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified', metadata) self.assertIn('\n', metadata) - self.assertIn('', metadata) + self.assertIn('', metadata) self.assertIn('\n\n', metadata) diff --git a/tests/src/OneLogin/saml2_tests/utils_test.py b/tests/src/OneLogin/saml2_tests/utils_test.py index 54d9ae66..39cb46de 100644 --- a/tests/src/OneLogin/saml2_tests/utils_test.py +++ b/tests/src/OneLogin/saml2_tests/utils_test.py @@ -792,8 +792,8 @@ def testAddSignCheckAlg(self): xml_authn = b64decode(self.file_contents(join(self.data_path, 'requests', 'authn_request.xml.base64'))) xml_authn_signed = compat.to_string(OneLogin_Saml2_Utils.add_sign(xml_authn, key, cert)) self.assertIn('', xml_authn_signed) - self.assertIn('', xml_authn_signed) - self.assertIn('', xml_authn_signed) + self.assertIn('', xml_authn_signed) + self.assertIn('', xml_authn_signed) xml_authn_signed_2 = compat.to_string(OneLogin_Saml2_Utils.add_sign(xml_authn, key, cert, False, OneLogin_Saml2_Constants.RSA_SHA256, OneLogin_Saml2_Constants.SHA384)) self.assertIn('', xml_authn_signed_2) From 171a89e75a6e39f9cb38472162418555c85aa02d Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Fri, 28 Jan 2022 20:53:13 +0100 Subject: [PATCH 119/205] Add rejectDeprecatedAlgorithm settings. Define DEPRECATED_ALGORITHMS list on Constants. If flag enabled, reject signatures on response, logout_request and logout_response with deprecated algorithm --- README.md | 9 +++- demo-django/saml/advanced_settings.json | 3 +- demo-flask/saml/advanced_settings.json | 3 +- demo-tornado/saml/advanced_settings.json | 3 +- .../demo_pyramid/saml/advanced_settings.json | 3 +- src/onelogin/saml2/auth.py | 11 ++++- src/onelogin/saml2/constants.py | 3 ++ src/onelogin/saml2/errors.py | 2 + src/onelogin/saml2/response.py | 23 +++++++++ src/onelogin/saml2/settings.py | 3 ++ tests/src/OneLogin/saml2_tests/auth_test.py | 47 ++++++++++++++++++- .../src/OneLogin/saml2_tests/response_test.py | 13 +++++ 12 files changed, 115 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 53e94e6c..43fd5322 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,8 @@ This version supports Python3. There is a separate version that only support Pyt #### Warning #### +Version 1.13.0 sets sha256 and rsa-sha256 as default algorithms + Version 1.8.0 sets strict mode active by default Update ``python3-saml`` to ``1.5.0``, this version includes security improvements for preventing XEE and Xpath Injections. @@ -485,7 +487,12 @@ In addition to the required settings data (idp, sp), extra settings can be defin // Specify if you want the SP to view assertions with duplicated Name or FriendlyName attributes to be valid // Defaults to false if not specified - 'allowRepeatAttributeName': false + 'allowRepeatAttributeName': false, + + // If the toolkit receive a message signed with a + // deprecated algoritm (defined at the constant class) + // will raise an error and reject the message + "rejectDeprecatedAlgorithm": true }, // Contact information template, it is recommended to suply a diff --git a/demo-django/saml/advanced_settings.json b/demo-django/saml/advanced_settings.json index fef16fe9..3960911a 100644 --- a/demo-django/saml/advanced_settings.json +++ b/demo-django/saml/advanced_settings.json @@ -12,7 +12,8 @@ "wantAssertionsEncrypted": false, "allowSingleLabelDomains": false, "signatureAlgorithm": "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", - "digestAlgorithm": "http://www.w3.org/2001/04/xmlenc#sha256" + "digestAlgorithm": "http://www.w3.org/2001/04/xmlenc#sha256", + "rejectDeprecatedAlgorithm": true }, "contactPerson": { "technical": { diff --git a/demo-flask/saml/advanced_settings.json b/demo-flask/saml/advanced_settings.json index fef16fe9..3960911a 100644 --- a/demo-flask/saml/advanced_settings.json +++ b/demo-flask/saml/advanced_settings.json @@ -12,7 +12,8 @@ "wantAssertionsEncrypted": false, "allowSingleLabelDomains": false, "signatureAlgorithm": "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", - "digestAlgorithm": "http://www.w3.org/2001/04/xmlenc#sha256" + "digestAlgorithm": "http://www.w3.org/2001/04/xmlenc#sha256", + "rejectDeprecatedAlgorithm": true }, "contactPerson": { "technical": { diff --git a/demo-tornado/saml/advanced_settings.json b/demo-tornado/saml/advanced_settings.json index fef16fe9..3960911a 100644 --- a/demo-tornado/saml/advanced_settings.json +++ b/demo-tornado/saml/advanced_settings.json @@ -12,7 +12,8 @@ "wantAssertionsEncrypted": false, "allowSingleLabelDomains": false, "signatureAlgorithm": "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", - "digestAlgorithm": "http://www.w3.org/2001/04/xmlenc#sha256" + "digestAlgorithm": "http://www.w3.org/2001/04/xmlenc#sha256", + "rejectDeprecatedAlgorithm": true }, "contactPerson": { "technical": { diff --git a/demo_pyramid/demo_pyramid/saml/advanced_settings.json b/demo_pyramid/demo_pyramid/saml/advanced_settings.json index fef16fe9..3960911a 100644 --- a/demo_pyramid/demo_pyramid/saml/advanced_settings.json +++ b/demo_pyramid/demo_pyramid/saml/advanced_settings.json @@ -12,7 +12,8 @@ "wantAssertionsEncrypted": false, "allowSingleLabelDomains": false, "signatureAlgorithm": "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", - "digestAlgorithm": "http://www.w3.org/2001/04/xmlenc#sha256" + "digestAlgorithm": "http://www.w3.org/2001/04/xmlenc#sha256", + "rejectDeprecatedAlgorithm": true }, "contactPerson": { "technical": { diff --git a/src/onelogin/saml2/auth.py b/src/onelogin/saml2/auth.py index d17c7d48..dc045f25 100644 --- a/src/onelogin/saml2/auth.py +++ b/src/onelogin/saml2/auth.py @@ -167,7 +167,6 @@ def process_slo(self, keep_local_session=False, request_id=None, delete_session_ self._errors.append('Signature validation failed. Logout Response rejected') elif not logout_response.is_valid(self._request_data, request_id): self._errors.append('invalid_logout_response') - self._error_reason = logout_response.get_error() elif logout_response.get_status() != OneLogin_Saml2_Constants.STATUS_SUCCESS: self._errors.append('logout_not_success') else: @@ -183,7 +182,6 @@ def process_slo(self, keep_local_session=False, request_id=None, delete_session_ self._errors.append('Signature validation failed. Logout Request rejected') elif not logout_request.is_valid(self._request_data): self._errors.append('invalid_logout_request') - self._error_reason = logout_request.get_error() else: if not keep_local_session: OneLogin_Saml2_Utils.delete_local_session(delete_session_cb) @@ -694,6 +692,15 @@ def _validate_signature(self, data, saml_type, raise_exceptions=False): if isinstance(sign_alg, bytes): sign_alg = sign_alg.decode('utf8') + security = self._settings.get_security_data() + reject_deprecated_alg = security.get('rejectDeprecatedAlgorithm', False) + if reject_deprecated_alg: + if sign_alg in OneLogin_Saml2_Constants.DEPRECATED_ALGORITHMS: + raise OneLogin_Saml2_ValidationError( + 'Deprecated signature algorithm found: %s' % sign_alg, + OneLogin_Saml2_ValidationError.DEPRECATED_SIGNATURE_METHOD + ) + query_string = self._request_data.get('query_string') if query_string and self._request_data.get('validate_signature_from_qs'): signed_query = self._build_sign_query_from_qs(query_string, saml_type) diff --git a/src/onelogin/saml2/constants.py b/src/onelogin/saml2/constants.py index e85a7fb9..a938f8e3 100644 --- a/src/onelogin/saml2/constants.py +++ b/src/onelogin/saml2/constants.py @@ -114,3 +114,6 @@ class OneLogin_Saml2_Constants(object): AES256_CBC = 'http://www.w3.org/2001/04/xmlenc#aes256-cbc' RSA_1_5 = 'http://www.w3.org/2001/04/xmlenc#rsa-1_5' RSA_OAEP_MGF1P = 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p' + + # Define here the deprecated algorithms + DEPRECATED_ALGORITHMS = [DSA_SHA1, RSA_SHA1, SHA1] diff --git a/src/onelogin/saml2/errors.py b/src/onelogin/saml2/errors.py index 6a50f9f1..b231f982 100644 --- a/src/onelogin/saml2/errors.py +++ b/src/onelogin/saml2/errors.py @@ -110,6 +110,8 @@ class OneLogin_Saml2_ValidationError(Exception): WRONG_NUMBER_OF_SIGNATURES = 43 RESPONSE_EXPIRED = 44 AUTHN_CONTEXT_MISMATCH = 45 + DEPRECATED_SIGNATURE_METHOD = 46 + DEPRECATED_DIGEST_METHOD = 47 def __init__(self, message, code=0, errors=None): """ diff --git a/src/onelogin/saml2/response.py b/src/onelogin/saml2/response.py index 4ef0418c..3cf0964e 100644 --- a/src/onelogin/saml2/response.py +++ b/src/onelogin/saml2/response.py @@ -653,6 +653,9 @@ def process_signed_elements(self): response_tag = '{%s}Response' % OneLogin_Saml2_Constants.NS_SAMLP assertion_tag = '{%s}Assertion' % OneLogin_Saml2_Constants.NS_SAML + security = self._settings.get_security_data() + reject_deprecated_alg = security.get('rejectDeprecatedAlgorithm', False) + for sign_node in sign_nodes: signed_element = sign_node.getparent().tag if signed_element != response_tag and signed_element != assertion_tag: @@ -695,6 +698,26 @@ def process_signed_elements(self): ) verified_seis.append(sei) + # Check the signature and digest algorithm + if reject_deprecated_alg: + sig_method_node = OneLogin_Saml2_XML.query(sign_node, './/ds:SignatureMethod') + if sig_method_node: + sig_method = sig_method_node[0].get("Algorithm") + if sig_method in OneLogin_Saml2_Constants.DEPRECATED_ALGORITHMS: + raise OneLogin_Saml2_ValidationError( + 'Deprecated signature algorithm found: %s' % sig_method, + OneLogin_Saml2_ValidationError.DEPRECATED_SIGNATURE_METHOD + ) + + dig_method_node = OneLogin_Saml2_XML.query(sign_node, './/ds:DigestMethod') + if dig_method_node: + dig_method = dig_method_node[0].get("Algorithm") + if dig_method in OneLogin_Saml2_Constants.DEPRECATED_ALGORITHMS: + raise OneLogin_Saml2_ValidationError( + 'Deprecated digest algorithm found: %s' % dig_method, + OneLogin_Saml2_ValidationError.DEPRECATED_DIGEST_METHOD + ) + signed_elements.append(signed_element) if signed_elements: diff --git a/src/onelogin/saml2/settings.py b/src/onelogin/saml2/settings.py index c258758c..e6ea7b74 100644 --- a/src/onelogin/saml2/settings.py +++ b/src/onelogin/saml2/settings.py @@ -312,6 +312,9 @@ def _add_default_values(self): # Digest Algorithm self._security.setdefault('digestAlgorithm', OneLogin_Saml2_Constants.SHA256) + # Reject Deprecated Algorithms + self._security.setdefault('rejectDeprecatedAlgorithm', False) + # AttributeStatement required by default self._security.setdefault('wantAttributeStatement', True) diff --git a/tests/src/OneLogin/saml2_tests/auth_test.py b/tests/src/OneLogin/saml2_tests/auth_test.py index 4a872c81..efaf1b40 100644 --- a/tests/src/OneLogin/saml2_tests/auth_test.py +++ b/tests/src/OneLogin/saml2_tests/auth_test.py @@ -1215,9 +1215,31 @@ def testIsInValidLogoutResponseSign(self): auth.process_slo() self.assertIn('Signature validation failed. Logout Response rejected', auth.get_errors()) + def testIsInValidLogoutResponseSignatureRejectingDeprecatedAlgorithm(self): + """ + Tests the process_slo method of the OneLogin_Saml2_Auth + """ + request_data = { + 'http_host': 'example.com', + 'script_name': 'index.html', + 'get_data': { + 'SAMLResponse': 'fZHbasJAEIZfJey9ZrNZc1gSodRSBKtQxYveyGQz1kCyu2Q24OM3jS21UHo3p++f4Z+CoGud2th3O/hXJGcNYXDtWkNqapVs6I2yQA0pAx2S8lrtH142Ssy5cr31VtuW3SH/E0CEvW+sYcF6VbLTIktFLMWZgxQR8DSP85wDB4GJGMOqShYVaoBUsOCIPY1kyUahEScacG3Ig/FjiUdyxuOZ4IcoUVGq4vSNBSsk3xjwE3Xx3qkwJD+cz3NtuxBN7WxjPN1F1NLcXdwob77tONiS7bZPm93zenvCqopxgVJmuU50jREsZF4noKWAOuNZJbNznnBky+LTDDVd2S+/dje1m+MVOtfidEER3g8Vt2fsPfiBfmePtsbgCO2A/9tL07TaD1ojEQuXtw0/ouFfD19+AA==', + 'RelayState': 'http://stuff.com/endpoints/endpoints/index.php', + 'SigAlg': 'http://www.w3.org/2000/09/xmldsig#rsa-sha1', + 'Signature': 'OV9c4R0COSjN69fAKCpV7Uj/yx6/KFxvbluVCzdK3UuortpNMpgHFF2wYNlMSG9GcYGk6p3I8nB7Z+1TQchMWZOlO/StjAqgtZhtpiwPcWryNuq8vm/6hnJ3zMDhHTS7F8KG4qkCXmJ9sQD3Y31UNcuygBwIbNakvhDT5Qo9Nsw=' + } + } + settings_info = self.loadSettingsJSON('settings8.json') + settings_info['security']['rejectDeprecatedAlgorithm'] = True + settings = OneLogin_Saml2_Settings(settings_info) + auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) + auth.process_slo() + self.assertIn('Signature validation failed. Logout Response rejected', auth.get_errors()) + self.assertEqual('Deprecated signature algorithm found: http://www.w3.org/2000/09/xmldsig#rsa-sha1', auth.get_last_error_reason()) + def testIsValidLogoutRequestSign(self): """ - Tests the is_valid method of the OneLogin_Saml2_LogoutRequest + Tests the process_slo method of the OneLogin_Saml2_Auth """ request_data = { 'http_host': 'example.com', @@ -1303,6 +1325,29 @@ def testIsValidLogoutRequestSign(self): auth.process_slo() self.assertIn('Signature validation failed. Logout Request rejected', auth.get_errors()) + def testIsInValidLogoutRequestSignatureRejectingDeprecatedAlgorithm(self): + """ + Tests the process_slo method of the OneLogin_Saml2_Auth + """ + request_data = { + 'http_host': 'example.com', + 'script_name': 'index.html', + 'get_data': { + 'SAMLRequest': 'fZJNa+MwEIb/itHdiTz6sC0SQyEsBPoB27KHXoIsj7cGW3IlGfLzV7G7kN1DL2KYmeedmRcdgp7GWT26326JP/FzwRCz6zTaoNbKkSzeKqfDEJTVEwYVjXp9eHpUsKNq9i4640Zyh3xP6BDQx8FZkp1PR3KpqexAl72QmpUCS8SW01IiZz2TVVGD4X1VQYlAsl/oQyKPJAklPIQFzzZEbWNK0YLnlOVA3wqpQCoB7yQ7pWsGq+NKfcQ4q/0+xKXvd8ZNe7Td7AYbw10UxrCbP2aSPbv4Yl/8Qx/R3+SB5bTOoXiDQvFNvjnc7lXrIr75kh+6eYdXPc0jrkMO+/umjXhOtpxP2Q/nJx2/9+uWGbq8X1tV9NqGAW0kzaVvoe1AAJeCSWqYaUVRM2SilKKuqDTpFSlszdcK29RthVm9YriZebYdXpsLdhVAB7VJzif3haYMqqTVcl0JMBR4y+s2zak3sf/4v8l/vlHzBw==', + 'RelayState': '_1037fbc88ec82ce8e770b2bed1119747bb812a07e6', + 'SigAlg': 'http://www.w3.org/2000/09/xmldsig#rsa-sha1', + 'Signature': 'Ouxo9BV6zmq4yrgamT9EbSKy/UmvSxGS8z26lIMgKOEP4LFR/N23RftdANmo4HafrzSfA0YTXwhKDqbOByS0j+Ql8OdQOes7vGioSjo5qq/Bi+5i6jXwQfphnfcHAQiJL4gYVIifkhhHRWpvYeiysF1Y9J02me0izwazFmoRXr4=' + } + } + settings_info = self.loadSettingsJSON('settings8.json') + settings_info = self.loadSettingsJSON('settings8.json') + settings_info['security']['rejectDeprecatedAlgorithm'] = True + settings = OneLogin_Saml2_Settings(settings_info) + auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) + auth.process_slo() + self.assertIn('Signature validation failed. Logout Request rejected', auth.get_errors()) + self.assertEqual('Deprecated signature algorithm found: http://www.w3.org/2000/09/xmldsig#rsa-sha1', auth.get_last_error_reason()) + def testGetLastRequestID(self): settings_info = self.loadSettingsJSON() request_data = self.get_request() diff --git a/tests/src/OneLogin/saml2_tests/response_test.py b/tests/src/OneLogin/saml2_tests/response_test.py index 3f83bc2b..ada973a6 100644 --- a/tests/src/OneLogin/saml2_tests/response_test.py +++ b/tests/src/OneLogin/saml2_tests/response_test.py @@ -1030,6 +1030,19 @@ def testIsInValidNoKey(self): with self.assertRaisesRegex(Exception, 'Signature validation failed. SAML Response rejected'): response.is_valid(self.get_request_data(), raise_exceptions=True) + def testIsInValidDeprecatedAlgorithm(self): + """ + Tests the is_valid method of the OneLogin_Saml2_Response + Case Deprecated algorithm used + """ + settings_dict = self.loadSettingsJSON() + settings_dict['security']['rejectDeprecatedAlgorithm'] = True + settings = OneLogin_Saml2_Settings(settings_dict) + xml = self.file_contents(join(self.data_path, 'responses', 'valid_response.xml.base64')) + response = OneLogin_Saml2_Response(settings, xml) + with self.assertRaisesRegex(Exception, 'Deprecated signature algorithm found: http://www.w3.org/2000/09/xmldsig#rsa-sha1'): + response.is_valid(self.get_request_data(), raise_exceptions=True) + def testIsInValidMultipleAssertions(self): """ Tests the is_valid method of the OneLogin_Saml2_Response From 4fe73342d61363a0b7b7ec2f8a8fdc2caee21206 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Fri, 28 Jan 2022 22:47:16 +0100 Subject: [PATCH 120/205] Release 1.13.0 --- changelog.md | 10 ++++++++++ setup.py | 4 ++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index cc17cb4e..2dc040c6 100644 --- a/changelog.md +++ b/changelog.md @@ -1,4 +1,14 @@ # python3-saml changelog +### 1.13.0 (Jan 28, 2022) + +- [#296](https://github.com/onelogin/python3-saml/pull/296) Add rejectDeprecatedAlgorithm settings in order to be able reject messages signed with deprecated algorithms. +- Set sha256 and rsa-sha256 as default algorithms +- [#288](https://github.com/onelogin/python3-saml/pull/288) Support building a LogoutResponse with non-success status +- Added warning about Open Redirect and Reply attacks +- [##274](https://github.com/onelogin/python3-saml/pull/274) Replace double-underscored names with single underscores +- Add at OneLogin_Saml2_Auth get_last_assertion_issue_instant() and get_last_response_in_response_to() methods +- Upgrade dependencies + ### 1.12.0 (Aug 13, 2021) * [#276](https://github.com/onelogin/python3-saml/pull/276) Deprecate server_port from request data dictionary diff --git a/setup.py b/setup.py index 21108ec8..c309285b 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ #! /usr/bin/env python # -*- coding: utf-8 -*- -# Copyright (c) 2010-2021 OneLogin, Inc. +# Copyright (c) 2010-2022 OneLogin, Inc. # MIT License from setuptools import setup @@ -9,7 +9,7 @@ setup( name='python3-saml', - version='1.12.0', + version='1.13.0', description='Onelogin Python Toolkit. Add SAML support to your Python software using this library', classifiers=[ 'Development Status :: 5 - Production/Stable', From dc05f2bec590088e54d88f78af78034bb0f1c6c0 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Fri, 28 Jan 2022 22:48:38 +0100 Subject: [PATCH 121/205] Remove extra space --- changelog.md | 1 - 1 file changed, 1 deletion(-) diff --git a/changelog.md b/changelog.md index 2dc040c6..cb4a5024 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,5 @@ # python3-saml changelog ### 1.13.0 (Jan 28, 2022) - - [#296](https://github.com/onelogin/python3-saml/pull/296) Add rejectDeprecatedAlgorithm settings in order to be able reject messages signed with deprecated algorithms. - Set sha256 and rsa-sha256 as default algorithms - [#288](https://github.com/onelogin/python3-saml/pull/288) Support building a LogoutResponse with non-success status From c94fe93172de89140bed3fca3957544570aa0c91 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Mon, 31 Jan 2022 11:29:52 +0200 Subject: [PATCH 122/205] Don't require yanked version of lxml --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index c309285b..b794f921 100644 --- a/setup.py +++ b/setup.py @@ -38,7 +38,7 @@ }, test_suite='tests', install_requires=[ - 'lxml==4.7.0', + 'lxml~=4.7.1', 'isodate>=0.6.1', 'xmlsec>=1.3.9' ], From b75a927bd0475b3de8cac3ffee6da7aa0ba2e75d Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Mon, 31 Jan 2022 11:23:58 +0200 Subject: [PATCH 123/205] CI: fix targeting rules --- .github/workflows/python-package.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 1cc6b96a..5dcec1eb 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -3,7 +3,13 @@ name: Python package -on: [push, pull_request] +on: + push: + branches: + - master + pull_request: + branches: + - master jobs: test: From 34ffd8c46d16f4d5babd16e8e44cd2c7eb429610 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Mon, 31 Jan 2022 11:09:40 +0200 Subject: [PATCH 124/205] CI: fix quoting for python-version list --- .github/workflows/python-package.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 5dcec1eb..53fd4645 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -17,8 +17,13 @@ jobs: strategy: fail-fast: false matrix: - python-version: [2.7,3.5,3.6,3.7, 3.8,3.9] - + python-version: + - "2.7" + - "3.5" + - "3.6" + - "3.7" + - "3.8" + - "3.9" steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} From 38f44ba9e09e7e4b55e7699f59c7e2a5e71487eb Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Mon, 31 Jan 2022 11:32:20 +0200 Subject: [PATCH 125/205] CI: upgrade, don't downgrade, setuptools --- .github/workflows/python-package.yml | 2 ++ Makefile | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 53fd4645..6330e7f7 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -38,6 +38,7 @@ jobs: ${{ runner.os }}-pip- - name: Install dependencies run: | + pip install -U setuptools sudo apt-get update -qq sudo apt-get install -qq swig python-dev libxml2-dev libxmlsec1-dev make install-req @@ -59,6 +60,7 @@ jobs: ${{ runner.os }}-pip- - name: Install dependencies run: | + pip install -U setuptools sudo apt-get update -qq sudo apt-get install -qq swig python-dev libxml2-dev libxmlsec1-dev make install-req diff --git a/Makefile b/Makefile index 45d73ecb..95848867 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,6 @@ TESTS=tests/src/OneLogin/saml2_tests SOURCES=$(MAIN_SOURCE) $(DEMOS) $(TESTS) install-req: - $(PIP) install --upgrade 'setuptools<45.0.0' $(PIP) install . install-test: From 891228ed247c19c22ced8c6cbe7c0dd7f4392fbd Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Mon, 31 Jan 2022 11:33:18 +0200 Subject: [PATCH 126/205] CI: run on Python 3.10 Refs #294 --- .github/workflows/python-package.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 6330e7f7..0f1e37f2 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -24,6 +24,7 @@ jobs: - "3.7" - "3.8" - "3.9" + - "3.10" steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} @@ -51,7 +52,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 with: - python-version: 3.9 + python-version: "3.10" - uses: actions/cache@v2 with: path: ~/.cache/pip From 66f367de4b0462d5d3011bbc21253554162bf07a Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Mon, 31 Jan 2022 11:33:27 +0200 Subject: [PATCH 127/205] Set Python 3.10 trove specifier --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index c309285b..b49ab57f 100644 --- a/setup.py +++ b/setup.py @@ -23,6 +23,7 @@ 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', ], author='OneLogin', author_email='support@onelogin.com', From f10edf68feaddeb0472819d220f28014c8ac8834 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Mon, 31 Jan 2022 12:43:43 +0200 Subject: [PATCH 128/205] Remove coveralls mentions The stats haven't been updated since 2017 --- .travis.yml | 2 -- README.md | 1 - setup.py | 1 - 3 files changed, 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index b690a1d0..820f9499 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,5 +23,3 @@ script: - 'coverage report -m --rcfile=tests/coverage.rc' # - 'pylint src/onelogin/saml2 --rcfile=tests/pylint.rc' - 'flake8 .' - -after_success: 'coveralls' diff --git a/README.md b/README.md index 43fd5322..8894edaf 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # OneLogin's SAML Python Toolkit (compatible with Python3) [![Build Status](https://api.travis-ci.org/onelogin/python3-saml.png?branch=master)](http://travis-ci.org/onelogin/python3-saml) -[![Coverage Status](https://coveralls.io/repos/github/onelogin/python3-saml/badge.svg?branch=master)](https://coveralls.io/github/onelogin/python3-saml?branch=master) [![PyPi Version](https://img.shields.io/pypi/v/python3-saml.svg)](https://pypi.python.org/pypi/python3-saml) ![Python versions](https://img.shields.io/pypi/pyversions/python3-saml.svg) diff --git a/setup.py b/setup.py index c309285b..04c23b40 100644 --- a/setup.py +++ b/setup.py @@ -49,7 +49,6 @@ 'freezegun>=0.3.11, <=1.1.0', 'pylint==1.9.4', 'flake8>=3.6.0', - 'coveralls>=1.11.1', 'pytest>=4.6', ), }, From 3dad0d3fff75636a70840c55ff430801227246b1 Mon Sep 17 00:00:00 2001 From: eriktalvi Date: Tue, 15 Feb 2022 16:25:49 -0800 Subject: [PATCH 129/205] Update setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index b794f921..a148b49a 100644 --- a/setup.py +++ b/setup.py @@ -38,7 +38,7 @@ }, test_suite='tests', install_requires=[ - 'lxml~=4.7.1', + 'lxml>4.7.1', 'isodate>=0.6.1', 'xmlsec>=1.3.9' ], From 34098971c8bb4ed810849db65a78e5066f2725e7 Mon Sep 17 00:00:00 2001 From: eriktalvi Date: Tue, 15 Feb 2022 16:27:47 -0800 Subject: [PATCH 130/205] Update setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index a148b49a..691f86d5 100644 --- a/setup.py +++ b/setup.py @@ -38,7 +38,7 @@ }, test_suite='tests', install_requires=[ - 'lxml>4.7.1', + 'lxml<4.7.1', 'isodate>=0.6.1', 'xmlsec>=1.3.9' ], From 809912de0862dd0e44fcbb11774d8da7b64e3418 Mon Sep 17 00:00:00 2001 From: Bryan Vestey Date: Fri, 18 Feb 2022 14:50:12 -0800 Subject: [PATCH 131/205] Release 1.14.0 --- changelog.md | 5 +++++ setup.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index cb4a5024..93037656 100644 --- a/changelog.md +++ b/changelog.md @@ -1,4 +1,9 @@ # python3-saml changelog +### 1.14.0 (Feb 18, 2022) +- [#297](https://github.com/onelogin/python3-saml/pull/297) Don't require yanked version of lxml. +- [#298](https://github.com/onelogin/python3-saml/pull/298) Add support for python 3.10 and cleanup the GHA. +- [#299](https://github.com/onelogin/python3-saml/pull/299) Remove stats from coveralls removed as they are no longer maintained. + ### 1.13.0 (Jan 28, 2022) - [#296](https://github.com/onelogin/python3-saml/pull/296) Add rejectDeprecatedAlgorithm settings in order to be able reject messages signed with deprecated algorithms. - Set sha256 and rsa-sha256 as default algorithms diff --git a/setup.py b/setup.py index 6df390ec..21105b36 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ setup( name='python3-saml', - version='1.13.0', + version='1.14.0', description='Onelogin Python Toolkit. Add SAML support to your Python software using this library', classifiers=[ 'Development Status :: 5 - Production/Stable', From 220a3359afd7db10fbbd11f6c359d91960b0ef9d Mon Sep 17 00:00:00 2001 From: Noam <69756316+noamsan@users.noreply.github.com> Date: Mon, 18 Jul 2022 13:54:30 +0300 Subject: [PATCH 132/205] Typo fix: reply -> replay "Reply attacks" should be "Replay attacks". --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 8894edaf..d22347a2 100644 --- a/README.md +++ b/README.md @@ -139,9 +139,9 @@ a trusted and expected URL. Read more about Open Redirect [CWE-601](https://cwe.mitre.org/data/definitions/601.html). -### Avoiding Reply attacks ### +### Avoiding Replay attacks ### -A reply attack is basically try to reuse an intercepted valid SAML Message in order to impersonate a SAML action (SSO or SLO). +A replay attack is basically try to reuse an intercepted valid SAML Message in order to impersonate a SAML action (SSO or SLO). SAML Messages have a limited timelife (NotBefore, NotOnOrAfter) that make harder this kind of attacks, but they are still possible. @@ -152,8 +152,7 @@ we don't need to store all processed message/assertion Ids, but the most recent The OneLogin_Saml2_Auth class contains the [get_last_request_id](https://github.com/onelogin/python3-saml/blob/ab62b0d6f3e5ac2ae8e95ce3ed2f85389252a32d/src/onelogin/saml2/auth.py#L357), [get_last_message_id](https://github.com/onelogin/python3-saml/blob/ab62b0d6f3e5ac2ae8e95ce3ed2f85389252a32d/src/onelogin/saml2/auth.py#L364) and [get_last_assertion_id](https://github.com/onelogin/python3-saml/blob/ab62b0d6f3e5ac2ae8e95ce3ed2f85389252a32d/src/onelogin/saml2/auth.py#L371) methods to retrieve the IDs -Checking that the ID of the current Message/Assertion does not exists in the lis of the ones already processed will prevent reply -attacks. +Checking that the ID of the current Message/Assertion does not exists in the lis of the ones already processed will prevent replay attacks. Getting Started From 0a79044f0c429132ceb79f527677a4ca5caa3124 Mon Sep 17 00:00:00 2001 From: Anand Nanduri Date: Wed, 3 Aug 2022 17:58:00 -0400 Subject: [PATCH 133/205] handle unicode characters gracefully in python 2 --- src/onelogin/saml2/compat.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/onelogin/saml2/compat.py b/src/onelogin/saml2/compat.py index 90bcfac7..804d4d46 100644 --- a/src/onelogin/saml2/compat.py +++ b/src/onelogin/saml2/compat.py @@ -39,6 +39,8 @@ def to_string(data): def to_bytes(data): """ return bytes """ + if isinstance(data, unicode): + return data.encode("utf8") return str(data) else: # py 3.x From ff2b31c0083605f032f3bd3b6d350d87b3ca94aa Mon Sep 17 00:00:00 2001 From: Bryan Vestey Date: Fri, 12 Aug 2022 09:46:11 -0700 Subject: [PATCH 134/205] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d22347a2..6c2a9127 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ [![PyPi Version](https://img.shields.io/pypi/v/python3-saml.svg)](https://pypi.python.org/pypi/python3-saml) ![Python versions](https://img.shields.io/pypi/pyversions/python3-saml.svg) +**Notice:** This project is currently not under active development, please see [#320](https://github.com/onelogin/python3-saml/issues/320) for more information. Add SAML support to your Python software using this library. Forget those complicated libraries and use the open source library provided From ba572e24fd3028c0e38c8f9dcd02af46ddcc0870 Mon Sep 17 00:00:00 2001 From: Bryan Vestey Date: Fri, 12 Aug 2022 09:47:11 -0700 Subject: [PATCH 135/205] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6c2a9127..9986ff5e 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![PyPi Version](https://img.shields.io/pypi/v/python3-saml.svg)](https://pypi.python.org/pypi/python3-saml) ![Python versions](https://img.shields.io/pypi/pyversions/python3-saml.svg) -**Notice:** This project is currently not under active development, please see [#320](https://github.com/onelogin/python3-saml/issues/320) for more information. +## **Notice:** This project is currently not under active development, please see [#320](https://github.com/onelogin/python3-saml/issues/320) for more information. Add SAML support to your Python software using this library. Forget those complicated libraries and use the open source library provided From 344c28640f8a718aeffd72f1ff56835488965eb0 Mon Sep 17 00:00:00 2001 From: George Khaburzaniya Date: Fri, 18 Nov 2022 13:31:08 -0800 Subject: [PATCH 136/205] Remove references to onelogin provided support. --- LICENSE | 2 +- README.md | 52 ++- demo-django/saml/certs/README | 2 +- demo-django/templates/base.html | 4 +- demo-flask/saml/certs/README | 2 +- demo-flask/templates/base.html | 4 +- demo-tornado/saml/certs/README | 2 +- demo-tornado/templates/base.html | 4 +- demo_pyramid/demo_pyramid/saml/certs/README | 2 +- docs/saml2/_modules/index.html | 21 +- docs/saml2/_modules/saml2/auth.html | 29 +- docs/saml2/_modules/saml2/authn_request.html | 30 +- docs/saml2/_modules/saml2/constants.html | 30 +- docs/saml2/_modules/saml2/errors.html | 30 +- docs/saml2/_modules/saml2/logout_request.html | 30 +- .../saml2/_modules/saml2/logout_response.html | 29 +- docs/saml2/_modules/saml2/metadata.html | 29 +- docs/saml2/_modules/saml2/response.html | 29 +- docs/saml2/_modules/saml2/settings.html | 29 +- docs/saml2/_modules/saml2/utils.html | 29 +- docs/saml2/_sources/index.txt | 2 +- docs/saml2/genindex.html | 359 +++++++++--------- docs/saml2/index.html | 27 +- docs/saml2/py-modindex.html | 23 +- docs/saml2/saml2.html | 29 +- docs/saml2/search.html | 29 +- setup.py | 9 +- src/onelogin/__init__.py | 6 +- src/onelogin/saml2/__init__.py | 6 +- src/onelogin/saml2/auth.py | 4 +- src/onelogin/saml2/authn_request.py | 4 +- src/onelogin/saml2/compat.py | 2 - src/onelogin/saml2/constants.py | 6 +- src/onelogin/saml2/errors.py | 4 +- src/onelogin/saml2/idp_metadata_parser.py | 4 +- src/onelogin/saml2/logout_request.py | 4 +- src/onelogin/saml2/logout_response.py | 4 +- src/onelogin/saml2/metadata.py | 4 +- src/onelogin/saml2/response.py | 4 +- src/onelogin/saml2/utils.py | 4 +- src/onelogin/saml2/xml_templates.py | 4 +- src/onelogin/saml2/xml_utils.py | 4 +- tests/data/metadata/idp_metadata.xml | 4 +- ...tadata_different_sign_and_encrypt_cert.xml | 4 +- ...dp_metadata_same_sign_and_encrypt_cert.xml | 4 +- tests/src/OneLogin/saml2_tests/auth_test.py | 2 - .../saml2_tests/authn_request_test.py | 2 - tests/src/OneLogin/saml2_tests/error_test.py | 2 - .../saml2_tests/idp_metadata_parser_test.py | 2 - .../saml2_tests/logout_request_test.py | 2 - .../saml2_tests/logout_response_test.py | 2 - .../src/OneLogin/saml2_tests/metadata_test.py | 2 - .../src/OneLogin/saml2_tests/response_test.py | 8 +- .../src/OneLogin/saml2_tests/settings_test.py | 2 - .../saml2_tests/signed_response_test.py | 2 - tests/src/OneLogin/saml2_tests/utils_test.py | 2 - .../OneLogin/saml2_tests/xml_utils_test.py | 2 - 57 files changed, 433 insertions(+), 540 deletions(-) diff --git a/LICENSE b/LICENSE index 734c18ba..0fd253e9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2010-2021 OneLogin, Inc. +Copyright (c) 2010-2022 OneLogin, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/README.md b/README.md index 9986ff5e..08f15fde 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,13 @@ -# OneLogin's SAML Python Toolkit (compatible with Python3) +# SAML Python Toolkit (compatible with Python3) [![Build Status](https://api.travis-ci.org/onelogin/python3-saml.png?branch=master)](http://travis-ci.org/onelogin/python3-saml) [![PyPi Version](https://img.shields.io/pypi/v/python3-saml.svg)](https://pypi.python.org/pypi/python3-saml) ![Python versions](https://img.shields.io/pypi/pyversions/python3-saml.svg) -## **Notice:** This project is currently not under active development, please see [#320](https://github.com/onelogin/python3-saml/issues/320) for more information. - Add SAML support to your Python software using this library. -Forget those complicated libraries and use the open source library provided -and supported by OneLogin Inc. +Forget those complicated libraries and use the open source library provided by the SAML tool community. -This version supports Python3. There is a separate version that only support Python2: [python-saml](https://github.com/onelogin/python-saml) +This version supports Python3. Python 2 support was deprecated on Jan 1st, 2020: [python-saml](https://github.com/onelogin/python-saml) #### Warning #### @@ -34,7 +31,7 @@ Update ``python3-saml`` to ``>= 1.2.1``, ``1.2.0`` had a bug on signature valida #### Security Guidelines #### -If you believe you have discovered a security vulnerability in this toolkit, please report it at https://www.onelogin.com/security with a description. We follow responsible disclosure guidelines, and will work with you to quickly find a resolution. +If you believe you have discovered a security vulnerability in this toolkit, please report it in an issue with a description. We follow responsible disclosure guidelines, and will work with you to quickly find a resolution. Why add SAML support to my software? ------------------------------------ @@ -62,7 +59,7 @@ since 2002, but lately it is becoming popular due its advantages: General Description ------------------- -OneLogin's SAML Python toolkit lets you turn your Python application into a SP +SAML Python toolkit lets you turn your Python application into a SP (Service Provider) that can be connected to an IdP (Identity Provider). **Supports:** @@ -83,7 +80,6 @@ OneLogin's SAML Python toolkit lets you turn your Python application into a SP * **Easy to use** - Programmer will be allowed to code high-level and low-level programming, 2 easy to use APIs are available. * **Tested** - Thoroughly tested. - * **Popular** - OneLogin's customers use it. Add easy support to your Django/Flask web projects. Installation ------------ @@ -103,8 +99,8 @@ Review the ``setup.py`` file to know the version of the library that ``python3-s The toolkit is hosted on GitHub. You can download it from: - * Latest release: https://github.com/onelogin/python3-saml/releases/latest - * Master repo: https://github.com/onelogin/python3-saml/tree/master + * Latest release: https://github.com/saml-toolkits/python3-saml/releases/latest + * Master repo: https://github.com/saml-toolkits/python3-saml/tree/master Copy the core of the library ``(src/onelogin/saml2 folder)`` and merge the ``setup.py`` inside the Python application. (Each application has its structure so take your time to locate the Python SAML toolkit in the best place). @@ -148,10 +144,10 @@ SAML Messages have a limited timelife (NotBefore, NotOnOrAfter) that make harder this kind of attacks, but they are still possible. In order to avoid them, the SP can keep a list of SAML Messages or Assertion IDs alredy valdidated and processed. Those values only need -to be stored the amount of time of the SAML Message life time, so +to be stored the amount of time of the SAML Message life time, so we don't need to store all processed message/assertion Ids, but the most recent ones. -The OneLogin_Saml2_Auth class contains the [get_last_request_id](https://github.com/onelogin/python3-saml/blob/ab62b0d6f3e5ac2ae8e95ce3ed2f85389252a32d/src/onelogin/saml2/auth.py#L357), [get_last_message_id](https://github.com/onelogin/python3-saml/blob/ab62b0d6f3e5ac2ae8e95ce3ed2f85389252a32d/src/onelogin/saml2/auth.py#L364) and [get_last_assertion_id](https://github.com/onelogin/python3-saml/blob/ab62b0d6f3e5ac2ae8e95ce3ed2f85389252a32d/src/onelogin/saml2/auth.py#L371) methods to retrieve the IDs +The OneLogin_Saml2_Auth class contains the [get_last_request_id](https://github.com/onelogin/python3-saml/blob/ab62b0d6f3e5ac2ae8e95ce3ed2f85389252a32d/src/onelogin/saml2/auth.py#L357), [get_last_message_id](https://github.com/onelogin/python3-saml/blob/ab62b0d6f3e5ac2ae8e95ce3ed2f85389252a32d/src/onelogin/saml2/auth.py#L364) and [get_last_assertion_id](https://github.com/onelogin/python3-saml/blob/ab62b0d6f3e5ac2ae8e95ce3ed2f85389252a32d/src/onelogin/saml2/auth.py#L371) methods to retrieve the IDs Checking that the ID of the current Message/Assertion does not exists in the lis of the ones already processed will prevent replay attacks. @@ -161,7 +157,7 @@ Getting Started ### Knowing the toolkit ### -The new OneLogin SAML Toolkit contains different folders (``certs``, ``lib``, ``demo-django``, ``demo-flask`` and ``tests``) and some files. +The new SAML Toolkit contains different folders (``certs``, ``lib``, ``demo-django``, ``demo-flask`` and ``tests``) and some files. Let's start describing them: @@ -267,7 +263,7 @@ This is the ``settings.json`` file: // URL Location where the from the IdP will be returned "url": "https:///?acs", // SAML protocol binding to be used when returning the - // message. OneLogin Toolkit supports this endpoint for the + // message. SAML Toolkit supports this endpoint for the // HTTP-POST binding only. "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" }, @@ -279,7 +275,7 @@ This is the ``settings.json`` file: // OPTIONAL: only specify if different from url parameter //"responseUrl": "https:///?sls", // SAML protocol binding to be used when returning the - // message. OneLogin Toolkit supports the HTTP-Redirect binding + // message. SAML Toolkit supports the HTTP-Redirect binding // only for this endpoint. "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" }, @@ -289,7 +285,7 @@ This is the ``settings.json`` file: "attributeConsumingService": { // OPTIONAL: only specifiy if SP requires this. // index is an integer which identifies the attributeConsumingService used - // to the SP. OneLogin toolkit supports configuring only one attributeConsumingService + // to the SP. SAML toolkit supports configuring only one attributeConsumingService // but in certain cases the SP requires a different value. Defaults to '1'. // "index": '1', "serviceName": "SP test", @@ -333,7 +329,7 @@ This is the ``settings.json`` file: // will be sent. "url": "https://app.onelogin.com/trust/saml2/http-post/sso/", // SAML protocol binding to be used when returning the - // message. OneLogin Toolkit supports the HTTP-Redirect binding + // message. SAML Toolkit supports the HTTP-Redirect binding // only for this endpoint. "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" }, @@ -342,10 +338,10 @@ This is the ``settings.json`` file: // URL Location where the from the IdP will be sent (IdP-initiated logout) "url": "https://app.onelogin.com/trust/saml2/http-redirect/slo/", // URL Location where the from the IdP will sent (SP-initiated logout, reply) - // OPTIONAL: only specify if different from url parameter + // OPTIONAL: only specify if different from url parameter "responseUrl": "https://app.onelogin.com/trust/saml2/http-redirect/slo_return/", // SAML protocol binding to be used when returning the - // message. OneLogin Toolkit supports the HTTP-Redirect binding + // message. SAML Toolkit supports the HTTP-Redirect binding // only for this endpoint. "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" }, @@ -483,7 +479,7 @@ In addition to the required settings data (idp, sp), extra settings can be defin // 'http://www.w3.org/2001/04/xmldsig-more#sha384' // 'http://www.w3.org/2001/04/xmlenc#sha512' 'digestAlgorithm': "http://www.w3.org/2001/04/xmlenc#sha256", - + // Specify if you want the SP to view assertions with duplicated Name or FriendlyName attributes to be valid // Defaults to false if not specified 'allowRepeatAttributeName': false, @@ -562,7 +558,7 @@ There's an easier method -- use a metadata exchange. Metadata is just an XML fi Using ````parse_remote```` IdP metadata can be obtained and added to the settings without further ado. -Take in mind that the OneLogin_Saml2_IdPMetadataParser class does not validate in any way the URL that is introduced in order to be parsed. +Take in mind that the OneLogin_Saml2_IdPMetadataParser class does not validate in any way the URL that is introduced in order to be parsed. Usually the same administrator that handles the Service Provider also sets the URL to the IdP, which should be a trusted resource. @@ -985,7 +981,7 @@ Described below are the main classes and methods that can be invoked from the SA #### OneLogin_Saml2_Auth - auth.py #### -Main class of OneLogin Python Toolkit +Main class of SAML Python Toolkit * `__init__` Initializes the SP SAML instance. * ***login*** Initiates the SSO process. @@ -1078,7 +1074,7 @@ SAML 2 Logout Response class #### OneLogin_Saml2_Settings - settings.py #### -Configuration of the OneLogin Python Toolkit +Configuration of the SAML Python Toolkit * `__init__` Initializes the settings: Sets the paths of the different folders and Loads settings info from settings file or array/object provided. * ***check_settings*** Checks the settings info. @@ -1246,7 +1242,7 @@ The flask project contains: #### SP setup #### -The Onelogin's Python Toolkit allows you to provide the settings info in 2 ways: Settings files or define a setting dict. In the ``demo-flask``, it uses the first method. +The SAML Python Toolkit allows you to provide the settings info in 2 ways: Settings files or define a setting dict. In the ``demo-flask``, it uses the first method. In the ``index.py`` file we define the ``app.config['SAML_PATH']``, that will target to the ``saml`` folder. We require it in order to load the settings files. @@ -1319,7 +1315,7 @@ The tornado project contains: #### SP setup #### -The Onelogin's Python Toolkit allows you to provide the settings info in 2 ways: Settings files or define a setting dict. In the ``demo-tornado``, it uses the first method. +The SAML Python Toolkit allows you to provide the settings info in 2 ways: Settings files or define a setting dict. In the ``demo-tornado``, it uses the first method. In the ``settings.py`` file we define the ``SAML_PATH``, that will target to the ``saml`` folder. We require it in order to load the settings files. @@ -1392,7 +1388,7 @@ The django project contains: #### SP setup #### -The Onelogin's Python Toolkit allows you to provide the settings info in 2 ways: settings files or define a setting dict. In the demo-django it used the first method. +The SAML Python Toolkit allows you to provide the settings info in 2 ways: settings files or define a setting dict. In the demo-django it used the first method. After set the ``SAML_FOLDER`` in the ``demo/settings.py``, the settings of the Python toolkit will be loaded on the Django web. @@ -1472,7 +1468,7 @@ The Pyramid project contains: #### SP setup #### -The Onelogin's Python Toolkit allows you to provide the settings info in 2 ways: settings files or define a setting dict. In ``demo_pyramid`` the first method is used. +The SAML Python Toolkit allows you to provide the settings info in 2 ways: settings files or define a setting dict. In ``demo_pyramid`` the first method is used. In the ``views.py`` file we define the ``SAML_PATH``, which will target the ``saml`` folder. We require it in order to load the settings files. diff --git a/demo-django/saml/certs/README b/demo-django/saml/certs/README index 7e837fb9..ed973e05 100644 --- a/demo-django/saml/certs/README +++ b/demo-django/saml/certs/README @@ -1,6 +1,6 @@ Take care of this folder that could contain private key. Be sure that this folder never is published. -Onelogin Python Toolkit expects that certs for the SP could be stored in this folder as: +SAML Python Toolkit expects that certs for the SP could be stored in this folder as: * sp.key Private Key * sp.crt Public cert diff --git a/demo-django/templates/base.html b/demo-django/templates/base.html index a55dbf0b..960ca0bc 100644 --- a/demo-django/templates/base.html +++ b/demo-django/templates/base.html @@ -5,7 +5,7 @@ - A Python SAML Toolkit by OneLogin demo + A Python SAML Toolkit demo @@ -18,7 +18,7 @@
    -

    A Python SAML Toolkit by OneLogin demo

    +

    A Python SAML Toolkit demo

    {% block content %}{% endblock %}
    diff --git a/demo-flask/saml/certs/README b/demo-flask/saml/certs/README index 7e837fb9..ed973e05 100644 --- a/demo-flask/saml/certs/README +++ b/demo-flask/saml/certs/README @@ -1,6 +1,6 @@ Take care of this folder that could contain private key. Be sure that this folder never is published. -Onelogin Python Toolkit expects that certs for the SP could be stored in this folder as: +SAML Python Toolkit expects that certs for the SP could be stored in this folder as: * sp.key Private Key * sp.crt Public cert diff --git a/demo-flask/templates/base.html b/demo-flask/templates/base.html index a55dbf0b..960ca0bc 100644 --- a/demo-flask/templates/base.html +++ b/demo-flask/templates/base.html @@ -5,7 +5,7 @@ - A Python SAML Toolkit by OneLogin demo + A Python SAML Toolkit demo @@ -18,7 +18,7 @@
    -

    A Python SAML Toolkit by OneLogin demo

    +

    A Python SAML Toolkit demo

    {% block content %}{% endblock %}
    diff --git a/demo-tornado/saml/certs/README b/demo-tornado/saml/certs/README index 7e837fb9..ed973e05 100644 --- a/demo-tornado/saml/certs/README +++ b/demo-tornado/saml/certs/README @@ -1,6 +1,6 @@ Take care of this folder that could contain private key. Be sure that this folder never is published. -Onelogin Python Toolkit expects that certs for the SP could be stored in this folder as: +SAML Python Toolkit expects that certs for the SP could be stored in this folder as: * sp.key Private Key * sp.crt Public cert diff --git a/demo-tornado/templates/base.html b/demo-tornado/templates/base.html index e403a8ef..e71bb136 100644 --- a/demo-tornado/templates/base.html +++ b/demo-tornado/templates/base.html @@ -5,7 +5,7 @@ - A Python SAML Toolkit by OneLogin demo + A Python SAML Toolkit demo @@ -18,7 +18,7 @@
    -

    A Python SAML Toolkit by OneLogin demo

    +

    A Python SAML Toolkit demo

    {% block content %}{% end %}
    diff --git a/demo_pyramid/demo_pyramid/saml/certs/README b/demo_pyramid/demo_pyramid/saml/certs/README index 7e837fb9..ed973e05 100644 --- a/demo_pyramid/demo_pyramid/saml/certs/README +++ b/demo_pyramid/demo_pyramid/saml/certs/README @@ -1,6 +1,6 @@ Take care of this folder that could contain private key. Be sure that this folder never is published. -Onelogin Python Toolkit expects that certs for the SP could be stored in this folder as: +SAML Python Toolkit expects that certs for the SP could be stored in this folder as: * sp.key Private Key * sp.crt Public cert diff --git a/docs/saml2/_modules/index.html b/docs/saml2/_modules/index.html index d672e465..12fa0756 100644 --- a/docs/saml2/_modules/index.html +++ b/docs/saml2/_modules/index.html @@ -7,12 +7,12 @@ - - Overview: module code — OneLogin SAML Python library classes and methods - + + Overview: module code — SAML Python library classes and methods + - + - + +
    - +

    All modules for which code is available

    - \ No newline at end of file + diff --git a/docs/saml2/_modules/saml2/auth.html b/docs/saml2/_modules/saml2/auth.html index f70e70b2..494ef85c 100644 --- a/docs/saml2/_modules/saml2/auth.html +++ b/docs/saml2/_modules/saml2/auth.html @@ -7,12 +7,12 @@ - - saml2.auth — OneLogin SAML Python library classes and methods - + + saml2.auth — SAML Python library classes and methods + - + - - + + +
    - +

    Source code for saml2.auth

     # -*- coding: utf-8 -*-
     
    -# Copyright (c) 2010-2018 OneLogin, Inc.
    -# MIT License
     
     from base64 import b64encode
     from urllib import urlencode, quote
    @@ -363,7 +361,7 @@ 

    Source code for saml2.auth

             dsig_ctx = xmlsec.DSigCtx()
             dsig_ctx.signKey = xmlsec.Key.load(file_key.name, xmlsec.KeyDataFormatPem, None)
             file_key.close()
    -        
    +
             data = {
                 'SAMLRequest': quote(saml_request),
                 'RelayState': quote(relay_state),
    @@ -448,12 +446,11 @@ 

    Navigation

  • modules |
  • -
  • OneLogin SAML Python library classes and methods »
  • -
  • Class code »
  • +
  • SAML Python library classes and methods »
  • +
  • Class code »
  • diff --git a/docs/saml2/_modules/saml2/authn_request.html b/docs/saml2/_modules/saml2/authn_request.html index 207ee7a6..2bd22b2f 100644 --- a/docs/saml2/_modules/saml2/authn_request.html +++ b/docs/saml2/_modules/saml2/authn_request.html @@ -7,12 +7,12 @@ - - saml2.authn_request — OneLogin SAML Python library classes and methods - + + saml2.authn_request — SAML Python library classes and methods + - + - - + + +
    - +

    Source code for saml2.authn_request

     # -*- coding: utf-8 -*-
     
    -# Copyright (c) 2010-2018 OneLogin, Inc.
    -# MIT License
    -
     from base64 import b64encode
     from datetime import datetime
     from zlib import compress
    @@ -164,13 +161,12 @@ 

    Navigation

  • modules |
  • -
  • OneLogin SAML Python library classes and methods »
  • -
  • Class code »
  • +
  • SAML Python library classes and methods »
  • +
  • Class code »
  • - \ No newline at end of file + diff --git a/docs/saml2/_modules/saml2/constants.html b/docs/saml2/_modules/saml2/constants.html index 306ec1b3..1b0af52f 100644 --- a/docs/saml2/_modules/saml2/constants.html +++ b/docs/saml2/_modules/saml2/constants.html @@ -7,12 +7,12 @@ - - saml2.constants — OneLogin SAML Python library classes and methods - + + saml2.constants — SAML Python library classes and methods + - + - - + + +
    - +

    Source code for saml2.constants

     # -*- coding: utf-8 -*-
     
    -# Copyright (c) 2010-2018 OneLogin, Inc.
    -# MIT License
    -
     
     
    [docs]class OneLogin_Saml2_Constants: # Value added to the current time in time condition validations @@ -154,13 +151,12 @@

    Navigation

  • modules |
  • -
  • OneLogin SAML Python library classes and methods »
  • -
  • Class code »
  • +
  • SAML Python library classes and methods »
  • +
  • Class code »
  • - \ No newline at end of file + diff --git a/docs/saml2/_modules/saml2/errors.html b/docs/saml2/_modules/saml2/errors.html index ef958f31..ea6894b1 100644 --- a/docs/saml2/_modules/saml2/errors.html +++ b/docs/saml2/_modules/saml2/errors.html @@ -7,12 +7,12 @@ - - saml2.errors — OneLogin SAML Python library classes and methods - + + saml2.errors — SAML Python library classes and methods + - + - - + + +
    - +

    Source code for saml2.errors

     # -*- coding: utf-8 -*-
     
    -# Copyright (c) 2010-2018 OneLogin, Inc.
    -# MIT License
    -
     
     
    [docs]class OneLogin_Saml2_Error(Exception): @@ -123,13 +120,12 @@

    Navigation

  • modules |
  • -
  • OneLogin SAML Python library classes and methods »
  • -
  • Class code »
  • +
  • SAML Python library classes and methods »
  • +
  • Class code »
  • - \ No newline at end of file + diff --git a/docs/saml2/_modules/saml2/logout_request.html b/docs/saml2/_modules/saml2/logout_request.html index 1b690dd4..ceb9779f 100644 --- a/docs/saml2/_modules/saml2/logout_request.html +++ b/docs/saml2/_modules/saml2/logout_request.html @@ -7,12 +7,12 @@ - - saml2.logout_request — OneLogin SAML Python library classes and methods - + + saml2.logout_request — SAML Python library classes and methods + - + - - + + +
    - +

    Source code for saml2.logout_request

     # -*- coding: utf-8 -*-
     
    -# Copyright (c) 2010-2018 OneLogin, Inc.
    -# MIT License
    -
     from base64 import b64decode
     from datetime import datetime
     from lxml import etree
    @@ -360,13 +357,12 @@ 

    Navigation

  • modules |
  • -
  • OneLogin SAML Python library classes and methods »
  • -
  • Class code »
  • +
  • SAML Python library classes and methods »
  • +
  • Class code »
  • - \ No newline at end of file + diff --git a/docs/saml2/_modules/saml2/logout_response.html b/docs/saml2/_modules/saml2/logout_response.html index 3ea3bbf7..9362300f 100644 --- a/docs/saml2/_modules/saml2/logout_response.html +++ b/docs/saml2/_modules/saml2/logout_response.html @@ -7,12 +7,12 @@ - - saml2.logout_response — OneLogin SAML Python library classes and methods - + + saml2.logout_response — SAML Python library classes and methods + - + - - + + +
    - +

    Source code for saml2.logout_response

     # -*- coding: utf-8 -*-
     
    -# Copyright (c) 2010-2018 OneLogin, Inc.
    -# MIT License
     
     from base64 import b64decode
     from datetime import datetime
    @@ -270,13 +268,12 @@ 

    Navigation

  • modules |
  • -
  • OneLogin SAML Python library classes and methods »
  • -
  • Class code »
  • +
  • SAML Python library classes and methods »
  • +
  • Class code »
  • - \ No newline at end of file + diff --git a/docs/saml2/_modules/saml2/metadata.html b/docs/saml2/_modules/saml2/metadata.html index 7b0a1be5..6f3d0b93 100644 --- a/docs/saml2/_modules/saml2/metadata.html +++ b/docs/saml2/_modules/saml2/metadata.html @@ -7,12 +7,12 @@ - - saml2.metadata — OneLogin SAML Python library classes and methods - + + saml2.metadata — SAML Python library classes and methods + - + - - + + +
    - +

    Source code for saml2.metadata

     # -*- coding: utf-8 -*-
     
    -# Copyright (c) 2010-2018 OneLogin, Inc.
    -# MIT License
     
     from time import gmtime, strftime
     from datetime import datetime
    @@ -277,13 +275,12 @@ 

    Navigation

  • modules |
  • -
  • OneLogin SAML Python library classes and methods »
  • -
  • Class code »
  • +
  • SAML Python library classes and methods »
  • +
  • Class code »
  • - \ No newline at end of file + diff --git a/docs/saml2/_modules/saml2/response.html b/docs/saml2/_modules/saml2/response.html index d3fee4e3..7f815169 100644 --- a/docs/saml2/_modules/saml2/response.html +++ b/docs/saml2/_modules/saml2/response.html @@ -7,12 +7,12 @@ - - saml2.response — OneLogin SAML Python library classes and methods - + + saml2.response — SAML Python library classes and methods + - + - - + + +
    - +

    Source code for saml2.response

     # -*- coding: utf-8 -*-
     
    -# Copyright (c) 2010-2018 OneLogin, Inc.
    -# MIT License
     
     from base64 import b64decode
     from copy import deepcopy
    @@ -520,13 +518,12 @@ 

    Navigation

  • modules |
  • -
  • OneLogin SAML Python library classes and methods »
  • -
  • Class code »
  • +
  • SAML Python library classes and methods »
  • +
  • Class code »
  • - \ No newline at end of file + diff --git a/docs/saml2/_modules/saml2/settings.html b/docs/saml2/_modules/saml2/settings.html index 42800b51..fe2bf6f5 100644 --- a/docs/saml2/_modules/saml2/settings.html +++ b/docs/saml2/_modules/saml2/settings.html @@ -7,12 +7,12 @@ - - saml2.settings — OneLogin SAML Python library classes and methods - + + saml2.settings — SAML Python library classes and methods + - + - - + + +
    - +

    Source code for saml2.settings

     # -*- coding: utf-8 -*-
     
    -# Copyright (c) 2010-2018 OneLogin, Inc.
    -# MIT License
     
     from datetime import datetime
     import json
    @@ -683,13 +681,12 @@ 

    Navigation

  • modules |
  • -
  • OneLogin SAML Python library classes and methods »
  • -
  • Class code »
  • +
  • SAML Python library classes and methods »
  • +
  • Class code »
  • - \ No newline at end of file + diff --git a/docs/saml2/_modules/saml2/utils.html b/docs/saml2/_modules/saml2/utils.html index 613d2a09..63cc42a3 100644 --- a/docs/saml2/_modules/saml2/utils.html +++ b/docs/saml2/_modules/saml2/utils.html @@ -7,12 +7,12 @@ - - saml2.utils — OneLogin SAML Python library classes and methods - + + saml2.utils — SAML Python library classes and methods + - + - - + + +
    - +

    Source code for saml2.utils

     # -*- coding: utf-8 -*-
     
    -# Copyright (c) 2010-2018 OneLogin, Inc.
    -# MIT License
     
     import base64
     from datetime import datetime
    @@ -793,13 +791,12 @@ 

    Navigation

  • modules |
  • -
  • OneLogin SAML Python library classes and methods »
  • -
  • Class code »
  • +
  • SAML Python library classes and methods »
  • +
  • Class code »
  • - \ No newline at end of file + diff --git a/docs/saml2/_sources/index.txt b/docs/saml2/_sources/index.txt index 43d48f47..50ca26a4 100644 --- a/docs/saml2/_sources/index.txt +++ b/docs/saml2/_sources/index.txt @@ -3,7 +3,7 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -Welcome to OneLogin SAML Python library documentation +Welcome to SAML Python library documentation ===================================================== Contents: diff --git a/docs/saml2/genindex.html b/docs/saml2/genindex.html index 3ee2939c..d9f60b97 100644 --- a/docs/saml2/genindex.html +++ b/docs/saml2/genindex.html @@ -9,12 +9,12 @@ - - Index — OneLogin SAML Python library classes and methods - + + Index — SAML Python library classes and methods + - + - + +
    - +

    Index

    @@ -70,54 +70,54 @@

    Index

    | T | V | W - +

    A

    - +
    AC_KERBEROS (saml2.constants.OneLogin_Saml2_Constants attribute)
    - +
    AC_PASSWORD (saml2.constants.OneLogin_Saml2_Constants attribute)
    - +
    AC_SMARTCARD (saml2.constants.OneLogin_Saml2_Constants attribute)
    - +
    AC_UNSPECIFIED (saml2.constants.OneLogin_Saml2_Constants attribute)
    - +
    AC_X509 (saml2.constants.OneLogin_Saml2_Constants attribute)
    - +
    add_sign() (saml2.utils.OneLogin_Saml2_Utils static method)
    - +
    add_x509_key_descriptors() (saml2.metadata.OneLogin_Saml2_Metadata static method)
    - +
    ALOWED_CLOCK_DRIFT (saml2.constants.OneLogin_Saml2_Constants attribute)
    - +
    ATTRNAME_FORMAT_BASIC (saml2.constants.OneLogin_Saml2_Constants attribute)
    - +
    ATTRNAME_FORMAT_UNSPECIFIED (saml2.constants.OneLogin_Saml2_Constants attribute)
    - +
    ATTRNAME_FORMAT_URI (saml2.constants.OneLogin_Saml2_Constants attribute)
    @@ -127,41 +127,41 @@

    A

    B

    - +
    BINDING_DEFLATE (saml2.constants.OneLogin_Saml2_Constants attribute)
    - +
    BINDING_HTTP_ARTIFACT (saml2.constants.OneLogin_Saml2_Constants attribute)
    - +
    BINDING_HTTP_POST (saml2.constants.OneLogin_Saml2_Constants attribute)
    - +
    BINDING_HTTP_REDIRECT (saml2.constants.OneLogin_Saml2_Constants attribute)
    - +
    BINDING_SOAP (saml2.constants.OneLogin_Saml2_Constants attribute)
    - +
    build() (saml2.logout_response.OneLogin_Saml2_Logout_Response method)
    - +
    build_request_signature() (saml2.auth.OneLogin_Saml2_Auth method)
    - +
    build_response_signature() (saml2.auth.OneLogin_Saml2_Auth method)
    - +
    builder() (saml2.metadata.OneLogin_Saml2_Metadata static method)
    @@ -171,33 +171,33 @@

    B

    C

    - +
    calculate_x509_fingerprint() (saml2.utils.OneLogin_Saml2_Utils static method)
    - +
    check_settings() (saml2.settings.OneLogin_Saml2_Settings method)
    - +
    check_sp_certs() (saml2.settings.OneLogin_Saml2_Settings method)
    - +
    check_status() (saml2.response.OneLogin_Saml2_Response method)
    - +
    CM_BEARER (saml2.constants.OneLogin_Saml2_Constants attribute)
    - +
    CM_HOLDER_KEY (saml2.constants.OneLogin_Saml2_Constants attribute)
    - +
    CM_SENDER_VOUCHES (saml2.constants.OneLogin_Saml2_Constants attribute)
    @@ -207,21 +207,21 @@

    C

    D

    - +
    decode_base64_and_inflate() (saml2.utils.OneLogin_Saml2_Utils static method)
    - +
    decrypt_element() (saml2.utils.OneLogin_Saml2_Utils static method)
    - +
    deflate_and_base64_encode() (saml2.utils.OneLogin_Saml2_Utils static method)
    - +
    delete_local_session() (saml2.utils.OneLogin_Saml2_Utils static method)
    @@ -231,17 +231,17 @@

    D

    F

    - +
    format_cert() (saml2.utils.OneLogin_Saml2_Utils static method)
    - +
    format_finger_print() (saml2.utils.OneLogin_Saml2_Utils static method)
    - +
    format_idp_cert() (saml2.settings.OneLogin_Saml2_Settings method)
    @@ -251,200 +251,200 @@

    F

    G

    - +
    generate_name_id() (saml2.utils.OneLogin_Saml2_Utils static method)
    - +
    generate_unique_id() (saml2.utils.OneLogin_Saml2_Utils static method)
    - +
    get_attribute() (saml2.auth.OneLogin_Saml2_Auth method)
    - +
    get_attributes() (saml2.auth.OneLogin_Saml2_Auth method)
    - +
    (saml2.response.OneLogin_Saml2_Response method)
    - +
    get_audiences() (saml2.response.OneLogin_Saml2_Response method)
    - +
    get_base_path() (saml2.settings.OneLogin_Saml2_Settings method)
    - +
    get_cert_path() (saml2.settings.OneLogin_Saml2_Settings method)
    - +
    get_contacts() (saml2.settings.OneLogin_Saml2_Settings method)
    - +
    get_errors() (saml2.auth.OneLogin_Saml2_Auth method)
    - +
    (saml2.settings.OneLogin_Saml2_Settings method)
    - +
    get_expire_time() (saml2.utils.OneLogin_Saml2_Utils static method)
    - +
    get_ext_lib_path() (saml2.settings.OneLogin_Saml2_Settings method)
    - +
    get_id() (saml2.logout_request.OneLogin_Saml2_Logout_Request static method)
    - +
    get_idp_data() (saml2.settings.OneLogin_Saml2_Settings method)
    - +
    get_issuer() (saml2.logout_request.OneLogin_Saml2_Logout_Request static method)
    - +
    (saml2.logout_response.OneLogin_Saml2_Logout_Response method)
    - +
    get_issuers() (saml2.response.OneLogin_Saml2_Response method)
    - +
    get_lib_path() (saml2.settings.OneLogin_Saml2_Settings method)
    - +
    get_name_id() (saml2.logout_request.OneLogin_Saml2_Logout_Request static method)
    - +
    get_name_id_data() (saml2.logout_request.OneLogin_Saml2_Logout_Request static method)
    - +
    get_nameid() (saml2.auth.OneLogin_Saml2_Auth method)
    - +
    (saml2.response.OneLogin_Saml2_Response method)
    - +
    get_nameid_data() (saml2.response.OneLogin_Saml2_Response method)
    - +
    get_organization() (saml2.settings.OneLogin_Saml2_Settings method)
    - +
    get_request() (saml2.authn_request.OneLogin_Saml2_Authn_Request method)
    - +
    (saml2.logout_request.OneLogin_Saml2_Logout_Request method)
    - +
    get_response() (saml2.logout_response.OneLogin_Saml2_Logout_Response method)
    - +
    get_schemas_path() (saml2.settings.OneLogin_Saml2_Settings method)
    - +
    get_security_data() (saml2.settings.OneLogin_Saml2_Settings method)
    - +
    get_self_host() (saml2.utils.OneLogin_Saml2_Utils static method)
    - +
    get_self_url() (saml2.utils.OneLogin_Saml2_Utils static method)
    - +
    get_self_url_host() (saml2.utils.OneLogin_Saml2_Utils static method)
    - +
    get_self_url_no_query() (saml2.utils.OneLogin_Saml2_Utils static method)
    - +
    get_session_index() (saml2.response.OneLogin_Saml2_Response method)
    - +
    get_session_indexes() (saml2.logout_request.OneLogin_Saml2_Logout_Request static method)
    - +
    get_session_not_on_or_after() (saml2.response.OneLogin_Saml2_Response method)
    - +
    get_settings() (saml2.auth.OneLogin_Saml2_Auth method)
    - +
    get_slo_url() (saml2.auth.OneLogin_Saml2_Auth method)
    - +
    get_sp_cert() (saml2.settings.OneLogin_Saml2_Settings method)
    - +
    get_sp_data() (saml2.settings.OneLogin_Saml2_Settings method)
    - +
    get_sp_key() (saml2.settings.OneLogin_Saml2_Settings method)
    - +
    get_sp_metadata() (saml2.settings.OneLogin_Saml2_Settings method)
    - +
    get_sso_url() (saml2.auth.OneLogin_Saml2_Auth method)
    - +
    get_status() (saml2.logout_response.OneLogin_Saml2_Logout_Response method)
    - +
    (saml2.utils.OneLogin_Saml2_Utils static method)
    @@ -455,34 +455,34 @@

    G

    I

    - +
    is_authenticated() (saml2.auth.OneLogin_Saml2_Auth method)
    - +
    is_debug_active() (saml2.settings.OneLogin_Saml2_Settings method)
    - +
    is_https() (saml2.utils.OneLogin_Saml2_Utils static method)
    - +
    is_strict() (saml2.settings.OneLogin_Saml2_Settings method)
    - +
    is_valid() (saml2.logout_request.OneLogin_Saml2_Logout_Request static method)
    - +
    (saml2.logout_response.OneLogin_Saml2_Logout_Response method)
    - +
    (saml2.response.OneLogin_Saml2_Response method)
    @@ -493,13 +493,13 @@

    I

    L

    - +
    login() (saml2.auth.OneLogin_Saml2_Auth method)
    - +
    logout() (saml2.auth.OneLogin_Saml2_Auth method)
    @@ -509,7 +509,7 @@

    L

    M

    - +
    METADATA_SP_INVALID (saml2.errors.OneLogin_Saml2_Error attribute)
    @@ -519,73 +519,73 @@

    M

    N

    - +
    NAMEID_EMAIL_ADDRESS (saml2.constants.OneLogin_Saml2_Constants attribute)
    - +
    NAMEID_ENCRYPTED (saml2.constants.OneLogin_Saml2_Constants attribute)
    - +
    NAMEID_ENTITY (saml2.constants.OneLogin_Saml2_Constants attribute)
    - +
    NAMEID_KERBEROS (saml2.constants.OneLogin_Saml2_Constants attribute)
    - +
    NAMEID_PERSISTENT (saml2.constants.OneLogin_Saml2_Constants attribute)
    - +
    NAMEID_TRANSIENT (saml2.constants.OneLogin_Saml2_Constants attribute)
    - +
    NAMEID_WINDOWS_DOMAIN_QUALIFIED_NAME (saml2.constants.OneLogin_Saml2_Constants attribute)
    - +
    NAMEID_X509_SUBJECT_NAME (saml2.constants.OneLogin_Saml2_Constants attribute)
    - +
    NS_DS (saml2.constants.OneLogin_Saml2_Constants attribute)
    - +
    NS_MD (saml2.constants.OneLogin_Saml2_Constants attribute)
    - +
    NS_SAML (saml2.constants.OneLogin_Saml2_Constants attribute)
    - +
    NS_SAMLP (saml2.constants.OneLogin_Saml2_Constants attribute)
    - +
    NS_SOAP (saml2.constants.OneLogin_Saml2_Constants attribute)
    - +
    NS_XENC (saml2.constants.OneLogin_Saml2_Constants attribute)
    - +
    NS_XS (saml2.constants.OneLogin_Saml2_Constants attribute)
    - +
    NS_XSI (saml2.constants.OneLogin_Saml2_Constants attribute)
    - +
    NSMAP (saml2.constants.OneLogin_Saml2_Constants attribute)
    @@ -595,45 +595,45 @@

    N

    O

    - +
    OneLogin_Saml2_Auth (class in saml2.auth)
    - +
    OneLogin_Saml2_Authn_Request (class in saml2.authn_request)
    - +
    OneLogin_Saml2_Constants (class in saml2.constants)
    - +
    OneLogin_Saml2_Error
    - +
    OneLogin_Saml2_Logout_Request (class in saml2.logout_request)
    - +
    OneLogin_Saml2_Logout_Response (class in saml2.logout_response)
    - +
    OneLogin_Saml2_Metadata (class in saml2.metadata)
    - +
    OneLogin_Saml2_Response (class in saml2.response)
    - +
    OneLogin_Saml2_Settings (class in saml2.settings)
    - +
    OneLogin_Saml2_Utils (class in saml2.utils)
    @@ -643,33 +643,33 @@

    O

    P

    - +
    parse_duration() (saml2.utils.OneLogin_Saml2_Utils static method)
    - +
    parse_SAML_to_time() (saml2.utils.OneLogin_Saml2_Utils static method)
    - +
    parse_time_to_SAML() (saml2.utils.OneLogin_Saml2_Utils static method)
    - +
    PRIVATE_KEY_FILE_NOT_FOUND (saml2.errors.OneLogin_Saml2_Error attribute)
    - +
    process_response() (saml2.auth.OneLogin_Saml2_Auth method)
    - +
    process_slo() (saml2.auth.OneLogin_Saml2_Auth method)
    - +
    PUBLIC_CERT_FILE_NOT_FOUND (saml2.errors.OneLogin_Saml2_Error attribute)
    @@ -679,7 +679,7 @@

    P

    Q

    - +
    query() (saml2.utils.OneLogin_Saml2_Utils static method)
    @@ -689,21 +689,21 @@

    Q

    R

    - +
    redirect() (saml2.utils.OneLogin_Saml2_Utils static method)
    - +
    REDIRECT_INVALID_URL (saml2.errors.OneLogin_Saml2_Error attribute)
    - +
    redirect_to() (saml2.auth.OneLogin_Saml2_Auth method)
    - +
    RSA_SHA1 (saml2.constants.OneLogin_Saml2_Constants attribute)
    @@ -713,123 +713,123 @@

    R

    S

    - +
    saml2.auth (module)
    - +
    saml2.authn_request (module)
    - +
    saml2.constants (module)
    - +
    saml2.errors (module)
    - +
    saml2.logout_request (module)
    - +
    saml2.logout_response (module)
    - +
    saml2.metadata (module)
    - +
    saml2.response (module)
    - +
    saml2.settings (module)
    - +
    saml2.utils (module)
    - +
    SAML_LOGOUTMESSAGE_NOT_FOUND (saml2.errors.OneLogin_Saml2_Error attribute)
    - +
    SAML_LOGOUTREQUEST_INVALID (saml2.errors.OneLogin_Saml2_Error attribute)
    - +
    SAML_LOGOUTRESPONSE_INVALID (saml2.errors.OneLogin_Saml2_Error attribute)
    - +
    SAML_RESPONSE_NOT_FOUND (saml2.errors.OneLogin_Saml2_Error attribute)
    - +
    SAML_SINGLE_LOGOUT_NOT_SUPPORTED (saml2.errors.OneLogin_Saml2_Error attribute)
    - +
    set_strict() (saml2.auth.OneLogin_Saml2_Auth method)
    - +
    (saml2.settings.OneLogin_Saml2_Settings method)
    - +
    SETTINGS_FILE_NOT_FOUND (saml2.errors.OneLogin_Saml2_Error attribute)
    - +
    SETTINGS_INVALID (saml2.errors.OneLogin_Saml2_Error attribute)
    - +
    SETTINGS_INVALID_SYNTAX (saml2.errors.OneLogin_Saml2_Error attribute)
    - +
    sign_metadata() (saml2.metadata.OneLogin_Saml2_Metadata static method)
    - +
    SP_CERTS_NOT_FOUND (saml2.errors.OneLogin_Saml2_Error attribute)
    - +
    STATUS_NO_PASSIVE (saml2.constants.OneLogin_Saml2_Constants attribute)
    - +
    STATUS_PARTIAL_LOGOUT (saml2.constants.OneLogin_Saml2_Constants attribute)
    - +
    STATUS_PROXY_COUNT_EXCEEDED (saml2.constants.OneLogin_Saml2_Constants attribute)
    - +
    STATUS_REQUESTER (saml2.constants.OneLogin_Saml2_Constants attribute)
    - +
    STATUS_RESPONDER (saml2.constants.OneLogin_Saml2_Constants attribute)
    - +
    STATUS_SUCCESS (saml2.constants.OneLogin_Saml2_Constants attribute)
    - +
    STATUS_VERSION_MISMATCH (saml2.constants.OneLogin_Saml2_Constants attribute)
    @@ -839,13 +839,13 @@

    S

    T

    - +
    TIME_CACHED (saml2.metadata.OneLogin_Saml2_Metadata attribute)
    - +
    TIME_VALID (saml2.metadata.OneLogin_Saml2_Metadata attribute)
    @@ -855,29 +855,29 @@

    T

    V

    - +
    validate_metadata() (saml2.settings.OneLogin_Saml2_Settings method)
    - +
    validate_num_assertions() (saml2.response.OneLogin_Saml2_Response method)
    - +
    validate_sign() (saml2.utils.OneLogin_Saml2_Utils static method)
    - +
    validate_timestamps() (saml2.response.OneLogin_Saml2_Response method)
    - +
    validate_url() (in module saml2.settings)
    - +
    validate_xml() (saml2.utils.OneLogin_Saml2_Utils static method)
    @@ -887,7 +887,7 @@

    V

    W

    ]", "i"), - // checked="checked" or checked - rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, - rscriptType = /\/(java|ecma)script/i, - rcleanScript = /^\s*", "" ], - legend: [ 1, "
    ", "
    " ], - thead: [ 1, "
    - +
    write_temp_file() (saml2.utils.OneLogin_Saml2_Utils static method)
    @@ -902,7 +902,7 @@

    W

    - + - \ No newline at end of file + diff --git a/docs/saml2/index.html b/docs/saml2/index.html index d17bd654..1015fa57 100644 --- a/docs/saml2/index.html +++ b/docs/saml2/index.html @@ -7,12 +7,12 @@ - - Welcome to OneLogin SAML Python library documentation - + + Welcome to SAML Python library documentation + - + - - + + +
    - +
    -

    Welcome to OneLogin SAML Python library documentation

    +

    Welcome to SAML Python library documentation

    Contents:

    - \ No newline at end of file + diff --git a/docs/saml2/py-modindex.html b/docs/saml2/py-modindex.html index e7321859..5f2d4393 100644 --- a/docs/saml2/py-modindex.html +++ b/docs/saml2/py-modindex.html @@ -7,12 +7,12 @@ - - Python Class Index — OneLogin SAML Python library classes and methods - + + Python Class Index — SAML Python library classes and methods + - + - - + + @@ -40,15 +40,15 @@

    Navigation

  • modules |
  • -
  • OneLogin SAML Python library classes and methods »
  • +
  • SAML Python library classes and methods »
  • -
    +
    - +

    Python Class Index

    @@ -143,12 +143,11 @@

    Navigation

  • modules |
  • -
  • OneLogin SAML Python library classes and methods »
  • +
  • SAML Python library classes and methods »
  • - \ No newline at end of file + diff --git a/docs/saml2/saml2.html b/docs/saml2/saml2.html index 6dd1e00a..6b9631f0 100644 --- a/docs/saml2/saml2.html +++ b/docs/saml2/saml2.html @@ -7,12 +7,12 @@ - - OneLogin saml2 Module — OneLogin SAML Python library classes and methods - + + OneLogin saml2 Module — SAML Python library classes and methods + - + - - + + +
    - +

    OneLogin saml2 Module

    @@ -1998,7 +1998,7 @@

    Table Of Contents

    Previous topic

    Welcome to OneLogin SAML Python library documentation

    + title="previous chapter">Welcome to SAML Python library documentation

    This Page

    - \ No newline at end of file + diff --git a/docs/saml2/search.html b/docs/saml2/search.html index 531d00de..933fde97 100644 --- a/docs/saml2/search.html +++ b/docs/saml2/search.html @@ -7,12 +7,12 @@ - - Search — OneLogin SAML Python library classes and methods - + + Search — SAML Python library classes and methods + - + - + - + - + @@ -45,15 +45,15 @@

    Navigation

  • modules |
  • -
  • OneLogin SAML Python library classes and methods »
  • +
  • SAML Python library classes and methods »
  • -
    +
    - +

    Search

    @@ -73,9 +73,9 @@

    Search

    - +
    - +
    @@ -96,12 +96,11 @@

    Navigation

  • modules |
  • -
  • OneLogin SAML Python library classes and methods »
  • +
  • SAML Python library classes and methods »
  • - \ No newline at end of file + diff --git a/setup.py b/setup.py index 21105b36..8e0b7355 100644 --- a/setup.py +++ b/setup.py @@ -1,16 +1,13 @@ #! /usr/bin/env python # -*- coding: utf-8 -*- -# Copyright (c) 2010-2022 OneLogin, Inc. -# MIT License - from setuptools import setup setup( name='python3-saml', version='1.14.0', - description='Onelogin Python Toolkit. Add SAML support to your Python software using this library', + description='Saml Python Toolkit. Add SAML support to your Python software using this library', classifiers=[ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', @@ -25,10 +22,8 @@ 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', ], - author='OneLogin', - author_email='support@onelogin.com', license='MIT', - url='https://github.com/onelogin/python3-saml', + url='https://github.com/saml-toolkit/python3-saml', packages=['onelogin', 'onelogin/saml2'], include_package_data=True, package_data={ diff --git a/src/onelogin/__init__.py b/src/onelogin/__init__.py index 52ea1212..57f87a32 100644 --- a/src/onelogin/__init__.py +++ b/src/onelogin/__init__.py @@ -1,14 +1,10 @@ # -*- coding: utf-8 -*- """ -Copyright (c) 2010-2021 OneLogin, Inc. -MIT License Add SAML support to your Python softwares using this library. -Forget those complicated libraries and use that open source -library provided and supported by OneLogin Inc. -OneLogin's SAML Python toolkit let you build a SP (Service Provider) +SAML Python toolkit let you build a SP (Service Provider) over your Python application and connect it to any IdP (Identity Provider). Supports: diff --git a/src/onelogin/saml2/__init__.py b/src/onelogin/saml2/__init__.py index 52ea1212..57f87a32 100644 --- a/src/onelogin/saml2/__init__.py +++ b/src/onelogin/saml2/__init__.py @@ -1,14 +1,10 @@ # -*- coding: utf-8 -*- """ -Copyright (c) 2010-2021 OneLogin, Inc. -MIT License Add SAML support to your Python softwares using this library. -Forget those complicated libraries and use that open source -library provided and supported by OneLogin Inc. -OneLogin's SAML Python toolkit let you build a SP (Service Provider) +SAML Python toolkit let you build a SP (Service Provider) over your Python application and connect it to any IdP (Identity Provider). Supports: diff --git a/src/onelogin/saml2/auth.py b/src/onelogin/saml2/auth.py index dc045f25..ac85ebc1 100644 --- a/src/onelogin/saml2/auth.py +++ b/src/onelogin/saml2/auth.py @@ -2,10 +2,8 @@ """ OneLogin_Saml2_Auth class -Copyright (c) 2010-2021 OneLogin, Inc. -MIT License -Main class of OneLogin's Python Toolkit. +Main class of SAML Python Toolkit. Initializes the SP SAML instance diff --git a/src/onelogin/saml2/authn_request.py b/src/onelogin/saml2/authn_request.py index 0aa1623d..0a9f4438 100644 --- a/src/onelogin/saml2/authn_request.py +++ b/src/onelogin/saml2/authn_request.py @@ -2,10 +2,8 @@ """ OneLogin_Saml2_Authn_Request class -Copyright (c) 2010-2021 OneLogin, Inc. -MIT License -AuthNRequest class of OneLogin's Python Toolkit. +AuthNRequest class of SAML Python Toolkit. """ diff --git a/src/onelogin/saml2/compat.py b/src/onelogin/saml2/compat.py index 90bcfac7..42e32b08 100644 --- a/src/onelogin/saml2/compat.py +++ b/src/onelogin/saml2/compat.py @@ -2,8 +2,6 @@ """ py3 compatibility class -Copyright (c) 2010-2021 OneLogin, Inc. -MIT License """ diff --git a/src/onelogin/saml2/constants.py b/src/onelogin/saml2/constants.py index a938f8e3..44220c44 100644 --- a/src/onelogin/saml2/constants.py +++ b/src/onelogin/saml2/constants.py @@ -2,10 +2,8 @@ """ OneLogin_Saml2_Constants class -Copyright (c) 2010-2021 OneLogin, Inc. -MIT License -Constants class of OneLogin's Python Toolkit. +Constants class of SAML Python Toolkit. """ @@ -14,7 +12,7 @@ class OneLogin_Saml2_Constants(object): """ This class defines all the constants that will be used - in the OneLogin's Python Toolkit. + in the SAML Python Toolkit. """ diff --git a/src/onelogin/saml2/errors.py b/src/onelogin/saml2/errors.py index b231f982..bcb4890d 100644 --- a/src/onelogin/saml2/errors.py +++ b/src/onelogin/saml2/errors.py @@ -2,10 +2,8 @@ """ OneLogin_Saml2_Error class -Copyright (c) 2010-2021 OneLogin, Inc. -MIT License -Error class of OneLogin's Python Toolkit. +Error class of SAML Python Toolkit. Defines common Error codes and has a custom initializator. diff --git a/src/onelogin/saml2/idp_metadata_parser.py b/src/onelogin/saml2/idp_metadata_parser.py index 1b94e1a8..aa8fe2a2 100644 --- a/src/onelogin/saml2/idp_metadata_parser.py +++ b/src/onelogin/saml2/idp_metadata_parser.py @@ -1,9 +1,7 @@ # -*- coding: utf-8 -*- """ OneLogin_Saml2_IdPMetadataParser class -Copyright (c) 2010-2021 OneLogin, Inc. -MIT License -Metadata class of OneLogin's Python Toolkit. +Metadata class of SAML Python Toolkit. """ diff --git a/src/onelogin/saml2/logout_request.py b/src/onelogin/saml2/logout_request.py index 84cdd86b..23fc6216 100644 --- a/src/onelogin/saml2/logout_request.py +++ b/src/onelogin/saml2/logout_request.py @@ -2,10 +2,8 @@ """ OneLogin_Saml2_Logout_Request class -Copyright (c) 2010-2021 OneLogin, Inc. -MIT License -Logout Request class of OneLogin's Python Toolkit. +Logout Request class of SAML Python Toolkit. """ diff --git a/src/onelogin/saml2/logout_response.py b/src/onelogin/saml2/logout_response.py index d1110a73..40e9978a 100644 --- a/src/onelogin/saml2/logout_response.py +++ b/src/onelogin/saml2/logout_response.py @@ -2,10 +2,8 @@ """ OneLogin_Saml2_Logout_Response class -Copyright (c) 2010-2021 OneLogin, Inc. -MIT License -Logout Response class of OneLogin's Python Toolkit. +Logout Response class of SAML Python Toolkit. """ diff --git a/src/onelogin/saml2/metadata.py b/src/onelogin/saml2/metadata.py index cccd7ce0..d07685f8 100644 --- a/src/onelogin/saml2/metadata.py +++ b/src/onelogin/saml2/metadata.py @@ -2,10 +2,8 @@ """ OneLoginSaml2Metadata class -Copyright (c) 2010-2021 OneLogin, Inc. -MIT License -Metadata class of OneLogin's Python Toolkit. +Metadata class of SAML Python Toolkit. """ diff --git a/src/onelogin/saml2/response.py b/src/onelogin/saml2/response.py index 3cf0964e..595b8449 100644 --- a/src/onelogin/saml2/response.py +++ b/src/onelogin/saml2/response.py @@ -2,10 +2,8 @@ """ OneLogin_Saml2_Response class -Copyright (c) 2010-2021 OneLogin, Inc. -MIT License -SAML Response class of OneLogin's Python Toolkit. +SAML Response class of SAML Python Toolkit. """ diff --git a/src/onelogin/saml2/utils.py b/src/onelogin/saml2/utils.py index 34176e25..b3498337 100644 --- a/src/onelogin/saml2/utils.py +++ b/src/onelogin/saml2/utils.py @@ -2,10 +2,8 @@ """ OneLogin_Saml2_Utils class -Copyright (c) 2010-2021 OneLogin, Inc. -MIT License -Auxiliary class of OneLogin's Python Toolkit. +Auxiliary class of SAML Python Toolkit. """ diff --git a/src/onelogin/saml2/xml_templates.py b/src/onelogin/saml2/xml_templates.py index 5575d116..2484d6f3 100644 --- a/src/onelogin/saml2/xml_templates.py +++ b/src/onelogin/saml2/xml_templates.py @@ -2,10 +2,8 @@ """ OneLogin_Saml2_Auth class -Copyright (c) 2010-2021 OneLogin, Inc. -MIT License -Main class of OneLogin's Python Toolkit. +Main class of SAML Python Toolkit. Initializes the SP SAML instance diff --git a/src/onelogin/saml2/xml_utils.py b/src/onelogin/saml2/xml_utils.py index 8cbd434b..5cda946c 100644 --- a/src/onelogin/saml2/xml_utils.py +++ b/src/onelogin/saml2/xml_utils.py @@ -2,10 +2,8 @@ """ OneLogin_Saml2_XML class -Copyright (c) 2010-2021 OneLogin, Inc. -MIT License -Auxiliary class of OneLogin's Python Toolkit. +Auxiliary class of SAML Python Toolkit. """ diff --git a/tests/data/metadata/idp_metadata.xml b/tests/data/metadata/idp_metadata.xml index 146428c1..7c57cdf1 100644 --- a/tests/data/metadata/idp_metadata.xml +++ b/tests/data/metadata/idp_metadata.xml @@ -37,6 +37,6 @@ QOPR6cEwFZzP0tHTYbI839WgxX6hfhIUTUz6mLqq4+3P4BG3+1OXeVDg63y8Uh78 Support - support@onelogin.com + support@example.com - \ No newline at end of file + diff --git a/tests/data/metadata/idp_metadata_different_sign_and_encrypt_cert.xml b/tests/data/metadata/idp_metadata_different_sign_and_encrypt_cert.xml index df90353a..5736ff48 100644 --- a/tests/data/metadata/idp_metadata_different_sign_and_encrypt_cert.xml +++ b/tests/data/metadata/idp_metadata_different_sign_and_encrypt_cert.xml @@ -67,6 +67,6 @@ WQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw== Support - support@onelogin.com + support@example.com - \ No newline at end of file + diff --git a/tests/data/metadata/idp_metadata_same_sign_and_encrypt_cert.xml b/tests/data/metadata/idp_metadata_same_sign_and_encrypt_cert.xml index e7fd250b..ae8d09e9 100644 --- a/tests/data/metadata/idp_metadata_same_sign_and_encrypt_cert.xml +++ b/tests/data/metadata/idp_metadata_same_sign_and_encrypt_cert.xml @@ -66,6 +66,6 @@ QOPR6cEwFZzP0tHTYbI839WgxX6hfhIUTUz6mLqq4+3P4BG3+1OXeVDg63y8Uh78 Support - support@onelogin.com + support@example.com - \ No newline at end of file + diff --git a/tests/src/OneLogin/saml2_tests/auth_test.py b/tests/src/OneLogin/saml2_tests/auth_test.py index efaf1b40..82db914f 100644 --- a/tests/src/OneLogin/saml2_tests/auth_test.py +++ b/tests/src/OneLogin/saml2_tests/auth_test.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2010-2021 OneLogin, Inc. -# MIT License from base64 import b64decode, b64encode import json diff --git a/tests/src/OneLogin/saml2_tests/authn_request_test.py b/tests/src/OneLogin/saml2_tests/authn_request_test.py index cbf225a1..af7aa2cc 100644 --- a/tests/src/OneLogin/saml2_tests/authn_request_test.py +++ b/tests/src/OneLogin/saml2_tests/authn_request_test.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2010-2021 OneLogin, Inc. -# MIT License import json from os.path import dirname, join, exists diff --git a/tests/src/OneLogin/saml2_tests/error_test.py b/tests/src/OneLogin/saml2_tests/error_test.py index 9a36a283..7d311130 100644 --- a/tests/src/OneLogin/saml2_tests/error_test.py +++ b/tests/src/OneLogin/saml2_tests/error_test.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2010-2021 OneLogin, Inc. -# MIT License import unittest from onelogin.saml2.errors import OneLogin_Saml2_Error diff --git a/tests/src/OneLogin/saml2_tests/idp_metadata_parser_test.py b/tests/src/OneLogin/saml2_tests/idp_metadata_parser_test.py index 4aa653b5..46cfdbf4 100644 --- a/tests/src/OneLogin/saml2_tests/idp_metadata_parser_test.py +++ b/tests/src/OneLogin/saml2_tests/idp_metadata_parser_test.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2010-2021 OneLogin, Inc. -# MIT License try: from urllib.error import URLError diff --git a/tests/src/OneLogin/saml2_tests/logout_request_test.py b/tests/src/OneLogin/saml2_tests/logout_request_test.py index c18eaee4..cabe7b47 100644 --- a/tests/src/OneLogin/saml2_tests/logout_request_test.py +++ b/tests/src/OneLogin/saml2_tests/logout_request_test.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2010-2021 OneLogin, Inc. -# MIT License import json from os.path import dirname, join, exists diff --git a/tests/src/OneLogin/saml2_tests/logout_response_test.py b/tests/src/OneLogin/saml2_tests/logout_response_test.py index 4b3da96a..eea09882 100644 --- a/tests/src/OneLogin/saml2_tests/logout_response_test.py +++ b/tests/src/OneLogin/saml2_tests/logout_response_test.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2010-2021 OneLogin, Inc. -# MIT License import json from os.path import dirname, join, exists diff --git a/tests/src/OneLogin/saml2_tests/metadata_test.py b/tests/src/OneLogin/saml2_tests/metadata_test.py index c95ceead..31d94cf8 100644 --- a/tests/src/OneLogin/saml2_tests/metadata_test.py +++ b/tests/src/OneLogin/saml2_tests/metadata_test.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2010-2021 OneLogin, Inc. -# MIT License import json from os.path import dirname, join, exists diff --git a/tests/src/OneLogin/saml2_tests/response_test.py b/tests/src/OneLogin/saml2_tests/response_test.py index ada973a6..6bd31144 100644 --- a/tests/src/OneLogin/saml2_tests/response_test.py +++ b/tests/src/OneLogin/saml2_tests/response_test.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2010-2021 OneLogin, Inc. -# MIT License from base64 import b64decode @@ -112,7 +110,7 @@ def testReturnNameId(self): settings = OneLogin_Saml2_Settings(json_settings) xml = self.file_contents(join(self.data_path, 'responses', 'response1.xml.base64')) response = OneLogin_Saml2_Response(settings, xml) - self.assertEqual('support@onelogin.com', response.get_nameid()) + self.assertEqual('support@example.com', response.get_nameid()) xml_2 = self.file_contents(join(self.data_path, 'responses', 'response_encrypted_nameid.xml.base64')) response_2 = OneLogin_Saml2_Response(settings, xml_2) @@ -391,7 +389,7 @@ def testGetNameIdData(self): xml = self.file_contents(join(self.data_path, 'responses', 'response1.xml.base64')) response = OneLogin_Saml2_Response(settings, xml) expected_nameid_data = { - 'Value': 'support@onelogin.com', + 'Value': 'support@example.com', 'Format': 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress' } nameid_data = response.get_nameid_data() @@ -824,7 +822,7 @@ def testNodeTextAttack(self): attributes = response.get_attributes() nameid = response.get_nameid() self.assertEqual("smith", attributes.get('surname')[0]) - self.assertEqual('support@onelogin.com', nameid) + self.assertEqual('support@example.com', nameid) def testGetSessionNotOnOrAfter(self): """ diff --git a/tests/src/OneLogin/saml2_tests/settings_test.py b/tests/src/OneLogin/saml2_tests/settings_test.py index 16f59e26..36505cae 100644 --- a/tests/src/OneLogin/saml2_tests/settings_test.py +++ b/tests/src/OneLogin/saml2_tests/settings_test.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2010-2021 OneLogin, Inc. -# MIT License import json from os.path import dirname, join, exists, sep diff --git a/tests/src/OneLogin/saml2_tests/signed_response_test.py b/tests/src/OneLogin/saml2_tests/signed_response_test.py index b820e6c7..bed0e028 100644 --- a/tests/src/OneLogin/saml2_tests/signed_response_test.py +++ b/tests/src/OneLogin/saml2_tests/signed_response_test.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2010-2021 OneLogin, Inc. -# MIT License import json from os.path import dirname, join, exists diff --git a/tests/src/OneLogin/saml2_tests/utils_test.py b/tests/src/OneLogin/saml2_tests/utils_test.py index 39cb46de..f1e66453 100644 --- a/tests/src/OneLogin/saml2_tests/utils_test.py +++ b/tests/src/OneLogin/saml2_tests/utils_test.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2010-2021 OneLogin, Inc. -# MIT License from base64 import b64decode import json diff --git a/tests/src/OneLogin/saml2_tests/xml_utils_test.py b/tests/src/OneLogin/saml2_tests/xml_utils_test.py index c9e01a61..45a57452 100644 --- a/tests/src/OneLogin/saml2_tests/xml_utils_test.py +++ b/tests/src/OneLogin/saml2_tests/xml_utils_test.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2010-2021 OneLogin, Inc. -# MIT License import json import unittest From 21003d11286163758a87744a0ea0c91c80e90545 Mon Sep 17 00:00:00 2001 From: George Khaburzaniya Date: Fri, 18 Nov 2022 13:47:02 -0800 Subject: [PATCH 137/205] Pin ubuntu version for github workflow to 20.04. --- .github/workflows/python-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 0f1e37f2..e9b9cae8 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -13,7 +13,7 @@ on: jobs: test: - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 strategy: fail-fast: false matrix: @@ -47,7 +47,7 @@ jobs: - name: Test run: make pytest lint: - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 From e07d54c6a8b89de45459ff4c28bd1bd06171c9ce Mon Sep 17 00:00:00 2001 From: George Khaburzaniya Date: Fri, 18 Nov 2022 13:58:02 -0800 Subject: [PATCH 138/205] Fix tests and whitespace. --- src/onelogin/saml2/settings.py | 2 +- tests/src/OneLogin/saml2_tests/response_test.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/onelogin/saml2/settings.py b/src/onelogin/saml2/settings.py index e6ea7b74..ace5cf50 100644 --- a/src/onelogin/saml2/settings.py +++ b/src/onelogin/saml2/settings.py @@ -404,7 +404,7 @@ def check_idp_settings(self, settings): nameid_enc = bool(security.get('nameIdEncrypted')) if (want_assert_sign or want_mes_signed) and \ - not(exists_x509 or exists_fingerprint or exists_multix509sign): + not (exists_x509 or exists_fingerprint or exists_multix509sign): errors.append('idp_cert_or_fingerprint_not_found_and_required') if nameid_enc and not (exists_x509 or exists_multix509enc): errors.append('idp_cert_not_found_and_required') diff --git a/tests/src/OneLogin/saml2_tests/response_test.py b/tests/src/OneLogin/saml2_tests/response_test.py index 6bd31144..a4bcf0de 100644 --- a/tests/src/OneLogin/saml2_tests/response_test.py +++ b/tests/src/OneLogin/saml2_tests/response_test.py @@ -110,7 +110,7 @@ def testReturnNameId(self): settings = OneLogin_Saml2_Settings(json_settings) xml = self.file_contents(join(self.data_path, 'responses', 'response1.xml.base64')) response = OneLogin_Saml2_Response(settings, xml) - self.assertEqual('support@example.com', response.get_nameid()) + self.assertEqual('support@onelogin.com', response.get_nameid()) xml_2 = self.file_contents(join(self.data_path, 'responses', 'response_encrypted_nameid.xml.base64')) response_2 = OneLogin_Saml2_Response(settings, xml_2) @@ -389,7 +389,7 @@ def testGetNameIdData(self): xml = self.file_contents(join(self.data_path, 'responses', 'response1.xml.base64')) response = OneLogin_Saml2_Response(settings, xml) expected_nameid_data = { - 'Value': 'support@example.com', + 'Value': 'support@onelogin.com', 'Format': 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress' } nameid_data = response.get_nameid_data() @@ -822,7 +822,7 @@ def testNodeTextAttack(self): attributes = response.get_attributes() nameid = response.get_nameid() self.assertEqual("smith", attributes.get('surname')[0]) - self.assertEqual('support@example.com', nameid) + self.assertEqual('support@onelogin.com', nameid) def testGetSessionNotOnOrAfter(self): """ From 960b3a1d44a92329747de46302c15a95a151bab3 Mon Sep 17 00:00:00 2001 From: Stu Tomlinson Date: Tue, 30 Aug 2022 14:38:36 +0100 Subject: [PATCH 139/205] Remove lxml version restriction Add documentation on how to avoid libxml2 version incompatibilities --- README.md | 9 +++++++++ setup.py | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 08f15fde..9367178c 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,7 @@ Installation * python 2.7 // python 3.6 * [xmlsec](https://pypi.python.org/pypi/xmlsec) Python bindings for the XML Security Library. + * [lxml](https://pypi.python.org/pypi/lxml) Python bindings for the libxml2 and libxslt libraries. * [isodate](https://pypi.python.org/pypi/isodate) An ISO 8601 date/time/ duration parser and formatter @@ -115,6 +116,14 @@ $ pip install python3-saml If you want to know how a project can handle python packages review this [guide](https://packaging.python.org/en/latest/tutorial.html) and review this [sampleproject](https://github.com/pypa/sampleproject) +#### NOTE #### +To avoid ``libxml2`` library version incompatibilities between ``xmlsec`` and ``lxml`` it is recommended that ``lxml`` is not installed from binary. + +This can be ensured by executing: +``` +$ pip install --force-reinstall --no-binary lxml lxml +``` + Security Warning ---------------- diff --git a/setup.py b/setup.py index 8e0b7355..eedf9671 100644 --- a/setup.py +++ b/setup.py @@ -34,7 +34,7 @@ }, test_suite='tests', install_requires=[ - 'lxml<4.7.1', + 'lxml>=4.6.5', 'isodate>=0.6.1', 'xmlsec>=1.3.9' ], From 01a154cda1bde7bfd1edd572679d886062c45674 Mon Sep 17 00:00:00 2001 From: Stu Tomlinson Date: Fri, 16 Dec 2022 16:02:27 +0000 Subject: [PATCH 140/205] Add test for libxml2 version compatibility --- .../OneLogin/saml2_tests/xml_utils_test.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/src/OneLogin/saml2_tests/xml_utils_test.py b/tests/src/OneLogin/saml2_tests/xml_utils_test.py index 45a57452..bc563621 100644 --- a/tests/src/OneLogin/saml2_tests/xml_utils_test.py +++ b/tests/src/OneLogin/saml2_tests/xml_utils_test.py @@ -3,6 +3,7 @@ import json import unittest +import xmlsec from base64 import b64decode from lxml import etree @@ -32,6 +33,29 @@ def file_contents(self, filename): f.close() return content + def testLibxml2(self): + """ + Tests that libxml2 versions used by xmlsec and lxml are compatible + + If this test fails, reinstall lxml without using binary to ensure it is + linked to same version of libxml2 as xmlsec: + pip install --force-reinstall --no-binary lxml lxml + + See https://bugs.launchpad.net/lxml/+bug/1960668 + """ + env = etree.fromstring('') + sig = xmlsec.template.create( + env, + xmlsec.Transform.EXCL_C14N, + xmlsec.Transform.RSA_SHA256, + ns="ds" + ) + + ds = etree.QName(sig).namespace + cm = sig.find(".//{%s}CanonicalizationMethod" % ds) + + self.assertIsNotNone(cm) + def testValidateXML(self): """ Tests the validate_xml method of the OneLogin_Saml2_XML From 53dd64e094f33e6444d7381b8306a45fec9b5445 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Sun, 25 Dec 2022 13:09:05 +0100 Subject: [PATCH 141/205] Avoid lxml 4.7.0 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index eedf9671..36898896 100644 --- a/setup.py +++ b/setup.py @@ -34,7 +34,7 @@ }, test_suite='tests', install_requires=[ - 'lxml>=4.6.5', + 'lxml>=4.6.5, !=4.7.0', 'isodate>=0.6.1', 'xmlsec>=1.3.9' ], From 379f906d21a7ff1203517c3189c06db281c5a1b5 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Sun, 25 Dec 2022 13:27:31 +0100 Subject: [PATCH 142/205] Force travis to always use lxml from no binary --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 820f9499..09b1e7e1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,7 @@ install: - sudo apt-get update -qq - sudo apt-get install -qq swig python-dev libxml2-dev libxmlsec1-dev - 'travis_retry pip install .' + - 'travis_retry pip install --force-reinstall --no-binary lxml lxml' - 'travis_retry pip install -e ".[test]"' script: From 62163143dc22b333172f2838adc66d29675c1195 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Sun, 25 Dec 2022 21:36:48 +0100 Subject: [PATCH 143/205] Adopt Poetry. Prepare for 1.15.0 release. --- .gitignore | 1 + .travis.yml | 4 +- pyproject.toml | 180 +++++++++++++++++++++++++++++++++++++++++++++++++ setup.py | 8 +-- 4 files changed, 187 insertions(+), 6 deletions(-) create mode 100644 pyproject.toml diff --git a/.gitignore b/.gitignore index d449f63f..7504184a 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,7 @@ __pycache_ /.idea .mypy_cache/ .pytest_cache +poetry.lock *.key *.crt diff --git a/.travis.yml b/.travis.yml index 09b1e7e1..6ce39386 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,8 +19,8 @@ install: - 'travis_retry pip install --force-reinstall --no-binary lxml lxml' - 'travis_retry pip install -e ".[test]"' -script: +script: - 'coverage run --source=src/onelogin/saml2 --rcfile=tests/coverage.rc setup.py test' - 'coverage report -m --rcfile=tests/coverage.rc' # - 'pylint src/onelogin/saml2 --rcfile=tests/pylint.rc' - - 'flake8 .' + - 'flake8 --toml-config pyproject.toml' diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..71bf94f4 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,180 @@ +[tool.poetry] +name = "python3-saml" +version = "1.15.0" +description = "Saml Python Toolkit. Add SAML support to your Python software using this library" +license = "Apache-2.0" +authors = ["SAML-Toolkits "] +maintainers = ["Sixto Martin "] +readme = "README.md" +homepage = "https://saml.info" +repository = "https://github.com/SAML-Toolkits/python3-saml" +documentation = "https://pysaml2.readthedocs.io" +keywords = [ + "saml", + "saml2", + "sso", + "xmlsec", + "federation", + "identity", +] +classifiers = [ + "Topic :: Software Development :: Build Tools", + "Topic :: Software Development :: Libraries :: Python Modules", +] +packages = [ + { include = "onelogin", from = "src" }, + { include = "onelogin/saml2", from = "src" }, +] + +include = [ + { path = "src/onelogin/saml2/schemas"}, + { path = "tests", format = "sdist" } +] + +[tool.poetry.urls] +"Bug Tracker" = "https://github.com/SAML-Toolkits/python3-saml/issues" + +[tool.poetry.dependencies] +python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +lxml = ">=4.6.5, !=4.7.0" +xmlsec = ">=1.3.9" +isodate = ">=0.6.1" + +[tool.poetry.group.dev] +optional = true + +[tool.poetry.group.dev.dependencies] +black = "*" +isort = {version = "^5.10.1", extras = ["pyproject"]} +flake8 = ">=3.6.0" +Flake8-pyproject = "^1.1.0.post0" +flake8-bugbear = "^22.8.23" +flake8-logging-format = "^0.7.5" +ipdb = "^0.13.9" + +[tool.poetry.group.test] +optional = true + +[tool.poetry.group.test.dependencies] +freezegun= ">=0.3.11, <=1.1.0" +pytest = ">=6.0" +flake8 = ">=3.6.0" +#pylint = ">=1.9.4" + +[tool.poetry.group.coverage] +optional = true + +[tool.poetry.group.coverage.dependencies] +coverage = ">=4.5.2" +pytest-cov = "*" + +[tool.poetry.group.docs] +optional = true + +[tool.poetry.group.docs.dependencies] +sphinx = "*" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" + +[tool.pytest.ini_options] +minversion = "6.0" +addopts = "-ra -vvv" +testpaths = [ + "tests", +] +pythonpath = [ + "tests", +] + +[tool.coverage.run] +branch = true +source = ["src/onelogin/saml2"] +ignore_errors = true + +[tool.coverage.report] +exclude_lines = [ + "pragma: no cover", + "def __repr__", + "def __str__", + "raise AssertionError", + "raise NotImplementedError", + "if __name__ == .__main__.:", + "if TYPE_CHECKING:", + "if typing.TYPE_CHECKING:", +] + +[tool.coverage.html] +directory = "cov_html" + +[tool.flake8] +max-line-length = 210 +max-complexity = 22 +count = true +show-source = true +statistics = true +disable-noqa = false +# 'ignore' defaults to: E121,E123,E126,E226,E24,E704,W503,W504 +extend-ignore = [ + 'B904', + 'B006', + 'B950', + 'B017', + 'C901', + 'E501', + 'E731', +] +per-file-ignores = [ + '__init__.py:F401', +] +# 'select' defaults to: E,F,W,C90 +extend-select = [ + # * Default warnings reported by flake8-bugbear (B) - + # https://github.com/PyCQA/flake8-bugbear#list-of-warnings + 'B', + # * The B950 flake8-bugbear opinionated warnings - + # https://github.com/PyCQA/flake8-bugbear#opinionated-warnings + 'B9', +] +extend-exclude = [ + '.github', '.gitlab', + '.Python', '.*.pyc', '.*.pyo', '.*.pyd', '.*.py.class', '*.egg-info', + 'venv*', '.venv*', '.*_cache', + 'lib', 'lib64', '.*.so', + 'build', 'dist', 'sdist', 'wheels', +] + +[tool.black] +line-length = 200 +extend-exclude = ''' +# A regex preceded with ^/ will apply only to files and directories +# in the root of the project. +( + \.pytest_cache +) +''' + +[tool.isort] +profile = 'black' +# The 'black' profile means: +# multi_line_output = 3 +# include_trailing_comma = true +# force_grid_wrap = 0 +# use_parentheses = true +# ensure_newline_before_comments = true +# line_length = 88 +line_length = 200 # override black provile line_length +force_single_line = true # override black profile multi_line_output +star_first = true +group_by_package = true +force_sort_within_sections = true +lines_after_imports = 2 +honor_noqa = true +atomic = true +ignore_comments = true +skip_gitignore = true +src_paths = [ + 'src', + 'tests', +] diff --git a/setup.py b/setup.py index 36898896..72544008 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setup( name='python3-saml', - version='1.14.0', + version='1.15.0', description='Saml Python Toolkit. Add SAML support to your Python software using this library', classifiers=[ 'Development Status :: 5 - Production/Stable', @@ -23,7 +23,7 @@ 'Programming Language :: Python :: 3.10', ], license='MIT', - url='https://github.com/saml-toolkit/python3-saml', + url='https://github.com/SAML-Toolkits/python3-saml', packages=['onelogin', 'onelogin/saml2'], include_package_data=True, package_data={ @@ -43,10 +43,10 @@ 'test': ( 'coverage>=4.5.2', 'freezegun>=0.3.11, <=1.1.0', - 'pylint==1.9.4', +# 'pylint>=1.9.4', 'flake8>=3.6.0', 'pytest>=4.6', ), }, - keywords='saml saml2 xmlsec django flask pyramid python3', + keywords='saml saml2 sso xmlsec federation identity', ) From 36e071f1aa4efe2e74faba15ed1a45eba44b916a Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Sun, 25 Dec 2022 22:08:18 +0100 Subject: [PATCH 144/205] Fix #310. Remove python 3.4 references --- .travis.yml | 1 + pyproject.toml | 2 +- setup.py | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6ce39386..16f9a5d5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ python: - '3.7' - '3.8' - '3.9' + - '3.10' matrix: include: diff --git a/pyproject.toml b/pyproject.toml index 71bf94f4..54ce0363 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,7 +35,7 @@ include = [ "Bug Tracker" = "https://github.com/SAML-Toolkits/python3-saml/issues" [tool.poetry.dependencies] -python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" lxml = ">=4.6.5, !=4.7.0" xmlsec = ">=1.3.9" isodate = ">=0.6.1" diff --git a/setup.py b/setup.py index 72544008..44323e5a 100644 --- a/setup.py +++ b/setup.py @@ -14,7 +14,6 @@ 'Intended Audience :: System Administrators', 'Operating System :: OS Independent', 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', From b9d15cf37924b93f7d56aeaa351b9c985a7271cf Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Sun, 25 Dec 2022 23:10:39 +0100 Subject: [PATCH 145/205] Document how install dependencies for testing --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9367178c..6303ee58 100644 --- a/README.md +++ b/README.md @@ -224,7 +224,12 @@ Read more at https://pythonhosted.org/an_example_pypi_project/setuptools.html Contains the unit test of the toolkit. -In order to execute the test you only need to load the virtualenv with the toolkit installed on it and execute: +In order to execute the test you only need to load the virtualenv with the toolkit installed on it properly: +``` +pip install -e ".[test]" +``` + +and execute: ``` python setup.py test ``` From 12e019c4c7a5987a4df87bafe8c5d7b7189afb04 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Mon, 26 Dec 2022 00:11:55 +0100 Subject: [PATCH 146/205] Fix WantAuthnRequestsSigned parser. See #306 --- src/onelogin/saml2/idp_metadata_parser.py | 2 +- .../metadata/idp_multiple_descriptors.xml | 2 +- .../saml2_tests/idp_metadata_parser_test.py | 31 +++++++++++++++++-- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/onelogin/saml2/idp_metadata_parser.py b/src/onelogin/saml2/idp_metadata_parser.py index aa8fe2a2..f5085157 100644 --- a/src/onelogin/saml2/idp_metadata_parser.py +++ b/src/onelogin/saml2/idp_metadata_parser.py @@ -148,7 +148,7 @@ def parse( idp_entity_id = entity_descriptor_node.get('entityID', None) - want_authn_requests_signed = entity_descriptor_node.get('WantAuthnRequestsSigned', None) + want_authn_requests_signed = idp_descriptor_node.get('WantAuthnRequestsSigned', None) name_id_format_nodes = OneLogin_Saml2_XML.query(idp_descriptor_node, './md:NameIDFormat') if len(name_id_format_nodes) > 0: diff --git a/tests/data/metadata/idp_multiple_descriptors.xml b/tests/data/metadata/idp_multiple_descriptors.xml index a74f8e4a..863cbb8d 100644 --- a/tests/data/metadata/idp_multiple_descriptors.xml +++ b/tests/data/metadata/idp_multiple_descriptors.xml @@ -26,7 +26,7 @@ - + diff --git a/tests/src/OneLogin/saml2_tests/idp_metadata_parser_test.py b/tests/src/OneLogin/saml2_tests/idp_metadata_parser_test.py index 46cfdbf4..13dbdbc8 100644 --- a/tests/src/OneLogin/saml2_tests/idp_metadata_parser_test.py +++ b/tests/src/OneLogin/saml2_tests/idp_metadata_parser_test.py @@ -291,12 +291,37 @@ def test_parse_with_entity_id(self): data = OneLogin_Saml2_IdPMetadataParser.parse(xml_idp_metadata) self.assertEqual("https://foo.example.com/access/saml/idp.xml", data["idp"]["entityId"]) + expected_settings_json = """ + { + "security": {"authnRequestsSigned": "true"}, + "sp": { + "NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" + }, + "idp": { + "singleLogoutService": { + "url": "https://hello.example.com/access/saml/logout", + "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" + }, + "entityId": "https://foo.example.com/access/saml/idp.xml", + "x509cert": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURxekNDQXhTZ0F3SUJBZ0lCQVRBTkJna3Foa2lHOXcwQkFRc0ZBRENCaGpFTE1Ba0dBMVVFQmhNQ1FWVXgKRERBS0JnTlZCQWdUQTA1VFZ6RVBNQTBHQTFVRUJ4TUdVM2xrYm1WNU1Rd3dDZ1lEVlFRS0RBTlFTVlF4Q1RBSApCZ05WQkFzTUFERVlNQllHQTFVRUF3d1BiR0YzY21WdVkyVndhWFF1WTI5dE1TVXdJd1lKS29aSWh2Y05BUWtCCkRCWnNZWGR5Wlc1alpTNXdhWFJBWjIxaGFXd3VZMjl0TUI0WERURXlNRFF4T1RJeU5UUXhPRm9YRFRNeU1EUXgKTkRJeU5UUXhPRm93Z1lZeEN6QUpCZ05WQkFZVEFrRlZNUXd3Q2dZRFZRUUlFd05PVTFjeER6QU5CZ05WQkFjVApCbE41Wkc1bGVURU1NQW9HQTFVRUNnd0RVRWxVTVFrd0J3WURWUVFMREFBeEdEQVdCZ05WQkFNTUQyeGhkM0psCmJtTmxjR2wwTG1OdmJURWxNQ01HQ1NxR1NJYjNEUUVKQVF3V2JHRjNjbVZ1WTJVdWNHbDBRR2R0WVdsc0xtTnYKYlRDQm56QU5CZ2txaGtpRzl3MEJBUUVGQUFPQmpRQXdnWWtDZ1lFQXFqaWUzUjJvaStwRGFldndJeXMvbWJVVApubkdsa3h0ZGlrcnExMXZleHd4SmlQTmhtaHFSVzNtVXVKRXpsbElkVkw2RW14R1lUcXBxZjkzSGxoa3NhZUowCjhVZ2pQOVVtTVlyaFZKdTFqY0ZXVjdmei9yKzIxL2F3VG5EVjlzTVlRcXVJUllZeTdiRzByMU9iaXdkb3ZudGsKN2dGSTA2WjB2WmFjREU1Ym9xVUNBd0VBQWFPQ0FTVXdnZ0VoTUFrR0ExVWRFd1FDTUFBd0N3WURWUjBQQkFRRApBZ1VnTUIwR0ExVWREZ1FXQkJTUk9OOEdKOG8rOGpnRnRqa3R3WmRxeDZCUnlUQVRCZ05WSFNVRUREQUtCZ2dyCkJnRUZCUWNEQVRBZEJnbGdoa2dCaHZoQ0FRMEVFQllPVkdWemRDQllOVEE1SUdObGNuUXdnYk1HQTFVZEl3U0IKcXpDQnFJQVVrVGpmQmlmS1B2STRCYlk1TGNHWGFzZWdVY21oZ1l5a2dZa3dnWVl4Q3pBSkJnTlZCQVlUQWtGVgpNUXd3Q2dZRFZRUUlFd05PVTFjeER6QU5CZ05WQkFjVEJsTjVaRzVsZVRFTU1Bb0dBMVVFQ2d3RFVFbFVNUWt3CkJ3WURWUVFMREFBeEdEQVdCZ05WQkFNTUQyeGhkM0psYm1ObGNHbDBMbU52YlRFbE1DTUdDU3FHU0liM0RRRUoKQVF3V2JHRjNjbVZ1WTJVdWNHbDBRR2R0WVdsc0xtTnZiWUlCQVRBTkJna3Foa2lHOXcwQkFRc0ZBQU9CZ1FDRQpUQWVKVERTQVc2ejFVRlRWN1FyZWg0VUxGT1JhajkrZUN1RjNLV0RIYyswSVFDajlyZG5ERzRRL3dmNy9yYVEwCkpuUFFDU0NkclBMSmV5b1BIN1FhVHdvYUY3ZHpWdzRMQ3N5TkpURld4NGNNNTBWdzZSNWZET2dpQzhic2ZmUzgKQkptb3VscnJaRE5OVmpHOG1XNmNMeHJZdlZRT3JSVmVjQ0ZJZ3NzQ2JBPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=", + "singleSignOnService": { + "url": "https://hello.example.com/access/saml/login", + "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" + } + } + } + """ + expected_settings = json.loads(expected_settings_json) + self.assertEqual(expected_settings, data) + + # should find desired descriptor data2 = OneLogin_Saml2_IdPMetadataParser.parse(xml_idp_metadata, entity_id="https://bar.example.com/access/saml/idp.xml") self.assertEqual("https://bar.example.com/access/saml/idp.xml", data2["idp"]["entityId"]) - expected_settings_json = """ + expected_settings_json2 = """ { + "security": {"authnRequestsSigned": "false"}, "sp": { "NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" }, @@ -314,8 +339,8 @@ def test_parse_with_entity_id(self): } } """ - expected_settings = json.loads(expected_settings_json) - self.assertEqual(expected_settings, data2) + expected_settings2 = json.loads(expected_settings_json2) + self.assertEqual(expected_settings2, data2) def test_parse_multi_certs(self): """ From acef8ebbdf467b1875fc1fdaf15d61d1884b8c9f Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Mon, 26 Dec 2022 02:53:27 +0100 Subject: [PATCH 147/205] Fix Poetry with py27 --- .travis.yml | 3 ++- pyproject.toml | 62 ++++++++++++++++++++++++++------------------------ setup.py | 2 +- 3 files changed, 35 insertions(+), 32 deletions(-) diff --git a/.travis.yml b/.travis.yml index 16f9a5d5..c5f1fc4f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,4 +24,5 @@ script: - 'coverage run --source=src/onelogin/saml2 --rcfile=tests/coverage.rc setup.py test' - 'coverage report -m --rcfile=tests/coverage.rc' # - 'pylint src/onelogin/saml2 --rcfile=tests/pylint.rc' - - 'flake8 --toml-config pyproject.toml' +# - 'flake8 --toml-config pyproject.toml' + - 'flake8 .' diff --git a/pyproject.toml b/pyproject.toml index 54ce0363..8280a909 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,46 +40,48 @@ lxml = ">=4.6.5, !=4.7.0" xmlsec = ">=1.3.9" isodate = ">=0.6.1" -[tool.poetry.group.dev] -optional = true - -[tool.poetry.group.dev.dependencies] -black = "*" -isort = {version = "^5.10.1", extras = ["pyproject"]} -flake8 = ">=3.6.0" -Flake8-pyproject = "^1.1.0.post0" -flake8-bugbear = "^22.8.23" -flake8-logging-format = "^0.7.5" -ipdb = "^0.13.9" - -[tool.poetry.group.test] -optional = true - -[tool.poetry.group.test.dependencies] -freezegun= ">=0.3.11, <=1.1.0" -pytest = ">=6.0" -flake8 = ">=3.6.0" +#[tool.poetry.group.dev] +#optional = true + +#[tool.poetry.group.dev.dependencies] +#black = "*" +#isort = {version = "^5.10.1", extras = ["pyproject"]} +flake8 = { version = ">=3.6.0, <=5.0.0", optional = true} +#Flake8-pyproject = "^1.1.0.post0" +#flake8-bugbear = "^22.8.23" +#flake8-logging-format = "^0.7.5" +ipdb = { version = "^0.13.9", optional = true} +#[tool.poetry.group.test.dependencies] +freezegun= { version = ">=0.3.11, <=1.1.0", optional = true} +pytest = { version = ">=4.6.11", optional = true} +coverage = { version = ">=4.5.2", optional = true} #pylint = ">=1.9.4" -[tool.poetry.group.coverage] -optional = true +[tool.poetry.extras] +test = ["flake8", "ipdb", "freezegun", "pytest", "coverage"] -[tool.poetry.group.coverage.dependencies] -coverage = ">=4.5.2" -pytest-cov = "*" +#[tool.poetry.group.test] +#optional = true -[tool.poetry.group.docs] -optional = true +#[tool.poetry.group.coverage] +#optional = true -[tool.poetry.group.docs.dependencies] -sphinx = "*" +#[tool.poetry.group.coverage.dependencies] +#coverage = ">=4.5.2" +#pytest-cov = "*" + +#[tool.poetry.group.docs] +#optional = true + +#[tool.poetry.group.docs.dependencies] +#sphinx = "*" [build-system] -requires = ["poetry-core"] +requires = ["poetry>=1.1.15"] build-backend = "poetry.core.masonry.api" [tool.pytest.ini_options] -minversion = "6.0" +minversion = "4.6.11" addopts = "-ra -vvv" testpaths = [ "tests", diff --git a/setup.py b/setup.py index 44323e5a..79dfa8d9 100644 --- a/setup.py +++ b/setup.py @@ -43,7 +43,7 @@ 'coverage>=4.5.2', 'freezegun>=0.3.11, <=1.1.0', # 'pylint>=1.9.4', - 'flake8>=3.6.0', + 'flake8>=3.6.0, <=5.0.0', 'pytest>=4.6', ), }, From 89b961c2a143eb8a434aeb1035afebe4ae9fb6e9 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Mon, 26 Dec 2022 03:06:53 +0100 Subject: [PATCH 148/205] Fix travis setuptools issue --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index c5f1fc4f..546e6caf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,7 @@ matrix: install: - sudo apt-get update -qq - sudo apt-get install -qq swig python-dev libxml2-dev libxmlsec1-dev + - 'travis_retry pip install --upgrade setuptools' - 'travis_retry pip install .' - 'travis_retry pip install --force-reinstall --no-binary lxml lxml' - 'travis_retry pip install -e ".[test]"' From 31f1a856987d2a38cf6a5e2654c4c9d013eecfea Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Mon, 26 Dec 2022 03:36:04 +0100 Subject: [PATCH 149/205] Remove Poetry file --- .travis.yml | 3 +- pyproject.toml | 182 ------------------------------------------------- 2 files changed, 1 insertion(+), 184 deletions(-) delete mode 100644 pyproject.toml diff --git a/.travis.yml b/.travis.yml index 546e6caf..3fa95d6c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,9 +16,8 @@ matrix: install: - sudo apt-get update -qq - sudo apt-get install -qq swig python-dev libxml2-dev libxmlsec1-dev - - 'travis_retry pip install --upgrade setuptools' - - 'travis_retry pip install .' - 'travis_retry pip install --force-reinstall --no-binary lxml lxml' + - 'travis_retry pip install .' - 'travis_retry pip install -e ".[test]"' script: diff --git a/pyproject.toml b/pyproject.toml deleted file mode 100644 index 8280a909..00000000 --- a/pyproject.toml +++ /dev/null @@ -1,182 +0,0 @@ -[tool.poetry] -name = "python3-saml" -version = "1.15.0" -description = "Saml Python Toolkit. Add SAML support to your Python software using this library" -license = "Apache-2.0" -authors = ["SAML-Toolkits "] -maintainers = ["Sixto Martin "] -readme = "README.md" -homepage = "https://saml.info" -repository = "https://github.com/SAML-Toolkits/python3-saml" -documentation = "https://pysaml2.readthedocs.io" -keywords = [ - "saml", - "saml2", - "sso", - "xmlsec", - "federation", - "identity", -] -classifiers = [ - "Topic :: Software Development :: Build Tools", - "Topic :: Software Development :: Libraries :: Python Modules", -] -packages = [ - { include = "onelogin", from = "src" }, - { include = "onelogin/saml2", from = "src" }, -] - -include = [ - { path = "src/onelogin/saml2/schemas"}, - { path = "tests", format = "sdist" } -] - -[tool.poetry.urls] -"Bug Tracker" = "https://github.com/SAML-Toolkits/python3-saml/issues" - -[tool.poetry.dependencies] -python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -lxml = ">=4.6.5, !=4.7.0" -xmlsec = ">=1.3.9" -isodate = ">=0.6.1" - -#[tool.poetry.group.dev] -#optional = true - -#[tool.poetry.group.dev.dependencies] -#black = "*" -#isort = {version = "^5.10.1", extras = ["pyproject"]} -flake8 = { version = ">=3.6.0, <=5.0.0", optional = true} -#Flake8-pyproject = "^1.1.0.post0" -#flake8-bugbear = "^22.8.23" -#flake8-logging-format = "^0.7.5" -ipdb = { version = "^0.13.9", optional = true} -#[tool.poetry.group.test.dependencies] -freezegun= { version = ">=0.3.11, <=1.1.0", optional = true} -pytest = { version = ">=4.6.11", optional = true} -coverage = { version = ">=4.5.2", optional = true} -#pylint = ">=1.9.4" - -[tool.poetry.extras] -test = ["flake8", "ipdb", "freezegun", "pytest", "coverage"] - -#[tool.poetry.group.test] -#optional = true - -#[tool.poetry.group.coverage] -#optional = true - -#[tool.poetry.group.coverage.dependencies] -#coverage = ">=4.5.2" -#pytest-cov = "*" - -#[tool.poetry.group.docs] -#optional = true - -#[tool.poetry.group.docs.dependencies] -#sphinx = "*" - -[build-system] -requires = ["poetry>=1.1.15"] -build-backend = "poetry.core.masonry.api" - -[tool.pytest.ini_options] -minversion = "4.6.11" -addopts = "-ra -vvv" -testpaths = [ - "tests", -] -pythonpath = [ - "tests", -] - -[tool.coverage.run] -branch = true -source = ["src/onelogin/saml2"] -ignore_errors = true - -[tool.coverage.report] -exclude_lines = [ - "pragma: no cover", - "def __repr__", - "def __str__", - "raise AssertionError", - "raise NotImplementedError", - "if __name__ == .__main__.:", - "if TYPE_CHECKING:", - "if typing.TYPE_CHECKING:", -] - -[tool.coverage.html] -directory = "cov_html" - -[tool.flake8] -max-line-length = 210 -max-complexity = 22 -count = true -show-source = true -statistics = true -disable-noqa = false -# 'ignore' defaults to: E121,E123,E126,E226,E24,E704,W503,W504 -extend-ignore = [ - 'B904', - 'B006', - 'B950', - 'B017', - 'C901', - 'E501', - 'E731', -] -per-file-ignores = [ - '__init__.py:F401', -] -# 'select' defaults to: E,F,W,C90 -extend-select = [ - # * Default warnings reported by flake8-bugbear (B) - - # https://github.com/PyCQA/flake8-bugbear#list-of-warnings - 'B', - # * The B950 flake8-bugbear opinionated warnings - - # https://github.com/PyCQA/flake8-bugbear#opinionated-warnings - 'B9', -] -extend-exclude = [ - '.github', '.gitlab', - '.Python', '.*.pyc', '.*.pyo', '.*.pyd', '.*.py.class', '*.egg-info', - 'venv*', '.venv*', '.*_cache', - 'lib', 'lib64', '.*.so', - 'build', 'dist', 'sdist', 'wheels', -] - -[tool.black] -line-length = 200 -extend-exclude = ''' -# A regex preceded with ^/ will apply only to files and directories -# in the root of the project. -( - \.pytest_cache -) -''' - -[tool.isort] -profile = 'black' -# The 'black' profile means: -# multi_line_output = 3 -# include_trailing_comma = true -# force_grid_wrap = 0 -# use_parentheses = true -# ensure_newline_before_comments = true -# line_length = 88 -line_length = 200 # override black provile line_length -force_single_line = true # override black profile multi_line_output -star_first = true -group_by_package = true -force_sort_within_sections = true -lines_after_imports = 2 -honor_noqa = true -atomic = true -ignore_comments = true -skip_gitignore = true -src_paths = [ - 'src', - 'tests', -] From 2b5743a1decb850027308a817aa4b98492398a67 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Mon, 26 Dec 2022 03:38:13 +0100 Subject: [PATCH 150/205] Add Poetry file --- pyproject.toml | 182 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 pyproject.toml diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..8280a909 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,182 @@ +[tool.poetry] +name = "python3-saml" +version = "1.15.0" +description = "Saml Python Toolkit. Add SAML support to your Python software using this library" +license = "Apache-2.0" +authors = ["SAML-Toolkits "] +maintainers = ["Sixto Martin "] +readme = "README.md" +homepage = "https://saml.info" +repository = "https://github.com/SAML-Toolkits/python3-saml" +documentation = "https://pysaml2.readthedocs.io" +keywords = [ + "saml", + "saml2", + "sso", + "xmlsec", + "federation", + "identity", +] +classifiers = [ + "Topic :: Software Development :: Build Tools", + "Topic :: Software Development :: Libraries :: Python Modules", +] +packages = [ + { include = "onelogin", from = "src" }, + { include = "onelogin/saml2", from = "src" }, +] + +include = [ + { path = "src/onelogin/saml2/schemas"}, + { path = "tests", format = "sdist" } +] + +[tool.poetry.urls] +"Bug Tracker" = "https://github.com/SAML-Toolkits/python3-saml/issues" + +[tool.poetry.dependencies] +python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +lxml = ">=4.6.5, !=4.7.0" +xmlsec = ">=1.3.9" +isodate = ">=0.6.1" + +#[tool.poetry.group.dev] +#optional = true + +#[tool.poetry.group.dev.dependencies] +#black = "*" +#isort = {version = "^5.10.1", extras = ["pyproject"]} +flake8 = { version = ">=3.6.0, <=5.0.0", optional = true} +#Flake8-pyproject = "^1.1.0.post0" +#flake8-bugbear = "^22.8.23" +#flake8-logging-format = "^0.7.5" +ipdb = { version = "^0.13.9", optional = true} +#[tool.poetry.group.test.dependencies] +freezegun= { version = ">=0.3.11, <=1.1.0", optional = true} +pytest = { version = ">=4.6.11", optional = true} +coverage = { version = ">=4.5.2", optional = true} +#pylint = ">=1.9.4" + +[tool.poetry.extras] +test = ["flake8", "ipdb", "freezegun", "pytest", "coverage"] + +#[tool.poetry.group.test] +#optional = true + +#[tool.poetry.group.coverage] +#optional = true + +#[tool.poetry.group.coverage.dependencies] +#coverage = ">=4.5.2" +#pytest-cov = "*" + +#[tool.poetry.group.docs] +#optional = true + +#[tool.poetry.group.docs.dependencies] +#sphinx = "*" + +[build-system] +requires = ["poetry>=1.1.15"] +build-backend = "poetry.core.masonry.api" + +[tool.pytest.ini_options] +minversion = "4.6.11" +addopts = "-ra -vvv" +testpaths = [ + "tests", +] +pythonpath = [ + "tests", +] + +[tool.coverage.run] +branch = true +source = ["src/onelogin/saml2"] +ignore_errors = true + +[tool.coverage.report] +exclude_lines = [ + "pragma: no cover", + "def __repr__", + "def __str__", + "raise AssertionError", + "raise NotImplementedError", + "if __name__ == .__main__.:", + "if TYPE_CHECKING:", + "if typing.TYPE_CHECKING:", +] + +[tool.coverage.html] +directory = "cov_html" + +[tool.flake8] +max-line-length = 210 +max-complexity = 22 +count = true +show-source = true +statistics = true +disable-noqa = false +# 'ignore' defaults to: E121,E123,E126,E226,E24,E704,W503,W504 +extend-ignore = [ + 'B904', + 'B006', + 'B950', + 'B017', + 'C901', + 'E501', + 'E731', +] +per-file-ignores = [ + '__init__.py:F401', +] +# 'select' defaults to: E,F,W,C90 +extend-select = [ + # * Default warnings reported by flake8-bugbear (B) - + # https://github.com/PyCQA/flake8-bugbear#list-of-warnings + 'B', + # * The B950 flake8-bugbear opinionated warnings - + # https://github.com/PyCQA/flake8-bugbear#opinionated-warnings + 'B9', +] +extend-exclude = [ + '.github', '.gitlab', + '.Python', '.*.pyc', '.*.pyo', '.*.pyd', '.*.py.class', '*.egg-info', + 'venv*', '.venv*', '.*_cache', + 'lib', 'lib64', '.*.so', + 'build', 'dist', 'sdist', 'wheels', +] + +[tool.black] +line-length = 200 +extend-exclude = ''' +# A regex preceded with ^/ will apply only to files and directories +# in the root of the project. +( + \.pytest_cache +) +''' + +[tool.isort] +profile = 'black' +# The 'black' profile means: +# multi_line_output = 3 +# include_trailing_comma = true +# force_grid_wrap = 0 +# use_parentheses = true +# ensure_newline_before_comments = true +# line_length = 88 +line_length = 200 # override black provile line_length +force_single_line = true # override black profile multi_line_output +star_first = true +group_by_package = true +force_sort_within_sections = true +lines_after_imports = 2 +honor_noqa = true +atomic = true +ignore_comments = true +skip_gitignore = true +src_paths = [ + 'src', + 'tests', +] From 0078a91b132b64e00dc7d8dc7a20b79bcc9c7e11 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Mon, 26 Dec 2022 03:50:05 +0100 Subject: [PATCH 151/205] Fix pycodestyle --- tests/src/OneLogin/saml2_tests/idp_metadata_parser_test.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/src/OneLogin/saml2_tests/idp_metadata_parser_test.py b/tests/src/OneLogin/saml2_tests/idp_metadata_parser_test.py index 13dbdbc8..22970b88 100644 --- a/tests/src/OneLogin/saml2_tests/idp_metadata_parser_test.py +++ b/tests/src/OneLogin/saml2_tests/idp_metadata_parser_test.py @@ -314,7 +314,6 @@ def test_parse_with_entity_id(self): expected_settings = json.loads(expected_settings_json) self.assertEqual(expected_settings, data) - # should find desired descriptor data2 = OneLogin_Saml2_IdPMetadataParser.parse(xml_idp_metadata, entity_id="https://bar.example.com/access/saml/idp.xml") self.assertEqual("https://bar.example.com/access/saml/idp.xml", data2["idp"]["entityId"]) From aa984e0c324c78fd4ace1dddc5de0540cf8fffdc Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Mon, 26 Dec 2022 04:02:14 +0100 Subject: [PATCH 152/205] Force pip and setuptools upgrade --- .travis.yml | 1 + pyproject.toml | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3fa95d6c..11dace15 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,6 +17,7 @@ install: - sudo apt-get update -qq - sudo apt-get install -qq swig python-dev libxml2-dev libxmlsec1-dev - 'travis_retry pip install --force-reinstall --no-binary lxml lxml' + - 'travis retry pip install --upgrade pip setuptools' - 'travis_retry pip install .' - 'travis_retry pip install -e ".[test]"' diff --git a/pyproject.toml b/pyproject.toml index 8280a909..fe5e9ce3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,7 +50,8 @@ flake8 = { version = ">=3.6.0, <=5.0.0", optional = true} #Flake8-pyproject = "^1.1.0.post0" #flake8-bugbear = "^22.8.23" #flake8-logging-format = "^0.7.5" -ipdb = { version = "^0.13.9", optional = true} +#ipdb = "^0.13.9" + #[tool.poetry.group.test.dependencies] freezegun= { version = ">=0.3.11, <=1.1.0", optional = true} pytest = { version = ">=4.6.11", optional = true} @@ -58,7 +59,7 @@ coverage = { version = ">=4.5.2", optional = true} #pylint = ">=1.9.4" [tool.poetry.extras] -test = ["flake8", "ipdb", "freezegun", "pytest", "coverage"] +test = ["flake8", "freezegun", "pytest", "coverage"] #[tool.poetry.group.test] #optional = true From 28e52caf405f5a593c53189a731d98061ed9b582 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Mon, 26 Dec 2022 04:10:44 +0100 Subject: [PATCH 153/205] Adding setuptools and wheel at build-system of pyproject.toml --- pyproject.toml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index fe5e9ce3..b9bdb1ad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -78,7 +78,11 @@ test = ["flake8", "freezegun", "pytest", "coverage"] #sphinx = "*" [build-system] -requires = ["poetry>=1.1.15"] +requires = [ + "poetry>=1.1.15", + "setuptools >= 40.1.0", + "wheel" +] build-backend = "poetry.core.masonry.api" [tool.pytest.ini_options] From ff3423e9e14922d0ffeb14152a95d983815dc800 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Mon, 26 Dec 2022 04:16:00 +0100 Subject: [PATCH 154/205] Remove setuptools and pip upgrade from travis --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 11dace15..3fa95d6c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,6 @@ install: - sudo apt-get update -qq - sudo apt-get install -qq swig python-dev libxml2-dev libxmlsec1-dev - 'travis_retry pip install --force-reinstall --no-binary lxml lxml' - - 'travis retry pip install --upgrade pip setuptools' - 'travis_retry pip install .' - 'travis_retry pip install -e ".[test]"' From cb414ffc16a7a324056a596475dc27a2fedc881f Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Mon, 26 Dec 2022 04:39:36 +0100 Subject: [PATCH 155/205] Update Django demo --- demo-django/demo/urls.py | 8 ++++---- demo-django/requirements.txt | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/demo-django/demo/urls.py b/demo-django/demo/urls.py index cb05b321..5b64e7ce 100644 --- a/demo-django/demo/urls.py +++ b/demo-django/demo/urls.py @@ -1,10 +1,10 @@ -from django.conf.urls import url +from django.urls import re_path from django.contrib import admin from .views import attrs, index, metadata admin.autodiscover() urlpatterns = [ - url(r'^$', index, name='index'), - url(r'^attrs/$', attrs, name='attrs'), - url(r'^metadata/$', metadata, name='metadata'), + re_path(r'^$', index, name='index'), + re_path(r'^attrs/$', attrs, name='attrs'), + re_path(r'^metadata/$', metadata, name='metadata'), ] diff --git a/demo-django/requirements.txt b/demo-django/requirements.txt index 46ceaced..59c062ab 100644 --- a/demo-django/requirements.txt +++ b/demo-django/requirements.txt @@ -1,2 +1,2 @@ -Django==1.11.29 +Django==4.0.4 python3-saml From ca87c0156aa399b1a0b5a5b36edf5fbdd4bc8f4d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Dec 2022 03:40:23 +0000 Subject: [PATCH 156/205] Bump django from 4.0.4 to 4.0.8 in /demo-django Bumps [django](https://github.com/django/django) from 4.0.4 to 4.0.8. - [Release notes](https://github.com/django/django/releases) - [Commits](https://github.com/django/django/compare/4.0.4...4.0.8) --- updated-dependencies: - dependency-name: django dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- demo-django/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo-django/requirements.txt b/demo-django/requirements.txt index 59c062ab..b9e19128 100644 --- a/demo-django/requirements.txt +++ b/demo-django/requirements.txt @@ -1,2 +1,2 @@ -Django==4.0.4 +Django==4.0.8 python3-saml From 27a9122f282af738442535a9bf89956157b7b271 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Tue, 27 Dec 2022 22:54:09 +0100 Subject: [PATCH 157/205] Release 1.15.0 --- changelog.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/changelog.md b/changelog.md index 93037656..4bdb83ee 100644 --- a/changelog.md +++ b/changelog.md @@ -1,4 +1,13 @@ # python3-saml changelog +### 1.15.0 (Feb 18, 2022) +- [#317](https://github.com/SAML-Toolkits/python3-saml/pull/317) Handle unicode characters gracefully in python 2 +- [#338](https://github.com/SAML-Toolkits/python3-saml/pull/338) Fix WantAuthnRequestsSigned parser +- [#339](https://github.com/SAML-Toolkits/python3-saml/pull/339) Add Poetry support +- Remove version restriction on lxml dependency +- Updated Django demo to 4.X (only py3 compatible) +- Updated Travis file. Forced lxml to be installed using no-validate_binary +- Removed references to OneLogin from documentation + ### 1.14.0 (Feb 18, 2022) - [#297](https://github.com/onelogin/python3-saml/pull/297) Don't require yanked version of lxml. - [#298](https://github.com/onelogin/python3-saml/pull/298) Add support for python 3.10 and cleanup the GHA. From 352a42554a4bbc6221f47b12f4d3048a671a1500 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Wed, 28 Dec 2022 01:34:39 +0100 Subject: [PATCH 158/205] Update poetry file --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index b9bdb1ad..6b60e493 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,6 @@ maintainers = ["Sixto Martin "] readme = "README.md" homepage = "https://saml.info" repository = "https://github.com/SAML-Toolkits/python3-saml" -documentation = "https://pysaml2.readthedocs.io" keywords = [ "saml", "saml2", From d468f7422706b8595afed25c06944fbcc7410d18 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Wed, 28 Dec 2022 01:48:22 +0100 Subject: [PATCH 159/205] Update flake8 options --- .travis.yml | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3fa95d6c..6313f31b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,4 +25,4 @@ script: - 'coverage report -m --rcfile=tests/coverage.rc' # - 'pylint src/onelogin/saml2 --rcfile=tests/pylint.rc' # - 'flake8 --toml-config pyproject.toml' - - 'flake8 .' + - 'flake8 --ignore E226,E302,E41,E731,E501,C901,W504' diff --git a/setup.py b/setup.py index 79dfa8d9..fe264709 100644 --- a/setup.py +++ b/setup.py @@ -42,7 +42,7 @@ 'test': ( 'coverage>=4.5.2', 'freezegun>=0.3.11, <=1.1.0', -# 'pylint>=1.9.4', + # 'pylint>=1.9.4', 'flake8>=3.6.0, <=5.0.0', 'pytest>=4.6', ), From ce0516e8f66efe2adc91180a66b25ba1f7921df0 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Wed, 28 Dec 2022 01:55:11 +0100 Subject: [PATCH 160/205] Fix date of changelog --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 4bdb83ee..6d9435c0 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,5 @@ # python3-saml changelog -### 1.15.0 (Feb 18, 2022) +### 1.15.0 (Dec 27, 2022) - [#317](https://github.com/SAML-Toolkits/python3-saml/pull/317) Handle unicode characters gracefully in python 2 - [#338](https://github.com/SAML-Toolkits/python3-saml/pull/338) Fix WantAuthnRequestsSigned parser - [#339](https://github.com/SAML-Toolkits/python3-saml/pull/339) Add Poetry support From 69680a3916bf9215f2a3c6bb5a6286ffa8d2d771 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Tue, 3 Jan 2023 14:59:53 +0100 Subject: [PATCH 161/205] Update CI status badget --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6303ee58..bbb04566 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # SAML Python Toolkit (compatible with Python3) -[![Build Status](https://api.travis-ci.org/onelogin/python3-saml.png?branch=master)](http://travis-ci.org/onelogin/python3-saml) +[![Python package](https://github.com/SAML-Toolkits/python3-saml/actions/workflows/python-package.yml/badge.svg)](https://github.com/SAML-Toolkits/python3-saml/actions/workflows/python-package.yml) [![PyPi Version](https://img.shields.io/pypi/v/python3-saml.svg)](https://pypi.python.org/pypi/python3-saml) ![Python versions](https://img.shields.io/pypi/pyversions/python3-saml.svg) From dfb6a272e8c21350161545524d7cc9de534ff8e2 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Tue, 3 Jan 2023 15:03:07 +0100 Subject: [PATCH 162/205] Add coverage badget --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index bbb04566..d9c474bb 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # SAML Python Toolkit (compatible with Python3) [![Python package](https://github.com/SAML-Toolkits/python3-saml/actions/workflows/python-package.yml/badge.svg)](https://github.com/SAML-Toolkits/python3-saml/actions/workflows/python-package.yml) +[![Coverage Status](https://coveralls.io/repos/github/SAML-Toolkits/python3-saml/badge.svg?branch=master)](https://coveralls.io/github/SAML-Toolkits/python3-saml?branch=master) [![PyPi Version](https://img.shields.io/pypi/v/python3-saml.svg)](https://pypi.python.org/pypi/python3-saml) ![Python versions](https://img.shields.io/pypi/pyversions/python3-saml.svg) From a1db9bcba7c81baf159ba5027b3f8e0b2f88e9e6 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Tue, 3 Jan 2023 15:58:59 +0100 Subject: [PATCH 163/205] Update License --- LICENSE | 1 + 1 file changed, 1 insertion(+) diff --git a/LICENSE b/LICENSE index 0fd253e9..c141165e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,5 @@ Copyright (c) 2010-2022 OneLogin, Inc. +Copyright (c) 2023 IAM Digital Services, SL. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation From 7b17e62521cd76833563b16ca029228192f18417 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Wed, 4 Jan 2023 20:25:58 +0100 Subject: [PATCH 164/205] Add Pypi downloads badget --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d9c474bb..bccfef27 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # SAML Python Toolkit (compatible with Python3) [![Python package](https://github.com/SAML-Toolkits/python3-saml/actions/workflows/python-package.yml/badge.svg)](https://github.com/SAML-Toolkits/python3-saml/actions/workflows/python-package.yml) +![PyPI Downloads](https://img.shields.io/pypi/dm/python3-saml.svg?label=PyPI%20Downloads) [![Coverage Status](https://coveralls.io/repos/github/SAML-Toolkits/python3-saml/badge.svg?branch=master)](https://coveralls.io/github/SAML-Toolkits/python3-saml?branch=master) [![PyPi Version](https://img.shields.io/pypi/v/python3-saml.svg)](https://pypi.python.org/pypi/python3-saml) ![Python versions](https://img.shields.io/pypi/pyversions/python3-saml.svg) From 028411f154e1429faff2a7a8009896e53383f032 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Wed, 4 Jan 2023 21:25:42 +0100 Subject: [PATCH 165/205] Update Security Guidelines --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bccfef27..aeb7b4dd 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ Update ``python3-saml`` to ``>= 1.2.1``, ``1.2.0`` had a bug on signature valida #### Security Guidelines #### -If you believe you have discovered a security vulnerability in this toolkit, please report it in an issue with a description. We follow responsible disclosure guidelines, and will work with you to quickly find a resolution. +If you believe you have discovered a security vulnerability in this gem, please report it by mail to the maintainer: sixto.martin.garcia+security@gmail.com Why add SAML support to my software? ------------------------------------ From cc23a40c7dbcf56b6213d915c0ab1793893b6d42 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Wed, 4 Jan 2023 21:26:52 +0100 Subject: [PATCH 166/205] Typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index aeb7b4dd..52c65e13 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ Update ``python3-saml`` to ``>= 1.2.1``, ``1.2.0`` had a bug on signature valida #### Security Guidelines #### -If you believe you have discovered a security vulnerability in this gem, please report it by mail to the maintainer: sixto.martin.garcia+security@gmail.com +If you believe you have discovered a security vulnerability in this toolkit, please report it by mail to the maintainer: sixto.martin.garcia+security@gmail.com Why add SAML support to my software? ------------------------------------ From 538622df70b45520f60e7fb6b569aa00be54f504 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Sat, 7 Jan 2023 23:25:16 +0100 Subject: [PATCH 167/205] Add coveralls support (#342) * Add coveralls support * Set environment on CI * Fix coverage * Add coveralls dependency * Try removing service_name * Another try to upload to coveralls --- .github/workflows/python-package.yml | 9 +++++++++ pyproject.toml | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index e9b9cae8..3a2a5f33 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -48,6 +48,7 @@ jobs: run: make pytest lint: runs-on: ubuntu-20.04 + environment: CI steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 @@ -70,3 +71,11 @@ jobs: run: | make pycodestyle make flake8 + - name: Run coveralls + env: + COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} + run: | + pip install coveralls + coverage run setup.py test + coverage report -m + coveralls diff --git a/pyproject.toml b/pyproject.toml index 6b60e493..962caeac 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -97,7 +97,6 @@ pythonpath = [ [tool.coverage.run] branch = true source = ["src/onelogin/saml2"] -ignore_errors = true [tool.coverage.report] exclude_lines = [ @@ -110,6 +109,7 @@ exclude_lines = [ "if TYPE_CHECKING:", "if typing.TYPE_CHECKING:", ] +ignore_errors = true [tool.coverage.html] directory = "cov_html" From e3f5519a44ebfc9ab4d85bbde2dae82607073aff Mon Sep 17 00:00:00 2001 From: David Ongaro Date: Mon, 1 May 2023 13:34:51 -0700 Subject: [PATCH 168/205] Fix spelling in documentation and comments (#352) Co-authored-by: Ongaro, David --- README.md | 28 +++++++++---------- changelog.md | 2 +- src/onelogin/saml2/utils.py | 2 +- tests/src/OneLogin/saml2_tests/auth_test.py | 2 +- .../src/OneLogin/saml2_tests/response_test.py | 2 +- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 52c65e13..fad22156 100644 --- a/README.md +++ b/README.md @@ -154,7 +154,7 @@ A replay attack is basically try to reuse an intercepted valid SAML Message in o SAML Messages have a limited timelife (NotBefore, NotOnOrAfter) that make harder this kind of attacks, but they are still possible. -In order to avoid them, the SP can keep a list of SAML Messages or Assertion IDs alredy valdidated and processed. Those values only need +In order to avoid them, the SP can keep a list of SAML Messages or Assertion IDs already validated and processed. Those values only need to be stored the amount of time of the SAML Message life time, so we don't need to store all processed message/assertion Ids, but the most recent ones. @@ -297,9 +297,9 @@ This is the ``settings.json`` file: }, // If you need to specify requested attributes, set a // attributeConsumingService. nameFormat, attributeValue and - // friendlyName can be ommited + // friendlyName can be omitted "attributeConsumingService": { - // OPTIONAL: only specifiy if SP requires this. + // OPTIONAL: only specify if SP requires this. // index is an integer which identifies the attributeConsumingService used // to the SP. SAML toolkit supports configuring only one attributeConsumingService // but in certain cases the SP requires a different value. Defaults to '1'. @@ -366,7 +366,7 @@ This is the ``settings.json`` file: /* * Instead of using the whole X.509cert you can use a fingerprint in order to * validate a SAMLResponse (but you still need the X.509cert to validate LogoutRequest and LogoutResponse using the HTTP-Redirect binding). - * But take in mind that the algortithm for the fingerprint should be as strong as the algorithm in a normal certificate signature + * But take in mind that the algorithm for the fingerprint should be as strong as the algorithm in a normal certificate signature * (e.g. SHA256 or strong) * * (openssl x509 -noout -fingerprint -in "idp.crt" to generate it, @@ -501,7 +501,7 @@ In addition to the required settings data (idp, sp), extra settings can be defin 'allowRepeatAttributeName': false, // If the toolkit receive a message signed with a - // deprecated algoritm (defined at the constant class) + // deprecated algorithm (defined at the constant class) // will raise an error and reject the message "rejectDeprecatedAlgorithm": true }, @@ -520,7 +520,7 @@ In addition to the required settings data (idp, sp), extra settings can be defin }, // Organization information template, the info in en_US lang is - // recomended, add more if required. + // recommended, add more if required. "organization": { "en-US": { "name": "sp_test", @@ -690,7 +690,7 @@ We can set a ``return_to`` url parameter to the login function and that will be target_url = 'https://example.com' auth.login(return_to=target_url) ``` -The login method can recieve 3 more optional parameters: +The login method can receive 3 more optional parameters: * ``force_authn`` When ``true``, the ``AuthNReuqest`` will set the ``ForceAuthn='true'`` * ``is_passive`` When true, the ``AuthNReuqest`` will set the ``Ispassive='true'`` @@ -785,7 +785,7 @@ If we execute print attributes we could get: } ``` -Each attribute name can be used as a key to obtain the value. Every attribute is a list of values. A single-valued attribute is a listy of a single element. +Each attribute name can be used as a key to obtain the value. Every attribute is a list of values. A single-valued attribute is a list of a single element. The following code is equivalent: @@ -813,7 +813,7 @@ if len(errors) == 0: # the value of the url is a trusted URL. return redirect(url) else: - print("Sucessfully Logged out") + print("Successfully Logged out") else: print("Error when processing SLO: %s %s" % (', '.join(errors), auth.get_last_error_reason())) ``` @@ -955,7 +955,7 @@ elif 'sls' in request.args: # Single # the value of the url is a trusted URL. return redirect(url) else: - msg = "Sucessfully logged out" + msg = "Successfully logged out" if len(errors) == 0: print(msg) @@ -1071,7 +1071,7 @@ SAML 2 Logout Request class * ***get_nameid*** Gets the NameID of the Logout Request Message (returns a string). * ***get_issuer*** Gets the Issuer of the Logout Request Message. * ***get_session_indexes*** Gets the ``SessionIndexes`` from the Logout Request. -* ***is_valid*** Checks if the Logout Request recieved is valid. +* ***is_valid*** Checks if the Logout Request received is valid. * ***get_error*** After execute a validation process, if fails this method returns the cause. * ***get_xml*** Returns the XML that will be sent as part of the request or that was received at the SP @@ -1154,7 +1154,7 @@ Auxiliary class that contains several methods * ***get_expire_time*** Compares 2 dates and returns the earliest. * ***delete_local_session*** Deletes the local session. * ***calculate_X.509_fingerprint*** Calculates the fingerprint of a X.509 cert. -* ***format_finger_print*** Formates a fingerprint. +* ***format_finger_print*** Formats a fingerprint. * ***generate_name_id*** Generates a nameID. * ***get_status*** Gets Status from a Response. * ***decrypt_element*** Decrypts an encrypted element. @@ -1204,7 +1204,7 @@ let's see how fast is it to deploy them. The use of a [virtualenv](http://virtualenv.readthedocs.org/en/latest/) is highly recommended. -Virtualenv helps isolating the python enviroment used to run the toolkit. You +Virtualenv helps isolating the python environment used to run the toolkit. You can find more details and an installation guide in the [official documentation](http://virtualenv.readthedocs.org/en/latest/). @@ -1508,7 +1508,7 @@ Once the SP is configured, the metadata of the SP is published at the ``/metadat 4. We are logged in the app and the user attributes are showed. At this point, we can test the single log out functionality. - The single log out funcionality could be tested by 2 ways. + The single log out functionality could be tested by 2 ways. 5.1 SLO Initiated by SP. Click on the "logout" link at the SP, after that a Logout Request is sent to the IdP, the session at the IdP is closed and replies through the client to the SP with a Logout Response (sent to the Single Logout Service endpoint). The SLS endpoint /?sls of the SP process the Logout Response and if is valid, close the user session of the local app. Notice that the SLO Workflow starts and ends at the SP. diff --git a/changelog.md b/changelog.md index 6d9435c0..a47a9865 100644 --- a/changelog.md +++ b/changelog.md @@ -101,7 +101,7 @@ ### 1.3.0 (Sep 15, 2017) * Improve decrypt method, Add an option to decrypt an element in place or copy it before decryption. * [#63](https://github.com/onelogin/python3-saml/pull/63) Be able to get at the auth object the last processed ID (response/assertion) and the last generated ID, as well as the NotOnOrAfter value of the valid SubjectConfirmationData in the processed SAMLResponse -* On a LogoutRequest if the NameIdFormat is entity, NameQualifier and SPNameQualifier will be ommited. If the NameIdFormat is not entity and a NameQualifier is provided, then the SPNameQualifier will be also added. +* On a LogoutRequest if the NameIdFormat is entity, NameQualifier and SPNameQualifier will be omitted. If the NameIdFormat is not entity and a NameQualifier is provided, then the SPNameQualifier will be also added. * Reset errorReason attribute of the auth object before each Process method * [#65](https://github.com/onelogin/python3-saml/pull/65) Fix issue on getting multiple certs when only sign or encryption certs diff --git a/src/onelogin/saml2/utils.py b/src/onelogin/saml2/utils.py index b3498337..6050ea8d 100644 --- a/src/onelogin/saml2/utils.py +++ b/src/onelogin/saml2/utils.py @@ -901,7 +901,7 @@ def validate_metadata_sign(xml, cert=None, fingerprint=None, fingerprintalg='sha if len(signature_nodes) > 0: for signature_node in signature_nodes: - # Raises expection if invalid + # Raises exception if invalid OneLogin_Saml2_Utils.validate_node_sign(signature_node, elem, cert, fingerprint, fingerprintalg, validatecert, debug, raise_exceptions=True) return True else: diff --git a/tests/src/OneLogin/saml2_tests/auth_test.py b/tests/src/OneLogin/saml2_tests/auth_test.py index 82db914f..b088874f 100644 --- a/tests/src/OneLogin/saml2_tests/auth_test.py +++ b/tests/src/OneLogin/saml2_tests/auth_test.py @@ -310,7 +310,7 @@ def testProcessSLOResponseInvalid(self): def testProcessSLOResponseNoSucess(self): """ Tests the process_slo method of the OneLogin_Saml2_Auth class - Case Logout Response not sucess + Case Logout Response not success """ request_data = self.get_request() message = self.file_contents(join(self.data_path, 'logout_responses', 'invalids', 'status_code_responder.xml.base64')) diff --git a/tests/src/OneLogin/saml2_tests/response_test.py b/tests/src/OneLogin/saml2_tests/response_test.py index a4bcf0de..fbe714f4 100644 --- a/tests/src/OneLogin/saml2_tests/response_test.py +++ b/tests/src/OneLogin/saml2_tests/response_test.py @@ -1282,7 +1282,7 @@ def testIsInValidSessionIndex(self): def testDatetimeWithMiliseconds(self): """ Tests the is_valid method of the OneLogin_Saml2_Response class - Somtimes IdPs uses datetimes with miliseconds, this + Sometimes IdPs uses datetimes with miliseconds, this test is to verify that the toolkit supports them """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) From 07d4730f7ed2018b854c3de733466f8d90e92b0b Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Tue, 14 Mar 2023 00:37:38 +0100 Subject: [PATCH 169/205] Fix WantAuthnRequestsSigned parser --- src/onelogin/saml2/idp_metadata_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/onelogin/saml2/idp_metadata_parser.py b/src/onelogin/saml2/idp_metadata_parser.py index f5085157..dc802ae5 100644 --- a/src/onelogin/saml2/idp_metadata_parser.py +++ b/src/onelogin/saml2/idp_metadata_parser.py @@ -201,7 +201,7 @@ def parse( if want_authn_requests_signed is not None: data['security'] = {} - data['security']['authnRequestsSigned'] = want_authn_requests_signed + data['security']['authnRequestsSigned'] = want_authn_requests_signed == "true" if idp_name_id_format: data['sp'] = {} From 24a8f095b06cb15cb43aac3abae2aefc183c73a4 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Tue, 14 Mar 2023 00:47:52 +0100 Subject: [PATCH 170/205] Fix tests --- tests/src/OneLogin/saml2_tests/idp_metadata_parser_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/src/OneLogin/saml2_tests/idp_metadata_parser_test.py b/tests/src/OneLogin/saml2_tests/idp_metadata_parser_test.py index 22970b88..93c08dab 100644 --- a/tests/src/OneLogin/saml2_tests/idp_metadata_parser_test.py +++ b/tests/src/OneLogin/saml2_tests/idp_metadata_parser_test.py @@ -293,7 +293,7 @@ def test_parse_with_entity_id(self): expected_settings_json = """ { - "security": {"authnRequestsSigned": "true"}, + "security": {"authnRequestsSigned": true}, "sp": { "NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" }, @@ -320,7 +320,7 @@ def test_parse_with_entity_id(self): expected_settings_json2 = """ { - "security": {"authnRequestsSigned": "false"}, + "security": {"authnRequestsSigned": false}, "sp": { "NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" }, From bd65578e5a21494c89320094c61c1c77250bea33 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Sat, 13 May 2023 09:51:33 +0200 Subject: [PATCH 171/205] Fix payloads of tests that had expiration dates on May 2023 --- tests/data/logout_requests/invalids/invalid_issuer.xml | 2 +- tests/data/logout_requests/invalids/invalid_issuer.xml.base64 | 2 +- tests/data/logout_requests/invalids/no_nameId.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/data/logout_requests/invalids/invalid_issuer.xml b/tests/data/logout_requests/invalids/invalid_issuer.xml index e1edabde..4b7714d1 100644 --- a/tests/data/logout_requests/invalids/invalid_issuer.xml +++ b/tests/data/logout_requests/invalids/invalid_issuer.xml @@ -5,7 +5,7 @@ Version="2.0" IssueInstant="2013-12-10T04:39:31Z" Destination="http://stuff.com/endpoints/endpoints/sls.php" - NotOnOrAfter="2023-05-10T04:39:31Z" + NotOnOrAfter="2053-05-10T04:39:31Z" > https://example.hello.com/access/saml https://example.hello.com/access/saml From 918b4bbecf46da46e117e1c5da8ec7a27779ce7d Mon Sep 17 00:00:00 2001 From: ManInDark <61268856+ManInDark@users.noreply.github.com> Date: Fri, 19 May 2023 18:56:53 +0200 Subject: [PATCH 172/205] Fixed error in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fad22156..251b79e1 100644 --- a/README.md +++ b/README.md @@ -160,7 +160,7 @@ we don't need to store all processed message/assertion Ids, but the most recent The OneLogin_Saml2_Auth class contains the [get_last_request_id](https://github.com/onelogin/python3-saml/blob/ab62b0d6f3e5ac2ae8e95ce3ed2f85389252a32d/src/onelogin/saml2/auth.py#L357), [get_last_message_id](https://github.com/onelogin/python3-saml/blob/ab62b0d6f3e5ac2ae8e95ce3ed2f85389252a32d/src/onelogin/saml2/auth.py#L364) and [get_last_assertion_id](https://github.com/onelogin/python3-saml/blob/ab62b0d6f3e5ac2ae8e95ce3ed2f85389252a32d/src/onelogin/saml2/auth.py#L371) methods to retrieve the IDs -Checking that the ID of the current Message/Assertion does not exists in the lis of the ones already processed will prevent replay attacks. +Checking that the ID of the current Message/Assertion does not exists in the list of the ones already processed will prevent replay attacks. Getting Started From b82703f29648237a6288e48de84135637ede0238 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Fri, 21 Jul 2023 12:34:10 +0200 Subject: [PATCH 173/205] Add support github-action python2.7 by the use of a container (#367) * Add support github-action python2.7 by the use of a container See https://github.com/actions/runner-images/issues/7401#issuecomment-1571065641 --- .github/workflows/python-package.yml | 30 ++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 3a2a5f33..49477719 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -12,13 +12,12 @@ on: - master jobs: - test: + test_py3: runs-on: ubuntu-20.04 strategy: fail-fast: false matrix: python-version: - - "2.7" - "3.5" - "3.6" - "3.7" @@ -26,7 +25,7 @@ jobs: - "3.9" - "3.10" steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: @@ -46,11 +45,34 @@ jobs: make install-test - name: Test run: make pytest + test_py2: + runs-on: ubuntu-20.04 + container: + image: python:2.7.18-buster + strategy: + fail-fast: false + steps: + - uses: actions/checkout@v3 + - uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/setup.py') }} + restore-keys: | + ${{ runner.os }}-pip- + - name: Install dependencies + run: | + pip install -U setuptools + apt-get update -qq + apt-get install -qq swig python-dev libxml2-dev libxmlsec1-dev + make install-req + make install-test + - name: Test + run: make pytest lint: runs-on: ubuntu-20.04 environment: CI steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: actions/setup-python@v2 with: python-version: "3.10" From 888c8bc623c2bab44ba51917f2d76395d4d5ab41 Mon Sep 17 00:00:00 2001 From: gahooa Date: Fri, 21 Jul 2023 06:38:07 -0400 Subject: [PATCH 174/205] Clarify wording about auth.login in readme (#363) The previous wording indicated that auth.login() would take some action, whereas it really just returns a URL for you to do something with (redirect or print out). This clarifies that. --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 251b79e1..8f903035 100644 --- a/README.md +++ b/README.md @@ -677,7 +677,8 @@ req = prepare_request_for_toolkit(request) auth = OneLogin_Saml2_Auth(req) # Constructor of the SP, loads settings.json # and advanced_settings.json -auth.login() # Method that builds and sends the AuthNRequest +auth.login() # This method will build and return a AuthNRequest URL that can be + # either redirected to, or printed out onto the screen as a hyperlink ``` The ``AuthNRequest`` will be sent signed or unsigned based on the security info of the ``advanced_settings.json`` file (i.e. ``authnRequestsSigned``). From d1bfaeb17a786735827b8252b91deafde29dabd8 Mon Sep 17 00:00:00 2001 From: Galyna Zholtkevych Date: Fri, 21 Jul 2023 15:34:36 +0300 Subject: [PATCH 175/205] Issue #364: Some urls come with 403 response (#366) * Issue #364: Some urls come with 403 response python3-saml urllib dependency responds with 403 status to urls with default Python user-agent --- src/onelogin/saml2/idp_metadata_parser.py | 12 ++++++++---- .../saml2_tests/idp_metadata_parser_test.py | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/onelogin/saml2/idp_metadata_parser.py b/src/onelogin/saml2/idp_metadata_parser.py index dc802ae5..29ba08be 100644 --- a/src/onelogin/saml2/idp_metadata_parser.py +++ b/src/onelogin/saml2/idp_metadata_parser.py @@ -27,7 +27,7 @@ class OneLogin_Saml2_IdPMetadataParser(object): """ @classmethod - def get_metadata(cls, url, validate_cert=True, timeout=None): + def get_metadata(cls, url, validate_cert=True, timeout=None, headers=None): """ Gets the metadata XML from the provided URL :param url: Url where the XML of the Identity Provider Metadata is published. @@ -38,19 +38,23 @@ def get_metadata(cls, url, validate_cert=True, timeout=None): :param timeout: Timeout in seconds to wait for metadata response :type timeout: int + :param headers: Extra headers to send in the request + :type headers: dict :returns: metadata XML :rtype: string """ valid = False + request = urllib2.Request(url, headers=headers or {}) + if validate_cert: - response = urllib2.urlopen(url, timeout=timeout) + response = urllib2.urlopen(request, timeout=timeout) else: ctx = ssl.create_default_context() ctx.check_hostname = False ctx.verify_mode = ssl.CERT_NONE - response = urllib2.urlopen(url, context=ctx, timeout=timeout) + response = urllib2.urlopen(request, context=ctx, timeout=timeout) xml = response.read() if xml: @@ -87,7 +91,7 @@ def parse_remote(cls, url, validate_cert=True, entity_id=None, timeout=None, **k :returns: settings dict with extracted data :rtype: dict """ - idp_metadata = cls.get_metadata(url, validate_cert, timeout) + idp_metadata = cls.get_metadata(url, validate_cert, timeout, headers=kwargs.pop('headers', None)) return cls.parse(idp_metadata, entity_id=entity_id, **kwargs) @classmethod diff --git a/tests/src/OneLogin/saml2_tests/idp_metadata_parser_test.py b/tests/src/OneLogin/saml2_tests/idp_metadata_parser_test.py index 93c08dab..3d027ed1 100644 --- a/tests/src/OneLogin/saml2_tests/idp_metadata_parser_test.py +++ b/tests/src/OneLogin/saml2_tests/idp_metadata_parser_test.py @@ -54,6 +54,12 @@ def testGetMetadata(self): except URLError: pass + def testGetMetadataWithHeaders(self): + data = OneLogin_Saml2_IdPMetadataParser.get_metadata('https://samltest.id/saml/providers', + headers={'User-Agent': 'Mozilla/5.0'}) + self.assertIsNotNone(data) + self.assertIn(b'entityID=', data) + def testParseRemote(self): """ Tests the parse_remote method of the OneLogin_Saml2_IdPMetadataParser @@ -86,6 +92,15 @@ def testParseRemote(self): expected_settings = json.loads(expected_settings_json) self.assertEqual(expected_settings, data) + def testParseRemoteWithHeaders(self): + """ + Tests the parse_remote method passing headers of the OneLogin_Saml2_IdPMetadataParser + """ + data = OneLogin_Saml2_IdPMetadataParser.parse_remote('https://samltest.id/saml/providers') + self.assertEqual(data['idp']['entityId'], 'https://samltest.id/saml/idp') + self.assertIsNotNone(data['idp']['singleSignOnService']['url']) + self.assertIsNotNone(data['idp']['x509certMulti']) + def testParse(self): """ Tests the parse method of the OneLogin_Saml2_IdPMetadataParser From af6fffcdf1e240ae7c2fa758bb3d21449428ca2b Mon Sep 17 00:00:00 2001 From: Joey Chai Date: Sun, 10 Sep 2023 16:54:28 +0800 Subject: [PATCH 176/205] docs: restore flask request.host (#371) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8f903035..5c9d0ab2 100644 --- a/README.md +++ b/README.md @@ -648,7 +648,7 @@ def prepare_from_django_request(request): def prepare_from_flask_request(request): url_data = urlparse(request.url) return { - 'http_host': request.netloc, + 'http_host': request.host, 'script_name': request.path, 'get_data': request.args.copy(), 'post_data': request.form.copy() From 08719379e5bfba49365dbd22f3633e0f3fa8eb09 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Sun, 1 Oct 2023 02:55:45 +0200 Subject: [PATCH 177/205] Upload PDF version of documentation --- docs/SAML_Python3_Toolkit_Guide.pdf | Bin 0 -> 311673 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/SAML_Python3_Toolkit_Guide.pdf diff --git a/docs/SAML_Python3_Toolkit_Guide.pdf b/docs/SAML_Python3_Toolkit_Guide.pdf new file mode 100644 index 0000000000000000000000000000000000000000..3425fb195608c896f7bad8ec0302ff9546d3251e GIT binary patch literal 311673 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돿Tn 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�zRBG09EJ>VuQ4h=LObNO=0?K*6qI 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 literal 0 HcmV?d00001 From cb7ed14c711833cd6d0b557a708bc557c1a1f427 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Sun, 1 Oct 2023 02:56:33 +0200 Subject: [PATCH 178/205] Remove old html doc --- docs/saml2/.buildinfo | 4 - docs/saml2/_modules/index.html | 100 - docs/saml2/_modules/saml2/auth.html | 457 - docs/saml2/_modules/saml2/authn_request.html | 172 - docs/saml2/_modules/saml2/constants.html | 162 - docs/saml2/_modules/saml2/errors.html | 131 - docs/saml2/_modules/saml2/logout_request.html | 368 - .../saml2/_modules/saml2/logout_response.html | 279 - docs/saml2/_modules/saml2/metadata.html | 286 - docs/saml2/_modules/saml2/response.html | 529 - docs/saml2/_modules/saml2/settings.html | 692 -- docs/saml2/_modules/saml2/utils.html | 802 -- docs/saml2/_sources/index.txt | 23 - docs/saml2/_sources/saml2.txt | 83 - docs/saml2/_static/ajax-loader.gif | Bin 673 -> 0 bytes docs/saml2/_static/basic.css | 540 - docs/saml2/_static/comment-bright.png | Bin 3500 -> 0 bytes docs/saml2/_static/comment-close.png | Bin 3578 -> 0 bytes docs/saml2/_static/comment.png | Bin 3445 -> 0 bytes docs/saml2/_static/default.css | 256 - docs/saml2/_static/doctools.js | 247 - docs/saml2/_static/down-pressed.png | Bin 368 -> 0 bytes docs/saml2/_static/down.png | Bin 363 -> 0 bytes docs/saml2/_static/file.png | Bin 392 -> 0 bytes docs/saml2/_static/jquery.js | 9404 ----------------- docs/saml2/_static/minus.png | Bin 199 -> 0 bytes docs/saml2/_static/plus.png | Bin 199 -> 0 bytes docs/saml2/_static/pygments.css | 62 - docs/saml2/_static/searchtools.js | 567 - docs/saml2/_static/sidebar.js | 151 - docs/saml2/_static/underscore.js | 1226 --- docs/saml2/_static/up-pressed.png | Bin 372 -> 0 bytes docs/saml2/_static/up.png | Bin 363 -> 0 bytes docs/saml2/_static/websupport.js | 808 -- docs/saml2/genindex.html | 940 -- docs/saml2/index.html | 141 - docs/saml2/objects.inv | Bin 1959 -> 0 bytes docs/saml2/py-modindex.html | 153 - docs/saml2/saml2.html | 2043 ---- docs/saml2/search.html | 106 - docs/saml2/searchindex.js | 1 - 41 files changed, 20733 deletions(-) delete mode 100644 docs/saml2/.buildinfo delete mode 100644 docs/saml2/_modules/index.html delete mode 100644 docs/saml2/_modules/saml2/auth.html delete mode 100644 docs/saml2/_modules/saml2/authn_request.html delete mode 100644 docs/saml2/_modules/saml2/constants.html delete mode 100644 docs/saml2/_modules/saml2/errors.html delete mode 100644 docs/saml2/_modules/saml2/logout_request.html delete mode 100644 docs/saml2/_modules/saml2/logout_response.html delete mode 100644 docs/saml2/_modules/saml2/metadata.html delete mode 100644 docs/saml2/_modules/saml2/response.html delete mode 100644 docs/saml2/_modules/saml2/settings.html delete mode 100644 docs/saml2/_modules/saml2/utils.html delete mode 100644 docs/saml2/_sources/index.txt delete mode 100644 docs/saml2/_sources/saml2.txt delete mode 100644 docs/saml2/_static/ajax-loader.gif delete mode 100644 docs/saml2/_static/basic.css delete mode 100644 docs/saml2/_static/comment-bright.png delete mode 100644 docs/saml2/_static/comment-close.png delete mode 100644 docs/saml2/_static/comment.png delete mode 100644 docs/saml2/_static/default.css delete mode 100644 docs/saml2/_static/doctools.js delete mode 100644 docs/saml2/_static/down-pressed.png delete mode 100644 docs/saml2/_static/down.png delete mode 100644 docs/saml2/_static/file.png delete mode 100644 docs/saml2/_static/jquery.js delete mode 100644 docs/saml2/_static/minus.png delete mode 100644 docs/saml2/_static/plus.png delete mode 100644 docs/saml2/_static/pygments.css delete mode 100644 docs/saml2/_static/searchtools.js delete mode 100644 docs/saml2/_static/sidebar.js delete mode 100644 docs/saml2/_static/underscore.js delete mode 100644 docs/saml2/_static/up-pressed.png delete mode 100644 docs/saml2/_static/up.png delete mode 100644 docs/saml2/_static/websupport.js delete mode 100644 docs/saml2/genindex.html delete mode 100644 docs/saml2/index.html delete mode 100644 docs/saml2/objects.inv delete mode 100644 docs/saml2/py-modindex.html delete mode 100644 docs/saml2/saml2.html delete mode 100644 docs/saml2/search.html delete mode 100644 docs/saml2/searchindex.js diff --git a/docs/saml2/.buildinfo b/docs/saml2/.buildinfo deleted file mode 100644 index 5e197dbf..00000000 --- a/docs/saml2/.buildinfo +++ /dev/null @@ -1,4 +0,0 @@ -# Sphinx build info version 1 -# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. -config: e10660514f5c62e16e90878c60a15170 -tags: fbb0d17656682115ca4d033fb2f83ba1 diff --git a/docs/saml2/_modules/index.html b/docs/saml2/_modules/index.html deleted file mode 100644 index 12fa0756..00000000 --- a/docs/saml2/_modules/index.html +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - - - - - Overview: module code — SAML Python library classes and methods - - - - - - - - - - - -

    - -
    - -
    -
    - - -
    -
    -
    -
    - - - - diff --git a/docs/saml2/_modules/saml2/auth.html b/docs/saml2/_modules/saml2/auth.html deleted file mode 100644 index 494ef85c..00000000 --- a/docs/saml2/_modules/saml2/auth.html +++ /dev/null @@ -1,457 +0,0 @@ - - - - - - - - - - saml2.auth — SAML Python library classes and methods - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for saml2.auth

    -# -*- coding: utf-8 -*-
    -
    -
    -from base64 import b64encode
    -from urllib import urlencode, quote
    -from xml.etree.ElementTree import tostring
    -
    -import dm.xmlsec.binding as xmlsec
    -
    -from saml2.settings import OneLogin_Saml2_Settings
    -from saml2.response import OneLogin_Saml2_Response
    -from saml2.errors import OneLogin_Saml2_Error
    -from saml2.logout_response import OneLogin_Saml2_Logout_Response
    -from saml2.constants import OneLogin_Saml2_Constants
    -from saml2.utils import OneLogin_Saml2_Utils
    -from saml2.logout_request import OneLogin_Saml2_Logout_Request
    -from saml2.authn_request import OneLogin_Saml2_Authn_Request
    -
    -
    -
    [docs]class OneLogin_Saml2_Auth(object): - - def __init__(self, request_data, old_settings=None): - """ - Initializes the SP SAML instance. - - Arguments are: - * (dict) old_settings. Setting data - """ - self.__request_data = request_data - self.__settings = OneLogin_Saml2_Settings(old_settings) - self.__attributes = [] - self.__nameid = '' - self.__authenticated = False - self.__errors = [] - -
    [docs] def get_settings(self): - """ - Returns the settings info - :return: Setting info - :rtype: OneLogin_Saml2_Setting object - """ - return self.__settings -
    -
    [docs] def set_strict(self, value): - """ - Set the strict mode active/disable - - :param value: - :type value: bool - """ - assert isinstance(value, bool) - self.__settings.set_strict(value) -
    -
    [docs] def process_response(self, request_id=None): - """ - Process the SAML Response sent by the IdP. - - :param request_id: Is an optional argument. Is the ID of the AuthNRequest sent by this SP to the IdP. - :type request_id: string - - :raises: OneLogin_Saml2_Error.SAML_RESPONSE_NOT_FOUND, when a POST with a SAMLResponse is not found - """ - self.__errors = [] - - if 'post_data' in self.__request_data and 'SAMLResponse' in self.__request_data['post_data']: - # AuthnResponse -- HTTP_POST Binding - response = OneLogin_Saml2_Response(self.__settings, self.__request_data['post_data']['SAMLResponse']) - - if response.is_valid(request_id): - self.__attributes = response.get_attributes() - self.__nameid = response.get_nameid() - self.__authenticated = True - else: - self.__errors.append('invalid_response') - - else: - self.__errors.append('invalid_binding') - raise OneLogin_Saml2_Error( - 'SAML Response not found, Only supported HTTP_POST Binding', - OneLogin_Saml2_Error.SAML_RESPONSE_NOT_FOUND - ) -
    -
    [docs] def process_slo(self, keep_local_session=False, request_id=None, delete_session_cb=None): - """ - Process the SAML Logout Response / Logout Request sent by the IdP. - - :param keep_local_session: When false will destroy the local session, otherwise will destroy it - :type keep_local_session: bool - - :param request_id: The ID of the LogoutRequest sent by this SP to the IdP - :type request_id: string - - :returns: Redirection url - """ - self.__errors = [] - - if 'get_data' in self.__request_data and 'SAMLResponse' in self.__request_data['get_data']: - logout_response = OneLogin_Saml2_Logout_Response(self.__settings, self.__request_data['get_data']['SAMLResponse']) - if not logout_response.is_valid(self.__request_data, request_id): - self.__errors.append('invalid_logout_response') - elif logout_response.get_status() != OneLogin_Saml2_Constants.STATUS_SUCCESS: - self.__errors.append('logout_not_success') - elif not keep_local_session: - OneLogin_Saml2_Utils.delete_local_session(delete_session_cb) - - elif 'get_data' in self.__request_data and 'SAMLRequest' in self.__request_data['get_data']: - request = OneLogin_Saml2_Utils.decode_base64_and_inflate(self.__request_data['get_data']['SAMLRequest']) - if not OneLogin_Saml2_Logout_Request.is_valid(self.__settings, request, self.__request_data): - self.__errors.append('invalid_logout_request') - else: - if not keep_local_session: - OneLogin_Saml2_Utils.delete_local_session(delete_session_cb) - - in_response_to = OneLogin_Saml2_Logout_Request.get_id(request) - response_builder = OneLogin_Saml2_Logout_Response(self.__settings) - response_builder.build(in_response_to) - logout_response = response_builder.get_response() - - parameters = {'SAMLResponse': logout_response} - if 'RelayState' in self.__request_data['get_data']: - parameters['RelayState'] = self.__request_data['get_data']['RelayState'] - - security = self.__settings.get_security_data() - if 'logoutResponseSigned' in security and security['logoutResponseSigned']: - signature = self.build_response_signature(logout_response, parameters.get('RelayState', None)) - parameters['SigAlg'] = OneLogin_Saml2_Constants.RSA_SHA1 - parameters['Signature'] = signature - - return self.redirect_to(self.get_slo_url(), parameters) - - else: - self.__errors.append('invalid_binding') - raise OneLogin_Saml2_Error( - 'SAML LogoutRequest/LogoutResponse not found. Only supported HTTP_REDIRECT Binding', - OneLogin_Saml2_Error.SAML_LOGOUTMESSAGE_NOT_FOUND - ) -
    -
    [docs] def redirect_to(self, url=None, parameters={}): - """ - Redirects the user to the url past by parameter or to the url that we defined in our SSO Request. - - :param url: The target URL to redirect the user - :type url: string - :param parameters: Extra parameters to be passed as part of the url - :type parameters: dict - - :returns: Redirection url - """ - if url is None and 'RelayState' in self.__request_data['get_data']: - url = self.__request_data['get_data']['RelayState'] - return OneLogin_Saml2_Utils.redirect(url, parameters, request_data=self.__request_data) -
    -
    [docs] def is_authenticated(self): - """ - Checks if the user is authenticated or not. - - :returns: True if is authenticated, False if not - :rtype: bool - """ - return self.__authenticated -
    -
    [docs] def get_attributes(self): - """ - Returns the set of SAML attributes. - - :returns: SAML attributes - :rtype: dict - """ - return self.__attributes -
    -
    [docs] def get_nameid(self): - """ - Returns the nameID. - - :returns: NameID - :rtype: string - """ - return self.__nameid -
    -
    [docs] def get_errors(self): - """ - Returns a list with code errors if something went wrong - - :returns: List of errors - :rtype: list - """ - return self.__errors -
    -
    [docs] def get_attribute(self, name): - """ - Returns the requested SAML attribute. - - :param name: Name of the attribute - :type name: string - - :returns: Attribute value if exists or None - :rtype: string - """ - assert isinstance(name, basestring) - value = None - if name in self.__attributes.keys(): - value = self.__attributes[name] - return value -
    -
    [docs] def login(self, return_to=None): - """ - Initiates the SSO process. - - :param return_to: Optional argument. The target URL the user should be redirected to after login. - :type return_to: string - - :returns: Redirection url - """ - authn_request = OneLogin_Saml2_Authn_Request(self.__settings) - - saml_request = authn_request.get_request() - parameters = {'SAMLRequest': saml_request} - - if return_to is not None: - parameters['RelayState'] = return_to - else: - parameters['RelayState'] = OneLogin_Saml2_Utils.get_self_url_no_query(self.__request_data) - - security = self.__settings.get_security_data() - if security.get('authnRequestsSigned', False): - parameters['SigAlg'] = OneLogin_Saml2_Constants.RSA_SHA1 - parameters['Signature'] = self.build_request_signature(saml_request, parameters['RelayState']) - return self.redirect_to(self.get_sso_url(), parameters) -
    -
    [docs] def logout(self, return_to=None, name_id=None, session_index=None): - """ - Initiates the SLO process. - - :param return_to: Optional argument. The target URL the user should be redirected to after logout. - :type return_to: string - :param name_id: Optional argument. The NameID that will be set in the LogoutRequest. - :type name_id: string - :param session_index: Optional argument. SessionIndex that identifies the session of the user. - :type session_index: string - :returns: Redirection url - """ - slo_url = self.get_slo_url() - if slo_url is None: - raise OneLogin_Saml2_Error( - 'The IdP does not support Single Log Out', - OneLogin_Saml2_Error.SAML_SINGLE_LOGOUT_NOT_SUPPORTED - ) - - logout_request = OneLogin_Saml2_Logout_Request(self.__settings) - - saml_request = logout_request.get_request() - - parameters = {'SAMLRequest': logout_request.get_request()} - if return_to is not None: - parameters['RelayState'] = return_to - else: - parameters['RelayState'] = OneLogin_Saml2_Utils.get_self_url_no_query(self.__request_data) - - security = self.__settings.get_security_data() - if security.get('logoutRequestSigned', False): - parameters['SigAlg'] = OneLogin_Saml2_Constants.RSA_SHA1 - parameters['Signature'] = self.build_request_signature(saml_request, parameters['RelayState']) - return self.redirect_to(slo_url, parameters) -
    -
    [docs] def get_sso_url(self): - """ - Gets the SSO url. - - :returns: An URL, the SSO endpoint of the IdP - :rtype: string - """ - idp_data = self.__settings.get_idp_data() - return idp_data['singleSignOnService']['url'] -
    -
    [docs] def get_slo_url(self): - """ - Gets the SLO url. - - :returns: An URL, the SLO endpoint of the IdP - :rtype: string - """ - url = None - idp_data = self.__settings.get_idp_data() - if 'singleLogoutService' in idp_data.keys() and 'url' in idp_data['singleLogoutService']: - url = idp_data['singleLogoutService']['url'] - return url -
    -
    [docs] def build_request_signature(self, saml_request, relay_state): - """ - Builds the Signature of the SAML Request. - - :param saml_request: The SAML Request - :type saml_request: string - - :param relay_state: The target URL the user should be redirected to - :type relay_state: string - """ - if not self.__settings.check_sp_certs(): - raise OneLogin_Saml2_Error( - "Trying to sign the SAML Request but can't load the SP certs", - OneLogin_Saml2_Error.SP_CERTS_NOT_FOUND - ) - - xmlsec.initialize() - - # Load the key into the xmlsec context - key = self.__settings.get_sp_key() - file_key = OneLogin_Saml2_Utils.write_temp_file(key) # FIXME avoid writing a file - - dsig_ctx = xmlsec.DSigCtx() - dsig_ctx.signKey = xmlsec.Key.load(file_key.name, xmlsec.KeyDataFormatPem, None) - file_key.close() - - data = { - 'SAMLRequest': quote(saml_request), - 'RelayState': quote(relay_state), - 'SignAlg': quote(OneLogin_Saml2_Constants.RSA_SHA1), - } - msg = urlencode(data) - signature = dsig_ctx.signBinary(msg, xmlsec.TransformRsaSha1) - return b64encode(signature) -
    -
    [docs] def build_response_signature(self, saml_response, relay_state): - """ - Builds the Signature of the SAML Response. - :param saml_request: The SAML Response - :type saml_request: string - - :param relay_state: The target URL the user should be redirected to - :type relay_state: string - """ - if not self.__settings.check_sp_certs(): - raise OneLogin_Saml2_Error( - "Trying to sign the SAML Response but can't load the SP certs", - OneLogin_Saml2_Error.SP_CERTS_NOT_FOUND - ) - - xmlsec.initialize() - - # Load the key into the xmlsec context - key = self.__settings.get_sp_key() - file_key = OneLogin_Saml2_Utils.write_temp_file(key) # FIXME avoid writing a file - - dsig_ctx = xmlsec.DSigCtx() - dsig_ctx.signKey = xmlsec.Key.load(file_key.name, xmlsec.KeyDataFormatPem, None) - file_key.close() - - data = { - 'SAMLResponse': quote(saml_response), - 'RelayState': quote(relay_state), - 'SignAlg': quote(OneLogin_Saml2_Constants.RSA_SHA1), - } - msg = urlencode(data) - import pdb; dbp.set_trace() - print msg - data2 = { - 'SAMLResponse': saml_response, - 'RelayState': relay_state, - 'SignAlg': OneLogin_Saml2_Constants.RSA_SHA1, - } - msg2 = urlencode(data2) - print msg2 - signature = dsig_ctx.signBinary(msg, xmlsec.TransformRsaSha1) - return b64encode(signature)
    -
    - -
    -
    -
    -
    -
    - - -
    -
    -
    -
    - - - - diff --git a/docs/saml2/_modules/saml2/authn_request.html b/docs/saml2/_modules/saml2/authn_request.html deleted file mode 100644 index 2bd22b2f..00000000 --- a/docs/saml2/_modules/saml2/authn_request.html +++ /dev/null @@ -1,172 +0,0 @@ - - - - - - - - - - saml2.authn_request — SAML Python library classes and methods - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for saml2.authn_request

    -# -*- coding: utf-8 -*-
    -
    -from base64 import b64encode
    -from datetime import datetime
    -from zlib import compress
    -
    -from saml2.utils import OneLogin_Saml2_Utils
    -from saml2.constants import OneLogin_Saml2_Constants
    -
    -
    -
    [docs]class OneLogin_Saml2_Authn_Request: - - def __init__(self, settings): - """ - Constructs the AuthnRequest object. - - Arguments are: - * (OneLogin_Saml2_Settings) settings. Setting data - """ - self.__settings = settings - - sp_data = self.__settings.get_sp_data() - security = self.__settings.get_security_data() - - uid = OneLogin_Saml2_Utils.generate_unique_id() - issue_instant = OneLogin_Saml2_Utils.parse_time_to_SAML( - int(datetime.now().strftime("%s")) - ) - - name_id_policy_format = sp_data['NameIDFormat'] - if 'wantNameIdEncrypted' in security and security['wantNameIdEncrypted']: - name_id_policy_format = OneLogin_Saml2_Constants.NAMEID_ENCRYPTED - - provider_name_str = '' - organization_data = settings.get_organization() - if isinstance(organization_data, dict): - langs = organization_data.keys() - if 'en-US' in langs: - lang = 'en-US' - else: - lang = langs[0] - if 'displayname' in organization_data[lang] and organization_data[lang]['displayname'] is not None: - provider_name_str = 'ProviderName="%s"' % organization_data[lang]['displayname'] - - request = """<samlp:AuthnRequest - xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" - xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" - ID="%(id)s" - Version="2.0" - %(provider_name)s - IssueInstant="%(issue_instant)s" - ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" - AssertionConsumerServiceURL="%(assertion_url)s"> - <saml:Issuer>%(entity_id)s</saml:Issuer> - <samlp:NameIDPolicy - Format="%(name_id_policy)s" - AllowCreate="true" /> - <samlp:RequestedAuthnContext Comparison="exact"> - <saml:AuthnContextMethodRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextMethodRef> - </samlp:RequestedAuthnContext> -</samlp:AuthnRequest>""" % { - 'id': uid, - 'provider_name': provider_name_str, - 'issue_instant': issue_instant, - 'assertion_url': sp_data['assertionConsumerService']['url'], - 'entity_id': sp_data['entityId'], - 'name_id_policy': name_id_policy_format, - } - - self.__authn_request = request - -
    [docs] def get_request(self): - """ - Returns unsigned AuthnRequest. - :return: Unsigned AuthnRequest - :rtype: str object - """ - deflated_request = compress(self.__authn_request)[2:-4] - return b64encode(deflated_request)
    -
    - -
    -
    -
    -
    -
    - - -
    -
    -
    -
    - - - - diff --git a/docs/saml2/_modules/saml2/constants.html b/docs/saml2/_modules/saml2/constants.html deleted file mode 100644 index 1b0af52f..00000000 --- a/docs/saml2/_modules/saml2/constants.html +++ /dev/null @@ -1,162 +0,0 @@ - - - - - - - - - - saml2.constants — SAML Python library classes and methods - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for saml2.constants

    -# -*- coding: utf-8 -*-
    -
    -
    -
    [docs]class OneLogin_Saml2_Constants: - # Value added to the current time in time condition validations - ALOWED_CLOCK_DRIFT = 180 - - # NameID Formats - NAMEID_EMAIL_ADDRESS = 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress' - NAMEID_X509_SUBJECT_NAME = 'urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName' - NAMEID_WINDOWS_DOMAIN_QUALIFIED_NAME = 'urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName' - NAMEID_KERBEROS = 'urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos' - NAMEID_ENTITY = 'urn:oasis:names:tc:SAML:2.0:nameid-format:entity' - NAMEID_TRANSIENT = 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient' - NAMEID_PERSISTENT = 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent' - NAMEID_ENCRYPTED = 'urn:oasis:names:tc:SAML:2.0:nameid-format:encrypted' - - # Attribute Name Formats - ATTRNAME_FORMAT_UNSPECIFIED = 'urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified' - ATTRNAME_FORMAT_URI = 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri' - ATTRNAME_FORMAT_BASIC = 'urn:oasis:names:tc:SAML:2.0:attrname-format:basic' - - # Namespaces - NS_SAML = 'urn:oasis:names:tc:SAML:2.0:assertion' - NS_SAMLP = 'urn:oasis:names:tc:SAML:2.0:protocol' - NS_SOAP = 'http://schemas.xmlsoap.org/soap/envelope/' - NS_MD = 'urn:oasis:names:tc:SAML:2.0:metadata' - NS_XS = 'http://www.w3.org/2001/XMLSchema' - NS_XSI = 'http://www.w3.org/2001/XMLSchema-instance' - NS_XENC = 'http://www.w3.org/2001/04/xmlenc#' - NS_DS = 'http://www.w3.org/2000/09/xmldsig#' - - # Bindings - BINDING_HTTP_POST = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST' - BINDING_HTTP_REDIRECT = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect' - BINDING_HTTP_ARTIFACT = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact' - BINDING_SOAP = 'urn:oasis:names:tc:SAML:2.0:bindings:SOAP' - BINDING_DEFLATE = 'urn:oasis:names:tc:SAML:2.0:bindings:URL-Encoding:DEFLATE' - - # Auth Context Method - AC_UNSPECIFIED = 'urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified' - AC_PASSWORD = 'urn:oasis:names:tc:SAML:2.0:ac:classes:Password' - AC_X509 = 'urn:oasis:names:tc:SAML:2.0:ac:classes:X509' - AC_SMARTCARD = 'urn:oasis:names:tc:SAML:2.0:ac:classes:Smartcard' - AC_KERBEROS = 'urn:oasis:names:tc:SAML:2.0:ac:classes:Kerberos' - - # Subject Confirmation - CM_BEARER = 'urn:oasis:names:tc:SAML:2.0:cm:bearer' - CM_HOLDER_KEY = 'urn:oasis:names:tc:SAML:2.0:cm:holder-of-key' - CM_SENDER_VOUCHES = 'urn:oasis:names:tc:SAML:2.0:cm:sender-vouches' - - # Status Codes - STATUS_SUCCESS = 'urn:oasis:names:tc:SAML:2.0:status:Success' - STATUS_REQUESTER = 'urn:oasis:names:tc:SAML:2.0:status:Requester' - STATUS_RESPONDER = 'urn:oasis:names:tc:SAML:2.0:status:Responder' - STATUS_VERSION_MISMATCH = 'urn:oasis:names:tc:SAML:2.0:status:VersionMismatch' - STATUS_NO_PASSIVE = 'urn:oasis:names:tc:SAML:2.0:status:NoPassive' - STATUS_PARTIAL_LOGOUT = 'urn:oasis:names:tc:SAML:2.0:status:PartialLogout' - STATUS_PROXY_COUNT_EXCEEDED = 'urn:oasis:names:tc:SAML:2.0:status:ProxyCountExceeded' - - # Crypto - RSA_SHA1 = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1' - - NSMAP = { - 'samlp': NS_SAMLP, - 'saml': NS_SAML, - 'ds': NS_DS, - 'xenc': NS_XENC - }
    -
    - -
    -
    -
    -
    -
    - - -
    -
    -
    -
    - - - - diff --git a/docs/saml2/_modules/saml2/errors.html b/docs/saml2/_modules/saml2/errors.html deleted file mode 100644 index ea6894b1..00000000 --- a/docs/saml2/_modules/saml2/errors.html +++ /dev/null @@ -1,131 +0,0 @@ - - - - - - - - - - saml2.errors — SAML Python library classes and methods - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for saml2.errors

    -# -*- coding: utf-8 -*-
    -
    -
    -
    [docs]class OneLogin_Saml2_Error(Exception): - - # Errors - SETTINGS_FILE_NOT_FOUND = 0 - SETTINGS_INVALID_SYNTAX = 1 - SETTINGS_INVALID = 2 - METADATA_SP_INVALID = 3 - SP_CERTS_NOT_FOUND = 4 - REDIRECT_INVALID_URL = 5 - PUBLIC_CERT_FILE_NOT_FOUND = 6 - PRIVATE_KEY_FILE_NOT_FOUND = 7 - SAML_RESPONSE_NOT_FOUND = 8 - SAML_LOGOUTMESSAGE_NOT_FOUND = 9 - SAML_LOGOUTREQUEST_INVALID = 10 - SAML_LOGOUTRESPONSE_INVALID = 11 - SAML_SINGLE_LOGOUT_NOT_SUPPORTED = 12 - - def __init__(self, message, code=0, errors=None): - """ - Initializes the Exception instance. - - Arguments are: - * (str) message. Describes the error. - * (int) code. The code error (defined in the error class). - """ - from saml2.utils import _ - - assert isinstance(message, basestring) - assert isinstance(code, int) - - if errors is not None: - message = message % errors - - Exception.__init__(self, _(message)) - self.code = code
    -
    - -
    -
    -
    -
    -
    - - -
    -
    -
    -
    - - - - diff --git a/docs/saml2/_modules/saml2/logout_request.html b/docs/saml2/_modules/saml2/logout_request.html deleted file mode 100644 index ceb9779f..00000000 --- a/docs/saml2/_modules/saml2/logout_request.html +++ /dev/null @@ -1,368 +0,0 @@ - - - - - - - - - - saml2.logout_request — SAML Python library classes and methods - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for saml2.logout_request

    -# -*- coding: utf-8 -*-
    -
    -from base64 import b64decode
    -from datetime import datetime
    -from lxml import etree
    -from os.path import basename
    -from urllib import urlencode
    -from urlparse import parse_qs
    -from xml.dom.minidom import Document, parseString
    -
    -import dm.xmlsec.binding as xmlsec
    -
    -from saml2.constants import OneLogin_Saml2_Constants
    -from saml2.utils import OneLogin_Saml2_Utils
    -
    -
    -
    [docs]class OneLogin_Saml2_Logout_Request: - - def __init__(self, settings,request=None,name_id=None, session_index=None): - """ - Constructs the Logout Request object. - - Arguments are: - * (OneLogin_Saml2_Settings) settings. Setting data - """ - self.__settings = settings - - sp_data = self.__settings.get_sp_data() - idp_data = self.__settings.get_idp_data() - security = self.__settings.get_security_data() - - uid = OneLogin_Saml2_Utils.generate_unique_id() - name_id_value = OneLogin_Saml2_Utils.generate_unique_id() - issue_instant = OneLogin_Saml2_Utils.parse_time_to_SAML(int(datetime.now().strftime("%s"))) - - key = None - if 'nameIdEncrypted' in security and security['nameIdEncrypted']: - key = idp_data['x509cert'] - - name_id = OneLogin_Saml2_Utils.generate_name_id( - name_id_value, - sp_data['entityId'], - sp_data['NameIDFormat'], - key - ) - - logout_request = """<samlp:LogoutRequest - xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" - xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" - ID="%(id)s" - Version="2.0" - IssueInstant="%(issue_instant)s" - Destination="%(single_logout_url)s"> - <saml:Issuer>%(entity_id)s</saml:Issuer> - %(name_id)s -</samlp:LogoutRequest>""" % { - 'id': uid, - 'issue_instant': issue_instant, - 'single_logout_url': idp_data['singleLogoutService']['url'], - 'entity_id': sp_data['entityId'], - 'name_id': name_id, - } - - self.__logout_request = logout_request - -
    [docs] def get_request(self): - """ - Returns the Logout Request defated, base64encoded - :return: Deflated base64 encoded Logout Request - :rtype: str object - """ - return OneLogin_Saml2_Utils.deflate_and_base64_encode(self.__logout_request) -
    - @staticmethod -
    [docs] def get_id(request): - """ - Returns the ID of the Logout Request - :param request: Logout Request Message - :type request: string|DOMDocument - :return: string ID - :rtype: str object - """ - if isinstance(request, Document): - dom = request - else: - dom = parseString(request) - return dom.documentElement.getAttribute('ID') -
    - @staticmethod -
    [docs] def get_name_id_data(request, key=None): - """ - Gets the NameID Data of the the Logout Request - :param request: Logout Request Message - :type request: string|DOMDocument - :param key: The SP key - :type key: string - :return: Name ID Data (Value, Format, NameQualifier, SPNameQualifier) - :rtype: dict - """ - if isinstance(request, Document): - request = request.toxml() - dom = etree.fromstring(request) - name_id = None - - encrypted_entries = OneLogin_Saml2_Utils.query(dom, '/samlp:LogoutRequest/saml:EncryptedID') - - if len(encrypted_entries) == 1: - if key is None: - raise Exception('Key is required in order to decrypt the NameID') - - elem = parseString(etree.tostring(encrypted_entries[0])) - encrypted_data_nodes = elem.documentElement.getElementsByTagName('xenc:EncryptedData') - encrypted_data = encrypted_data_nodes[0] - - xmlsec.initialize() - - # Load the key into the xmlsec context - file_key = OneLogin_Saml2_Utils.write_temp_file(key) # FIXME avoid writing a file - enc_key = xmlsec.Key.load(file_key.name, xmlsec.KeyDataFormatPem, None) - enc_key.name = basename(file_key.name) - file_key.close() - enc_ctx = xmlsec.EncCtx() - enc_ctx.encKey = enc_key - - name_id = OneLogin_Saml2_Utils.decrypt_element(encrypted_data, enc_ctx) - else: - entries = OneLogin_Saml2_Utils.query(dom, '/samlp:LogoutRequest/saml:NameID') - if len(entries) == 1: - name_id = entries[0] - - if name_id is None: - raise Exception('Not NameID found in the Logout Request') - - name_id_data = { - 'Value': name_id.text - } - for attr in ['Format', 'SPNameQualifier', 'NameQualifier']: - if attr in name_id.attrib.keys(): - name_id_data[attr] = name_id.attrib[attr] - - return name_id_data -
    - @staticmethod -
    [docs] def get_name_id(request, key=None): - """ - Gets the NameID of the Logout Request Message - :param request: Logout Request Message - :type request: string|DOMDocument - :param key: The SP key - :type key: string - :return: Name ID Value - :rtype: string - """ - name_id = OneLogin_Saml2_Logout_Request.get_name_id_data(request, key) - return name_id['Value'] -
    - @staticmethod -
    [docs] def get_issuer(request): - """ - Gets the Issuer of the Logout Request Message - :param request: Logout Request Message - :type request: string|DOMDocument - :return: The Issuer - :rtype: string - """ - if isinstance(request, Document): - request = request.toxml() - dom = etree.fromstring(request) - - issuer = None - issuer_nodes = OneLogin_Saml2_Utils.query(dom, '/samlp:LogoutRequest/saml:Issuer') - if len(issuer_nodes) == 1: - issuer = issuer_nodes[0].text - return issuer -
    - @staticmethod -
    [docs] def get_session_indexes(request): - """ - Gets the SessionIndexes from the Logout Request - :param request: Logout Request Message - :type request: string|DOMDocument - :return: The SessionIndex value - :rtype: list - """ - if isinstance(request, Document): - request = request.toxml() - dom = etree.fromstring(request) - - session_indexes = [] - session_index_nodes = OneLogin_Saml2_Utils.query(dom, '/samlp:LogoutRequest/samlp:SessionIndex') - for session_index_node in session_index_nodes: - session_indexes.append(session_index_node.text) - return session_indexes -
    - @staticmethod -
    [docs] def is_valid(settings, request, get_data, debug=False): - """ - Checks if the Logout Request recieved is valid - :param settings: Settings - :type settings: OneLogin_Saml2_Settings - :param request: Logout Request Message - :type request: string|DOMDocument - :return: If the Logout Request is or not valid - :rtype: boolean - """ - try: - if isinstance(request, Document): - dom = request - else: - dom = parseString(request) - - idp_data = settings.get_idp_data() - idp_entity_id = idp_data['entityId'] - - if settings.is_strict(): - res = OneLogin_Saml2_Utils.validate_xml(dom, 'saml-schema-protocol-2.0.xsd', debug) - if not isinstance(res, Document): - raise Exception('Invalid SAML Logout Request. Not match the saml-schema-protocol-2.0.xsd') - - security = settings.get_security_data() - - current_url = OneLogin_Saml2_Utils.get_self_url_no_query(get_data) - - # Check NotOnOrAfter - if dom.documentElement.hasAttribute('NotOnOrAfter'): - na = OneLogin_Saml2_Utils.parse_SAML_to_time(dom.documentElement.getAttribute('NotOnOrAfter')) - if na <= datetime.now(): - raise Exception('Timing issues (please check your clock settings)') - - # Check destination - if dom.documentElement.hasAttribute('Destination'): - destination = dom.documentElement.getAttribute('Destination') - if destination is not None: - if current_url not in destination: - raise Exception('The LogoutRequest was received at $currentURL instead of $destination') - - # Check issuer - issuer = OneLogin_Saml2_Logout_Request.get_issuer(dom) - if issuer is None or issuer != idp_entity_id: - raise Exception('Invalid issuer in the Logout Request') - - if security['wantMessagesSigned']: - if 'Signature' not in get_data: - raise Exception('The Message of the Logout Request is not signed and the SP require it') - - if 'Signature' in get_data: - if 'SigAlg' not in get_data: - sign_alg = OneLogin_Saml2_Constants.RSA_SHA1 - else: - sign_alg = get_data['SigAlg'] - - if sign_alg != OneLogin_Saml2_Constants.RSA_SHA1: - raise Exception('Invalid signAlg in the recieved Logout Request') - - signed_query = 'SAMLRequest=%s' % urlencode(get_data['SAMLRequest']) - if 'RelayState' in get_data: - signed_query = '%s&RelayState=%s' % (signed_query, urlencode(get_data['RelayState'])) - signed_query = '%s&SigAlg=%s' % (signed_query, urlencode(sign_alg)) - - if 'x509cert' not in idp_data or idp_data['x509cert'] is None: - raise Exception('In order to validate the sign on the Logout Request, the x509cert of the IdP is required') - cert = idp_data['x509cert'] - - xmlsec.initialize() - objkey = xmlsec.Key.load(cert, xmlsec.KeyDataFormatPem, None) # FIXME is this right? - - if not objkey.verifySignature(signed_query, b64decode(get_data['Signature'])): - raise Exception('Signature validation failed. Logout Request rejected') - - return True - except Exception as e: - debug = settings.is_debug_active() - if debug: - print(e.strerror) - return False
    -
    - -
    -
    -
    -
    -
    - - -
    -
    -
    -
    - - - - diff --git a/docs/saml2/_modules/saml2/logout_response.html b/docs/saml2/_modules/saml2/logout_response.html deleted file mode 100644 index 9362300f..00000000 --- a/docs/saml2/_modules/saml2/logout_response.html +++ /dev/null @@ -1,279 +0,0 @@ - - - - - - - - - - saml2.logout_response — SAML Python library classes and methods - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for saml2.logout_response

    -# -*- coding: utf-8 -*-
    -
    -
    -from base64 import b64decode
    -from datetime import datetime
    -from lxml import etree
    -from urllib import quote_plus
    -from xml.dom.minidom import Document, parseString
    -
    -import dm.xmlsec.binding as xmlsec
    -
    -from saml2.constants import OneLogin_Saml2_Constants
    -from saml2.utils import OneLogin_Saml2_Utils
    -
    -
    -
    [docs]class OneLogin_Saml2_Logout_Response(): - - def __init__(self, settings, response=None): - """ - Constructs a Logout Response object (Initialize params from settings - and if provided load the Logout Response. - - Arguments are: - * (OneLogin_Saml2_Settings) settings. Setting data - * (string) response. An UUEncoded SAML Logout - response from the IdP. - """ - self.__settings = settings - if response is not None: - self.__logout_response = OneLogin_Saml2_Utils.decode_base64_and_inflate(response) - self.document = parseString(self.__logout_response) - -
    [docs] def get_issuer(self): - """ - Gets the Issuer of the Logout Response Message - :return: The Issuer - :rtype: string - """ - issuer = None - issuer_nodes = self.__query('/samlp:LogoutResponse/saml:Issuer') - if len(issuer_nodes) == 1: - issuer = issuer_nodes[0].text - return issuer -
    -
    [docs] def get_status(self): - """ - Gets the Status - :return: The Status - :rtype: string - """ - entries = self.__query('/samlp:LogoutResponse/samlp:Status/samlp:StatusCode') - if len(entries) == 0: - return None - status = entries[0].attrib['Value'] - return status -
    -
    [docs] def is_valid(self, request_data, request_id=None): - """ - Determines if the SAML LogoutResponse is valid - :param request_id: The ID of the LogoutRequest sent by this SP to the IdP - :type request_id: string - :return: Returns if the SAML LogoutResponse is or not valid - :rtype: boolean - """ - try: - idp_data = self.__settings.get_idp_data() - idp_entity_id = idp_data['entityId'] - get_data = request_data['get_data'] - - if self.__settings.is_strict(): - res = OneLogin_Saml2_Utils.validate_xml(self.document, 'saml-schema-protocol-2.0.xsd', self.__settings.is_debug_active()) - if not isinstance(res, Document): - raise Exception('Invalid SAML Logout Request. Not match the saml-schema-protocol-2.0.xsd') - - security = self.__settings.get_security_data() - - # Check if the InResponseTo of the Logout Response matchs the ID of the Logout Request (requestId) if provided - if request_id is not None and self.document.documentElement.hasAttribute('InResponseTo'): - in_response_to = self.document.documentElement.getAttribute('InResponseTo') - if request_id != in_response_to: - raise Exception('The InResponseTo of the Logout Response: %s, does not match the ID of the Logout request sent by the SP: %s' % (in_response_to, request_id)) - - # Check issuer - issuer = self.get_issuer() - if issuer is None or issuer != idp_entity_id: - raise Exception('Invalid issuer in the Logout Request') - - current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data) - - # Check destination - if self.document.documentElement.hasAttribute('Destination'): - destination = self.document.documentElement.getAttribute('Destination') - if destination is not None: - if current_url not in destination: - raise Exception('The LogoutRequest was received at $currentURL instead of $destination') - - if security['wantMessagesSigned']: - if 'Signature' not in get_data: - raise Exception('The Message of the Logout Response is not signed and the SP require it') - - if 'Signature' in get_data: - if 'SigAlg' not in get_data: - sign_alg = OneLogin_Saml2_Constants.RSA_SHA1 - else: - sign_alg = get_data['SigAlg'] - - if sign_alg != OneLogin_Saml2_Constants.RSA_SHA1: - raise Exception('Invalid signAlg in the recieved Logout Response') - - signed_query = 'SAMLResponse=%s' % quote_plus(get_data['SAMLResponse']) - if 'RelayState' in get_data: - signed_query = '%s&RelayState=%s' % (signed_query, quote_plus(get_data['RelayState'])) - signed_query = '%s&SigAlg=%s' % (signed_query, quote_plus(sign_alg)) - - if 'x509cert' not in idp_data or idp_data['x509cert'] is None: - raise Exception('In order to validate the sign on the Logout Response, the x509cert of the IdP is required') - cert = idp_data['x509cert'] - - xmlsec.initialize() - objkey = xmlsec.Key.load(cert, xmlsec.KeyDataFormatPem, None) # FIXME is this right? - - if not objkey.verifySignature(signed_query, b64decode(get_data['Signature'])): - raise Exception('Signature validation failed. Logout Response rejected') - - return True - except Exception as e: - debug = self.__settings.is_debug_active() - if debug: - print(e.strerror) - return False -
    - def __query(self, query): - """ - Extracts a node from the DOMDocument (Logout Response Menssage) - :param query: Xpath Expresion - :type query: string - :return: The queried node - :rtype: DOMNodeList - """ - # Switch to lxml for querying - xml = self.document.toxml() - return OneLogin_Saml2_Utils.query(etree.fromstring(xml), query) - -
    [docs] def build(self, in_response_to): - """ - Creates a Logout Response object. - :param in_response_to: InResponseTo value for the Logout Response. - :type in_response_to: string - """ - sp_data = self.__settings.get_sp_data() - idp_data = self.__settings.get_idp_data() - - uid = OneLogin_Saml2_Utils.generate_unique_id() - issue_instant = OneLogin_Saml2_Utils.parse_time_to_SAML( - int(datetime.now().strftime("%s")) - ) - - logout_response = """<samlp:LogoutResponse xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" - xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" - ID="%(id)s" - Version="2.0" - IssueInstant="%(issue_instant)s" - Destination="%(destination)s" - InResponseTo="%(in_response_to)s" -> - <saml:Issuer>%(entity_id)s</saml:Issuer> - <samlp:Status> - <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" /> - </samlp:Status> -</samlp:LogoutResponse>""" % { - 'id': uid, - 'issue_instant': issue_instant, - 'destination': idp_data['singleLogoutService']['url'], - 'in_response_to': in_response_to, - 'entity_id': sp_data['entityId'], - } - - self.__logout_response = logout_response -
    -
    [docs] def get_response(self): - """ - Returns a Logout Response object. - :return: Logout Response deflated and base64 encoded - :rtype: string - """ - return OneLogin_Saml2_Utils.deflate_and_base64_encode(self.__logout_response)
    -
    - -
    -
    -
    -
    -
    - - -
    -
    -
    -
    - - - - diff --git a/docs/saml2/_modules/saml2/metadata.html b/docs/saml2/_modules/saml2/metadata.html deleted file mode 100644 index 6f3d0b93..00000000 --- a/docs/saml2/_modules/saml2/metadata.html +++ /dev/null @@ -1,286 +0,0 @@ - - - - - - - - - - saml2.metadata — SAML Python library classes and methods - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for saml2.metadata

    -# -*- coding: utf-8 -*-
    -
    -
    -from time import gmtime, strftime
    -from datetime import datetime
    -from xml.dom.minidom import parseString
    -
    -from saml2.constants import OneLogin_Saml2_Constants
    -from saml2.utils import OneLogin_Saml2_Utils
    -
    -
    -
    [docs]class OneLogin_Saml2_Metadata: - - TIME_VALID = 172800 # 2 days - TIME_CACHED = 604800 # 1 week - - @staticmethod -
    [docs] def builder(sp, authnsign=False, wsign=False, valid_until=None, cache_duration=None, contacts=None, organization=None): - """ - Build the metadata of the SP - - :param sp: The SP data - :type sp: string - - :param authnsign: authnRequestsSigned attribute - :type authnsign: string - - :param wsign: wantAssertionsSigned attribute - :type wsign: string - - :param valid_until: Metadata's valid time - :type valid_until: DateTime - - :param cache_duration: Duration of the cache in seconds - :type cache_duration: Timestamp - - :param contacts: Contacts info - :type contacts: dict - - :param organization: Organization ingo - :type organization: dict - """ - if valid_until is None: - valid_until = int(datetime.now().strftime("%s")) + OneLogin_Saml2_Metadata.TIME_VALID - valid_until_time = gmtime(valid_until) - valid_until_time = strftime(r'%Y-%m-%dT%H:%M:%SZ', valid_until_time) - if cache_duration is None: - cache_duration = int(datetime.now().strftime("%s")) + OneLogin_Saml2_Metadata.TIME_CACHED - if contacts is None: - contacts = {} - if organization is None: - organization = {} - - sls = '' - if 'singleLogoutService' in sp: - sls = """<md:SingleLogoutService Binding="%(binding)s" - Location="%(location)s" />""" % { - 'binding': sp['singleLogoutService']['binding'], - 'location': sp['singleLogoutService']['url'], - } - - str_authnsign = 'true' if authnsign else 'false' - str_wsign = 'true' if wsign else 'false' - - str_organization = '' - if len(organization) > 0: - organization_info = [] - for (lang, info) in organization.items(): - organization_info.append(""" <md:Organization> - <md:OrganizationName xml:lang="%(lang)s">%(name)s</md:OrganizationName> - <md:OrganizationDisplayName xml:lang="%(lang)s">%(display_name)s</md:OrganizationDisplayName> - <md:OrganizationURL xml:lang="%(lang)s">%(url)s</md:OrganizationURL> - </md:Organization>""" % { - 'lang': lang, - 'name': info['name'], - 'display_name': info['displayname'], - 'url': info['url'], - }) - str_organization = '\n'.join(organization_info) - - str_contacts = '' - if len(contacts) > 0: - contacts_info = [] - for (ctype, info) in contacts.items(): - contacts_info.append(""" <md:ContactPerson contactType="%(type)s"> - <md:GivenName>%(name)s</md:GivenName> - <md:EmailAddress>%(email)s</md:EmailAddress> - </md:ContactPerson>""" % { - 'type': ctype, - 'name': info['givenName'], - 'email': info['emailAddress'], - }) - str_contacts = '\n'.join(contacts_info) - - metadata = """<?xml version="1.0"?> -<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" - validUntil="%(valid)s" - cacheDuration="PT%(cache)sS" - entityID="%(entity_id)s"> - <md:SPSSODescriptor AuthnRequestsSigned="%(authnsign)s" WantAssertionsSigned="%(wsign)s" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> - <md:NameIDFormat>%(name_id_format)s</md:NameIDFormat> - <md:AssertionConsumerService Binding="%(binding)s" - Location="%(location)s" - index="1" /> -%(sls)s - </md:SPSSODescriptor> -%(organization)s -%(contacts)s -</md:EntityDescriptor>""" % { - 'valid': valid_until_time, - 'cache': cache_duration, - 'entity_id': sp['entityId'], - 'authnsign': str_authnsign, - 'wsign': str_wsign, - 'name_id_format': sp['NameIDFormat'], - 'binding': sp['assertionConsumerService']['binding'], - 'location': sp['assertionConsumerService']['url'], - 'sls': sls, - 'organization': str_organization, - 'contacts': str_contacts, - } - - return metadata -
    - @staticmethod -
    [docs] def sign_metadata(metadata, key, cert): - """ - Sign the metadata with the key/cert provided - - :param metadata: SAML Metadata XML - :type metadata: string - - :param key: x509 key - :type key: string - - :param cert: x509 cert - :type cert: string - - :returns: Signed Metadata - :rtype: string - """ - return OneLogin_Saml2_Utils.add_sign(metadata, key, cert) -
    - @staticmethod -
    [docs] def add_x509_key_descriptors(metadata, cert): - """ - Add the x509 descriptors (sign/encriptation to the metadata - The same cert will be used for sign/encrypt - - :param metadata: SAML Metadata XML - :type metadata: string - - :param cert: x509 cert - :type cert: string - - :returns: Metadata with KeyDescriptors - :rtype: string - """ - try: - xml = parseString(metadata) - except Exception as e: - raise Exception('Error parsing metadata. ' + e.message) - - formated_cert = OneLogin_Saml2_Utils.format_cert(cert, False) - x509_certificate = xml.createElementNS(OneLogin_Saml2_Constants.NS_DS, 'ds:X509Certificate') - content = xml.createTextNode(formated_cert) - x509_certificate.appendChild(content) - - key_data = xml.createElementNS(OneLogin_Saml2_Constants.NS_DS, 'ds:X509Data') - key_data.appendChild(x509_certificate) - - key_info = xml.createElementNS(OneLogin_Saml2_Constants.NS_DS, 'ds:KeyInfo') - key_info.appendChild(key_data) - - key_descriptor = xml.createElementNS(OneLogin_Saml2_Constants.NS_DS, 'md:KeyDescriptor') - - entity_descriptor = sp_sso_descriptor = xml.getElementsByTagName('md:EntityDescriptor')[0] - entity_descriptor.setAttribute('xmlns:ds', OneLogin_Saml2_Constants.NS_DS) - - sp_sso_descriptor = xml.getElementsByTagName('md:SPSSODescriptor')[0] - sp_sso_descriptor.insertBefore(key_descriptor.cloneNode(True), sp_sso_descriptor.firstChild) - sp_sso_descriptor.insertBefore(key_descriptor.cloneNode(True), sp_sso_descriptor.firstChild) - - signing = xml.getElementsByTagName('md:KeyDescriptor')[0] - signing.setAttribute('use', 'signing') - - encryption = xml.getElementsByTagName('md:KeyDescriptor')[1] - encryption.setAttribute('use', 'encryption') - - signing.appendChild(key_info) - encryption.appendChild(key_info.cloneNode(True)) - - return xml.toxml()
    -
    - -
    -
    -
    -
    -
    - - -
    -
    -
    -
    - - - - diff --git a/docs/saml2/_modules/saml2/response.html b/docs/saml2/_modules/saml2/response.html deleted file mode 100644 index 7f815169..00000000 --- a/docs/saml2/_modules/saml2/response.html +++ /dev/null @@ -1,529 +0,0 @@ - - - - - - - - - - saml2.response — SAML Python library classes and methods - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for saml2.response

    -# -*- coding: utf-8 -*-
    -
    -
    -from base64 import b64decode
    -from copy import deepcopy
    -from lxml import etree
    -from os.path import basename
    -from time import time
    -import sys
    -from xml.dom.minidom import Document
    -
    -import dm.xmlsec.binding as xmlsec
    -
    -from saml2.constants import OneLogin_Saml2_Constants
    -from saml2.utils import OneLogin_Saml2_Utils
    -
    -
    -
    [docs]class OneLogin_Saml2_Response(object): - - def __init__(self, settings, response): - """ - Constructs the response object. - - :param settings: The setting info - :type settings: OneLogin_Saml2_Setting object - - :param response: The base64 encoded, XML string containing the samlp:Response - :type response: string - """ - self.__settings = settings - self.response = b64decode(response) - self.document = etree.fromstring(self.response) - self.decrypted_document = None - self.encrypted = None - - # Quick check for the presence of EncryptedAssertion - encrypted_assertion_nodes = self.__query('//saml:EncryptedAssertion') - if encrypted_assertion_nodes: - decrypted_document = deepcopy(self.document) - self.encrypted = True - self.decrypted_document = self.__decrypt_assertion(decrypted_document) - -
    [docs] def is_valid(self, request_data, request_id=None): - """ - Constructs the response object. - - :param request_id: Optional argument. The ID of the AuthNRequest sent by this SP to the IdP - :type request_id: string - - :returns: True if the SAML Response is valid, False if not - :rtype: bool - """ - try: - # Checks SAML version - if self.document.get('Version', None) != '2.0': - raise Exception('Unsupported SAML version') - - # Checks that ID exists - if self.document.get('ID', None) is None: - raise Exception('Missing ID attribute on SAML Response') - - # Checks that the response only has one assertion - if not self.validate_num_assertions(): - raise Exception('Multiple assertions are not supported') - - # Checks that the response has the SUCCESS status - self.check_status() - - idp_data = self.__settings.get_idp_data() - idp_entityid = idp_data.get('entityId', '') - sp_data = self.__settings.get_sp_data() - sp_entityid = sp_data.get('entityId', '') - - sign_nodes = self.__query('//ds:Signature') - - signed_elements = [] - for sign_node in sign_nodes: - signed_elements.append(sign_node.getparent().tag) - - if self.__settings.is_strict(): - res = OneLogin_Saml2_Utils.validate_xml(etree.tostring(self.document), 'saml-schema-protocol-2.0.xsd', self.__settings.is_debug_active()) - if not isinstance(res, Document): - raise Exception('Invalid SAML Response. Not match the saml-schema-protocol-2.0.xsd') - - security = self.__settings.get_security_data() - current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data) - - # Check if the InResponseTo of the Response matchs the ID of the AuthNRequest (requestId) if provided - in_response_to = self.document.get('InResponseTo', None) - if in_response_to and request_id: - if in_response_to != request_id: - raise Exception('The InResponseTo of the Response: %s, does not match the ID of the AuthNRequest sent by the SP: %s' % (in_response_to, request_id)) - - if not self.encrypted and security.get('wantAssertionsEncrypted', False): - raise Exception('The assertion of the Response is not encrypted and the SP require it') - - if security.get('wantNameIdEncrypted', False): - encrypted_nameid_nodes = self.__query_assertion('/saml:Subject/saml:EncryptedID/xenc:EncryptedData') - if not encrypted_nameid_nodes: - raise Exception('The NameID of the Response is not encrypted and the SP require it') - - # Checks that there is at least one AttributeStatement - attribute_statement_nodes = self.__query_assertion('/saml:AttributeStatement') - if not attribute_statement_nodes: - raise Exception('There is no AttributeStatement on the Response') - - # Validates Asserion timestamps - if not self.validate_timestamps(): - raise Exception('Timing issues (please check your clock settings)') - - encrypted_attributes_nodes = self.__query_assertion('/saml:AttributeStatement/saml:EncryptedAttribute') - if encrypted_attributes_nodes: - raise Exception('There is an EncryptedAttribute in the Response and this SP not support them') - - # Checks destination - destination = self.document.get('Destination', None) - if destination: - if destination not in current_url: - raise Exception('The response was received at %s instead of %s' % (current_url, destination)) - - # Checks audience - valid_audiences = self.get_audiences() - if valid_audiences and sp_entityid not in valid_audiences: - raise Exception('%s is not a valid audience for this Response' % sp_entityid) - - # Checks the issuers - issuers = self.get_issuers() - for issuer in issuers: - if not issuer or issuer != idp_entityid: - raise Exception('Invalid issuer in the Assertion/Response') - - # Checks the session Expiration - session_expiration = self.get_session_not_on_or_after() - if not session_expiration and session_expiration <= time(): - raise Exception('The attributes have expired, based on the SessionNotOnOrAfter of the AttributeStatement of this Response') - - # Checks the SubjectConfirmation, at least one SubjectConfirmation must be valid - any_subject_confirmation = False - subject_confirmation_nodes = self.__query_assertion('/saml:Subject/saml:SubjectConfirmation') - - for scn in subject_confirmation_nodes: - method = scn.get('Method', None) - if method and method != OneLogin_Saml2_Constants.CM_BEARER: - continue - scData = scn.find('saml:SubjectConfirmationData', namespaces=OneLogin_Saml2_Constants.NSMAP) - if scData is None: - continue - else: - irt = scData.get('InResponseTo', None) - if irt != in_response_to: - continue - recipient = scData.get('Recipient', None) - if recipient not in current_url: - continue - nooa = scData.get('NotOnOrAfter', None) - if nooa: - parsed_nooa = OneLogin_Saml2_Utils.parse_SAML_to_time(nooa) - if parsed_nooa <= time(): - continue - nb = scData.get('NotBefore', None) - if nb: - parsed_nb = OneLogin_Saml2_Utils.parse_SAML_to_time(nb) - if (parsed_nb > time()): - continue - any_subject_confirmation = True - break - - if not any_subject_confirmation: - raise Exception('A valid SubjectConfirmation was not found on this Response') - - if security.get('wantAssertionsSigned', False) and 'saml:Assertion' not in signed_elements: - raise Exception('The Assertion of the Response is not signed and the SP require it') - - if security.get('wantMessagesSigned', False) and 'samlp:Response' not in signed_elements: - raise Exception('The Message of the Response is not signed and the SP require it') - - document_to_validate = None - if len(signed_elements) > 0: - cert = idp_data.get('x509cert', None) - fingerprint = idp_data.get('certFingerprint', None) - - # Only validates the first sign found - if 'samlp:Response' in signed_elements: - document_to_validate = self.document - else: - if self.encrypted: - document_to_validate = self.decrypted_document - else: - document_to_validate = self.document - - if document_to_validate is not None: - if not OneLogin_Saml2_Utils.validate_sign(document_to_validate, cert, fingerprint): - raise Exception('Signature validation failed. SAML Response rejected') - return True - except: - debug = self.__settings.is_debug_active() - if debug: - print sys.exc_info()[0] - return False -
    -
    [docs] def check_status(self): - """ - Check if the status of the response is success or not - - :raises: Exception. If the status is not success - """ - status = OneLogin_Saml2_Utils.get_status(self.document) - code = status.get('code', None) - if code and code != OneLogin_Saml2_Constants.STATUS_SUCCESS: - splited_code = code.split(':') - printable_code = splited_code.pop() - status_exception_msg = 'The status code of the Response was not Success, was %s' % printable_code - status_msg = status.get('msg', None) - if status_msg: - status_exception_msg += ' -> ' + status_msg - raise Exception(status_exception_msg) -
    -
    [docs] def get_audiences(self): - """ - Gets the audiences - - :returns: The valid audiences for the SAML Response - :rtype: list - """ - audiences = [] - - audience_nodes = self.__query_assertion('/saml:Conditions/saml:AudienceRestriction/saml:Audience') - for audience_node in audience_nodes: - audiences.append(audience_node.text) -
    -
    [docs] def get_issuers(self): - """ - Gets the issuers (from message and from assertion) - - :returns: The issuers - :rtype: list - """ - issuers = [] - - message_issuer_nodes = self.__query('/samlp:Response/saml:Issuer') - if message_issuer_nodes: - issuers.append(message_issuer_nodes[0].text) - - assertion_issuer_nodes = self.__query_assertion('/saml:Issuer') - if assertion_issuer_nodes: - issuers.append(assertion_issuer_nodes[0].text) - - return list(set(issuers)) -
    -
    [docs] def get_nameid_data(self): - """ - Gets the NameID Data provided by the SAML Response from the IdP - - :returns: Name ID Data (Value, Format, NameQualifier, SPNameQualifier) - :rtype: dict - """ - nameid = None - encrypted_id_data_nodes = self.__query_assertion('/saml:Subject/saml:EncryptedID/xenc:EncryptedData') - if encrypted_id_data_nodes: - encrypted_data = encrypted_id_data_nodes[0] - - xmlsec.initialize() - - # Load the key into the xmlsec context - key = self.__settings.get_sp_key() - file_key = OneLogin_Saml2_Utils.write_temp_file(key) # FIXME avoid writing a file - enc_key = xmlsec.Key.load(file_key.name, xmlsec.KeyDataFormatPem, None) - enc_key.name = basename(file_key.name) - file_key.close() - enc_ctx = xmlsec.EncCtx() - enc_ctx.encKey = enc_key - - nameid = OneLogin_Saml2_Utils.decrypt_element(encrypted_data, enc_ctx) - else: - nameid_nodes = self.__query_assertion('/saml:Subject/saml:NameID') - if nameid_nodes: - nameid = nameid_nodes[0] - if nameid is None: - raise Exception('Not NameID found in the assertion of the Response') - - nameid_data = {'Value': nameid.text} - for attr in ['Format', 'SPNameQualifier', 'NameQualifier']: - value = nameid.get(attr, None) - if value: - nameid_data[attr] = value - return nameid_data -
    -
    [docs] def get_nameid(self): - """ - Gets the NameID provided by the SAML Response from the IdP - - :returns: NameID (value) - :rtype: string - """ - nameid_data = self.get_nameid_data() - return nameid_data['Value'] -
    -
    [docs] def get_session_not_on_or_after(self): - """ - Gets the SessionNotOnOrAfter from the AuthnStatement - Could be used to set the local session expiration - - :returns: The SessionNotOnOrAfter value - :rtype: time|None - """ - not_on_or_after = None - authn_statement_nodes = self.__query_assertion('/saml:AuthnStatement[@SessionNotOnOrAfter]') - if authn_statement_nodes: - not_on_or_after = OneLogin_Saml2_Utils.parse_SAML_to_time(authn_statement_nodes[0].get('SessionNotOnOrAfter')) - return not_on_or_after -
    -
    [docs] def get_session_index(self): - """ - Gets the SessionIndex from the AuthnStatement - Could be used to be stored in the local session in order - to be used in a future Logout Request that the SP could - send to the SP, to set what specific session must be deleted - - :returns: The SessionIndex value - :rtype: string|None - """ - session_index = None - authn_statement_nodes = self.__query_assertion('/saml:AuthnStatement[@SessionIndex]') - if authn_statement_nodes: - session_index = authn_statement_nodes[0].get('SessionIndex') - return session_index -
    -
    [docs] def get_attributes(self): - """ - Gets the Attributes from the AttributeStatement element. - EncryptedAttributes are not supported - """ - attributes = {} - attribute_nodes = self.__query_assertion('/saml:AttributeStatement/saml:Attribute') - for attribute_node in attribute_nodes: - attr_name = attribute_node.get('Name') - values = [] - for attr in attribute_node.iterchildren('{%s}AttributeValue' % OneLogin_Saml2_Constants.NSMAP['saml']): - values.append(attr.text) - attributes[attr_name] = values - return attributes -
    -
    [docs] def validate_num_assertions(self): - """ - Verifies that the document only contains a single Assertion (encrypted or not) - - :returns: True if only 1 assertion encrypted or not - :rtype: bool - """ - encrypted_assertion_nodes = self.__query('//saml:EncryptedAssertion') - assertion_nodes = self.__query('//saml:Assertion') - return (len(encrypted_assertion_nodes) + len(assertion_nodes)) == 1 -
    -
    [docs] def validate_timestamps(self): - """ - Verifies that the document is valid according to Conditions Element - - :returns: True if the condition is valid, False otherwise - :rtype: bool - """ - conditions_nodes = self.__query('//saml:Conditions') - for conditions_node in conditions_nodes: - nb_attr = conditions_node.get('NotBefore') - nooa_attr = conditions_node.get('NotOnOrAfter') - if nb_attr and OneLogin_Saml2_Utils.parse_SAML_to_time(nb_attr) > time() + OneLogin_Saml2_Constants.ALOWED_CLOCK_DRIFT: - return False - if nooa_attr and OneLogin_Saml2_Utils.parse_SAML_to_time(nooa_attr) + OneLogin_Saml2_Constants.ALOWED_CLOCK_DRIFT <= time(): - return False - return True -
    - def __query_assertion(self, xpath_expr): - """ - Extracts nodes that match the query from the Assertion - - :param query: Xpath Expresion - :type query: String - - :returns: The queried nodes - :rtype: list - """ - if self.encrypted: - assertion_expr = '/saml:EncryptedAssertion/saml:Assertion' - else: - assertion_expr = '/saml:Assertion' - signature_expr = '/ds:Signature/ds:SignedInfo/ds:Reference' - signed_assertion_query = '/samlp:Response' + assertion_expr + signature_expr - assertion_reference_nodes = self.__query(signed_assertion_query) - - if not assertion_reference_nodes: - # Check if the message is signed - signed_message_query = '/samlp:Response' + signature_expr - message_reference_nodes = self.__query(signed_message_query) - if message_reference_nodes: - id = message_reference_nodes[0].get('URI') - final_query = "/samlp:Response[@ID='%s']/" % id[1:] - else: - final_query = "/samlp:Response/" - final_query += assertion_expr - else: - id = assertion_reference_nodes[0].get('URI') - final_query = '/samlp:Response' + assertion_expr + "[@ID='%s']" % id[1:] - final_query += xpath_expr - return self.__query(final_query) - - def __query(self, query): - """ - Extracts nodes that match the query from the Response - - :param query: Xpath Expresion - :type query: String - - :returns: The queried nodes - :rtype: list - """ - if self.encrypted: - document = self.decrypted_document - else: - document = self.document - return OneLogin_Saml2_Utils.query(document, query) - - def __decrypt_assertion(self, dom): - """ - Decrypts the Assertion - - :raises: Exception if no private key available - :param dom: Encrypted Assertion - :type dom: Element - :returns: Decrypted Assertion - :rtype: Element - """ - key = self.__settings.get_sp_key() - - if not key: - raise Exception('No private key available, check settings') - - # TODO Study how decrypt assertion
    -
    - -
    -
    -
    -
    -
    - - -
    -
    -
    -
    - - - - diff --git a/docs/saml2/_modules/saml2/settings.html b/docs/saml2/_modules/saml2/settings.html deleted file mode 100644 index fe2bf6f5..00000000 --- a/docs/saml2/_modules/saml2/settings.html +++ /dev/null @@ -1,692 +0,0 @@ - - - - - - - - - - saml2.settings — SAML Python library classes and methods - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for saml2.settings

    -# -*- coding: utf-8 -*-
    -
    -
    -from datetime import datetime
    -import json
    -import re
    -from os.path import dirname, exists, join, sep
    -from xml.dom.minidom import Document
    -
    -from saml2.constants import OneLogin_Saml2_Constants
    -from saml2.errors import OneLogin_Saml2_Error
    -from saml2.metadata import OneLogin_Saml2_Metadata
    -from saml2.utils import OneLogin_Saml2_Utils
    -
    -
    -# Regex from Django Software Foundation and individual contributors.
    -# Released under a BSD 3-Clause License
    -url_regex = re.compile(
    -    r'^(?:[a-z0-9\.\-]*)://'  # scheme is validated separately
    -    r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|'  # domain...
    -    r'localhost|'  # localhost...
    -    r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|'  # ...or ipv4
    -    r'\[?[A-F0-9]*:[A-F0-9:]+\]?)'  # ...or ipv6
    -    r'(?::\d+)?'  # optional port
    -    r'(?:/?|[/?]\S+)$', re.IGNORECASE)
    -url_schemes = ['http', 'https', 'ftp', 'ftps']
    -
    -
    -
    [docs]def validate_url(url): - scheme = url.split('://')[0].lower() - if scheme not in url_schemes: - return False - if not bool(url_regex.search(url)): - return False - return True - -
    -
    [docs]class OneLogin_Saml2_Settings: - - def __init__(self, settings=None, custom_base_path=None): - """ - Initializes the settings: - - Sets the paths of the different folders - - Loads settings info from settings file or array/object provided - - :param settings: SAML Toolkit Settings - :type settings: dict|object - """ - self.__paths = {} - self.__strict = False - self.__debug = False - self.__sp = {} - self.__idp = {} - self.__contacts = {} - self.__organization = {} - self.__errors = [] - - self.__load_paths(base_path=custom_base_path) - self.__update_paths(settings) - - if settings is None: - if not self.__load_settings_from_file(): - raise OneLogin_Saml2_Error( - 'Invalid file settings: %s', - OneLogin_Saml2_Error.SETTINGS_INVALID, - ','.join(self.__errors) - ) - self.__add_default_values() - elif isinstance(settings, dict): - if not self.__load_settings_from_dict(settings): - raise OneLogin_Saml2_Error( - 'Invalid dict settings: %s', - OneLogin_Saml2_Error.SETTINGS_INVALID, - ','.join(self.__errors) - ) - else: - raise Exception('Unsupported settings object') - - self.format_idp_cert() - - def __load_paths(self, base_path=None): - """ - Sets the paths of the different folders - """ - if base_path is None: - base_path = dirname(dirname(dirname(__file__))) - base_path += sep - self.__paths = { - 'base': base_path, - 'cert': base_path + 'certs' + sep, - 'lib': base_path + 'lib' + sep, - 'extlib': base_path + 'extlib' + sep, - } - - def __update_paths(self, settings): - """ - Set custom paths if necessary - """ - if not isinstance(settings, dict): - return - - if 'custom_base_path' in settings: - base_path = settings['custom_base_path'] - base_path = join(dirname(__file__), base_path) - self.__load_paths(base_path) - -
    [docs] def get_base_path(self): - """ - Returns base path - - :return: The base toolkit folder path - :rtype: string - """ - return self.__paths['base'] -
    -
    [docs] def get_cert_path(self): - """ - Returns cert path - - :return: The cert folder path - :rtype: string - """ - return self.__paths['cert'] -
    -
    [docs] def get_lib_path(self): - """ - Returns lib path - - :return: The library folder path - :rtype: string - """ - return self.__paths['lib'] -
    -
    [docs] def get_ext_lib_path(self): - """ - Returns external lib path - - :return: The external library folder path - :rtype: string - """ - return self.__paths['extlib'] -
    -
    [docs] def get_schemas_path(self): - """ - Returns schema path - - :return: The schema folder path - :rtype: string - """ - return self.__paths['lib'] + 'schemas/' -
    - def __load_settings_from_dict(self, settings): - """ - Loads settings info from a settings Dict - - :param settings: SAML Toolkit Settings - :type settings: dict - - :returns: True if the settings info is valid - :rtype: boolean - """ - errors = self.check_settings(settings) - if len(errors) == 0: - self.__errors = [] - self.__sp = settings['sp'] - self.__idp = settings['idp'] - - if 'strict' in settings: - self.__strict = settings['strict'] - if 'debug' in settings: - self.__debug = settings['debug'] - if 'security' in settings: - self.__security = settings['security'] - if 'contactPerson' in settings: - self.__contacts = settings['contactPerson'] - if 'organization' in settings: - self.__organization = settings['organization'] - - self.__add_default_values() - return True - - self.__errors = errors - return False - - def __load_settings_from_file(self): - """ - Loads settings info from the settings json file - - :returns: True if the settings info is valid - :rtype: boolean - """ - filename = self.get_base_path() + 'settings.json' - - if not exists(filename): - raise OneLogin_Saml2_Error( - 'Settings file not found: %s', - OneLogin_Saml2_Error.SETTINGS_FILE_NOT_FOUND, - filename - ) - - # In the php toolkit instead of being a json file it is a php file and - # it is directly included - json_data = open(filename, 'r') - settings = json.load(json_data) - json_data.close() - - advanced_filename = self.get_base_path() + 'advanced_settings.json' - if exists(advanced_filename): - json_data = open(advanced_filename, 'r') - settings.update(json.load(json_data)) # Merge settings - json_data.close() - - return self.__load_settings_from_dict(settings) - - def __add_default_values(self): - """ - Add default values if the settings info is not complete - """ - if 'binding' not in self.__sp['assertionConsumerService']: - self.__sp['assertionConsumerService']['binding'] = OneLogin_Saml2_Constants.BINDING_HTTP_POST - if 'singleLogoutService' in self.__sp and 'binding' not in self.__sp['singleLogoutService']: - self.__sp['singleLogoutService']['binding'] = OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT - - # Related to nameID - if 'NameIDFormat' not in self.__sp: - self.__sp['NameIDFormat'] = OneLogin_Saml2_Constants.NAMEID_PERSISTENT - if 'nameIdEncrypted' not in self.__security: - self.__security['nameIdEncrypted'] = False - - # Sign provided - if 'authnRequestsSigned' not in self.__security: - self.__security['authnRequestsSigned'] = False - if 'logoutRequestSigned' not in self.__security: - self.__security['logoutRequestSigned'] = False - if 'logoutResponseSigned' not in self.__security: - self.__security['logoutResponseSigned'] = False - if 'signMetadata' not in self.__security: - self.__security['signMetadata'] = False - - # Sign expected - if 'wantMessagesSigned' not in self.__security: - self.__security['wantMessagesSigned'] = False - if 'wantAssertionsSigned' not in self.__security: - self.__security['wantAssertionsSigned'] = False - - # Encrypt expected - if 'wantAssertionsEncrypted' not in self.__security: - self.__security['wantAssertionsEncrypted'] = False - if 'wantNameIdEncrypted' not in self.__security: - self.__security['wantNameIdEncrypted'] = False - - if 'x509cert' not in self.__idp: - self.__idp['x509cert'] = '' - if 'certFingerprint' not in self.__idp: - self.__idp['certFingerprint'] = '' - -
    [docs] def check_settings(self, settings): - """ - Checks the settings info. - - :param settings: Dict with settings data - :type settings: dict - - :returns: Errors found on the settings data - :rtype: list - """ - assert isinstance(settings, dict) - - errors = [] - if not isinstance(settings, dict) or len(settings) == 0: - errors.append('invalid_syntax') - return errors - - if 'idp' not in settings or len(settings['idp']) == 0: - errors.append('idp_not_found') - else: - idp = settings['idp'] - if 'entityId' not in idp or len(idp['entityId']) == 0: - errors.append('idp_entityId_not_found') - - if ('singleSignOnService' not in idp or - 'url' not in idp['singleSignOnService'] or - len(idp['singleSignOnService']['url']) == 0): - errors.append('idp_sso_not_found') - elif not validate_url(idp['singleSignOnService']['url']): - errors.append('idp_sso_url_invalid') - - if ('singleLogoutService' in idp and - 'url' in idp['singleLogoutService'] and - len(idp['singleLogoutService']['url']) > 0 and - not validate_url(idp['singleLogoutService']['url'])): - errors.append('idp_slo_url_invalid') - - if 'sp' not in settings or len(settings['sp']) == 0: - errors.append('sp_not_found') - else: - sp = settings['sp'] - security = {} - if 'security' in settings: - security = settings['security'] - - if 'entityId' not in sp or len(sp['entityId']) == 0: - errors.append('sp_entityId_not_found') - - if ('assertionConsumerService' not in sp or - 'url' not in sp['assertionConsumerService'] or - len(sp['assertionConsumerService']['url']) == 0): - errors.append('sp_acs_not_found') - elif not validate_url(sp['assertionConsumerService']['url']): - errors.append('sp_acs_url_invalid') - - if ('singleLogoutService' in sp and - 'url' in sp['singleLogoutService'] and - len(sp['singleLogoutService']['url']) > 0 and - not validate_url(sp['singleLogoutService']['url'])): - errors.append('sp_sls_url_invalid') - - if 'signMetadata' in security and isinstance(security['signMetadata'], dict): - if ('keyFileName' not in security['signMetadata'] or - 'certFileName' not in security['signMetadata']): - errors.append('sp_signMetadata_invalid') - - if ((('authnRequestsSigned' in security and security['authnRequestsSigned']) or - ('logoutRequestSigned' in security and security['logoutRequestSigned']) or - ('logoutResponseSigned' in security and security['logoutResponseSigned']) or - ('wantAssertionsEncrypted' in security and security['wantAssertionsEncrypted']) or - ('wantNameIdEncrypted' in security and security['wantNameIdEncrypted'])) and - not self.check_sp_certs()): - errors.append('sp_cert_not_found_and_required') - - exists_X509 = ('idp' in settings and - 'x509cert' in settings['idp'] and - len(settings['idp']['x509cert']) > 0) - exists_fingerprint = ('idp' in settings and - 'certFingerprint' in settings['idp'] and - len(settings['idp']['certFingerprint']) > 0) - if ((('wantAssertionsSigned' in security and security['wantAssertionsSigned']) or - ('wantMessagesSigned' in security and security['wantMessagesSigned'])) and - not(exists_X509 or exists_fingerprint)): - errors.append('idp_cert_or_fingerprint_not_found_and_required') - if ('nameIdEncrypted' in security and security['nameIdEncrypted']) and not exists_X509: - errors.append('idp_cert_not_found_and_required') - - if 'contactPerson' in settings: - types = settings['contactPerson'].keys() - valid_types = ['technical', 'support', 'administrative', 'billing', 'other'] - for t in types: - if t not in valid_types: - errors.append('contact_type_invalid') - break - - for t in settings['contactPerson']: - contact = settings['contactPerson'][t] - if (('givenName' not in contact or len(contact['givenName']) == 0) or - ('emailAddress' not in contact or len(contact['emailAddress']) == 0)): - errors.append('contact_not_enought_data') - break - - if 'organization' in settings: - for o in settings['organization']: - organization = settings['organization'][o] - if (('name' not in organization or len(organization['name']) == 0) or - ('displayname' not in organization or len(organization['displayname']) == 0) or - ('url' not in organization or len(organization['url']) == 0)): - errors.append('organization_not_enought_data') - break - - return errors -
    -
    [docs] def check_sp_certs(self): - """ - Checks if the x509 certs of the SP exists and are valid. - - :returns: If the x509 certs of the SP exists and are valid - :rtype: boolean - """ - key = self.get_sp_key() - cert = self.get_sp_cert() - return key is not None and cert is not None -
    -
    [docs] def get_sp_key(self): - """ - Returns the x509 private key of the SP. - - :returns: SP private key - :rtype: string - """ - key = None - key_file = self.__paths['cert'] + 'sp.key' - - if exists(key_file): - f = open(key_file, 'r') - key = f.read() - f.close() - - return key -
    -
    [docs] def get_sp_cert(self): - """ - Returns the x509 public cert of the SP. - - :returns: SP public cert - :rtype: string - """ - cert = None - cert_file = self.__paths['cert'] + 'sp.crt' - - if exists(cert_file): - f = open(cert_file, 'r') - cert = f.read() - f.close() - - return cert -
    -
    [docs] def get_idp_data(self): - """ - Gets the IdP data. - - :returns: IdP info - :rtype: dict - """ - return self.__idp -
    -
    [docs] def get_sp_data(self): - """ - Gets the SP data. - - :returns: SP info - :rtype: dict - """ - return self.__sp -
    -
    [docs] def get_security_data(self): - """ - Gets security data. - - :returns: Security info - :rtype: dict - """ - return self.__security -
    -
    [docs] def get_contacts(self): - """ - Gets contact data. - - :returns: Contacts info - :rtype: dict - """ - return self.__contacts -
    -
    [docs] def get_organization(self): - """ - Gets organization data. - - :returns: Organization info - :rtype: dict - """ - return self.__organization -
    -
    [docs] def get_sp_metadata(self): - """ - Gets the SP metadata. The XML representation. - - :returns: SP metadata (xml) - :rtype: string - """ - metadata = OneLogin_Saml2_Metadata.builder( - self.__sp, self.__security['authnRequestsSigned'], - self.__security['wantAssertionsSigned'], None, None, - self.get_contacts(), self.get_organization() - ) - cert = self.get_sp_cert() - if cert is not None: - metadata = OneLogin_Saml2_Metadata.add_x509_key_descriptors(metadata, cert) - - # Sign metadata - if 'signMetadata' in self.__security and self.__security['signMetadata'] is not False: - if self.__security['signMetadata'] is True: - key_file_name = 'sp.key' - cert_file_name = 'sp.crt' - else: - if ('keyFileName' not in self.__security['signMetadata'] or - 'certFileName' not in self.__security['signMetadata']): - raise OneLogin_Saml2_Error( - 'Invalid Setting: signMetadata value of the sp is not valid', - OneLogin_Saml2_Error.SETTINGS_INVALID_SYNTAX - ) - key_file_name = self.__security['signMetadata']['keyFileName'] - cert_file_name = self.__security['signMetadata']['certFileName'] - key_metadata_file = self.__paths['cert'] + key_file_name - cert_metadata_file = self.__paths['cert'] + cert_file_name - - if not exists(key_metadata_file): - raise OneLogin_Saml2_Error( - 'Private key file not found: %s', - OneLogin_Saml2_Error.PRIVATE_KEY_FILE_NOT_FOUND, - key_metadata_file - ) - - if not exists(cert_metadata_file): - raise OneLogin_Saml2_Error( - 'Public cert file not found: %s', - OneLogin_Saml2_Error.PUBLIC_CERT_FILE_NOT_FOUND, - cert_metadata_file - ) - - f = open(key_metadata_file, 'r') - key_metadata = f.read() - f.close() - f = open(cert_metadata_file, 'r') - cert_metadata = f.read() - f.close() - metadata = OneLogin_Saml2_Metadata.sign_metadata(metadata, key_metadata, cert_metadata) - - return metadata -
    -
    [docs] def validate_metadata(self, xml): - """ - Validates an XML SP Metadata. - - :param xml: Metadata's XML that will be validate - :type xml: string - - :returns: The list of found errors - :rtype: list - """ - - assert isinstance(xml, basestring) - - if len(xml) == 0: - raise Exception('Empty string supplied as input') - - errors = [] - res = OneLogin_Saml2_Utils.validate_xml(xml, 'saml-schema-metadata-2.0.xsd', self.__debug) - if not isinstance(res, Document): - errors.append(res) - else: - dom = res - element = dom.documentElement - if element.tagName != 'md:EntityDescriptor': - errors.append('noEntityDescriptor_xml') - else: - valid_until = cache_duration = expire_time = None - - if element.hasAttribute('validUntil'): - valid_until = OneLogin_Saml2_Utils.parse_SAML_to_time(element.getAttribute('validUntil')) - if element.hasAttribute('cacheDuration'): - cache_duration = element.getAttribute('cacheDuration') - - expire_time = OneLogin_Saml2_Utils.get_expire_time(cache_duration, valid_until) - if expire_time is not None and int(datetime.now().strftime('%s')) > int(expire_time): - errors.append('expired_xml') - - return errors -
    -
    [docs] def format_idp_cert(self): - """ - Formats the IdP cert. - """ - if self.__idp['x509cert'] is not None: - self.__idp['x509cert'] = OneLogin_Saml2_Utils.format_cert(self.__idp['x509cert']) -
    -
    [docs] def get_errors(self): - """ - Returns an array with the errors, the array is empty when the settings is ok. - - :returns: Errors - :rtype: list - """ - return self.__errors -
    -
    [docs] def set_strict(self, value): - """ - Activates or deactivates the strict mode. - - :param xml: Strict parameter - :type xml: boolean - """ - assert isinstance(value, bool) - - self.__strict = value -
    -
    [docs] def is_strict(self): - """ - Returns if the 'strict' mode is active. - - :returns: Strict parameter - :rtype: boolean - """ - return self.__strict -
    -
    [docs] def is_debug_active(self): - """ - Returns if the debug is active. - - :returns: Debug parameter - :rtype: boolean - """ - return self.__debug
    -
    - -
    -
    -
    -
    -
    - - -
    -
    -
    -
    - - - - diff --git a/docs/saml2/_modules/saml2/utils.html b/docs/saml2/_modules/saml2/utils.html deleted file mode 100644 index 63cc42a3..00000000 --- a/docs/saml2/_modules/saml2/utils.html +++ /dev/null @@ -1,802 +0,0 @@ - - - - - - - - - - saml2.utils — SAML Python library classes and methods - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for saml2.utils

    -# -*- coding: utf-8 -*-
    -
    -
    -import base64
    -from datetime import datetime
    -import calendar
    -from hashlib import sha1
    -from isodate import parse_duration as duration_parser
    -from lxml import etree
    -from lxml.etree import ElementBase
    -from os.path import basename, dirname, join
    -import re
    -from sys import stderr
    -from tempfile import NamedTemporaryFile
    -from textwrap import wrap
    -from urllib import quote_plus
    -from uuid import uuid4
    -from xml.dom.minidom import Document, parseString, Element
    -from xml.etree.ElementTree import tostring
    -import zlib
    -
    -import dm.xmlsec.binding as xmlsec
    -from dm.xmlsec.binding.tmpl import EncData, Signature
    -from M2Crypto import X509
    -
    -from saml2.constants import OneLogin_Saml2_Constants
    -from saml2.errors import OneLogin_Saml2_Error
    -
    -
    -def _(msg):
    -    # Fixme Add i18n support
    -    return msg
    -
    -
    -
    [docs]class OneLogin_Saml2_Utils: - - @staticmethod -
    [docs] def decode_base64_and_inflate(value): - """ base64 decodes and then inflates according to RFC1951 - :param value: a deflated and encoded string - :return: the string after decoding and inflating - """ - - return zlib.decompress(base64.b64decode(value), -15) -
    - @staticmethod -
    [docs] def deflate_and_base64_encode(value): - """ - Deflates and the base64 encodes a string - :param value: The string to deflate and encode - :return: The deflated and encoded string - """ - return base64.b64encode(zlib.compress(value)[2:-4]) -
    - @staticmethod -
    [docs] def validate_xml(xml, schema, debug=False): - """ - """ - assert (isinstance(xml, basestring) or isinstance(xml, Document)) - assert isinstance(schema, basestring) - - if isinstance(xml, Document): - xml = xml.toxml() - - # Switch to lxml for schema validation - try: - dom = etree.fromstring(xml) - except Exception: - return 'unloaded_xml' - - schema_file = join(dirname(__file__), 'schemas', schema) - f = open(schema_file, 'r') - schema_doc = etree.parse(f) - f.close() - xmlschema = etree.XMLSchema(schema_doc) - - if not xmlschema.validate(dom): - xml_errors = [xmlschema.error_log] - if debug: - stderr.write('Errors validating the metadata') - stderr.write(':\n\n') - for error in xml_errors: - stderr.write('%s\n' % error.message) - - return 'invalid_xml' - - return parseString(etree.tostring(dom)) -
    - @staticmethod -
    [docs] def format_cert(cert, heads=True): - """ - Returns a x509 cert (adding header & footer if required). - - :param cert: A x509 unformated cert - :type: string - - :param heads: True if we want to include head and footer - :type: boolean - - :returns: Formated cert - :rtype: string - """ - x509_cert = cert.replace('\x0D', '') - x509_cert = x509_cert.replace('\r', '') - x509_cert = x509_cert.replace('\n', '') - if len(x509_cert) > 0: - x509_cert = x509_cert.replace('-----BEGIN CERTIFICATE-----', '') - x509_cert = x509_cert.replace('-----END CERTIFICATE-----', '') - x509_cert = x509_cert.replace(' ', '') - - if heads: - x509_cert = '-----BEGIN CERTIFICATE-----\n' + '\n'.join(wrap(x509_cert, 64)) + '\n-----END CERTIFICATE-----\n' - - return x509_cert -
    - @staticmethod -
    [docs] def redirect(url, parameters={}, request_data={}): - """ - Executes a redirection to the provided url (or return the target url). - - :param url: The target url - :type: string - - :param parameters: Extra parameters to be passed as part of the url - :type: dict - - :param request_data: The request as a dict - :type: dict - - :returns: Url - :rtype: string - """ - assert isinstance(url, basestring) - assert isinstance(parameters, dict) - - if url.startswith('/'): - url = '%s%s' % (OneLogin_Saml2_Utils.get_self_url_host(request_data), url) - - # Verify that the URL is to a http or https site. - if re.search('^https?://', url) is None: - raise OneLogin_Saml2_Error( - 'Redirect to invalid URL: ' + url, - OneLogin_Saml2_Error.REDIRECT_INVALID_URL - ) - - # Add encoded parameters - if url.find('?') < 0: - param_prefix = '?' - else: - param_prefix = '&' - - for name, value in parameters.items(): - - if value is None: - param = urlencode(name) - elif isinstance(value, list): - param = '' - for val in value: - param += quote_plus(name) + '[]=' + quote_plus(val) + '&' - if len(param) > 0: - param = param[0:-1] - else: - param = quote_plus(name) + '=' + quote_plus(value) - - url += param_prefix + param - param_prefix = '&' - - return url -
    - @staticmethod -
    [docs] def get_self_url_host(request_data): - """ - Returns the protocol + the current host + the port (if different than - common ports). - - :param request_data: The request as a dict - :type: dict - - :return: Url - :rtype: string - """ - current_host = OneLogin_Saml2_Utils.get_self_host(request_data) - port = '' - if OneLogin_Saml2_Utils.is_https(request_data): - protocol = 'https' - else: - protocol = 'http' - - if 'server_port' in request_data: - port_number = request_data['server_port'] - port = ':' + port_number - - if protocol == 'http' and port_number == '80': - port = '' - elif protocol == 'https' and port_number == '443': - port = '' - - return '%s://%s%s' % (protocol, current_host, port) -
    - @staticmethod -
    [docs] def get_self_host(request_data): - """ - Returns the current host. - - :param request_data: The request as a dict - :type: dict - - :return: The current host - :rtype: string - """ - if 'http_host' in request_data: - current_host = request_data['http_host'] - elif 'server_name' in request_data: - current_host = request_data['server_name'] - else: - raise Exception('No hostname defined') - - if ':' in current_host: - current_host_data = current_host.split(':') - possible_port = current_host_data[-1] - try: - possible_port = float(possible_port) - current_host = current_host_data[0] - except ValueError: - current_host = ':'.join(current_host_data) - - return current_host -
    - @staticmethod -
    [docs] def is_https(request_data): - """ - Checks if https or http. - - :param request_data: The request as a dict - :type: dict - - :return: False if https is not active - :rtype: boolean - """ - is_https = 'https' in request_data and request_data['https'] != 'off' - is_https = is_https or ('server_port' in request_data and request_data['server_port'] == '443') - return is_https -
    - @staticmethod -
    [docs] def get_self_url_no_query(request_data): - """ - Returns the URL of the current host + current view. - - :param request_data: The request as a dict - :type: dict - - :return: The url of current host + current view - :rtype: string - """ - self_url_host = OneLogin_Saml2_Utils.get_self_url_host(request_data) - script_name = request_data['script_name'] - if script_name[0] != '/': - script_name = '/' + script_name - self_url_host += script_name - if 'path_info' in request_data: - self_url_host += request_data['path_info'] - - return self_url_host -
    - @staticmethod -
    [docs] def get_self_url(request_data): - """ - Returns the URL of the current host + current view + query. - - :param request_data: The request as a dict - :type: dict - - :return: The url of current host + current view + query - :rtype: string - """ - self_url_host = OneLogin_Saml2_Utils.get_self_url_host(request_data) - - request_uri = '' - if 'request_uri' in request_data: - request_uri = request_data['request_uri'] - if not request_uri.startswith('/'): - match = re.search('^https?://[^/]*(/.*)', request_uri) - if match is not None: - request_uri = match.groups()[0] - - return self_url_host + request_uri -
    - @staticmethod -
    [docs] def generate_unique_id(): - """ - Generates an unique string (used for example as ID for assertions). - - :return: A unique string - :rtype: string - """ - return 'ONELOGIN_%s' % sha1(uuid4().hex).hexdigest() -
    - @staticmethod -
    [docs] def parse_time_to_SAML(time): - """ - Converts a UNIX timestamp to SAML2 timestamp on the form - yyyy-mm-ddThh:mm:ss(\.s+)?Z. - - :param time: The time we should convert (DateTime). - :type: string - - :return: SAML2 timestamp. - :rtype: string - """ - data = datetime.utcfromtimestamp(float(time)) - return data.strftime('%Y-%m-%dT%H:%M:%SZ') -
    - @staticmethod -
    [docs] def parse_SAML_to_time(timestr): - """ - Converts a SAML2 timestamp on the form yyyy-mm-ddThh:mm:ss(\.s+)?Z - to a UNIX timestamp. The sub-second part is ignored. - - :param time: The time we should convert (SAML Timestamp). - :type: string - - :return: Converted to a unix timestamp. - :rtype: int - """ - try: - data = datetime.strptime(timestr, '%Y-%m-%dT%H:%M:%SZ') - except ValueError: - data = datetime.strptime(timestr, '%Y-%m-%dT%H:%M:%S.%fZ') - return calendar.timegm(data.utctimetuple()) -
    - @staticmethod -
    [docs] def parse_duration(duration, timestamp=None): - """ - Interprets a ISO8601 duration value relative to a given timestamp. - - :param duration: The duration, as a string. - :type: string - - :param timestamp: The unix timestamp we should apply the duration to. - Optional, default to the current time. - :type: string - - :return: The new timestamp, after the duration is applied. - :rtype: int - """ - assert isinstance(duration, basestring) - assert (timestamp is None or isinstance(timestamp, int)) - - timedelta = duration_parser(duration) - if timestamp is None: - data = datetime.utcnow() + timedelta - else: - data = datetime.utcfromtimestamp(timestamp) + timedelta - return calendar.timegm(data.utctimetuple()) -
    - @staticmethod -
    [docs] def get_expire_time(cache_duration=None, valid_until=None): - """ - Compares 2 dates and returns the earliest. - - :param cache_duration: The duration, as a string. - :type: string - - :param valid_until: The valid until date, as a string or as a timestamp - :type: string - - :return: The expiration time. - :rtype: int - """ - expire_time = None - - if cache_duration is not None: - expire_time = OneLogin_Saml2_Utils.parse_duration(cache_duration) - - if valid_until is not None: - if isinstance(valid_until, int): - valid_until_time = valid_until - else: - valid_until_time = OneLogin_Saml2_Utils.parse_SAML_to_time(valid_until) - if expire_time is None or expire_time > valid_until_time: - expire_time = valid_until_time - - if expire_time is not None: - return '%d' % expire_time - return None -
    - @staticmethod -
    [docs] def query(dom, query, context=None): - """ - Extracts nodes that match the query from the Element - - :param dom: The root of the lxml objet - :type: Element - - :param query: Xpath Expresion - :type: string - - :param context: Context Node - :type: DOMElement - - :returns: The queried nodes - :rtype: list - """ - if context is None: - return dom.xpath(query, namespaces=OneLogin_Saml2_Constants.NSMAP) - else: - return context.xpath(query, namespaces=OneLogin_Saml2_Constants.NSMAP) -
    - @staticmethod -
    [docs] def delete_local_session(callback=None): - """ - Deletes the local session. - """ - - if callback is not None: - callback() -
    - @staticmethod -
    [docs] def calculate_x509_fingerprint(x509_cert): - """ - Calculates the fingerprint of a x509cert. - - :param x509_cert: x509 cert - :type: string - - :returns: Formated fingerprint - :rtype: string - """ - assert isinstance(x509_cert, basestring) - - lines = x509_cert.split('\n') - data = '' - - for line in lines: - # Remove '\r' from end of line if present. - line = line.rstrip() - if line == '-----BEGIN CERTIFICATE-----': - # Delete junk from before the certificate. - data = '' - elif line == '-----END CERTIFICATE-----': - # Ignore data after the certificate. - break - elif line == '-----BEGIN PUBLIC KEY-----' or line == '-----BEGIN RSA PRIVATE KEY-----': - # This isn't an X509 certificate. - return None - else: - # Append the current line to the certificate data. - data += line - # "data" now contains the certificate as a base64-encoded string. The - # fingerprint of the certificate is the sha1-hash of the certificate. - return sha1(base64.b64decode(data)).hexdigest().lower() -
    - @staticmethod -
    [docs] def format_finger_print(fingerprint): - """ - Formates a fingerprint. - - :param fingerprint: fingerprint - :type: string - - :returns: Formated fingerprint - :rtype: string - """ - formated_fingerprint = fingerprint.replace(':', '') - return formated_fingerprint.lower() -
    - @staticmethod -
    [docs] def generate_name_id(value, sp_nq, sp_format, key=None): - """ - Generates a nameID. - - :param value: fingerprint - :type: string - - :param sp_nq: SP Name Qualifier - :type: string - - :param sp_format: SP Format - :type: string - - :param key: SP Key to encrypt the nameID - :type: string - - :returns: DOMElement | XMLSec nameID - :rtype: string - """ - doc = Document() - - name_id = doc.createElement('saml:NameID') - name_id.setAttribute('SPNameQualifier', sp_nq) - name_id.setAttribute('Format', sp_format) - name_id.appendChild(doc.createTextNode(value)) - - doc.appendChild(name_id) - - if key is not None: - xmlsec.initialize() - - # Load the private key - mngr = xmlsec.KeysMngr() - key = OneLogin_Saml2_Utils.format_cert(key, heads=False) - file_key = OneLogin_Saml2_Utils.write_temp_file(key) - key_data = xmlsec.Key.load(file_key.name, xmlsec.KeyDataFormatPem, None) - key_data.name = key_name = basename(file_key.name) - mngr.addKey(key_data) - file_key.close() - - # Prepare for encryption - enc_data = EncData(xmlsec.TransformAes128Cbc, type=xmlsec.TypeEncElement) - enc_data.ensureCipherValue() - key_info = enc_data.ensureKeyInfo() - enc_key = key_info.addEncryptedKey(xmlsec.TransformRsaPkcs1) - enc_key.ensureCipherValue() - enc_key_info = enc_key.ensureKeyInfo() - enc_key_info.addKeyName(key_name) - - # Encrypt! - enc_ctx = xmlsec.EncCtx(mngr) - enc_ctx.enc_key = xmlsec.Key.generate(xmlsec.KeyDataAes, 128, xmlsec.KeyDataTypeSession) - ed = enc_ctx.encryptXml(enc_data, doc.getroot()) - - # Build XML with encrypted data - newdoc = Document() - encrypted_id = newdoc.createElement('saml:EncryptedID') - newdoc.appendChild(encrypted_id) - encrypted_id.appendChild(encrypted_id.ownerDocument.importNode(ed, True)) - - return newdoc.saveXML(encrypted_id) - else: - return doc.saveXML(name_id) -
    - @staticmethod -
    [docs] def get_status(dom): - """ - Gets Status from a Response. - - :param dom: The Response as XML - :type: Document - - :returns: The Status, an array with the code and a message. - :rtype: dict - """ - status = {} - - status_entry = OneLogin_Saml2_Utils.query(dom, '/samlp:Response/samlp:Status') - if len(status_entry) == 0: - raise Exception('Missing Status on response') - - code_entry = OneLogin_Saml2_Utils.query(dom, '/samlp:Response/samlp:Status/samlp:StatusCode', status_entry[0]) - if len(code_entry) == 0: - raise Exception('Missing Status Code on response') - code = code_entry[0].values()[0] - status['code'] = code - - message_entry = OneLogin_Saml2_Utils.query(dom, '/samlp:Response/samlp:Status/samlp:StatusMessage', status_entry[0]) - if len(message_entry) == 0: - status['msg'] = '' - else: - status['msg'] = message_entry[0].text - - return status -
    - @staticmethod -
    [docs] def decrypt_element(encrypted_data, enc_ctx): - """ - Decrypts an encrypted element. - - :param encrypted_data: The encrypted data. - :type: DOMElement - - :param enc_ctx: The encryption context. - :type: Encryption Context - - :returns: The decrypted element. - :rtype: DOMElement - """ - if isinstance(encrypted_data, Element): - # Minidom element - encrypted_data = etree.fromstring(encrypted_data.toxml()) - - decrypted = enc_ctx.decrypt(encrypted_data) - if isinstance(decrypted, ElementBase): - # lxml element, decrypted xml data - return tostring(decrypted.getroottree()) - else: - # decrypted binary data - return decrypted -
    - @staticmethod -
    [docs] def write_temp_file(content): - """ - Writes some content into a temporary file and returns it. - - :param content: The file content - :type: string - - :returns: The temporary file - :rtype: file-like object - """ - f = NamedTemporaryFile(delete=True) - f.file.write(content) - f.file.flush() - return f -
    - @staticmethod -
    [docs] def add_sign(xml, key, cert): - """ - Adds signature key and senders certificate to an element (Message or - Assertion). - - :param xml: The element we should sign - :type: string | Document - - :param key: The private key - :type: string - - :param cert: The public - :type: string - """ - if isinstance(xml, Document): - dom = xml - else: - if xml == '': - raise Exception('Empty string supplied as input') - - try: - dom = parseString(xml) - except Exception: - raise Exception('Error parsing xml string') - - xmlsec.initialize() - - # TODO the key and cert could be file descriptors instead - # Load the private key. - file_key = OneLogin_Saml2_Utils.write_temp_file(key) - sign_key = xmlsec.Key.load(file_key.name, xmlsec.KeyDataFormatPem, None) - file_key.close() - # Add the certificate to the signature. - file_cert = OneLogin_Saml2_Utils.write_temp_file(cert) - sign_key.loadCert(file_cert.name, xmlsec.KeyDataFormatPem) - file_cert.close() - - # Get the EntityDescriptor node we should sign. - root_node = dom.firstChild - - # Sign the metadata with our private key. - signature = Signature(xmlsec.TransformExclC14N, xmlsec.TransformRsaSha1) - ref = signature.addReference(xmlsec.TransformSha1) - ref.addTransform(xmlsec.TransformEnveloped) - - key_info = signature.ensureKeyInfo() - key_info.addX509Data() - - dsig_ctx = xmlsec.DSigCtx() - dsig_ctx.signKey = sign_key - dsig_ctx.sign(signature) - - signature = tostring(signature).replace('ns0:', 'ds:').replace(':ns0', ':ds') - signature = parseString(signature).firstChild - - insert_before = root_node.getElementsByTagName('saml:Issuer') - if len(insert_before) > 0: - insert_before = insert_before[0].nextSibling - else: - insert_before = root_node.firstChild.nextSibling.nextSibling - root_node.insertBefore(signature, insert_before) - - return dom.toxml() -
    - @staticmethod -
    [docs] def validate_sign(xml, cert=None, fingerprint=None): - """ - Validates a signature (Message or Assertion). - - :param xml: The element we should validate - :type: string | Document - - :param cert: The pubic cert - :type: string - - :param fingerprint: The fingerprint of the public cert - :type: string - """ - if isinstance(xml, Document): - dom = etree.fromstring(xml.toxml()) - else: - if xml == '': - raise Exception('Empty string supplied as input') - - try: - dom = etree.fromstring(xml) - except Exception: - raise Exception('Error parsing xml string') - - xmlsec.initialize() - - # Find signature in the dom - signature_node = OneLogin_Saml2_Utils.query(dom, 'ds:Signature')[0] - - # Prepare context and load cert into it - dsig_ctx = xmlsec.DSigCtx() - sign_cert = X509.load_cert_string(str(cert), X509.FORMAT_PEM) - pub_key = sign_cert.get_pubkey().get_rsa() - sign_key = xmlsec.Key.loadMemory(pub_key.as_pem(cipher=None), - xmlsec.KeyDataFormatPem) - dsig_ctx.signKey = sign_key - - # Verify signature - dsig_ctx.verify(signature_node)
    -
    - -
    -
    -
    -
    -
    - - -
    -
    -
    -
    - - - - diff --git a/docs/saml2/_sources/index.txt b/docs/saml2/_sources/index.txt deleted file mode 100644 index 50ca26a4..00000000 --- a/docs/saml2/_sources/index.txt +++ /dev/null @@ -1,23 +0,0 @@ -.. saml2 documentation master file, created by - sphinx-quickstart on Thu Oct 23 03:29:00 2014. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -Welcome to SAML Python library documentation -===================================================== - -Contents: - -.. toctree:: - :maxdepth: 4 - - saml2 - - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` - diff --git a/docs/saml2/_sources/saml2.txt b/docs/saml2/_sources/saml2.txt deleted file mode 100644 index bf444d4d..00000000 --- a/docs/saml2/_sources/saml2.txt +++ /dev/null @@ -1,83 +0,0 @@ -OneLogin saml2 Module -====================== - -:mod:`auth` Class ------------------- - -.. automodule:: saml2.auth - :members: - :undoc-members: - :show-inheritance: - -:mod:`authn_request` Class ---------------------------- - -.. automodule:: saml2.authn_request - :members: - :undoc-members: - :show-inheritance: - -:mod:`constants` Class ------------------------ - -.. automodule:: saml2.constants - :members: - :undoc-members: - :show-inheritance: - -:mod:`errors` Class --------------------- - -.. automodule:: saml2.errors - :members: - :undoc-members: - :show-inheritance: - -:mod:`logout_request` Class ----------------------------- - -.. automodule:: saml2.logout_request - :members: - :undoc-members: - :show-inheritance: - -:mod:`logout_response` Class ------------------------------ - -.. automodule:: saml2.logout_response - :members: - :undoc-members: - :show-inheritance: - -:mod:`metadata` Class ----------------------- - -.. automodule:: saml2.metadata - :members: - :undoc-members: - :show-inheritance: - -:mod:`response` Class ----------------------- - -.. automodule:: saml2.response - :members: - :undoc-members: - :show-inheritance: - -:mod:`settings` Class ----------------------- - -.. automodule:: saml2.settings - :members: - :undoc-members: - :show-inheritance: - -:mod:`utils` Class -------------------- - -.. automodule:: saml2.utils - :members: - :undoc-members: - :show-inheritance: - diff --git a/docs/saml2/_static/ajax-loader.gif b/docs/saml2/_static/ajax-loader.gif deleted file mode 100644 index 61faf8cab23993bd3e1560bff0668bd628642330..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 673 zcmZ?wbhEHb6krfw_{6~Q|Nno%(3)e{?)x>&1u}A`t?OF7Z|1gRivOgXi&7IyQd1Pl zGfOfQ60;I3a`F>X^fL3(@);C=vM_KlFfb_o=k{|A33hf2a5d61U}gjg=>Rd%XaNQW zW@Cw{|b%Y*pl8F?4B9 zlo4Fz*0kZGJabY|>}Okf0}CCg{u4`zEPY^pV?j2@h+|igy0+Kz6p;@SpM4s6)XEMg z#3Y4GX>Hjlml5ftdH$4x0JGdn8~MX(U~_^d!Hi)=HU{V%g+mi8#UGbE-*ao8f#h+S z2a0-5+vc7MU$e-NhmBjLIC1v|)9+Im8x1yacJ7{^tLX(ZhYi^rpmXm0`@ku9b53aN zEXH@Y3JaztblgpxbJt{AtE1ad1Ca>{v$rwwvK(>{m~Gf_=-Ro7Fk{#;i~+{{>QtvI yb2P8Zac~?~=sRA>$6{!(^3;ZP0TPFR(G_-UDU(8Jl0?(IXu$~#4A!880|o%~Al1tN diff --git a/docs/saml2/_static/basic.css b/docs/saml2/_static/basic.css deleted file mode 100644 index 43e8bafa..00000000 --- a/docs/saml2/_static/basic.css +++ /dev/null @@ -1,540 +0,0 @@ -/* - * basic.css - * ~~~~~~~~~ - * - * Sphinx stylesheet -- basic theme. - * - * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -/* -- main layout ----------------------------------------------------------- */ - -div.clearer { - clear: both; -} - -/* -- relbar ---------------------------------------------------------------- */ - -div.related { - width: 100%; - font-size: 90%; -} - -div.related h3 { - display: none; -} - -div.related ul { - margin: 0; - padding: 0 0 0 10px; - list-style: none; -} - -div.related li { - display: inline; -} - -div.related li.right { - float: right; - margin-right: 5px; -} - -/* -- sidebar --------------------------------------------------------------- */ - -div.sphinxsidebarwrapper { - padding: 10px 5px 0 10px; -} - -div.sphinxsidebar { - float: left; - width: 230px; - margin-left: -100%; - font-size: 90%; -} - -div.sphinxsidebar ul { - list-style: none; -} - -div.sphinxsidebar ul ul, -div.sphinxsidebar ul.want-points { - margin-left: 20px; - list-style: square; -} - -div.sphinxsidebar ul ul { - margin-top: 0; - margin-bottom: 0; -} - -div.sphinxsidebar form { - margin-top: 10px; -} - -div.sphinxsidebar input { - border: 1px solid #98dbcc; - font-family: sans-serif; - font-size: 1em; -} - -div.sphinxsidebar #searchbox input[type="text"] { - width: 170px; -} - -div.sphinxsidebar #searchbox input[type="submit"] { - width: 30px; -} - -img { - border: 0; -} - -/* -- search page ----------------------------------------------------------- */ - -ul.search { - margin: 10px 0 0 20px; - padding: 0; -} - -ul.search li { - padding: 5px 0 5px 20px; - background-image: url(file.png); - background-repeat: no-repeat; - background-position: 0 7px; -} - -ul.search li a { - font-weight: bold; -} - -ul.search li div.context { - color: #888; - margin: 2px 0 0 30px; - text-align: left; -} - -ul.keywordmatches li.goodmatch a { - font-weight: bold; -} - -/* -- index page ------------------------------------------------------------ */ - -table.contentstable { - width: 90%; -} - -table.contentstable p.biglink { - line-height: 150%; -} - -a.biglink { - font-size: 1.3em; -} - -span.linkdescr { - font-style: italic; - padding-top: 5px; - font-size: 90%; -} - -/* -- general index --------------------------------------------------------- */ - -table.indextable { - width: 100%; -} - -table.indextable td { - text-align: left; - vertical-align: top; -} - -table.indextable dl, table.indextable dd { - margin-top: 0; - margin-bottom: 0; -} - -table.indextable tr.pcap { - height: 10px; -} - -table.indextable tr.cap { - margin-top: 10px; - background-color: #f2f2f2; -} - -img.toggler { - margin-right: 3px; - margin-top: 3px; - cursor: pointer; -} - -div.modindex-jumpbox { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 1em 0 1em 0; - padding: 0.4em; -} - -div.genindex-jumpbox { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 1em 0 1em 0; - padding: 0.4em; -} - -/* -- general body styles --------------------------------------------------- */ - -a.headerlink { - visibility: hidden; -} - -h1:hover > a.headerlink, -h2:hover > a.headerlink, -h3:hover > a.headerlink, -h4:hover > a.headerlink, -h5:hover > a.headerlink, -h6:hover > a.headerlink, -dt:hover > a.headerlink { - visibility: visible; -} - -div.body p.caption { - text-align: inherit; -} - -div.body td { - text-align: left; -} - -.field-list ul { - padding-left: 1em; -} - -.first { - margin-top: 0 !important; -} - -p.rubric { - margin-top: 30px; - font-weight: bold; -} - -img.align-left, .figure.align-left, object.align-left { - clear: left; - float: left; - margin-right: 1em; -} - -img.align-right, .figure.align-right, object.align-right { - clear: right; - float: right; - margin-left: 1em; -} - -img.align-center, .figure.align-center, object.align-center { - display: block; - margin-left: auto; - margin-right: auto; -} - -.align-left { - text-align: left; -} - -.align-center { - text-align: center; -} - -.align-right { - text-align: right; -} - -/* -- sidebars -------------------------------------------------------------- */ - -div.sidebar { - margin: 0 0 0.5em 1em; - border: 1px solid #ddb; - padding: 7px 7px 0 7px; - background-color: #ffe; - width: 40%; - float: right; -} - -p.sidebar-title { - font-weight: bold; -} - -/* -- topics ---------------------------------------------------------------- */ - -div.topic { - border: 1px solid #ccc; - padding: 7px 7px 0 7px; - margin: 10px 0 10px 0; -} - -p.topic-title { - font-size: 1.1em; - font-weight: bold; - margin-top: 10px; -} - -/* -- admonitions ----------------------------------------------------------- */ - -div.admonition { - margin-top: 10px; - margin-bottom: 10px; - padding: 7px; -} - -div.admonition dt { - font-weight: bold; -} - -div.admonition dl { - margin-bottom: 0; -} - -p.admonition-title { - margin: 0px 10px 5px 0px; - font-weight: bold; -} - -div.body p.centered { - text-align: center; - margin-top: 25px; -} - -/* -- tables ---------------------------------------------------------------- */ - -table.docutils { - border: 0; - border-collapse: collapse; -} - -table.docutils td, table.docutils th { - padding: 1px 8px 1px 5px; - border-top: 0; - border-left: 0; - border-right: 0; - border-bottom: 1px solid #aaa; -} - -table.field-list td, table.field-list th { - border: 0 !important; -} - -table.footnote td, table.footnote th { - border: 0 !important; -} - -th { - text-align: left; - padding-right: 5px; -} - -table.citation { - border-left: solid 1px gray; - margin-left: 1px; -} - -table.citation td { - border-bottom: none; -} - -/* -- other body styles ----------------------------------------------------- */ - -ol.arabic { - list-style: decimal; -} - -ol.loweralpha { - list-style: lower-alpha; -} - -ol.upperalpha { - list-style: upper-alpha; -} - -ol.lowerroman { - list-style: lower-roman; -} - -ol.upperroman { - list-style: upper-roman; -} - -dl { - margin-bottom: 15px; -} - -dd p { - margin-top: 0px; -} - -dd ul, dd table { - margin-bottom: 10px; -} - -dd { - margin-top: 3px; - margin-bottom: 10px; - margin-left: 30px; -} - -dt:target, .highlighted { - background-color: #fbe54e; -} - -dl.glossary dt { - font-weight: bold; - font-size: 1.1em; -} - -.field-list ul { - margin: 0; - padding-left: 1em; -} - -.field-list p { - margin: 0; -} - -.refcount { - color: #060; -} - -.optional { - font-size: 1.3em; -} - -.versionmodified { - font-style: italic; -} - -.system-message { - background-color: #fda; - padding: 5px; - border: 3px solid red; -} - -.footnote:target { - background-color: #ffa; -} - -.line-block { - display: block; - margin-top: 1em; - margin-bottom: 1em; -} - -.line-block .line-block { - margin-top: 0; - margin-bottom: 0; - margin-left: 1.5em; -} - -.guilabel, .menuselection { - font-family: sans-serif; -} - -.accelerator { - text-decoration: underline; -} - -.classifier { - font-style: oblique; -} - -abbr, acronym { - border-bottom: dotted 1px; - cursor: help; -} - -/* -- code displays --------------------------------------------------------- */ - -pre { - overflow: auto; - overflow-y: hidden; /* fixes display issues on Chrome browsers */ -} - -td.linenos pre { - padding: 5px 0px; - border: 0; - background-color: transparent; - color: #aaa; -} - -table.highlighttable { - margin-left: 0.5em; -} - -table.highlighttable td { - padding: 0 0.5em 0 0.5em; -} - -tt.descname { - background-color: transparent; - font-weight: bold; - font-size: 1.2em; -} - -tt.descclassname { - background-color: transparent; -} - -tt.xref, a tt { - background-color: transparent; - font-weight: bold; -} - -h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt { - background-color: transparent; -} - -.viewcode-link { - float: right; -} - -.viewcode-back { - float: right; - font-family: sans-serif; -} - -div.viewcode-block:target { - margin: -1px -10px; - padding: 0 10px; -} - -/* -- math display ---------------------------------------------------------- */ - -img.math { - vertical-align: middle; -} - -div.body div.math p { - text-align: center; -} - -span.eqno { - float: right; -} - -/* -- printout stylesheet --------------------------------------------------- */ - -@media print { - div.document, - div.documentwrapper, - div.bodywrapper { - margin: 0 !important; - width: 100%; - } - - div.sphinxsidebar, - div.related, - div.footer, - #top-link { - display: none; - } -} \ No newline at end of file diff --git a/docs/saml2/_static/comment-bright.png b/docs/saml2/_static/comment-bright.png deleted file mode 100644 index 551517b8c83b76f734ff791f847829a760ad1903..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3500 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 A2LJ#7 diff --git a/docs/saml2/_static/comment.png b/docs/saml2/_static/comment.png deleted file mode 100644 index 92feb52b8824c6b0f59b658b1196c61de9162a95..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3445 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 Xn!ZpXD%P(H00000NkvXXu0mjf= 0 && !jQuery(node.parentNode).hasMethod(className)) { - var span = document.createElement("span"); - span.className = className; - span.appendChild(document.createTextNode(val.substr(pos, text.length))); - node.parentNode.insertBefore(span, node.parentNode.insertBefore( - document.createTextNode(val.substr(pos + text.length)), - node.nextSibling)); - node.nodeValue = val.substr(0, pos); - } - } - else if (!jQuery(node).is("button, select, textarea")) { - jQuery.each(node.childNodes, function() { - highlight(this); - }); - } - } - return this.each(function() { - highlight(this); - }); -}; - -/** - * Small JavaScript module for the documentation. - */ -var Documentation = { - - init : function() { - this.fixFirefoxAnchorBug(); - this.highlightSearchWords(); - this.initIndexTable(); - }, - - /** - * i18n support - */ - TRANSLATIONS : {}, - PLURAL_EXPR : function(n) { return n == 1 ? 0 : 1; }, - LOCALE : 'unknown', - - // gettext and ngettext don't access this so that the functions - // can safely bound to a different name (_ = Documentation.gettext) - gettext : function(string) { - var translated = Documentation.TRANSLATIONS[string]; - if (typeof translated == 'undefined') - return string; - return (typeof translated == 'string') ? translated : translated[0]; - }, - - ngettext : function(singular, plural, n) { - var translated = Documentation.TRANSLATIONS[singular]; - if (typeof translated == 'undefined') - return (n == 1) ? singular : plural; - return translated[Documentation.PLURALEXPR(n)]; - }, - - addTranslations : function(catalog) { - for (var key in catalog.messages) - this.TRANSLATIONS[key] = catalog.messages[key]; - this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); - this.LOCALE = catalog.locale; - }, - - /** - * add context elements like header anchor links - */ - addContextElements : function() { - $('div[id] > :header:first').each(function() { - $('\u00B6'). - attr('href', '#' + this.id). - attr('title', _('Permalink to this headline')). - appendTo(this); - }); - $('dt[id]').each(function() { - $('\u00B6'). - attr('href', '#' + this.id). - attr('title', _('Permalink to this definition')). - appendTo(this); - }); - }, - - /** - * workaround a firefox stupidity - */ - fixFirefoxAnchorBug : function() { - if (document.location.hash && $.browser.mozilla) - window.setTimeout(function() { - document.location.href += ''; - }, 10); - }, - - /** - * highlight the search words provided in the url in the text - */ - highlightSearchWords : function() { - var params = $.getQueryParameters(); - var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; - if (terms.length) { - var body = $('div.body'); - window.setTimeout(function() { - $.each(terms, function() { - body.highlightText(this.toLowerCase(), 'highlighted'); - }); - }, 10); - $('') - .appendTo($('#searchbox')); - } - }, - - /** - * init the domain index toggle buttons - */ - initIndexTable : function() { - var togglers = $('img.toggler').click(function() { - var src = $(this).attr('src'); - var idnum = $(this).attr('id').substr(7); - $('tr.cg-' + idnum).toggle(); - if (src.substr(-9) == 'minus.png') - $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); - else - $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); - }).css('display', ''); - if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { - togglers.click(); - } - }, - - /** - * helper function to hide the search marks again - */ - hideSearchWords : function() { - $('#searchbox .highlight-link').fadeOut(300); - $('span.highlighted').removeMethod('highlighted'); - }, - - /** - * make the url absolute - */ - makeURL : function(relativeURL) { - return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; - }, - - /** - * get the current relative url - */ - getCurrentURL : function() { - var path = document.location.pathname; - var parts = path.split(/\//); - $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { - if (this == '..') - parts.pop(); - }); - var url = parts.join('/'); - return path.substring(url.lastIndexOf('/') + 1, path.length - 1); - } -}; - -// quick alias for translations -_ = Documentation.gettext; - -$(document).ready(function() { - Documentation.init(); -}); diff --git a/docs/saml2/_static/down-pressed.png b/docs/saml2/_static/down-pressed.png deleted file mode 100644 index 6f7ad782782e4f8e39b0c6e15c7344700cdd2527..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 368 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6U4S$Y z{B+)352QE?JR*yM+OLB!qm#z$3ZNi+iKnkC`z>}Z23@f-Ava~9&<9T!#}JFtXD=!G zGdl{fK6ro2OGiOl+hKvH6i=D3%%Y^j`yIkRn!8O>@bG)IQR0{Kf+mxNd=_WScA8u_ z3;8(7x2){m9`nt+U(Nab&1G)!{`SPVpDX$w8McLTzAJ39wprG3p4XLq$06M`%}2Yk zRPPsbES*dnYm1wkGL;iioAUB*Or2kz6(-M_r_#Me-`{mj$Z%( diff --git a/docs/saml2/_static/down.png b/docs/saml2/_static/down.png deleted file mode 100644 index 3003a88770de3977d47a2ba69893436a2860f9e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 363 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6U4S$Y z{B+)352QE?JR*yM+OLB!qm#z$3ZNi+iKnkC`z>}xaV3tUZ$qnrLa#kt978NlpS`ru z&)HFc^}^>{UOEce+71h5nn>6&w6A!ieNbu1wh)UGh{8~et^#oZ1# z>T7oM=FZ~xXWnTo{qnXm$ZLOlqGswI_m2{XwVK)IJmBjW{J3-B3x@C=M{ShWt#fYS9M?R;8K$~YwlIqwf>VA7q=YKcwf2DS4Zj5inDKXXB1zl=(YO3ST6~rDq)&z z*o>z)=hxrfG-cDBW0G$!?6{M<$@{_4{m1o%Ub!naEtn|@^frU1tDnm{r-UW|!^@B8 diff --git a/docs/saml2/_static/file.png b/docs/saml2/_static/file.png deleted file mode 100644 index d18082e397e7e54f20721af768c4c2983258f1b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 392 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmP$HyOL$D9)yc9|lc|nKf<9@eUiWd>3GuTC!a5vdfWYEazjncPj5ZQX%+1 zt8B*4=d)!cdDz4wr^#OMYfqGz$1LDFF>|#>*O?AGil(WEs?wLLy{Gj2J_@opDm%`dlax3yA*@*N$G&*ukFv>P8+2CBWO(qz zD0k1@kN>hhb1_6`&wrCswzINE(evt-5C1B^STi2@PmdKI;Vst0PQB6!2kdN diff --git a/docs/saml2/_static/jquery.js b/docs/saml2/_static/jquery.js deleted file mode 100644 index dc2f3dac..00000000 --- a/docs/saml2/_static/jquery.js +++ /dev/null @@ -1,9404 +0,0 @@ -/*! - * jQuery JavaScript Library v1.7.2 - * http://jquery.com/ - * - * Copyright 2011, John Resig - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * Includes Sizzle.js - * http://sizzlejs.com/ - * Copyright 2011, The Dojo Foundation - * Released under the MIT, BSD, and GPL Licenses. - * - * Date: Fri Jul 5 14:07:58 UTC 2013 - */ -(function( window, undefined ) { - -// Use the correct document accordingly with window argument (sandbox) -var document = window.document, - navigator = window.navigator, - location = window.location; -var jQuery = (function() { - -// Define a local copy of jQuery -var jQuery = function( selector, context ) { - // The jQuery object is actually just the init constructor 'enhanced' - return new jQuery.fn.init( selector, context, rootjQuery ); - }, - - // Map over jQuery in case of overwrite - _jQuery = window.jQuery, - - // Map over the $ in case of overwrite - _$ = window.$, - - // A central reference to the root jQuery(document) - rootjQuery, - - // A simple way to check for HTML strings or ID strings - // Prioritize #id over to avoid XSS via location.hash (#9521) - quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/, - - // Check if a string has a non-whitespace character in it - rnotwhite = /\S/, - - // Used for trimming whitespace - trimLeft = /^\s+/, - trimRight = /\s+$/, - - // Match a standalone tag - rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/, - - // JSON RegExp - rvalidchars = /^[\],:{}\s]*$/, - rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, - rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, - rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, - - // Useragent RegExp - rwebkit = /(webkit)[ \/]([\w.]+)/, - ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/, - rmsie = /(msie) ([\w.]+)/, - rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/, - - // Matches dashed string for camelizing - rdashAlpha = /-([a-z]|[0-9])/ig, - rmsPrefix = /^-ms-/, - - // Used by jQuery.camelCase as callback to replace() - fcamelCase = function( all, letter ) { - return ( letter + "" ).toUpperCase(); - }, - - // Keep a UserAgent string for use with jQuery.browser - userAgent = navigator.userAgent, - - // For matching the engine and version of the browser - browserMatch, - - // The deferred used on DOM ready - readyList, - - // The ready event handler - DOMContentLoaded, - - // Save a reference to some core methods - toString = Object.prototype.toString, - hasOwn = Object.prototype.hasOwnProperty, - push = Array.prototype.push, - slice = Array.prototype.slice, - trim = String.prototype.trim, - indexOf = Array.prototype.indexOf, - - // [[Method]] -> type pairs - class2type = {}; - -jQuery.fn = jQuery.prototype = { - constructor: jQuery, - init: function( selector, context, rootjQuery ) { - var match, elem, ret, doc; - - // Handle $(""), $(null), or $(undefined) - if ( !selector ) { - return this; - } - - // Handle $(DOMElement) - if ( selector.nodeType ) { - this.context = this[0] = selector; - this.length = 1; - return this; - } - - // The body element only exists once, optimize finding it - if ( selector === "body" && !context && document.body ) { - this.context = document; - this[0] = document.body; - this.selector = selector; - this.length = 1; - return this; - } - - // Handle HTML strings - if ( typeof selector === "string" ) { - // Are we dealing with HTML string or an ID? - if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { - // Assume that strings that start and end with <> are HTML and skip the regex check - match = [ null, selector, null ]; - - } else { - match = quickExpr.exec( selector ); - } - - // Verify a match, and that no context was specified for #id - if ( match && (match[1] || !context) ) { - - // HANDLE: $(html) -> $(array) - if ( match[1] ) { - context = context instanceof jQuery ? context[0] : context; - doc = ( context ? context.ownerDocument || context : document ); - - // If a single string is passed in and it's a single tag - // just do a createElement and skip the rest - ret = rsingleTag.exec( selector ); - - if ( ret ) { - if ( jQuery.isPlainObject( context ) ) { - selector = [ document.createElement( ret[1] ) ]; - jQuery.fn.attr.call( selector, context, true ); - - } else { - selector = [ doc.createElement( ret[1] ) ]; - } - - } else { - ret = jQuery.buildFragment( [ match[1] ], [ doc ] ); - selector = ( ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment ).childNodes; - } - - return jQuery.merge( this, selector ); - - // HANDLE: $("#id") - } else { - elem = document.getElementById( match[2] ); - - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - if ( elem && elem.parentNode ) { - // Handle the case where IE and Opera return items - // by name instead of ID - if ( elem.id !== match[2] ) { - return rootjQuery.find( selector ); - } - - // Otherwise, we inject the element directly into the jQuery object - this.length = 1; - this[0] = elem; - } - - this.context = document; - this.selector = selector; - return this; - } - - // HANDLE: $(expr, $(...)) - } else if ( !context || context.jquery ) { - return ( context || rootjQuery ).find( selector ); - - // HANDLE: $(expr, context) - // (which is just equivalent to: $(context).find(expr) - } else { - return this.constructor( context ).find( selector ); - } - - // HANDLE: $(function) - // Shortcut for document ready - } else if ( jQuery.isFunction( selector ) ) { - return rootjQuery.ready( selector ); - } - - if ( selector.selector !== undefined ) { - this.selector = selector.selector; - this.context = selector.context; - } - - return jQuery.makeArray( selector, this ); - }, - - // Start with an empty selector - selector: "", - - // The current version of jQuery being used - jquery: "1.7.2", - - // The default length of a jQuery object is 0 - length: 0, - - // The number of elements contained in the matched element set - size: function() { - return this.length; - }, - - toArray: function() { - return slice.call( this, 0 ); - }, - - // Get the Nth element in the matched element set OR - // Get the whole matched element set as a clean array - get: function( num ) { - return num == null ? - - // Return a 'clean' array - this.toArray() : - - // Return just the object - ( num < 0 ? this[ this.length + num ] : this[ num ] ); - }, - - // Take an array of elements and push it onto the stack - // (returning the new matched element set) - pushStack: function( elems, name, selector ) { - // Build a new jQuery matched element set - var ret = this.constructor(); - - if ( jQuery.isArray( elems ) ) { - push.apply( ret, elems ); - - } else { - jQuery.merge( ret, elems ); - } - - // Add the old object onto the stack (as a reference) - ret.prevObject = this; - - ret.context = this.context; - - if ( name === "find" ) { - ret.selector = this.selector + ( this.selector ? " " : "" ) + selector; - } else if ( name ) { - ret.selector = this.selector + "." + name + "(" + selector + ")"; - } - - // Return the newly-formed element set - return ret; - }, - - // Execute a callback for every element in the matched set. - // (You can seed the arguments with an array of args, but this is - // only used internally.) - each: function( callback, args ) { - return jQuery.each( this, callback, args ); - }, - - ready: function( fn ) { - // Attach the listeners - jQuery.bindReady(); - - // Add the callback - readyList.add( fn ); - - return this; - }, - - eq: function( i ) { - i = +i; - return i === -1 ? - this.slice( i ) : - this.slice( i, i + 1 ); - }, - - first: function() { - return this.eq( 0 ); - }, - - last: function() { - return this.eq( -1 ); - }, - - slice: function() { - return this.pushStack( slice.apply( this, arguments ), - "slice", slice.call(arguments).join(",") ); - }, - - map: function( callback ) { - return this.pushStack( jQuery.map(this, function( elem, i ) { - return callback.call( elem, i, elem ); - })); - }, - - end: function() { - return this.prevObject || this.constructor(null); - }, - - // For internal use only. - // Behaves like an Array's method, not like a jQuery method. - push: push, - sort: [].sort, - splice: [].splice -}; - -// Give the init function the jQuery prototype for later instantiation -jQuery.fn.init.prototype = jQuery.fn; - -jQuery.extend = jQuery.fn.extend = function() { - var options, name, src, copy, copyIsArray, clone, - target = arguments[0] || {}, - i = 1, - length = arguments.length, - deep = false; - - // Handle a deep copy situation - if ( typeof target === "boolean" ) { - deep = target; - target = arguments[1] || {}; - // skip the boolean and the target - i = 2; - } - - // Handle case when target is a string or something (possible in deep copy) - if ( typeof target !== "object" && !jQuery.isFunction(target) ) { - target = {}; - } - - // extend jQuery itself if only one argument is passed - if ( length === i ) { - target = this; - --i; - } - - for ( ; i < length; i++ ) { - // Only deal with non-null/undefined values - if ( (options = arguments[ i ]) != null ) { - // Extend the base object - for ( name in options ) { - src = target[ name ]; - copy = options[ name ]; - - // Prevent never-ending loop - if ( target === copy ) { - continue; - } - - // Recurse if we're merging plain objects or arrays - if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { - if ( copyIsArray ) { - copyIsArray = false; - clone = src && jQuery.isArray(src) ? src : []; - - } else { - clone = src && jQuery.isPlainObject(src) ? src : {}; - } - - // Never move original objects, clone them - target[ name ] = jQuery.extend( deep, clone, copy ); - - // Don't bring in undefined values - } else if ( copy !== undefined ) { - target[ name ] = copy; - } - } - } - } - - // Return the modified object - return target; -}; - -jQuery.extend({ - noConflict: function( deep ) { - if ( window.$ === jQuery ) { - window.$ = _$; - } - - if ( deep && window.jQuery === jQuery ) { - window.jQuery = _jQuery; - } - - return jQuery; - }, - - // Is the DOM ready to be used? Set to true once it occurs. - isReady: false, - - // A counter to track how many items to wait for before - // the ready event fires. See #6781 - readyWait: 1, - - // Hold (or release) the ready event - holdReady: function( hold ) { - if ( hold ) { - jQuery.readyWait++; - } else { - jQuery.ready( true ); - } - }, - - // Handle when the DOM is ready - ready: function( wait ) { - // Either a released hold or an DOMready/load event and not yet ready - if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) { - // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). - if ( !document.body ) { - return setTimeout( jQuery.ready, 1 ); - } - - // Remember that the DOM is ready - jQuery.isReady = true; - - // If a normal DOM Ready event fired, decrement, and wait if need be - if ( wait !== true && --jQuery.readyWait > 0 ) { - return; - } - - // If there are functions bound, to execute - readyList.fireWith( document, [ jQuery ] ); - - // Trigger any bound ready events - if ( jQuery.fn.trigger ) { - jQuery( document ).trigger( "ready" ).off( "ready" ); - } - } - }, - - bindReady: function() { - if ( readyList ) { - return; - } - - readyList = jQuery.Callbacks( "once memory" ); - - // Catch cases where $(document).ready() is called after the - // browser event has already occurred. - if ( document.readyState === "complete" ) { - // Handle it asynchronously to allow scripts the opportunity to delay ready - return setTimeout( jQuery.ready, 1 ); - } - - // Mozilla, Opera and webkit nightlies currently support this event - if ( document.addEventListener ) { - // Use the handy event callback - document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); - - // A fallback to window.onload, that will always work - window.addEventListener( "load", jQuery.ready, false ); - - // If IE event model is used - } else if ( document.attachEvent ) { - // ensure firing before onload, - // maybe late but safe also for iframes - document.attachEvent( "onreadystatechange", DOMContentLoaded ); - - // A fallback to window.onload, that will always work - window.attachEvent( "onload", jQuery.ready ); - - // If IE and not a frame - // continually check to see if the document is ready - var toplevel = false; - - try { - toplevel = window.frameElement == null; - } catch(e) {} - - if ( document.documentElement.doScroll && toplevel ) { - doScrollCheck(); - } - } - }, - - // See test/unit/core.js for details concerning isFunction. - // Since version 1.3, DOM methods and functions like alert - // aren't supported. They return false on IE (#2968). - isFunction: function( obj ) { - return jQuery.type(obj) === "function"; - }, - - isArray: Array.isArray || function( obj ) { - return jQuery.type(obj) === "array"; - }, - - isWindow: function( obj ) { - return obj != null && obj == obj.window; - }, - - isNumeric: function( obj ) { - return !isNaN( parseFloat(obj) ) && isFinite( obj ); - }, - - type: function( obj ) { - return obj == null ? - String( obj ) : - class2type[ toString.call(obj) ] || "object"; - }, - - isPlainObject: function( obj ) { - // Must be an Object. - // Because of IE, we also have to check the presence of the constructor property. - // Make sure that DOM nodes and window objects don't pass through, as well - if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { - return false; - } - - try { - // Not own constructor property must be Object - if ( obj.constructor && - !hasOwn.call(obj, "constructor") && - !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { - return false; - } - } catch ( e ) { - // IE8,9 Will throw exceptions on certain host objects #9897 - return false; - } - - // Own properties are enumerated firstly, so to speed up, - // if last one is own, then all properties are own. - - var key; - for ( key in obj ) {} - - return key === undefined || hasOwn.call( obj, key ); - }, - - isEmptyObject: function( obj ) { - for ( var name in obj ) { - return false; - } - return true; - }, - - error: function( msg ) { - throw new Error( msg ); - }, - - parseJSON: function( data ) { - if ( typeof data !== "string" || !data ) { - return null; - } - - // Make sure leading/trailing whitespace is removed (IE can't handle it) - data = jQuery.trim( data ); - - // Attempt to parse using the native JSON parser first - if ( window.JSON && window.JSON.parse ) { - return window.JSON.parse( data ); - } - - // Make sure the incoming data is actual JSON - // Logic borrowed from http://json.org/json2.js - if ( rvalidchars.test( data.replace( rvalidescape, "@" ) - .replace( rvalidtokens, "]" ) - .replace( rvalidbraces, "")) ) { - - return ( new Function( "return " + data ) )(); - - } - jQuery.error( "Invalid JSON: " + data ); - }, - - // Cross-browser xml parsing - parseXML: function( data ) { - if ( typeof data !== "string" || !data ) { - return null; - } - var xml, tmp; - try { - if ( window.DOMParser ) { // Standard - tmp = new DOMParser(); - xml = tmp.parseFromString( data , "text/xml" ); - } else { // IE - xml = new ActiveXObject( "Microsoft.XMLDOM" ); - xml.async = "false"; - xml.loadXML( data ); - } - } catch( e ) { - xml = undefined; - } - if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) { - jQuery.error( "Invalid XML: " + data ); - } - return xml; - }, - - noop: function() {}, - - // Evaluates a script in a global context - // Workarounds based on findings by Jim Driscoll - // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context - globalEval: function( data ) { - if ( data && rnotwhite.test( data ) ) { - // We use execScript on Internet Explorer - // We use an anonymous function so that context is window - // rather than jQuery in Firefox - ( window.execScript || function( data ) { - window[ "eval" ].call( window, data ); - } )( data ); - } - }, - - // Convert dashed to camelCase; used by the css and data modules - // Microsoft forgot to hump their vendor prefix (#9572) - camelCase: function( string ) { - return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); - }, - - nodeName: function( elem, name ) { - return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); - }, - - // args is for internal usage only - each: function( object, callback, args ) { - var name, i = 0, - length = object.length, - isObj = length === undefined || jQuery.isFunction( object ); - - if ( args ) { - if ( isObj ) { - for ( name in object ) { - if ( callback.apply( object[ name ], args ) === false ) { - break; - } - } - } else { - for ( ; i < length; ) { - if ( callback.apply( object[ i++ ], args ) === false ) { - break; - } - } - } - - // A special, fast, case for the most common use of each - } else { - if ( isObj ) { - for ( name in object ) { - if ( callback.call( object[ name ], name, object[ name ] ) === false ) { - break; - } - } - } else { - for ( ; i < length; ) { - if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) { - break; - } - } - } - } - - return object; - }, - - // Use native String.trim function wherever possible - trim: trim ? - function( text ) { - return text == null ? - "" : - trim.call( text ); - } : - - // Otherwise use our own trimming functionality - function( text ) { - return text == null ? - "" : - text.toString().replace( trimLeft, "" ).replace( trimRight, "" ); - }, - - // results is for internal usage only - makeArray: function( array, results ) { - var ret = results || []; - - if ( array != null ) { - // The window, strings (and functions) also have 'length' - // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930 - var type = jQuery.type( array ); - - if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) { - push.call( ret, array ); - } else { - jQuery.merge( ret, array ); - } - } - - return ret; - }, - - inArray: function( elem, array, i ) { - var len; - - if ( array ) { - if ( indexOf ) { - return indexOf.call( array, elem, i ); - } - - len = array.length; - i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; - - for ( ; i < len; i++ ) { - // Skip accessing in sparse arrays - if ( i in array && array[ i ] === elem ) { - return i; - } - } - } - - return -1; - }, - - merge: function( first, second ) { - var i = first.length, - j = 0; - - if ( typeof second.length === "number" ) { - for ( var l = second.length; j < l; j++ ) { - first[ i++ ] = second[ j ]; - } - - } else { - while ( second[j] !== undefined ) { - first[ i++ ] = second[ j++ ]; - } - } - - first.length = i; - - return first; - }, - - grep: function( elems, callback, inv ) { - var ret = [], retVal; - inv = !!inv; - - // Go through the array, only saving the items - // that pass the validator function - for ( var i = 0, length = elems.length; i < length; i++ ) { - retVal = !!callback( elems[ i ], i ); - if ( inv !== retVal ) { - ret.push( elems[ i ] ); - } - } - - return ret; - }, - - // arg is for internal usage only - map: function( elems, callback, arg ) { - var value, key, ret = [], - i = 0, - length = elems.length, - // jquery objects are treated as arrays - isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ; - - // Go through the array, translating each of the items to their - if ( isArray ) { - for ( ; i < length; i++ ) { - value = callback( elems[ i ], i, arg ); - - if ( value != null ) { - ret[ ret.length ] = value; - } - } - - // Go through every key on the object, - } else { - for ( key in elems ) { - value = callback( elems[ key ], key, arg ); - - if ( value != null ) { - ret[ ret.length ] = value; - } - } - } - - // Flatten any nested arrays - return ret.concat.apply( [], ret ); - }, - - // A global GUID counter for objects - guid: 1, - - // Bind a function to a context, optionally partially applying any - // arguments. - proxy: function( fn, context ) { - if ( typeof context === "string" ) { - var tmp = fn[ context ]; - context = fn; - fn = tmp; - } - - // Quick check to determine if target is callable, in the spec - // this throws a TypeError, but we will just return undefined. - if ( !jQuery.isFunction( fn ) ) { - return undefined; - } - - // Simulated bind - var args = slice.call( arguments, 2 ), - proxy = function() { - return fn.apply( context, args.concat( slice.call( arguments ) ) ); - }; - - // Set the guid of unique handler to the same of original handler, so it can be removed - proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++; - - return proxy; - }, - - // Mutifunctional method to get and set values to a collection - // The value/s can optionally be executed if it's a function - access: function( elems, fn, key, value, chainable, emptyGet, pass ) { - var exec, - bulk = key == null, - i = 0, - length = elems.length; - - // Sets many values - if ( key && typeof key === "object" ) { - for ( i in key ) { - jQuery.access( elems, fn, i, key[i], 1, emptyGet, value ); - } - chainable = 1; - - // Sets one value - } else if ( value !== undefined ) { - // Optionally, function values get executed if exec is true - exec = pass === undefined && jQuery.isFunction( value ); - - if ( bulk ) { - // Bulk operations only iterate when executing function values - if ( exec ) { - exec = fn; - fn = function( elem, key, value ) { - return exec.call( jQuery( elem ), value ); - }; - - // Otherwise they run against the entire set - } else { - fn.call( elems, value ); - fn = null; - } - } - - if ( fn ) { - for (; i < length; i++ ) { - fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass ); - } - } - - chainable = 1; - } - - return chainable ? - elems : - - // Gets - bulk ? - fn.call( elems ) : - length ? fn( elems[0], key ) : emptyGet; - }, - - now: function() { - return ( new Date() ).getTime(); - }, - - // Use of jQuery.browser is frowned upon. - // More details: http://docs.jquery.com/Utilities/jQuery.browser - uaMatch: function( ua ) { - ua = ua.toLowerCase(); - - var match = rwebkit.exec( ua ) || - ropera.exec( ua ) || - rmsie.exec( ua ) || - ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) || - []; - - return { browser: match[1] || "", version: match[2] || "0" }; - }, - - sub: function() { - function jQuerySub( selector, context ) { - return new jQuerySub.fn.init( selector, context ); - } - jQuery.extend( true, jQuerySub, this ); - jQuerySub.superclass = this; - jQuerySub.fn = jQuerySub.prototype = this(); - jQuerySub.fn.constructor = jQuerySub; - jQuerySub.sub = this.sub; - jQuerySub.fn.init = function init( selector, context ) { - if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) { - context = jQuerySub( context ); - } - - return jQuery.fn.init.call( this, selector, context, rootjQuerySub ); - }; - jQuerySub.fn.init.prototype = jQuerySub.fn; - var rootjQuerySub = jQuerySub(document); - return jQuerySub; - }, - - browser: {} -}); - -// Populate the class2type map -jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) { - class2type[ "[object " + name + "]" ] = name.toLowerCase(); -}); - -browserMatch = jQuery.uaMatch( userAgent ); -if ( browserMatch.browser ) { - jQuery.browser[ browserMatch.browser ] = true; - jQuery.browser.version = browserMatch.version; -} - -// Deprecated, use jQuery.browser.webkit instead -if ( jQuery.browser.webkit ) { - jQuery.browser.safari = true; -} - -// IE doesn't match non-breaking spaces with \s -if ( rnotwhite.test( "\xA0" ) ) { - trimLeft = /^[\s\xA0]+/; - trimRight = /[\s\xA0]+$/; -} - -// All jQuery objects should point back to these -rootjQuery = jQuery(document); - -// Cleanup functions for the document ready method -if ( document.addEventListener ) { - DOMContentLoaded = function() { - document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); - jQuery.ready(); - }; - -} else if ( document.attachEvent ) { - DOMContentLoaded = function() { - // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). - if ( document.readyState === "complete" ) { - document.detachEvent( "onreadystatechange", DOMContentLoaded ); - jQuery.ready(); - } - }; -} - -// The DOM ready check for Internet Explorer -function doScrollCheck() { - if ( jQuery.isReady ) { - return; - } - - try { - // If IE is used, use the trick by Diego Perini - // http://javascript.nwbox.com/IEContentLoaded/ - document.documentElement.doScroll("left"); - } catch(e) { - setTimeout( doScrollCheck, 1 ); - return; - } - - // and execute any waiting functions - jQuery.ready(); -} - -return jQuery; - -})(); - - -// String to Object flags format cache -var flagsCache = {}; - -// Convert String-formatted flags into Object-formatted ones and store in cache -function createFlags( flags ) { - var object = flagsCache[ flags ] = {}, - i, length; - flags = flags.split( /\s+/ ); - for ( i = 0, length = flags.length; i < length; i++ ) { - object[ flags[i] ] = true; - } - return object; -} - -/* - * Create a callback list using the following parameters: - * - * flags: an optional list of space-separated flags that will change how - * the callback list behaves - * - * By default a callback list will act like an event callback list and can be - * "fired" multiple times. - * - * Possible flags: - * - * once: will ensure the callback list can only be fired once (like a Deferred) - * - * memory: will keep track of previous values and will call any callback added - * after the list has been fired right away with the latest "memorized" - * values (like a Deferred) - * - * unique: will ensure a callback can only be added once (no duplicate in the list) - * - * stopOnFalse: interrupt callings when a callback returns false - * - */ -jQuery.Callbacks = function( flags ) { - - // Convert flags from String-formatted to Object-formatted - // (we check in cache first) - flags = flags ? ( flagsCache[ flags ] || createFlags( flags ) ) : {}; - - var // Actual callback list - list = [], - // Stack of fire calls for repeatable lists - stack = [], - // Last fire value (for non-forgettable lists) - memory, - // Flag to know if list was already fired - fired, - // Flag to know if list is currently firing - firing, - // First callback to fire (used internally by add and fireWith) - firingStart, - // End of the loop when firing - firingLength, - // Index of currently firing callback (modified by remove if needed) - firingIndex, - // Add one or several callbacks to the list - add = function( args ) { - var i, - length, - elem, - type, - actual; - for ( i = 0, length = args.length; i < length; i++ ) { - elem = args[ i ]; - type = jQuery.type( elem ); - if ( type === "array" ) { - // Inspect recursively - add( elem ); - } else if ( type === "function" ) { - // Add if not in unique mode and callback is not in - if ( !flags.unique || !self.has( elem ) ) { - list.push( elem ); - } - } - } - }, - // Fire callbacks - fire = function( context, args ) { - args = args || []; - memory = !flags.memory || [ context, args ]; - fired = true; - firing = true; - firingIndex = firingStart || 0; - firingStart = 0; - firingLength = list.length; - for ( ; list && firingIndex < firingLength; firingIndex++ ) { - if ( list[ firingIndex ].apply( context, args ) === false && flags.stopOnFalse ) { - memory = true; // Mark as halted - break; - } - } - firing = false; - if ( list ) { - if ( !flags.once ) { - if ( stack && stack.length ) { - memory = stack.shift(); - self.fireWith( memory[ 0 ], memory[ 1 ] ); - } - } else if ( memory === true ) { - self.disable(); - } else { - list = []; - } - } - }, - // Actual Callbacks object - self = { - // Add a callback or a collection of callbacks to the list - add: function() { - if ( list ) { - var length = list.length; - add( arguments ); - // Do we need to add the callbacks to the - // current firing batch? - if ( firing ) { - firingLength = list.length; - // With memory, if we're not firing then - // we should call right away, unless previous - // firing was halted (stopOnFalse) - } else if ( memory && memory !== true ) { - firingStart = length; - fire( memory[ 0 ], memory[ 1 ] ); - } - } - return this; - }, - // Remove a callback from the list - remove: function() { - if ( list ) { - var args = arguments, - argIndex = 0, - argLength = args.length; - for ( ; argIndex < argLength ; argIndex++ ) { - for ( var i = 0; i < list.length; i++ ) { - if ( args[ argIndex ] === list[ i ] ) { - // Handle firingIndex and firingLength - if ( firing ) { - if ( i <= firingLength ) { - firingLength--; - if ( i <= firingIndex ) { - firingIndex--; - } - } - } - // Remove the element - list.splice( i--, 1 ); - // If we have some unicity property then - // we only need to do this once - if ( flags.unique ) { - break; - } - } - } - } - } - return this; - }, - // Control if a given callback is in the list - has: function( fn ) { - if ( list ) { - var i = 0, - length = list.length; - for ( ; i < length; i++ ) { - if ( fn === list[ i ] ) { - return true; - } - } - } - return false; - }, - // Remove all callbacks from the list - empty: function() { - list = []; - return this; - }, - // Have the list do nothing anymore - disable: function() { - list = stack = memory = undefined; - return this; - }, - // Is it disabled? - disabled: function() { - return !list; - }, - // Lock the list in its current state - lock: function() { - stack = undefined; - if ( !memory || memory === true ) { - self.disable(); - } - return this; - }, - // Is it locked? - locked: function() { - return !stack; - }, - // Call all callbacks with the given context and arguments - fireWith: function( context, args ) { - if ( stack ) { - if ( firing ) { - if ( !flags.once ) { - stack.push( [ context, args ] ); - } - } else if ( !( flags.once && memory ) ) { - fire( context, args ); - } - } - return this; - }, - // Call all the callbacks with the given arguments - fire: function() { - self.fireWith( this, arguments ); - return this; - }, - // To know if the callbacks have already been called at least once - fired: function() { - return !!fired; - } - }; - - return self; -}; - - - - -var // Static reference to slice - sliceDeferred = [].slice; - -jQuery.extend({ - - Deferred: function( func ) { - var doneList = jQuery.Callbacks( "once memory" ), - failList = jQuery.Callbacks( "once memory" ), - progressList = jQuery.Callbacks( "memory" ), - state = "pending", - lists = { - resolve: doneList, - reject: failList, - notify: progressList - }, - promise = { - done: doneList.add, - fail: failList.add, - progress: progressList.add, - - state: function() { - return state; - }, - - // Deprecated - isResolved: doneList.fired, - isRejected: failList.fired, - - then: function( doneCallbacks, failCallbacks, progressCallbacks ) { - deferred.done( doneCallbacks ).fail( failCallbacks ).progress( progressCallbacks ); - return this; - }, - always: function() { - deferred.done.apply( deferred, arguments ).fail.apply( deferred, arguments ); - return this; - }, - pipe: function( fnDone, fnFail, fnProgress ) { - return jQuery.Deferred(function( newDefer ) { - jQuery.each( { - done: [ fnDone, "resolve" ], - fail: [ fnFail, "reject" ], - progress: [ fnProgress, "notify" ] - }, function( handler, data ) { - var fn = data[ 0 ], - action = data[ 1 ], - returned; - if ( jQuery.isFunction( fn ) ) { - deferred[ handler ](function() { - returned = fn.apply( this, arguments ); - if ( returned && jQuery.isFunction( returned.promise ) ) { - returned.promise().then( newDefer.resolve, newDefer.reject, newDefer.notify ); - } else { - newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] ); - } - }); - } else { - deferred[ handler ]( newDefer[ action ] ); - } - }); - }).promise(); - }, - // Get a promise for this deferred - // If obj is provided, the promise aspect is added to the object - promise: function( obj ) { - if ( obj == null ) { - obj = promise; - } else { - for ( var key in promise ) { - obj[ key ] = promise[ key ]; - } - } - return obj; - } - }, - deferred = promise.promise({}), - key; - - for ( key in lists ) { - deferred[ key ] = lists[ key ].fire; - deferred[ key + "With" ] = lists[ key ].fireWith; - } - - // Handle state - deferred.done( function() { - state = "resolved"; - }, failList.disable, progressList.lock ).fail( function() { - state = "rejected"; - }, doneList.disable, progressList.lock ); - - // Call given func if any - if ( func ) { - func.call( deferred, deferred ); - } - - // All done! - return deferred; - }, - - // Deferred helper - when: function( firstParam ) { - var args = sliceDeferred.call( arguments, 0 ), - i = 0, - length = args.length, - pValues = new Array( length ), - count = length, - pCount = length, - deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ? - firstParam : - jQuery.Deferred(), - promise = deferred.promise(); - function resolveFunc( i ) { - return function( value ) { - args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value; - if ( !( --count ) ) { - deferred.resolveWith( deferred, args ); - } - }; - } - function progressFunc( i ) { - return function( value ) { - pValues[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value; - deferred.notifyWith( promise, pValues ); - }; - } - if ( length > 1 ) { - for ( ; i < length; i++ ) { - if ( args[ i ] && args[ i ].promise && jQuery.isFunction( args[ i ].promise ) ) { - args[ i ].promise().then( resolveFunc(i), deferred.reject, progressFunc(i) ); - } else { - --count; - } - } - if ( !count ) { - deferred.resolveWith( deferred, args ); - } - } else if ( deferred !== firstParam ) { - deferred.resolveWith( deferred, length ? [ firstParam ] : [] ); - } - return promise; - } -}); - - - - -jQuery.support = (function() { - - var support, - all, - a, - select, - opt, - input, - fragment, - tds, - events, - eventName, - i, - isSupported, - div = document.createElement( "div" ), - documentElement = document.documentElement; - - // Preliminary tests - div.setAttribute("className", "t"); - div.innerHTML = "
    a"; - - all = div.getElementsByTagName( "*" ); - a = div.getElementsByTagName( "a" )[ 0 ]; - - // Can't get basic test support - if ( !all || !all.length || !a ) { - return {}; - } - - // First batch of supports tests - select = document.createElement( "select" ); - opt = select.appendChild( document.createElement("option") ); - input = div.getElementsByTagName( "input" )[ 0 ]; - - support = { - // IE strips leading whitespace when .innerHTML is used - leadingWhitespace: ( div.firstChild.nodeType === 3 ), - - // Make sure that tbody elements aren't automatically inserted - // IE will insert them into empty tables - tbody: !div.getElementsByTagName("tbody").length, - - // Make sure that link elements get serialized correctly by innerHTML - // This requires a wrapper element in IE - htmlSerialize: !!div.getElementsByTagName("link").length, - - // Get the style information from getAttribute - // (IE uses .cssText instead) - style: /top/.test( a.getAttribute("style") ), - - // Make sure that URLs aren't manipulated - // (IE normalizes it by default) - hrefNormalized: ( a.getAttribute("href") === "/a" ), - - // Make sure that element opacity exists - // (IE uses filter instead) - // Use a regex to work around a WebKit issue. See #5145 - opacity: /^0.55/.test( a.style.opacity ), - - // Verify style float existence - // (IE uses styleFloat instead of cssFloat) - cssFloat: !!a.style.cssFloat, - - // Make sure that if no value is specified for a checkbox - // that it defaults to "on". - // (WebKit defaults to "" instead) - checkOn: ( input.value === "on" ), - - // Make sure that a selected-by-default option has a working selected property. - // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) - optSelected: opt.selected, - - // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) - getSetAttribute: div.className !== "t", - - // Tests for enctype support on a form(#6743) - enctype: !!document.createElement("form").enctype, - - // Makes sure cloning an html5 element does not cause problems - // Where outerHTML is undefined, this still works - html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav>", - - // Will be defined later - submitBubbles: true, - changeBubbles: true, - focusinBubbles: false, - deleteExpando: true, - noCloneEvent: true, - inlineBlockNeedsLayout: false, - shrinkWrapBlocks: false, - reliableMarginRight: true, - pixelMargin: true - }; - - // jQuery.boxModel DEPRECATED in 1.3, use jQuery.support.boxModel instead - jQuery.boxModel = support.boxModel = (document.compatMode === "CSS1Compat"); - - // Make sure checked status is properly cloned - input.checked = true; - support.noCloneChecked = input.cloneNode( true ).checked; - - // Make sure that the options inside disabled selects aren't marked as disabled - // (WebKit marks them as disabled) - select.disabled = true; - support.optDisabled = !opt.disabled; - - // Test to see if it's possible to delete an expando from an element - // Fails in Internet Explorer - try { - delete div.test; - } catch( e ) { - support.deleteExpando = false; - } - - if ( !div.addEventListener && div.attachEvent && div.fireEvent ) { - div.attachEvent( "onclick", function() { - // Cloning a node shouldn't copy over any - // bound event handlers (IE does this) - support.noCloneEvent = false; - }); - div.cloneNode( true ).fireEvent( "onclick" ); - } - - // Check if a radio maintains its value - // after being appended to the DOM - input = document.createElement("input"); - input.value = "t"; - input.setAttribute("type", "radio"); - support.radioValue = input.value === "t"; - - input.setAttribute("checked", "checked"); - - // #11217 - WebKit loses check when the name is after the checked attribute - input.setAttribute( "name", "t" ); - - div.appendChild( input ); - fragment = document.createDocumentFragment(); - fragment.appendChild( div.lastChild ); - - // WebKit doesn't clone checked state correctly in fragments - support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; - - // Check if a disconnected checkbox will retain its checked - // value of true after appended to the DOM (IE6/7) - support.appendChecked = input.checked; - - fragment.removeChild( input ); - fragment.appendChild( div ); - - // Technique from Juriy Zaytsev - // http://perfectionkills.com/detecting-event-support-without-browser-sniffing/ - // We only care about the case where non-standard event systems - // are used, namely in IE. Short-circuiting here helps us to - // avoid an eval call (in setAttribute) which can cause CSP - // to go haywire. See: https://developer.mozilla.org/en/Security/CSP - if ( div.attachEvent ) { - for ( i in { - submit: 1, - change: 1, - focusin: 1 - }) { - eventName = "on" + i; - isSupported = ( eventName in div ); - if ( !isSupported ) { - div.setAttribute( eventName, "return;" ); - isSupported = ( typeof div[ eventName ] === "function" ); - } - support[ i + "Bubbles" ] = isSupported; - } - } - - fragment.removeChild( div ); - - // Null elements to avoid leaks in IE - fragment = select = opt = div = input = null; - - // Run tests that need a body at doc ready - jQuery(function() { - var container, outer, inner, table, td, offsetSupport, - marginDiv, conMarginTop, style, html, positionTopLeftWidthHeight, - paddingMarginBorderVisibility, paddingMarginBorder, - body = document.getElementsByTagName("body")[0]; - - if ( !body ) { - // Return for frameset docs that don't have a body - return; - } - - conMarginTop = 1; - paddingMarginBorder = "padding:0;margin:0;border:"; - positionTopLeftWidthHeight = "position:absolute;top:0;left:0;width:1px;height:1px;"; - paddingMarginBorderVisibility = paddingMarginBorder + "0;visibility:hidden;"; - style = "style='" + positionTopLeftWidthHeight + paddingMarginBorder + "5px solid #000;"; - html = "
    " + - "" + - "
    "; - - container = document.createElement("div"); - container.style.cssText = paddingMarginBorderVisibility + "width:0;height:0;position:static;top:0;margin-top:" + conMarginTop + "px"; - body.insertBefore( container, body.firstChild ); - - // Construct the test element - div = document.createElement("div"); - container.appendChild( div ); - - // Check if table cells still have offsetWidth/Height when they are set - // to display:none and there are still other visible table cells in a - // table row; if so, offsetWidth/Height are not reliable for use when - // determining if an element has been hidden directly using - // display:none (it is still safe to use offsets if a parent element is - // hidden; don safety goggles and see bug #4512 for more information). - // (only IE 8 fails this test) - div.innerHTML = "
    t
    "; - tds = div.getElementsByTagName( "td" ); - isSupported = ( tds[ 0 ].offsetHeight === 0 ); - - tds[ 0 ].style.display = ""; - tds[ 1 ].style.display = "none"; - - // Check if empty table cells still have offsetWidth/Height - // (IE <= 8 fail this test) - support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); - - // Check if div with explicit width and no margin-right incorrectly - // gets computed margin-right based on width of container. For more - // info see bug #3333 - // Fails in WebKit before Feb 2011 nightlies - // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right - if ( window.getComputedStyle ) { - div.innerHTML = ""; - marginDiv = document.createElement( "div" ); - marginDiv.style.width = "0"; - marginDiv.style.marginRight = "0"; - div.style.width = "2px"; - div.appendChild( marginDiv ); - support.reliableMarginRight = - ( parseInt( ( window.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0; - } - - if ( typeof div.style.zoom !== "undefined" ) { - // Check if natively block-level elements act like inline-block - // elements when setting their display to 'inline' and giving - // them layout - // (IE < 8 does this) - div.innerHTML = ""; - div.style.width = div.style.padding = "1px"; - div.style.border = 0; - div.style.overflow = "hidden"; - div.style.display = "inline"; - div.style.zoom = 1; - support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 ); - - // Check if elements with layout shrink-wrap their children - // (IE 6 does this) - div.style.display = "block"; - div.style.overflow = "visible"; - div.innerHTML = "
    "; - support.shrinkWrapBlocks = ( div.offsetWidth !== 3 ); - } - - div.style.cssText = positionTopLeftWidthHeight + paddingMarginBorderVisibility; - div.innerHTML = html; - - outer = div.firstChild; - inner = outer.firstChild; - td = outer.nextSibling.firstChild.firstChild; - - offsetSupport = { - doesNotAddBorder: ( inner.offsetTop !== 5 ), - doesAddBorderForTableAndCells: ( td.offsetTop === 5 ) - }; - - inner.style.position = "fixed"; - inner.style.top = "20px"; - - // safari subtracts parent border width here which is 5px - offsetSupport.fixedPosition = ( inner.offsetTop === 20 || inner.offsetTop === 15 ); - inner.style.position = inner.style.top = ""; - - outer.style.overflow = "hidden"; - outer.style.position = "relative"; - - offsetSupport.subtractsBorderForOverflowNotVisible = ( inner.offsetTop === -5 ); - offsetSupport.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== conMarginTop ); - - if ( window.getComputedStyle ) { - div.style.marginTop = "1%"; - support.pixelMargin = ( window.getComputedStyle( div, null ) || { marginTop: 0 } ).marginTop !== "1%"; - } - - if ( typeof container.style.zoom !== "undefined" ) { - container.style.zoom = 1; - } - - body.removeChild( container ); - marginDiv = div = container = null; - - jQuery.extend( support, offsetSupport ); - }); - - return support; -})(); - - - - -var rbrace = /^(?:\{.*\}|\[.*\])$/, - rmultiDash = /([A-Z])/g; - -jQuery.extend({ - cache: {}, - - // Please use with caution - uuid: 0, - - // Unique for each copy of jQuery on the page - // Non-digits removed to match rinlinejQuery - expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ), - - // The following elements throw uncatchable exceptions if you - // attempt to add expando properties to them. - noData: { - "embed": true, - // Ban all objects except for Flash (which handle expandos) - "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000", - "applet": true - }, - - hasData: function( elem ) { - elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; - return !!elem && !isEmptyDataObject( elem ); - }, - - data: function( elem, name, data, pvt /* Internal Use Only */ ) { - if ( !jQuery.acceptData( elem ) ) { - return; - } - - var privateCache, thisCache, ret, - internalKey = jQuery.expando, - getByName = typeof name === "string", - - // We have to handle DOM nodes and JS objects differently because IE6-7 - // can't GC object references properly across the DOM-JS boundary - isNode = elem.nodeType, - - // Only DOM nodes need the global jQuery cache; JS object data is - // attached directly to the object so GC can occur automatically - cache = isNode ? jQuery.cache : elem, - - // Only defining an ID for JS objects if its cache already exists allows - // the code to shortcut on the same path as a DOM node with no cache - id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey, - isEvents = name === "events"; - - // Avoid doing any more work than we need to when trying to get data on an - // object that has no data at all - if ( (!id || !cache[id] || (!isEvents && !pvt && !cache[id].data)) && getByName && data === undefined ) { - return; - } - - if ( !id ) { - // Only DOM nodes need a new unique ID for each element since their data - // ends up in the global cache - if ( isNode ) { - elem[ internalKey ] = id = ++jQuery.uuid; - } else { - id = internalKey; - } - } - - if ( !cache[ id ] ) { - cache[ id ] = {}; - - // Avoids exposing jQuery metadata on plain JS objects when the object - // is serialized using JSON.stringify - if ( !isNode ) { - cache[ id ].toJSON = jQuery.noop; - } - } - - // An object can be passed to jQuery.data instead of a key/value pair; this gets - // shallow copied over onto the existing cache - if ( typeof name === "object" || typeof name === "function" ) { - if ( pvt ) { - cache[ id ] = jQuery.extend( cache[ id ], name ); - } else { - cache[ id ].data = jQuery.extend( cache[ id ].data, name ); - } - } - - privateCache = thisCache = cache[ id ]; - - // jQuery data() is stored in a separate object inside the object's internal data - // cache in order to avoid key collisions between internal data and user-defined - // data. - if ( !pvt ) { - if ( !thisCache.data ) { - thisCache.data = {}; - } - - thisCache = thisCache.data; - } - - if ( data !== undefined ) { - thisCache[ jQuery.camelCase( name ) ] = data; - } - - // Users should not attempt to inspect the internal events object using jQuery.data, - // it is undocumented and subject to change. But does anyone listen? No. - if ( isEvents && !thisCache[ name ] ) { - return privateCache.events; - } - - // Check for both converted-to-camel and non-converted data property names - // If a data property was specified - if ( getByName ) { - - // First Try to find as-is property data - ret = thisCache[ name ]; - - // Test for null|undefined property data - if ( ret == null ) { - - // Try to find the camelCased property - ret = thisCache[ jQuery.camelCase( name ) ]; - } - } else { - ret = thisCache; - } - - return ret; - }, - - removeData: function( elem, name, pvt /* Internal Use Only */ ) { - if ( !jQuery.acceptData( elem ) ) { - return; - } - - var thisCache, i, l, - - // Reference to internal data cache key - internalKey = jQuery.expando, - - isNode = elem.nodeType, - - // See jQuery.data for more information - cache = isNode ? jQuery.cache : elem, - - // See jQuery.data for more information - id = isNode ? elem[ internalKey ] : internalKey; - - // If there is already no cache entry for this object, there is no - // purpose in continuing - if ( !cache[ id ] ) { - return; - } - - if ( name ) { - - thisCache = pvt ? cache[ id ] : cache[ id ].data; - - if ( thisCache ) { - - // Support array or space separated string names for data keys - if ( !jQuery.isArray( name ) ) { - - // try the string as a key before any manipulation - if ( name in thisCache ) { - name = [ name ]; - } else { - - // split the camel cased version by spaces unless a key with the spaces exists - name = jQuery.camelCase( name ); - if ( name in thisCache ) { - name = [ name ]; - } else { - name = name.split( " " ); - } - } - } - - for ( i = 0, l = name.length; i < l; i++ ) { - delete thisCache[ name[i] ]; - } - - // If there is no data left in the cache, we want to continue - // and let the cache object itself get destroyed - if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) { - return; - } - } - } - - // See jQuery.data for more information - if ( !pvt ) { - delete cache[ id ].data; - - // Don't destroy the parent cache unless the internal data object - // had been the only thing left in it - if ( !isEmptyDataObject(cache[ id ]) ) { - return; - } - } - - // Browsers that fail expando deletion also refuse to delete expandos on - // the window, but it will allow it on all other JS objects; other browsers - // don't care - // Ensure that `cache` is not a window object #10080 - if ( jQuery.support.deleteExpando || !cache.setInterval ) { - delete cache[ id ]; - } else { - cache[ id ] = null; - } - - // We destroyed the cache and need to eliminate the expando on the node to avoid - // false lookups in the cache for entries that no longer exist - if ( isNode ) { - // IE does not allow us to delete expando properties from nodes, - // nor does it have a removeAttribute function on Document nodes; - // we must handle all of these cases - if ( jQuery.support.deleteExpando ) { - delete elem[ internalKey ]; - } else if ( elem.removeAttribute ) { - elem.removeAttribute( internalKey ); - } else { - elem[ internalKey ] = null; - } - } - }, - - // For internal use only. - _data: function( elem, name, data ) { - return jQuery.data( elem, name, data, true ); - }, - - // A method for determining if a DOM node can handle the data expando - acceptData: function( elem ) { - if ( elem.nodeName ) { - var match = jQuery.noData[ elem.nodeName.toLowerCase() ]; - - if ( match ) { - return !(match === true || elem.getAttribute("classid") !== match); - } - } - - return true; - } -}); - -jQuery.fn.extend({ - data: function( key, value ) { - var parts, part, attr, name, l, - elem = this[0], - i = 0, - data = null; - - // Gets all values - if ( key === undefined ) { - if ( this.length ) { - data = jQuery.data( elem ); - - if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { - attr = elem.attributes; - for ( l = attr.length; i < l; i++ ) { - name = attr[i].name; - - if ( name.indexOf( "data-" ) === 0 ) { - name = jQuery.camelCase( name.substring(5) ); - - dataAttr( elem, name, data[ name ] ); - } - } - jQuery._data( elem, "parsedAttrs", true ); - } - } - - return data; - } - - // Sets multiple values - if ( typeof key === "object" ) { - return this.each(function() { - jQuery.data( this, key ); - }); - } - - parts = key.split( ".", 2 ); - parts[1] = parts[1] ? "." + parts[1] : ""; - part = parts[1] + "!"; - - return jQuery.access( this, function( value ) { - - if ( value === undefined ) { - data = this.triggerHandler( "getData" + part, [ parts[0] ] ); - - // Try to fetch any internally stored data first - if ( data === undefined && elem ) { - data = jQuery.data( elem, key ); - data = dataAttr( elem, key, data ); - } - - return data === undefined && parts[1] ? - this.data( parts[0] ) : - data; - } - - parts[1] = value; - this.each(function() { - var self = jQuery( this ); - - self.triggerHandler( "setData" + part, parts ); - jQuery.data( this, key, value ); - self.triggerHandler( "changeData" + part, parts ); - }); - }, null, value, arguments.length > 1, null, false ); - }, - - removeData: function( key ) { - return this.each(function() { - jQuery.removeData( this, key ); - }); - } -}); - -function dataAttr( elem, key, data ) { - // If nothing was found internally, try to fetch any - // data from the HTML5 data-* attribute - if ( data === undefined && elem.nodeType === 1 ) { - - var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); - - data = elem.getAttribute( name ); - - if ( typeof data === "string" ) { - try { - data = data === "true" ? true : - data === "false" ? false : - data === "null" ? null : - jQuery.isNumeric( data ) ? +data : - rbrace.test( data ) ? jQuery.parseJSON( data ) : - data; - } catch( e ) {} - - // Make sure we set the data so it isn't changed later - jQuery.data( elem, key, data ); - - } else { - data = undefined; - } - } - - return data; -} - -// checks a cache object for emptiness -function isEmptyDataObject( obj ) { - for ( var name in obj ) { - - // if the public data object is empty, the private is still empty - if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { - continue; - } - if ( name !== "toJSON" ) { - return false; - } - } - - return true; -} - - - - -function handleQueueMarkDefer( elem, type, src ) { - var deferDataKey = type + "defer", - queueDataKey = type + "queue", - markDataKey = type + "mark", - defer = jQuery._data( elem, deferDataKey ); - if ( defer && - ( src === "queue" || !jQuery._data(elem, queueDataKey) ) && - ( src === "mark" || !jQuery._data(elem, markDataKey) ) ) { - // Give room for hard-coded callbacks to fire first - // and eventually mark/queue something else on the element - setTimeout( function() { - if ( !jQuery._data( elem, queueDataKey ) && - !jQuery._data( elem, markDataKey ) ) { - jQuery.removeData( elem, deferDataKey, true ); - defer.fire(); - } - }, 0 ); - } -} - -jQuery.extend({ - - _mark: function( elem, type ) { - if ( elem ) { - type = ( type || "fx" ) + "mark"; - jQuery._data( elem, type, (jQuery._data( elem, type ) || 0) + 1 ); - } - }, - - _unmark: function( force, elem, type ) { - if ( force !== true ) { - type = elem; - elem = force; - force = false; - } - if ( elem ) { - type = type || "fx"; - var key = type + "mark", - count = force ? 0 : ( (jQuery._data( elem, key ) || 1) - 1 ); - if ( count ) { - jQuery._data( elem, key, count ); - } else { - jQuery.removeData( elem, key, true ); - handleQueueMarkDefer( elem, type, "mark" ); - } - } - }, - - queue: function( elem, type, data ) { - var q; - if ( elem ) { - type = ( type || "fx" ) + "queue"; - q = jQuery._data( elem, type ); - - // Speed up dequeue by getting out quickly if this is just a lookup - if ( data ) { - if ( !q || jQuery.isArray(data) ) { - q = jQuery._data( elem, type, jQuery.makeArray(data) ); - } else { - q.push( data ); - } - } - return q || []; - } - }, - - dequeue: function( elem, type ) { - type = type || "fx"; - - var queue = jQuery.queue( elem, type ), - fn = queue.shift(), - hooks = {}; - - // If the fx queue is dequeued, always remove the progress sentinel - if ( fn === "inprogress" ) { - fn = queue.shift(); - } - - if ( fn ) { - // Add a progress sentinel to prevent the fx queue from being - // automatically dequeued - if ( type === "fx" ) { - queue.unshift( "inprogress" ); - } - - jQuery._data( elem, type + ".run", hooks ); - fn.call( elem, function() { - jQuery.dequeue( elem, type ); - }, hooks ); - } - - if ( !queue.length ) { - jQuery.removeData( elem, type + "queue " + type + ".run", true ); - handleQueueMarkDefer( elem, type, "queue" ); - } - } -}); - -jQuery.fn.extend({ - queue: function( type, data ) { - var setter = 2; - - if ( typeof type !== "string" ) { - data = type; - type = "fx"; - setter--; - } - - if ( arguments.length < setter ) { - return jQuery.queue( this[0], type ); - } - - return data === undefined ? - this : - this.each(function() { - var queue = jQuery.queue( this, type, data ); - - if ( type === "fx" && queue[0] !== "inprogress" ) { - jQuery.dequeue( this, type ); - } - }); - }, - dequeue: function( type ) { - return this.each(function() { - jQuery.dequeue( this, type ); - }); - }, - // Based off of the plugin by Clint Helfers, with permission. - // http://blindsignals.com/index.php/2009/07/jquery-delay/ - delay: function( time, type ) { - time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; - type = type || "fx"; - - return this.queue( type, function( next, hooks ) { - var timeout = setTimeout( next, time ); - hooks.stop = function() { - clearTimeout( timeout ); - }; - }); - }, - clearQueue: function( type ) { - return this.queue( type || "fx", [] ); - }, - // Get a promise resolved when queues of a certain type - // are emptied (fx is the type by default) - promise: function( type, object ) { - if ( typeof type !== "string" ) { - object = type; - type = undefined; - } - type = type || "fx"; - var defer = jQuery.Deferred(), - elements = this, - i = elements.length, - count = 1, - deferDataKey = type + "defer", - queueDataKey = type + "queue", - markDataKey = type + "mark", - tmp; - function resolve() { - if ( !( --count ) ) { - defer.resolveWith( elements, [ elements ] ); - } - } - while( i-- ) { - if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) || - ( jQuery.data( elements[ i ], queueDataKey, undefined, true ) || - jQuery.data( elements[ i ], markDataKey, undefined, true ) ) && - jQuery.data( elements[ i ], deferDataKey, jQuery.Callbacks( "once memory" ), true ) )) { - count++; - tmp.add( resolve ); - } - } - resolve(); - return defer.promise( object ); - } -}); - - - - -var rclass = /[\n\t\r]/g, - rspace = /\s+/, - rreturn = /\r/g, - rtype = /^(?:button|input)$/i, - rfocusable = /^(?:button|input|object|select|textarea)$/i, - rclickable = /^a(?:rea)?$/i, - rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i, - getSetAttribute = jQuery.support.getSetAttribute, - nodeHook, boolHook, fixSpecified; - -jQuery.fn.extend({ - attr: function( name, value ) { - return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 ); - }, - - removeAttr: function( name ) { - return this.each(function() { - jQuery.removeAttr( this, name ); - }); - }, - - prop: function( name, value ) { - return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 ); - }, - - removeProp: function( name ) { - name = jQuery.propFix[ name ] || name; - return this.each(function() { - // try/catch handles cases where IE balks (such as removing a property on window) - try { - this[ name ] = undefined; - delete this[ name ]; - } catch( e ) {} - }); - }, - - addMethod: function( value ) { - var classNames, i, l, elem, - setMethod, c, cl; - - if ( jQuery.isFunction( value ) ) { - return this.each(function( j ) { - jQuery( this ).addMethod( value.call(this, j, this.className) ); - }); - } - - if ( value && typeof value === "string" ) { - classNames = value.split( rspace ); - - for ( i = 0, l = this.length; i < l; i++ ) { - elem = this[ i ]; - - if ( elem.nodeType === 1 ) { - if ( !elem.className && classNames.length === 1 ) { - elem.className = value; - - } else { - setMethod = " " + elem.className + " "; - - for ( c = 0, cl = classNames.length; c < cl; c++ ) { - if ( !~setMethod.indexOf( " " + classNames[ c ] + " " ) ) { - setMethod += classNames[ c ] + " "; - } - } - elem.className = jQuery.trim( setMethod ); - } - } - } - } - - return this; - }, - - removeMethod: function( value ) { - var classNames, i, l, elem, className, c, cl; - - if ( jQuery.isFunction( value ) ) { - return this.each(function( j ) { - jQuery( this ).removeMethod( value.call(this, j, this.className) ); - }); - } - - if ( (value && typeof value === "string") || value === undefined ) { - classNames = ( value || "" ).split( rspace ); - - for ( i = 0, l = this.length; i < l; i++ ) { - elem = this[ i ]; - - if ( elem.nodeType === 1 && elem.className ) { - if ( value ) { - className = (" " + elem.className + " ").replace( rclass, " " ); - for ( c = 0, cl = classNames.length; c < cl; c++ ) { - className = className.replace(" " + classNames[ c ] + " ", " "); - } - elem.className = jQuery.trim( className ); - - } else { - elem.className = ""; - } - } - } - } - - return this; - }, - - toggleMethod: function( value, stateVal ) { - var type = typeof value, - isBool = typeof stateVal === "boolean"; - - if ( jQuery.isFunction( value ) ) { - return this.each(function( i ) { - jQuery( this ).toggleMethod( value.call(this, i, this.className, stateVal), stateVal ); - }); - } - - return this.each(function() { - if ( type === "string" ) { - // toggle individual class names - var className, - i = 0, - self = jQuery( this ), - state = stateVal, - classNames = value.split( rspace ); - - while ( (className = classNames[ i++ ]) ) { - // check each className given, space seperated list - state = isBool ? state : !self.hasMethod( className ); - self[ state ? "addMethod" : "removeMethod" ]( className ); - } - - } else if ( type === "undefined" || type === "boolean" ) { - if ( this.className ) { - // store className if set - jQuery._data( this, "__className__", this.className ); - } - - // toggle whole className - this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; - } - }); - }, - - hasMethod: function( selector ) { - var className = " " + selector + " ", - i = 0, - l = this.length; - for ( ; i < l; i++ ) { - if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) { - return true; - } - } - - return false; - }, - - val: function( value ) { - var hooks, ret, isFunction, - elem = this[0]; - - if ( !arguments.length ) { - if ( elem ) { - hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ]; - - if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { - return ret; - } - - ret = elem.value; - - return typeof ret === "string" ? - // handle most common string cases - ret.replace(rreturn, "") : - // handle cases where value is null/undef or number - ret == null ? "" : ret; - } - - return; - } - - isFunction = jQuery.isFunction( value ); - - return this.each(function( i ) { - var self = jQuery(this), val; - - if ( this.nodeType !== 1 ) { - return; - } - - if ( isFunction ) { - val = value.call( this, i, self.val() ); - } else { - val = value; - } - - // Treat null/undefined as ""; convert numbers to string - if ( val == null ) { - val = ""; - } else if ( typeof val === "number" ) { - val += ""; - } else if ( jQuery.isArray( val ) ) { - val = jQuery.map(val, function ( value ) { - return value == null ? "" : value + ""; - }); - } - - hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; - - // If set returns undefined, fall back to normal setting - if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { - this.value = val; - } - }); - } -}); - -jQuery.extend({ - valHooks: { - option: { - get: function( elem ) { - // attributes.value is undefined in Blackberry 4.7 but - // uses .value. See #6932 - var val = elem.attributes.value; - return !val || val.specified ? elem.value : elem.text; - } - }, - select: { - get: function( elem ) { - var value, i, max, option, - index = elem.selectedIndex, - values = [], - options = elem.options, - one = elem.type === "select-one"; - - // Nothing was selected - if ( index < 0 ) { - return null; - } - - // Loop through all the selected options - i = one ? index : 0; - max = one ? index + 1 : options.length; - for ( ; i < max; i++ ) { - option = options[ i ]; - - // Don't return options that are disabled or in a disabled optgroup - if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) && - (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) { - - // Get the specific value for the option - value = jQuery( option ).val(); - - // We don't need an array for one selects - if ( one ) { - return value; - } - - // Multi-Selects return an array - values.push( value ); - } - } - - // Fixes Bug #2551 -- select.val() broken in IE after form.reset() - if ( one && !values.length && options.length ) { - return jQuery( options[ index ] ).val(); - } - - return values; - }, - - set: function( elem, value ) { - var values = jQuery.makeArray( value ); - - jQuery(elem).find("option").each(function() { - this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0; - }); - - if ( !values.length ) { - elem.selectedIndex = -1; - } - return values; - } - } - }, - - attrFn: { - val: true, - css: true, - html: true, - text: true, - data: true, - width: true, - height: true, - offset: true - }, - - attr: function( elem, name, value, pass ) { - var ret, hooks, notxml, - nType = elem.nodeType; - - // don't get/set attributes on text, comment and attribute nodes - if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { - return; - } - - if ( pass && name in jQuery.attrFn ) { - return jQuery( elem )[ name ]( value ); - } - - // Fallback to prop when attributes are not supported - if ( typeof elem.getAttribute === "undefined" ) { - return jQuery.prop( elem, name, value ); - } - - notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); - - // All attributes are lowercase - // Grab necessary hook if one is defined - if ( notxml ) { - name = name.toLowerCase(); - hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook ); - } - - if ( value !== undefined ) { - - if ( value === null ) { - jQuery.removeAttr( elem, name ); - return; - - } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) { - return ret; - - } else { - elem.setAttribute( name, "" + value ); - return value; - } - - } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) { - return ret; - - } else { - - ret = elem.getAttribute( name ); - - // Non-existent attributes return null, we normalize to undefined - return ret === null ? - undefined : - ret; - } - }, - - removeAttr: function( elem, value ) { - var propName, attrNames, name, l, isBool, - i = 0; - - if ( value && elem.nodeType === 1 ) { - attrNames = value.toLowerCase().split( rspace ); - l = attrNames.length; - - for ( ; i < l; i++ ) { - name = attrNames[ i ]; - - if ( name ) { - propName = jQuery.propFix[ name ] || name; - isBool = rboolean.test( name ); - - // See #9699 for explanation of this approach (setting first, then removal) - // Do not do this for boolean attributes (see #10870) - if ( !isBool ) { - jQuery.attr( elem, name, "" ); - } - elem.removeAttribute( getSetAttribute ? name : propName ); - - // Set corresponding property to false for boolean attributes - if ( isBool && propName in elem ) { - elem[ propName ] = false; - } - } - } - } - }, - - attrHooks: { - type: { - set: function( elem, value ) { - // We can't allow the type property to be changed (since it causes problems in IE) - if ( rtype.test( elem.nodeName ) && elem.parentNode ) { - jQuery.error( "type property can't be changed" ); - } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { - // Setting the type on a radio button after the value resets the value in IE6-9 - // Reset value to it's default in case type is set after value - // This is for element creation - var val = elem.value; - elem.setAttribute( "type", value ); - if ( val ) { - elem.value = val; - } - return value; - } - } - }, - // Use the value property for back compat - // Use the nodeHook for button elements in IE6/7 (#1954) - value: { - get: function( elem, name ) { - if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { - return nodeHook.get( elem, name ); - } - return name in elem ? - elem.value : - null; - }, - set: function( elem, value, name ) { - if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { - return nodeHook.set( elem, value, name ); - } - // Does not return so that setAttribute is also used - elem.value = value; - } - } - }, - - propFix: { - tabindex: "tabIndex", - readonly: "readOnly", - "for": "htmlFor", - "class": "className", - maxlength: "maxLength", - cellspacing: "cellSpacing", - cellpadding: "cellPadding", - rowspan: "rowSpan", - colspan: "colSpan", - usemap: "useMap", - frameborder: "frameBorder", - contenteditable: "contentEditable" - }, - - prop: function( elem, name, value ) { - var ret, hooks, notxml, - nType = elem.nodeType; - - // don't get/set properties on text, comment and attribute nodes - if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { - return; - } - - notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); - - if ( notxml ) { - // Fix name and attach hooks - name = jQuery.propFix[ name ] || name; - hooks = jQuery.propHooks[ name ]; - } - - if ( value !== undefined ) { - if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { - return ret; - - } else { - return ( elem[ name ] = value ); - } - - } else { - if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { - return ret; - - } else { - return elem[ name ]; - } - } - }, - - propHooks: { - tabIndex: { - get: function( elem ) { - // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set - // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ - var attributeNode = elem.getAttributeNode("tabindex"); - - return attributeNode && attributeNode.specified ? - parseInt( attributeNode.value, 10 ) : - rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? - 0 : - undefined; - } - } - } -}); - -// Add the tabIndex propHook to attrHooks for back-compat (different case is intentional) -jQuery.attrHooks.tabindex = jQuery.propHooks.tabIndex; - -// Hook for boolean attributes -boolHook = { - get: function( elem, name ) { - // Align boolean attributes with corresponding properties - // Fall back to attribute presence where some booleans are not supported - var attrNode, - property = jQuery.prop( elem, name ); - return property === true || typeof property !== "boolean" && ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ? - name.toLowerCase() : - undefined; - }, - set: function( elem, value, name ) { - var propName; - if ( value === false ) { - // Remove boolean attributes when set to false - jQuery.removeAttr( elem, name ); - } else { - // value is true since we know at this point it's type boolean and not false - // Set boolean attributes to the same name and set the DOM property - propName = jQuery.propFix[ name ] || name; - if ( propName in elem ) { - // Only set the IDL specifically if it already exists on the element - elem[ propName ] = true; - } - - elem.setAttribute( name, name.toLowerCase() ); - } - return name; - } -}; - -// IE6/7 do not support getting/setting some attributes with get/setAttribute -if ( !getSetAttribute ) { - - fixSpecified = { - name: true, - id: true, - coords: true - }; - - // Use this for any attribute in IE6/7 - // This fixes almost every IE6/7 issue - nodeHook = jQuery.valHooks.button = { - get: function( elem, name ) { - var ret; - ret = elem.getAttributeNode( name ); - return ret && ( fixSpecified[ name ] ? ret.nodeValue !== "" : ret.specified ) ? - ret.nodeValue : - undefined; - }, - set: function( elem, value, name ) { - // Set the existing or create a new attribute node - var ret = elem.getAttributeNode( name ); - if ( !ret ) { - ret = document.createAttribute( name ); - elem.setAttributeNode( ret ); - } - return ( ret.nodeValue = value + "" ); - } - }; - - // Apply the nodeHook to tabindex - jQuery.attrHooks.tabindex.set = nodeHook.set; - - // Set width and height to auto instead of 0 on empty string( Bug #8150 ) - // This is for removals - jQuery.each([ "width", "height" ], function( i, name ) { - jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { - set: function( elem, value ) { - if ( value === "" ) { - elem.setAttribute( name, "auto" ); - return value; - } - } - }); - }); - - // Set contenteditable to false on removals(#10429) - // Setting to empty string throws an error as an invalid value - jQuery.attrHooks.contenteditable = { - get: nodeHook.get, - set: function( elem, value, name ) { - if ( value === "" ) { - value = "false"; - } - nodeHook.set( elem, value, name ); - } - }; -} - - -// Some attributes require a special call on IE -if ( !jQuery.support.hrefNormalized ) { - jQuery.each([ "href", "src", "width", "height" ], function( i, name ) { - jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { - get: function( elem ) { - var ret = elem.getAttribute( name, 2 ); - return ret === null ? undefined : ret; - } - }); - }); -} - -if ( !jQuery.support.style ) { - jQuery.attrHooks.style = { - get: function( elem ) { - // Return undefined in the case of empty string - // Normalize to lowercase since IE uppercases css property names - return elem.style.cssText.toLowerCase() || undefined; - }, - set: function( elem, value ) { - return ( elem.style.cssText = "" + value ); - } - }; -} - -// Safari mis-reports the default selected property of an option -// Accessing the parent's selectedIndex property fixes it -if ( !jQuery.support.optSelected ) { - jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, { - get: function( elem ) { - var parent = elem.parentNode; - - if ( parent ) { - parent.selectedIndex; - - // Make sure that it also works with optgroups, see #5701 - if ( parent.parentNode ) { - parent.parentNode.selectedIndex; - } - } - return null; - } - }); -} - -// IE6/7 call enctype encoding -if ( !jQuery.support.enctype ) { - jQuery.propFix.enctype = "encoding"; -} - -// Radios and checkboxes getter/setter -if ( !jQuery.support.checkOn ) { - jQuery.each([ "radio", "checkbox" ], function() { - jQuery.valHooks[ this ] = { - get: function( elem ) { - // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified - return elem.getAttribute("value") === null ? "on" : elem.value; - } - }; - }); -} -jQuery.each([ "radio", "checkbox" ], function() { - jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], { - set: function( elem, value ) { - if ( jQuery.isArray( value ) ) { - return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 ); - } - } - }); -}); - - - - -var rformElems = /^(?:textarea|input|select)$/i, - rtypenamespace = /^([^\.]*)?(?:\.(.+))?$/, - rhoverHack = /(?:^|\s)hover(\.\S+)?\b/, - rkeyEvent = /^key/, - rmouseEvent = /^(?:mouse|contextmenu)|click/, - rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, - rquickIs = /^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/, - quickParse = function( selector ) { - var quick = rquickIs.exec( selector ); - if ( quick ) { - // 0 1 2 3 - // [ _, tag, id, class ] - quick[1] = ( quick[1] || "" ).toLowerCase(); - quick[3] = quick[3] && new RegExp( "(?:^|\\s)" + quick[3] + "(?:\\s|$)" ); - } - return quick; - }, - quickIs = function( elem, m ) { - var attrs = elem.attributes || {}; - return ( - (!m[1] || elem.nodeName.toLowerCase() === m[1]) && - (!m[2] || (attrs.id || {}).value === m[2]) && - (!m[3] || m[3].test( (attrs[ "class" ] || {}).value )) - ); - }, - hoverHack = function( events ) { - return jQuery.event.special.hover ? events : events.replace( rhoverHack, "mouseenter$1 mouseleave$1" ); - }; - -/* - * Helper functions for managing events -- not part of the public interface. - * Props to Dean Edwards' addEvent library for many of the ideas. - */ -jQuery.event = { - - add: function( elem, types, handler, data, selector ) { - - var elemData, eventHandle, events, - t, tns, type, namespaces, handleObj, - handleObjIn, quick, handlers, special; - - // Don't attach events to noData or text/comment nodes (allow plain objects tho) - if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) { - return; - } - - // Caller can pass in an object of custom data in lieu of the handler - if ( handler.handler ) { - handleObjIn = handler; - handler = handleObjIn.handler; - selector = handleObjIn.selector; - } - - // Make sure that the handler has a unique ID, used to find/remove it later - if ( !handler.guid ) { - handler.guid = jQuery.guid++; - } - - // Init the element's event structure and main handler, if this is the first - events = elemData.events; - if ( !events ) { - elemData.events = events = {}; - } - eventHandle = elemData.handle; - if ( !eventHandle ) { - elemData.handle = eventHandle = function( e ) { - // Discard the second event of a jQuery.event.trigger() and - // when an event is called after a page has unloaded - return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ? - jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : - undefined; - }; - // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events - eventHandle.elem = elem; - } - - // Handle multiple events separated by a space - // jQuery(...).bind("mouseover mouseout", fn); - types = jQuery.trim( hoverHack(types) ).split( " " ); - for ( t = 0; t < types.length; t++ ) { - - tns = rtypenamespace.exec( types[t] ) || []; - type = tns[1]; - namespaces = ( tns[2] || "" ).split( "." ).sort(); - - // If event changes its type, use the special event handlers for the changed type - special = jQuery.event.special[ type ] || {}; - - // If selector defined, determine special event api type, otherwise given type - type = ( selector ? special.delegateType : special.bindType ) || type; - - // Update special based on newly reset type - special = jQuery.event.special[ type ] || {}; - - // handleObj is passed to all event handlers - handleObj = jQuery.extend({ - type: type, - origType: tns[1], - data: data, - handler: handler, - guid: handler.guid, - selector: selector, - quick: selector && quickParse( selector ), - namespace: namespaces.join(".") - }, handleObjIn ); - - // Init the event handler queue if we're the first - handlers = events[ type ]; - if ( !handlers ) { - handlers = events[ type ] = []; - handlers.delegateCount = 0; - - // Only use addEventListener/attachEvent if the special events handler returns false - if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { - // Bind the global event handler to the element - if ( elem.addEventListener ) { - elem.addEventListener( type, eventHandle, false ); - - } else if ( elem.attachEvent ) { - elem.attachEvent( "on" + type, eventHandle ); - } - } - } - - if ( special.add ) { - special.add.call( elem, handleObj ); - - if ( !handleObj.handler.guid ) { - handleObj.handler.guid = handler.guid; - } - } - - // Add to the element's handler list, delegates in front - if ( selector ) { - handlers.splice( handlers.delegateCount++, 0, handleObj ); - } else { - handlers.push( handleObj ); - } - - // Keep track of which events have ever been used, for event optimization - jQuery.event.global[ type ] = true; - } - - // Nullify elem to prevent memory leaks in IE - elem = null; - }, - - global: {}, - - // Detach an event or set of events from an element - remove: function( elem, types, handler, selector, mappedTypes ) { - - var elemData = jQuery.hasData( elem ) && jQuery._data( elem ), - t, tns, type, origType, namespaces, origCount, - j, events, special, handle, eventType, handleObj; - - if ( !elemData || !(events = elemData.events) ) { - return; - } - - // Once for each type.namespace in types; type may be omitted - types = jQuery.trim( hoverHack( types || "" ) ).split(" "); - for ( t = 0; t < types.length; t++ ) { - tns = rtypenamespace.exec( types[t] ) || []; - type = origType = tns[1]; - namespaces = tns[2]; - - // Unbind all events (on this namespace, if provided) for the element - if ( !type ) { - for ( type in events ) { - jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); - } - continue; - } - - special = jQuery.event.special[ type ] || {}; - type = ( selector? special.delegateType : special.bindType ) || type; - eventType = events[ type ] || []; - origCount = eventType.length; - namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.)?") + "(\\.|$)") : null; - - // Remove matching events - for ( j = 0; j < eventType.length; j++ ) { - handleObj = eventType[ j ]; - - if ( ( mappedTypes || origType === handleObj.origType ) && - ( !handler || handler.guid === handleObj.guid ) && - ( !namespaces || namespaces.test( handleObj.namespace ) ) && - ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { - eventType.splice( j--, 1 ); - - if ( handleObj.selector ) { - eventType.delegateCount--; - } - if ( special.remove ) { - special.remove.call( elem, handleObj ); - } - } - } - - // Remove generic event handler if we removed something and no more handlers exist - // (avoids potential for endless recursion during removal of special event handlers) - if ( eventType.length === 0 && origCount !== eventType.length ) { - if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) { - jQuery.removeEvent( elem, type, elemData.handle ); - } - - delete events[ type ]; - } - } - - // Remove the expando if it's no longer used - if ( jQuery.isEmptyObject( events ) ) { - handle = elemData.handle; - if ( handle ) { - handle.elem = null; - } - - // removeData also checks for emptiness and clears the expando if empty - // so use it instead of delete - jQuery.removeData( elem, [ "events", "handle" ], true ); - } - }, - - // Events that are safe to short-circuit if no handlers are attached. - // Native DOM events should not be added, they may have inline handlers. - customEvent: { - "getData": true, - "setData": true, - "changeData": true - }, - - trigger: function( event, data, elem, onlyHandlers ) { - // Don't do events on text and comment nodes - if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) { - return; - } - - // Event object or event type - var type = event.type || event, - namespaces = [], - cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType; - - // focus/blur morphs to focusin/out; ensure we're not firing them right now - if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { - return; - } - - if ( type.indexOf( "!" ) >= 0 ) { - // Exclusive events trigger only for the exact event (no namespaces) - type = type.slice(0, -1); - exclusive = true; - } - - if ( type.indexOf( "." ) >= 0 ) { - // Namespaced trigger; create a regexp to match event type in handle() - namespaces = type.split("."); - type = namespaces.shift(); - namespaces.sort(); - } - - if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) { - // No jQuery handlers for this event type, and it can't have inline handlers - return; - } - - // Caller can pass in an Event, Object, or just an event type string - event = typeof event === "object" ? - // jQuery.Event object - event[ jQuery.expando ] ? event : - // Object literal - new jQuery.Event( type, event ) : - // Just the event type (string) - new jQuery.Event( type ); - - event.type = type; - event.isTrigger = true; - event.exclusive = exclusive; - event.namespace = namespaces.join( "." ); - event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)") : null; - ontype = type.indexOf( ":" ) < 0 ? "on" + type : ""; - - // Handle a global trigger - if ( !elem ) { - - // TODO: Stop taunting the data cache; remove global events and always attach to document - cache = jQuery.cache; - for ( i in cache ) { - if ( cache[ i ].events && cache[ i ].events[ type ] ) { - jQuery.event.trigger( event, data, cache[ i ].handle.elem, true ); - } - } - return; - } - - // Clean up the event in case it is being reused - event.result = undefined; - if ( !event.target ) { - event.target = elem; - } - - // Clone any incoming data and prepend the event, creating the handler arg list - data = data != null ? jQuery.makeArray( data ) : []; - data.unshift( event ); - - // Allow special events to draw outside the lines - special = jQuery.event.special[ type ] || {}; - if ( special.trigger && special.trigger.apply( elem, data ) === false ) { - return; - } - - // Determine event propagation path in advance, per W3C events spec (#9951) - // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) - eventPath = [[ elem, special.bindType || type ]]; - if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { - - bubbleType = special.delegateType || type; - cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode; - old = null; - for ( ; cur; cur = cur.parentNode ) { - eventPath.push([ cur, bubbleType ]); - old = cur; - } - - // Only add window if we got to document (e.g., not plain obj or detached DOM) - if ( old && old === elem.ownerDocument ) { - eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]); - } - } - - // Fire handlers on the event path - for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) { - - cur = eventPath[i][0]; - event.type = eventPath[i][1]; - - handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); - if ( handle ) { - handle.apply( cur, data ); - } - // Note that this is a bare JS function and not a jQuery handler - handle = ontype && cur[ ontype ]; - if ( handle && jQuery.acceptData( cur ) && handle.apply( cur, data ) === false ) { - event.preventDefault(); - } - } - event.type = type; - - // If nobody prevented the default action, do it now - if ( !onlyHandlers && !event.isDefaultPrevented() ) { - - if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) && - !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) { - - // Call a native DOM method on the target with the same name name as the event. - // Can't use an .isFunction() check here because IE6/7 fails that test. - // Don't do default actions on window, that's where global variables be (#6170) - // IE<9 dies on focus/blur to hidden element (#1486) - if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) { - - // Don't re-trigger an onFOO event when we call its FOO() method - old = elem[ ontype ]; - - if ( old ) { - elem[ ontype ] = null; - } - - // Prevent re-triggering of the same event, since we already bubbled it above - jQuery.event.triggered = type; - elem[ type ](); - jQuery.event.triggered = undefined; - - if ( old ) { - elem[ ontype ] = old; - } - } - } - } - - return event.result; - }, - - dispatch: function( event ) { - - // Make a writable jQuery.Event from the native event object - event = jQuery.event.fix( event || window.event ); - - var handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []), - delegateCount = handlers.delegateCount, - args = [].slice.call( arguments, 0 ), - run_all = !event.exclusive && !event.namespace, - special = jQuery.event.special[ event.type ] || {}, - handlerQueue = [], - i, j, cur, jqcur, ret, selMatch, matched, matches, handleObj, sel, related; - - // Use the fix-ed jQuery.Event rather than the (read-only) native event - args[0] = event; - event.delegateTarget = this; - - // Call the preDispatch hook for the mapped type, and let it bail if desired - if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { - return; - } - - // Determine handlers that should run if there are delegated events - // Avoid non-left-click bubbling in Firefox (#3861) - if ( delegateCount && !(event.button && event.type === "click") ) { - - // Pregenerate a single jQuery object for reuse with .is() - jqcur = jQuery(this); - jqcur.context = this.ownerDocument || this; - - for ( cur = event.target; cur != this; cur = cur.parentNode || this ) { - - // Don't process events on disabled elements (#6911, #8165) - if ( cur.disabled !== true ) { - selMatch = {}; - matches = []; - jqcur[0] = cur; - for ( i = 0; i < delegateCount; i++ ) { - handleObj = handlers[ i ]; - sel = handleObj.selector; - - if ( selMatch[ sel ] === undefined ) { - selMatch[ sel ] = ( - handleObj.quick ? quickIs( cur, handleObj.quick ) : jqcur.is( sel ) - ); - } - if ( selMatch[ sel ] ) { - matches.push( handleObj ); - } - } - if ( matches.length ) { - handlerQueue.push({ elem: cur, matches: matches }); - } - } - } - } - - // Add the remaining (directly-bound) handlers - if ( handlers.length > delegateCount ) { - handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) }); - } - - // Run delegates first; they may want to stop propagation beneath us - for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) { - matched = handlerQueue[ i ]; - event.currentTarget = matched.elem; - - for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) { - handleObj = matched.matches[ j ]; - - // Triggered event must either 1) be non-exclusive and have no namespace, or - // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). - if ( run_all || (!event.namespace && !handleObj.namespace) || event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) { - - event.data = handleObj.data; - event.handleObj = handleObj; - - ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) - .apply( matched.elem, args ); - - if ( ret !== undefined ) { - event.result = ret; - if ( ret === false ) { - event.preventDefault(); - event.stopPropagation(); - } - } - } - } - } - - // Call the postDispatch hook for the mapped type - if ( special.postDispatch ) { - special.postDispatch.call( this, event ); - } - - return event.result; - }, - - // Includes some event props shared by KeyEvent and MouseEvent - // *** attrChange attrName relatedNode srcElement are not normalized, non-W3C, deprecated, will be removed in 1.8 *** - props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), - - fixHooks: {}, - - keyHooks: { - props: "char charCode key keyCode".split(" "), - filter: function( event, original ) { - - // Add which for key events - if ( event.which == null ) { - event.which = original.charCode != null ? original.charCode : original.keyCode; - } - - return event; - } - }, - - mouseHooks: { - props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), - filter: function( event, original ) { - var eventDoc, doc, body, - button = original.button, - fromElement = original.fromElement; - - // Calculate pageX/Y if missing and clientX/Y available - if ( event.pageX == null && original.clientX != null ) { - eventDoc = event.target.ownerDocument || document; - doc = eventDoc.documentElement; - body = eventDoc.body; - - event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); - event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); - } - - // Add relatedTarget, if necessary - if ( !event.relatedTarget && fromElement ) { - event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; - } - - // Add which for click: 1 === left; 2 === middle; 3 === right - // Note: button is not normalized, so don't use it - if ( !event.which && button !== undefined ) { - event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); - } - - return event; - } - }, - - fix: function( event ) { - if ( event[ jQuery.expando ] ) { - return event; - } - - // Create a writable copy of the event object and normalize some properties - var i, prop, - originalEvent = event, - fixHook = jQuery.event.fixHooks[ event.type ] || {}, - copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; - - event = jQuery.Event( originalEvent ); - - for ( i = copy.length; i; ) { - prop = copy[ --i ]; - event[ prop ] = originalEvent[ prop ]; - } - - // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2) - if ( !event.target ) { - event.target = originalEvent.srcElement || document; - } - - // Target should not be a text node (#504, Safari) - if ( event.target.nodeType === 3 ) { - event.target = event.target.parentNode; - } - - // For mouse/key events; add metaKey if it's not there (#3368, IE6/7/8) - if ( event.metaKey === undefined ) { - event.metaKey = event.ctrlKey; - } - - return fixHook.filter? fixHook.filter( event, originalEvent ) : event; - }, - - special: { - ready: { - // Make sure the ready event is setup - setup: jQuery.bindReady - }, - - load: { - // Prevent triggered image.load events from bubbling to window.load - noBubble: true - }, - - focus: { - delegateType: "focusin" - }, - blur: { - delegateType: "focusout" - }, - - beforeunload: { - setup: function( data, namespaces, eventHandle ) { - // We only want to do this special case on windows - if ( jQuery.isWindow( this ) ) { - this.onbeforeunload = eventHandle; - } - }, - - teardown: function( namespaces, eventHandle ) { - if ( this.onbeforeunload === eventHandle ) { - this.onbeforeunload = null; - } - } - } - }, - - simulate: function( type, elem, event, bubble ) { - // Piggyback on a donor event to simulate a different one. - // Fake originalEvent to avoid donor's stopPropagation, but if the - // simulated event prevents default then we do the same on the donor. - var e = jQuery.extend( - new jQuery.Event(), - event, - { type: type, - isSimulated: true, - originalEvent: {} - } - ); - if ( bubble ) { - jQuery.event.trigger( e, null, elem ); - } else { - jQuery.event.dispatch.call( elem, e ); - } - if ( e.isDefaultPrevented() ) { - event.preventDefault(); - } - } -}; - -// Some plugins are using, but it's undocumented/deprecated and will be removed. -// The 1.7 special event interface should provide all the hooks needed now. -jQuery.event.handle = jQuery.event.dispatch; - -jQuery.removeEvent = document.removeEventListener ? - function( elem, type, handle ) { - if ( elem.removeEventListener ) { - elem.removeEventListener( type, handle, false ); - } - } : - function( elem, type, handle ) { - if ( elem.detachEvent ) { - elem.detachEvent( "on" + type, handle ); - } - }; - -jQuery.Event = function( src, props ) { - // Allow instantiation without the 'new' keyword - if ( !(this instanceof jQuery.Event) ) { - return new jQuery.Event( src, props ); - } - - // Event object - if ( src && src.type ) { - this.originalEvent = src; - this.type = src.type; - - // Events bubbling up the document may have been marked as prevented - // by a handler lower down the tree; reflect the correct value. - this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false || - src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse; - - // Event type - } else { - this.type = src; - } - - // Put explicitly provided properties onto the event object - if ( props ) { - jQuery.extend( this, props ); - } - - // Create a timestamp if incoming event doesn't have one - this.timeStamp = src && src.timeStamp || jQuery.now(); - - // Mark it as fixed - this[ jQuery.expando ] = true; -}; - -function returnFalse() { - return false; -} -function returnTrue() { - return true; -} - -// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding -// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html -jQuery.Event.prototype = { - preventDefault: function() { - this.isDefaultPrevented = returnTrue; - - var e = this.originalEvent; - if ( !e ) { - return; - } - - // if preventDefault exists run it on the original event - if ( e.preventDefault ) { - e.preventDefault(); - - // otherwise set the returnValue property of the original event to false (IE) - } else { - e.returnValue = false; - } - }, - stopPropagation: function() { - this.isPropagationStopped = returnTrue; - - var e = this.originalEvent; - if ( !e ) { - return; - } - // if stopPropagation exists run it on the original event - if ( e.stopPropagation ) { - e.stopPropagation(); - } - // otherwise set the cancelBubble property of the original event to true (IE) - e.cancelBubble = true; - }, - stopImmediatePropagation: function() { - this.isImmediatePropagationStopped = returnTrue; - this.stopPropagation(); - }, - isDefaultPrevented: returnFalse, - isPropagationStopped: returnFalse, - isImmediatePropagationStopped: returnFalse -}; - -// Create mouseenter/leave events using mouseover/out and event-time checks -jQuery.each({ - mouseenter: "mouseover", - mouseleave: "mouseout" -}, function( orig, fix ) { - jQuery.event.special[ orig ] = { - delegateType: fix, - bindType: fix, - - handle: function( event ) { - var target = this, - related = event.relatedTarget, - handleObj = event.handleObj, - selector = handleObj.selector, - ret; - - // For mousenter/leave call the handler if related is outside the target. - // NB: No relatedTarget if the mouse left/entered the browser window - if ( !related || (related !== target && !jQuery.contains( target, related )) ) { - event.type = handleObj.origType; - ret = handleObj.handler.apply( this, arguments ); - event.type = fix; - } - return ret; - } - }; -}); - -// IE submit delegation -if ( !jQuery.support.submitBubbles ) { - - jQuery.event.special.submit = { - setup: function() { - // Only need this for delegated form submit events - if ( jQuery.nodeName( this, "form" ) ) { - return false; - } - - // Lazy-add a submit handler when a descendant form may potentially be submitted - jQuery.event.add( this, "click._submit keypress._submit", function( e ) { - // Node name check avoids a VML-related crash in IE (#9807) - var elem = e.target, - form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; - if ( form && !form._submit_attached ) { - jQuery.event.add( form, "submit._submit", function( event ) { - event._submit_bubble = true; - }); - form._submit_attached = true; - } - }); - // return undefined since we don't need an event listener - }, - - postDispatch: function( event ) { - // If form was submitted by the user, bubble the event up the tree - if ( event._submit_bubble ) { - delete event._submit_bubble; - if ( this.parentNode && !event.isTrigger ) { - jQuery.event.simulate( "submit", this.parentNode, event, true ); - } - } - }, - - teardown: function() { - // Only need this for delegated form submit events - if ( jQuery.nodeName( this, "form" ) ) { - return false; - } - - // Remove delegated handlers; cleanData eventually reaps submit handlers attached above - jQuery.event.remove( this, "._submit" ); - } - }; -} - -// IE change delegation and checkbox/radio fix -if ( !jQuery.support.changeBubbles ) { - - jQuery.event.special.change = { - - setup: function() { - - if ( rformElems.test( this.nodeName ) ) { - // IE doesn't fire change on a check/radio until blur; trigger it on click - // after a propertychange. Eat the blur-change in special.change.handle. - // This still fires onchange a second time for check/radio after blur. - if ( this.type === "checkbox" || this.type === "radio" ) { - jQuery.event.add( this, "propertychange._change", function( event ) { - if ( event.originalEvent.propertyName === "checked" ) { - this._just_changed = true; - } - }); - jQuery.event.add( this, "click._change", function( event ) { - if ( this._just_changed && !event.isTrigger ) { - this._just_changed = false; - jQuery.event.simulate( "change", this, event, true ); - } - }); - } - return false; - } - // Delegated event; lazy-add a change handler on descendant inputs - jQuery.event.add( this, "beforeactivate._change", function( e ) { - var elem = e.target; - - if ( rformElems.test( elem.nodeName ) && !elem._change_attached ) { - jQuery.event.add( elem, "change._change", function( event ) { - if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { - jQuery.event.simulate( "change", this.parentNode, event, true ); - } - }); - elem._change_attached = true; - } - }); - }, - - handle: function( event ) { - var elem = event.target; - - // Swallow native change events from checkbox/radio, we already triggered them above - if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) { - return event.handleObj.handler.apply( this, arguments ); - } - }, - - teardown: function() { - jQuery.event.remove( this, "._change" ); - - return rformElems.test( this.nodeName ); - } - }; -} - -// Create "bubbling" focus and blur events -if ( !jQuery.support.focusinBubbles ) { - jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { - - // Attach a single capturing handler while someone wants focusin/focusout - var attaches = 0, - handler = function( event ) { - jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); - }; - - jQuery.event.special[ fix ] = { - setup: function() { - if ( attaches++ === 0 ) { - document.addEventListener( orig, handler, true ); - } - }, - teardown: function() { - if ( --attaches === 0 ) { - document.removeEventListener( orig, handler, true ); - } - } - }; - }); -} - -jQuery.fn.extend({ - - on: function( types, selector, data, fn, /*INTERNAL*/ one ) { - var origFn, type; - - // Types can be a map of types/handlers - if ( typeof types === "object" ) { - // ( types-Object, selector, data ) - if ( typeof selector !== "string" ) { // && selector != null - // ( types-Object, data ) - data = data || selector; - selector = undefined; - } - for ( type in types ) { - this.on( type, selector, data, types[ type ], one ); - } - return this; - } - - if ( data == null && fn == null ) { - // ( types, fn ) - fn = selector; - data = selector = undefined; - } else if ( fn == null ) { - if ( typeof selector === "string" ) { - // ( types, selector, fn ) - fn = data; - data = undefined; - } else { - // ( types, data, fn ) - fn = data; - data = selector; - selector = undefined; - } - } - if ( fn === false ) { - fn = returnFalse; - } else if ( !fn ) { - return this; - } - - if ( one === 1 ) { - origFn = fn; - fn = function( event ) { - // Can use an empty set, since event contains the info - jQuery().off( event ); - return origFn.apply( this, arguments ); - }; - // Use same guid so caller can remove using origFn - fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); - } - return this.each( function() { - jQuery.event.add( this, types, fn, data, selector ); - }); - }, - one: function( types, selector, data, fn ) { - return this.on( types, selector, data, fn, 1 ); - }, - off: function( types, selector, fn ) { - if ( types && types.preventDefault && types.handleObj ) { - // ( event ) dispatched jQuery.Event - var handleObj = types.handleObj; - jQuery( types.delegateTarget ).off( - handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, - handleObj.selector, - handleObj.handler - ); - return this; - } - if ( typeof types === "object" ) { - // ( types-object [, selector] ) - for ( var type in types ) { - this.off( type, selector, types[ type ] ); - } - return this; - } - if ( selector === false || typeof selector === "function" ) { - // ( types [, fn] ) - fn = selector; - selector = undefined; - } - if ( fn === false ) { - fn = returnFalse; - } - return this.each(function() { - jQuery.event.remove( this, types, fn, selector ); - }); - }, - - bind: function( types, data, fn ) { - return this.on( types, null, data, fn ); - }, - unbind: function( types, fn ) { - return this.off( types, null, fn ); - }, - - live: function( types, data, fn ) { - jQuery( this.context ).on( types, this.selector, data, fn ); - return this; - }, - die: function( types, fn ) { - jQuery( this.context ).off( types, this.selector || "**", fn ); - return this; - }, - - delegate: function( selector, types, data, fn ) { - return this.on( types, selector, data, fn ); - }, - undelegate: function( selector, types, fn ) { - // ( namespace ) or ( selector, types [, fn] ) - return arguments.length == 1? this.off( selector, "**" ) : this.off( types, selector, fn ); - }, - - trigger: function( type, data ) { - return this.each(function() { - jQuery.event.trigger( type, data, this ); - }); - }, - triggerHandler: function( type, data ) { - if ( this[0] ) { - return jQuery.event.trigger( type, data, this[0], true ); - } - }, - - toggle: function( fn ) { - // Save reference to arguments for access in closure - var args = arguments, - guid = fn.guid || jQuery.guid++, - i = 0, - toggler = function( event ) { - // Figure out which function to execute - var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i; - jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 ); - - // Make sure that clicks stop - event.preventDefault(); - - // and execute the function - return args[ lastToggle ].apply( this, arguments ) || false; - }; - - // link all the functions, so any of them can unbind this click handler - toggler.guid = guid; - while ( i < args.length ) { - args[ i++ ].guid = guid; - } - - return this.click( toggler ); - }, - - hover: function( fnOver, fnOut ) { - return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); - } -}); - -jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + - "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + - "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) { - - // Handle event binding - jQuery.fn[ name ] = function( data, fn ) { - if ( fn == null ) { - fn = data; - data = null; - } - - return arguments.length > 0 ? - this.on( name, null, data, fn ) : - this.trigger( name ); - }; - - if ( jQuery.attrFn ) { - jQuery.attrFn[ name ] = true; - } - - if ( rkeyEvent.test( name ) ) { - jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks; - } - - if ( rmouseEvent.test( name ) ) { - jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks; - } -}); - - - -/*! - * Sizzle CSS Selector Engine - * Copyright 2011, The Dojo Foundation - * Released under the MIT, BSD, and GPL Licenses. - * More information: http://sizzlejs.com/ - */ -(function(){ - -var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, - expando = "sizcache" + (Math.random() + '').replace('.', ''), - done = 0, - toString = Object.prototype.toString, - hasDuplicate = false, - baseHasDuplicate = true, - rBackslash = /\\/g, - rReturn = /\r\n/g, - rNonWord = /\W/; - -// Here we check if the JavaScript engine is using some sort of -// optimization where it does not always call our comparision -// function. If that is the case, discard the hasDuplicate value. -// Thus far that includes Google Chrome. -[0, 0].sort(function() { - baseHasDuplicate = false; - return 0; -}); - -var Sizzle = function( selector, context, results, seed ) { - results = results || []; - context = context || document; - - var origContext = context; - - if ( context.nodeType !== 1 && context.nodeType !== 9 ) { - return []; - } - - if ( !selector || typeof selector !== "string" ) { - return results; - } - - var m, set, checkSet, extra, ret, cur, pop, i, - prune = true, - contextXML = Sizzle.isXML( context ), - parts = [], - soFar = selector; - - // Reset the position of the chunker regexp (start from head) - do { - chunker.exec( "" ); - m = chunker.exec( soFar ); - - if ( m ) { - soFar = m[3]; - - parts.push( m[1] ); - - if ( m[2] ) { - extra = m[3]; - break; - } - } - } while ( m ); - - if ( parts.length > 1 && origPOS.exec( selector ) ) { - - if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { - set = posProcess( parts[0] + parts[1], context, seed ); - - } else { - set = Expr.relative[ parts[0] ] ? - [ context ] : - Sizzle( parts.shift(), context ); - - while ( parts.length ) { - selector = parts.shift(); - - if ( Expr.relative[ selector ] ) { - selector += parts.shift(); - } - - set = posProcess( selector, set, seed ); - } - } - - } else { - // Take a shortcut and set the context if the root selector is an ID - // (but not if it'll be faster if the inner selector is an ID) - if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML && - Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) { - - ret = Sizzle.find( parts.shift(), context, contextXML ); - context = ret.expr ? - Sizzle.filter( ret.expr, ret.set )[0] : - ret.set[0]; - } - - if ( context ) { - ret = seed ? - { expr: parts.pop(), set: makeArray(seed) } : - Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML ); - - set = ret.expr ? - Sizzle.filter( ret.expr, ret.set ) : - ret.set; - - if ( parts.length > 0 ) { - checkSet = makeArray( set ); - - } else { - prune = false; - } - - while ( parts.length ) { - cur = parts.pop(); - pop = cur; - - if ( !Expr.relative[ cur ] ) { - cur = ""; - } else { - pop = parts.pop(); - } - - if ( pop == null ) { - pop = context; - } - - Expr.relative[ cur ]( checkSet, pop, contextXML ); - } - - } else { - checkSet = parts = []; - } - } - - if ( !checkSet ) { - checkSet = set; - } - - if ( !checkSet ) { - Sizzle.error( cur || selector ); - } - - if ( toString.call(checkSet) === "[object Array]" ) { - if ( !prune ) { - results.push.apply( results, checkSet ); - - } else if ( context && context.nodeType === 1 ) { - for ( i = 0; checkSet[i] != null; i++ ) { - if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) { - results.push( set[i] ); - } - } - - } else { - for ( i = 0; checkSet[i] != null; i++ ) { - if ( checkSet[i] && checkSet[i].nodeType === 1 ) { - results.push( set[i] ); - } - } - } - - } else { - makeArray( checkSet, results ); - } - - if ( extra ) { - Sizzle( extra, origContext, results, seed ); - Sizzle.uniqueSort( results ); - } - - return results; -}; - -Sizzle.uniqueSort = function( results ) { - if ( sortOrder ) { - hasDuplicate = baseHasDuplicate; - results.sort( sortOrder ); - - if ( hasDuplicate ) { - for ( var i = 1; i < results.length; i++ ) { - if ( results[i] === results[ i - 1 ] ) { - results.splice( i--, 1 ); - } - } - } - } - - return results; -}; - -Sizzle.matches = function( expr, set ) { - return Sizzle( expr, null, null, set ); -}; - -Sizzle.matchesSelector = function( node, expr ) { - return Sizzle( expr, null, null, [node] ).length > 0; -}; - -Sizzle.find = function( expr, context, isXML ) { - var set, i, len, match, type, left; - - if ( !expr ) { - return []; - } - - for ( i = 0, len = Expr.order.length; i < len; i++ ) { - type = Expr.order[i]; - - if ( (match = Expr.leftMatch[ type ].exec( expr )) ) { - left = match[1]; - match.splice( 1, 1 ); - - if ( left.substr( left.length - 1 ) !== "\\" ) { - match[1] = (match[1] || "").replace( rBackslash, "" ); - set = Expr.find[ type ]( match, context, isXML ); - - if ( set != null ) { - expr = expr.replace( Expr.match[ type ], "" ); - break; - } - } - } - } - - if ( !set ) { - set = typeof context.getElementsByTagName !== "undefined" ? - context.getElementsByTagName( "*" ) : - []; - } - - return { set: set, expr: expr }; -}; - -Sizzle.filter = function( expr, set, inplace, not ) { - var match, anyFound, - type, found, item, filter, left, - i, pass, - old = expr, - result = [], - curLoop = set, - isXMLFilter = set && set[0] && Sizzle.isXML( set[0] ); - - while ( expr && set.length ) { - for ( type in Expr.filter ) { - if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) { - filter = Expr.filter[ type ]; - left = match[1]; - - anyFound = false; - - match.splice(1,1); - - if ( left.substr( left.length - 1 ) === "\\" ) { - continue; - } - - if ( curLoop === result ) { - result = []; - } - - if ( Expr.preFilter[ type ] ) { - match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); - - if ( !match ) { - anyFound = found = true; - - } else if ( match === true ) { - continue; - } - } - - if ( match ) { - for ( i = 0; (item = curLoop[i]) != null; i++ ) { - if ( item ) { - found = filter( item, match, i, curLoop ); - pass = not ^ found; - - if ( inplace && found != null ) { - if ( pass ) { - anyFound = true; - - } else { - curLoop[i] = false; - } - - } else if ( pass ) { - result.push( item ); - anyFound = true; - } - } - } - } - - if ( found !== undefined ) { - if ( !inplace ) { - curLoop = result; - } - - expr = expr.replace( Expr.match[ type ], "" ); - - if ( !anyFound ) { - return []; - } - - break; - } - } - } - - // Improper expression - if ( expr === old ) { - if ( anyFound == null ) { - Sizzle.error( expr ); - - } else { - break; - } - } - - old = expr; - } - - return curLoop; -}; - -Sizzle.error = function( msg ) { - throw new Error( "Syntax error, unrecognized expression: " + msg ); -}; - -/** - * Utility function for retreiving the text value of an array of DOM nodes - * @param {Array|Element} elem - */ -var getText = Sizzle.getText = function( elem ) { - var i, node, - nodeType = elem.nodeType, - ret = ""; - - if ( nodeType ) { - if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { - // Use textContent || innerText for elements - if ( typeof elem.textContent === 'string' ) { - return elem.textContent; - } else if ( typeof elem.innerText === 'string' ) { - // Replace IE's carriage returns - return elem.innerText.replace( rReturn, '' ); - } else { - // Traverse it's children - for ( elem = elem.firstChild; elem; elem = elem.nextSibling) { - ret += getText( elem ); - } - } - } else if ( nodeType === 3 || nodeType === 4 ) { - return elem.nodeValue; - } - } else { - - // If no nodeType, this is expected to be an array - for ( i = 0; (node = elem[i]); i++ ) { - // Do not traverse comment nodes - if ( node.nodeType !== 8 ) { - ret += getText( node ); - } - } - } - return ret; -}; - -var Expr = Sizzle.selectors = { - order: [ "ID", "NAME", "TAG" ], - - match: { - ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, - CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, - NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/, - ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/, - TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/, - CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/, - POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/, - PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/ - }, - - leftMatch: {}, - - attrMap: { - "class": "className", - "for": "htmlFor" - }, - - attrHandle: { - href: function( elem ) { - return elem.getAttribute( "href" ); - }, - type: function( elem ) { - return elem.getAttribute( "type" ); - } - }, - - relative: { - "+": function(checkSet, part){ - var isPartStr = typeof part === "string", - isTag = isPartStr && !rNonWord.test( part ), - isPartStrNotTag = isPartStr && !isTag; - - if ( isTag ) { - part = part.toLowerCase(); - } - - for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { - if ( (elem = checkSet[i]) ) { - while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} - - checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ? - elem || false : - elem === part; - } - } - - if ( isPartStrNotTag ) { - Sizzle.filter( part, checkSet, true ); - } - }, - - ">": function( checkSet, part ) { - var elem, - isPartStr = typeof part === "string", - i = 0, - l = checkSet.length; - - if ( isPartStr && !rNonWord.test( part ) ) { - part = part.toLowerCase(); - - for ( ; i < l; i++ ) { - elem = checkSet[i]; - - if ( elem ) { - var parent = elem.parentNode; - checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false; - } - } - - } else { - for ( ; i < l; i++ ) { - elem = checkSet[i]; - - if ( elem ) { - checkSet[i] = isPartStr ? - elem.parentNode : - elem.parentNode === part; - } - } - - if ( isPartStr ) { - Sizzle.filter( part, checkSet, true ); - } - } - }, - - "": function(checkSet, part, isXML){ - var nodeCheck, - doneName = done++, - checkFn = dirCheck; - - if ( typeof part === "string" && !rNonWord.test( part ) ) { - part = part.toLowerCase(); - nodeCheck = part; - checkFn = dirNodeCheck; - } - - checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML ); - }, - - "~": function( checkSet, part, isXML ) { - var nodeCheck, - doneName = done++, - checkFn = dirCheck; - - if ( typeof part === "string" && !rNonWord.test( part ) ) { - part = part.toLowerCase(); - nodeCheck = part; - checkFn = dirNodeCheck; - } - - checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML ); - } - }, - - find: { - ID: function( match, context, isXML ) { - if ( typeof context.getElementById !== "undefined" && !isXML ) { - var m = context.getElementById(match[1]); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - return m && m.parentNode ? [m] : []; - } - }, - - NAME: function( match, context ) { - if ( typeof context.getElementsByName !== "undefined" ) { - var ret = [], - results = context.getElementsByName( match[1] ); - - for ( var i = 0, l = results.length; i < l; i++ ) { - if ( results[i].getAttribute("name") === match[1] ) { - ret.push( results[i] ); - } - } - - return ret.length === 0 ? null : ret; - } - }, - - TAG: function( match, context ) { - if ( typeof context.getElementsByTagName !== "undefined" ) { - return context.getElementsByTagName( match[1] ); - } - } - }, - preFilter: { - CLASS: function( match, curLoop, inplace, result, not, isXML ) { - match = " " + match[1].replace( rBackslash, "" ) + " "; - - if ( isXML ) { - return match; - } - - for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { - if ( elem ) { - if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) { - if ( !inplace ) { - result.push( elem ); - } - - } else if ( inplace ) { - curLoop[i] = false; - } - } - } - - return false; - }, - - ID: function( match ) { - return match[1].replace( rBackslash, "" ); - }, - - TAG: function( match, curLoop ) { - return match[1].replace( rBackslash, "" ).toLowerCase(); - }, - - CHILD: function( match ) { - if ( match[1] === "nth" ) { - if ( !match[2] ) { - Sizzle.error( match[0] ); - } - - match[2] = match[2].replace(/^\+|\s*/g, ''); - - // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6' - var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec( - match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" || - !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); - - // calculate the numbers (first)n+(last) including if they are negative - match[2] = (test[1] + (test[2] || 1)) - 0; - match[3] = test[3] - 0; - } - else if ( match[2] ) { - Sizzle.error( match[0] ); - } - - // TODO: Move to normal caching system - match[0] = done++; - - return match; - }, - - ATTR: function( match, curLoop, inplace, result, not, isXML ) { - var name = match[1] = match[1].replace( rBackslash, "" ); - - if ( !isXML && Expr.attrMap[name] ) { - match[1] = Expr.attrMap[name]; - } - - // Handle if an un-quoted value was used - match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" ); - - if ( match[2] === "~=" ) { - match[4] = " " + match[4] + " "; - } - - return match; - }, - - PSEUDO: function( match, curLoop, inplace, result, not ) { - if ( match[1] === "not" ) { - // If we're dealing with a complex expression, or a simple one - if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) { - match[3] = Sizzle(match[3], null, null, curLoop); - - } else { - var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); - - if ( !inplace ) { - result.push.apply( result, ret ); - } - - return false; - } - - } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { - return true; - } - - return match; - }, - - POS: function( match ) { - match.unshift( true ); - - return match; - } - }, - - filters: { - enabled: function( elem ) { - return elem.disabled === false && elem.type !== "hidden"; - }, - - disabled: function( elem ) { - return elem.disabled === true; - }, - - checked: function( elem ) { - return elem.checked === true; - }, - - selected: function( elem ) { - // Accessing this property makes selected-by-default - // options in Safari work properly - if ( elem.parentNode ) { - elem.parentNode.selectedIndex; - } - - return elem.selected === true; - }, - - parent: function( elem ) { - return !!elem.firstChild; - }, - - empty: function( elem ) { - return !elem.firstChild; - }, - - has: function( elem, i, match ) { - return !!Sizzle( match[3], elem ).length; - }, - - header: function( elem ) { - return (/h\d/i).test( elem.nodeName ); - }, - - text: function( elem ) { - var attr = elem.getAttribute( "type" ), type = elem.type; - // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) - // use getAttribute instead to test this case - return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null ); - }, - - radio: function( elem ) { - return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type; - }, - - checkbox: function( elem ) { - return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type; - }, - - file: function( elem ) { - return elem.nodeName.toLowerCase() === "input" && "file" === elem.type; - }, - - password: function( elem ) { - return elem.nodeName.toLowerCase() === "input" && "password" === elem.type; - }, - - submit: function( elem ) { - var name = elem.nodeName.toLowerCase(); - return (name === "input" || name === "button") && "submit" === elem.type; - }, - - image: function( elem ) { - return elem.nodeName.toLowerCase() === "input" && "image" === elem.type; - }, - - reset: function( elem ) { - var name = elem.nodeName.toLowerCase(); - return (name === "input" || name === "button") && "reset" === elem.type; - }, - - button: function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && "button" === elem.type || name === "button"; - }, - - input: function( elem ) { - return (/input|select|textarea|button/i).test( elem.nodeName ); - }, - - focus: function( elem ) { - return elem === elem.ownerDocument.activeElement; - } - }, - setFilters: { - first: function( elem, i ) { - return i === 0; - }, - - last: function( elem, i, match, array ) { - return i === array.length - 1; - }, - - even: function( elem, i ) { - return i % 2 === 0; - }, - - odd: function( elem, i ) { - return i % 2 === 1; - }, - - lt: function( elem, i, match ) { - return i < match[3] - 0; - }, - - gt: function( elem, i, match ) { - return i > match[3] - 0; - }, - - nth: function( elem, i, match ) { - return match[3] - 0 === i; - }, - - eq: function( elem, i, match ) { - return match[3] - 0 === i; - } - }, - filter: { - PSEUDO: function( elem, match, i, array ) { - var name = match[1], - filter = Expr.filters[ name ]; - - if ( filter ) { - return filter( elem, i, match, array ); - - } else if ( name === "contains" ) { - return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0; - - } else if ( name === "not" ) { - var not = match[3]; - - for ( var j = 0, l = not.length; j < l; j++ ) { - if ( not[j] === elem ) { - return false; - } - } - - return true; - - } else { - Sizzle.error( name ); - } - }, - - CHILD: function( elem, match ) { - var first, last, - doneName, parent, cache, - count, diff, - type = match[1], - node = elem; - - switch ( type ) { - case "only": - case "first": - while ( (node = node.previousSibling) ) { - if ( node.nodeType === 1 ) { - return false; - } - } - - if ( type === "first" ) { - return true; - } - - node = elem; - - /* falls through */ - case "last": - while ( (node = node.nextSibling) ) { - if ( node.nodeType === 1 ) { - return false; - } - } - - return true; - - case "nth": - first = match[2]; - last = match[3]; - - if ( first === 1 && last === 0 ) { - return true; - } - - doneName = match[0]; - parent = elem.parentNode; - - if ( parent && (parent[ expando ] !== doneName || !elem.nodeIndex) ) { - count = 0; - - for ( node = parent.firstChild; node; node = node.nextSibling ) { - if ( node.nodeType === 1 ) { - node.nodeIndex = ++count; - } - } - - parent[ expando ] = doneName; - } - - diff = elem.nodeIndex - last; - - if ( first === 0 ) { - return diff === 0; - - } else { - return ( diff % first === 0 && diff / first >= 0 ); - } - } - }, - - ID: function( elem, match ) { - return elem.nodeType === 1 && elem.getAttribute("id") === match; - }, - - TAG: function( elem, match ) { - return (match === "*" && elem.nodeType === 1) || !!elem.nodeName && elem.nodeName.toLowerCase() === match; - }, - - CLASS: function( elem, match ) { - return (" " + (elem.className || elem.getAttribute("class")) + " ") - .indexOf( match ) > -1; - }, - - ATTR: function( elem, match ) { - var name = match[1], - result = Sizzle.attr ? - Sizzle.attr( elem, name ) : - Expr.attrHandle[ name ] ? - Expr.attrHandle[ name ]( elem ) : - elem[ name ] != null ? - elem[ name ] : - elem.getAttribute( name ), - value = result + "", - type = match[2], - check = match[4]; - - return result == null ? - type === "!=" : - !type && Sizzle.attr ? - result != null : - type === "=" ? - value === check : - type === "*=" ? - value.indexOf(check) >= 0 : - type === "~=" ? - (" " + value + " ").indexOf(check) >= 0 : - !check ? - value && result !== false : - type === "!=" ? - value !== check : - type === "^=" ? - value.indexOf(check) === 0 : - type === "$=" ? - value.substr(value.length - check.length) === check : - type === "|=" ? - value === check || value.substr(0, check.length + 1) === check + "-" : - false; - }, - - POS: function( elem, match, i, array ) { - var name = match[2], - filter = Expr.setFilters[ name ]; - - if ( filter ) { - return filter( elem, i, match, array ); - } - } - } -}; - -var origPOS = Expr.match.POS, - fescape = function(all, num){ - return "\\" + (num - 0 + 1); - }; - -for ( var type in Expr.match ) { - Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) ); - Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) ); -} -// Expose origPOS -// "global" as in regardless of relation to brackets/parens -Expr.match.globalPOS = origPOS; - -var makeArray = function( array, results ) { - array = Array.prototype.slice.call( array, 0 ); - - if ( results ) { - results.push.apply( results, array ); - return results; - } - - return array; -}; - -// Perform a simple check to determine if the browser is capable of -// converting a NodeList to an array using builtin methods. -// Also verifies that the returned array holds DOM nodes -// (which is not the case in the Blackberry browser) -try { - Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType; - -// Provide a fallback method if it does not work -} catch( e ) { - makeArray = function( array, results ) { - var i = 0, - ret = results || []; - - if ( toString.call(array) === "[object Array]" ) { - Array.prototype.push.apply( ret, array ); - - } else { - if ( typeof array.length === "number" ) { - for ( var l = array.length; i < l; i++ ) { - ret.push( array[i] ); - } - - } else { - for ( ; array[i]; i++ ) { - ret.push( array[i] ); - } - } - } - - return ret; - }; -} - -var sortOrder, siblingCheck; - -if ( document.documentElement.compareDocumentPosition ) { - sortOrder = function( a, b ) { - if ( a === b ) { - hasDuplicate = true; - return 0; - } - - if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) { - return a.compareDocumentPosition ? -1 : 1; - } - - return a.compareDocumentPosition(b) & 4 ? -1 : 1; - }; - -} else { - sortOrder = function( a, b ) { - // The nodes are identical, we can exit early - if ( a === b ) { - hasDuplicate = true; - return 0; - - // Fallback to using sourceIndex (in IE) if it's available on both nodes - } else if ( a.sourceIndex && b.sourceIndex ) { - return a.sourceIndex - b.sourceIndex; - } - - var al, bl, - ap = [], - bp = [], - aup = a.parentNode, - bup = b.parentNode, - cur = aup; - - // If the nodes are siblings (or identical) we can do a quick check - if ( aup === bup ) { - return siblingCheck( a, b ); - - // If no parents were found then the nodes are disconnected - } else if ( !aup ) { - return -1; - - } else if ( !bup ) { - return 1; - } - - // Otherwise they're somewhere else in the tree so we need - // to build up a full list of the parentNodes for comparison - while ( cur ) { - ap.unshift( cur ); - cur = cur.parentNode; - } - - cur = bup; - - while ( cur ) { - bp.unshift( cur ); - cur = cur.parentNode; - } - - al = ap.length; - bl = bp.length; - - // Start walking down the tree looking for a discrepancy - for ( var i = 0; i < al && i < bl; i++ ) { - if ( ap[i] !== bp[i] ) { - return siblingCheck( ap[i], bp[i] ); - } - } - - // We ended someplace up the tree so do a sibling check - return i === al ? - siblingCheck( a, bp[i], -1 ) : - siblingCheck( ap[i], b, 1 ); - }; - - siblingCheck = function( a, b, ret ) { - if ( a === b ) { - return ret; - } - - var cur = a.nextSibling; - - while ( cur ) { - if ( cur === b ) { - return -1; - } - - cur = cur.nextSibling; - } - - return 1; - }; -} - -// Check to see if the browser returns elements by name when -// querying by getElementById (and provide a workaround) -(function(){ - // We're going to inject a fake input element with a specified name - var form = document.createElement("div"), - id = "script" + (new Date()).getTime(), - root = document.documentElement; - - form.innerHTML = ""; - - // Inject it into the root element, check its status, and remove it quickly - root.insertBefore( form, root.firstChild ); - - // The workaround has to do additional checks after a getElementById - // Which slows things down for other browsers (hence the branching) - if ( document.getElementById( id ) ) { - Expr.find.ID = function( match, context, isXML ) { - if ( typeof context.getElementById !== "undefined" && !isXML ) { - var m = context.getElementById(match[1]); - - return m ? - m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? - [m] : - undefined : - []; - } - }; - - Expr.filter.ID = function( elem, match ) { - var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); - - return elem.nodeType === 1 && node && node.nodeValue === match; - }; - } - - root.removeChild( form ); - - // release memory in IE - root = form = null; -})(); - -(function(){ - // Check to see if the browser returns only elements - // when doing getElementsByTagName("*") - - // Create a fake element - var div = document.createElement("div"); - div.appendChild( document.createComment("") ); - - // Make sure no comments are found - if ( div.getElementsByTagName("*").length > 0 ) { - Expr.find.TAG = function( match, context ) { - var results = context.getElementsByTagName( match[1] ); - - // Filter out possible comments - if ( match[1] === "*" ) { - var tmp = []; - - for ( var i = 0; results[i]; i++ ) { - if ( results[i].nodeType === 1 ) { - tmp.push( results[i] ); - } - } - - results = tmp; - } - - return results; - }; - } - - // Check to see if an attribute returns normalized href attributes - div.innerHTML = ""; - - if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && - div.firstChild.getAttribute("href") !== "#" ) { - - Expr.attrHandle.href = function( elem ) { - return elem.getAttribute( "href", 2 ); - }; - } - - // release memory in IE - div = null; -})(); - -if ( document.querySelectorAll ) { - (function(){ - var oldSizzle = Sizzle, - div = document.createElement("div"), - id = "__sizzle__"; - - div.innerHTML = "

    "; - - // Safari can't handle uppercase or unicode characters when - // in quirks mode. - if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { - return; - } - - Sizzle = function( query, context, extra, seed ) { - context = context || document; - - // Only use querySelectorAll on non-XML documents - // (ID selectors don't work in non-HTML documents) - if ( !seed && !Sizzle.isXML(context) ) { - // See if we find a selector to speed up - var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query ); - - if ( match && (context.nodeType === 1 || context.nodeType === 9) ) { - // Speed-up: Sizzle("TAG") - if ( match[1] ) { - return makeArray( context.getElementsByTagName( query ), extra ); - - // Speed-up: Sizzle(".CLASS") - } else if ( match[2] && Expr.find.CLASS && context.getElementsByMethodName ) { - return makeArray( context.getElementsByMethodName( match[2] ), extra ); - } - } - - if ( context.nodeType === 9 ) { - // Speed-up: Sizzle("body") - // The body element only exists once, optimize finding it - if ( query === "body" && context.body ) { - return makeArray( [ context.body ], extra ); - - // Speed-up: Sizzle("#ID") - } else if ( match && match[3] ) { - var elem = context.getElementById( match[3] ); - - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - if ( elem && elem.parentNode ) { - // Handle the case where IE and Opera return items - // by name instead of ID - if ( elem.id === match[3] ) { - return makeArray( [ elem ], extra ); - } - - } else { - return makeArray( [], extra ); - } - } - - try { - return makeArray( context.querySelectorAll(query), extra ); - } catch(qsaError) {} - - // qSA works strangely on Element-rooted queries - // We can work around this by specifying an extra ID on the root - // and working up from there (Thanks to Andrew Dupont for the technique) - // IE 8 doesn't work on object elements - } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { - var oldContext = context, - old = context.getAttribute( "id" ), - nid = old || id, - hasParent = context.parentNode, - relativeHierarchySelector = /^\s*[+~]/.test( query ); - - if ( !old ) { - context.setAttribute( "id", nid ); - } else { - nid = nid.replace( /'/g, "\\$&" ); - } - if ( relativeHierarchySelector && hasParent ) { - context = context.parentNode; - } - - try { - if ( !relativeHierarchySelector || hasParent ) { - return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra ); - } - - } catch(pseudoError) { - } finally { - if ( !old ) { - oldContext.removeAttribute( "id" ); - } - } - } - } - - return oldSizzle(query, context, extra, seed); - }; - - for ( var prop in oldSizzle ) { - Sizzle[ prop ] = oldSizzle[ prop ]; - } - - // release memory in IE - div = null; - })(); -} - -(function(){ - var html = document.documentElement, - matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector; - - if ( matches ) { - // Check to see if it's possible to do matchesSelector - // on a disconnected node (IE 9 fails this) - var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ), - pseudoWorks = false; - - try { - // This should fail with an exception - // Gecko does not error, returns false instead - matches.call( document.documentElement, "[test!='']:sizzle" ); - - } catch( pseudoError ) { - pseudoWorks = true; - } - - Sizzle.matchesSelector = function( node, expr ) { - // Make sure that attribute selectors are quoted - expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']"); - - if ( !Sizzle.isXML( node ) ) { - try { - if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) { - var ret = matches.call( node, expr ); - - // IE 9's matchesSelector returns false on disconnected nodes - if ( ret || !disconnectedMatch || - // As well, disconnected nodes are said to be in a document - // fragment in IE 9, so check for that - node.document && node.document.nodeType !== 11 ) { - return ret; - } - } - } catch(e) {} - } - - return Sizzle(expr, null, null, [node]).length > 0; - }; - } -})(); - -(function(){ - var div = document.createElement("div"); - - div.innerHTML = "
    "; - - // Opera can't find a second classname (in 9.6) - // Also, make sure that getElementsByMethodName actually exists - if ( !div.getElementsByMethodName || div.getElementsByMethodName("e").length === 0 ) { - return; - } - - // Safari caches class attributes, doesn't catch changes (in 3.2) - div.lastChild.className = "e"; - - if ( div.getElementsByMethodName("e").length === 1 ) { - return; - } - - Expr.order.splice(1, 0, "CLASS"); - Expr.find.CLASS = function( match, context, isXML ) { - if ( typeof context.getElementsByMethodName !== "undefined" && !isXML ) { - return context.getElementsByMethodName(match[1]); - } - }; - - // release memory in IE - div = null; -})(); - -function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { - for ( var i = 0, l = checkSet.length; i < l; i++ ) { - var elem = checkSet[i]; - - if ( elem ) { - var match = false; - - elem = elem[dir]; - - while ( elem ) { - if ( elem[ expando ] === doneName ) { - match = checkSet[elem.sizset]; - break; - } - - if ( elem.nodeType === 1 && !isXML ){ - elem[ expando ] = doneName; - elem.sizset = i; - } - - if ( elem.nodeName.toLowerCase() === cur ) { - match = elem; - break; - } - - elem = elem[dir]; - } - - checkSet[i] = match; - } - } -} - -function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { - for ( var i = 0, l = checkSet.length; i < l; i++ ) { - var elem = checkSet[i]; - - if ( elem ) { - var match = false; - - elem = elem[dir]; - - while ( elem ) { - if ( elem[ expando ] === doneName ) { - match = checkSet[elem.sizset]; - break; - } - - if ( elem.nodeType === 1 ) { - if ( !isXML ) { - elem[ expando ] = doneName; - elem.sizset = i; - } - - if ( typeof cur !== "string" ) { - if ( elem === cur ) { - match = true; - break; - } - - } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { - match = elem; - break; - } - } - - elem = elem[dir]; - } - - checkSet[i] = match; - } - } -} - -if ( document.documentElement.contains ) { - Sizzle.contains = function( a, b ) { - return a !== b && (a.contains ? a.contains(b) : true); - }; - -} else if ( document.documentElement.compareDocumentPosition ) { - Sizzle.contains = function( a, b ) { - return !!(a.compareDocumentPosition(b) & 16); - }; - -} else { - Sizzle.contains = function() { - return false; - }; -} - -Sizzle.isXML = function( elem ) { - // documentElement is verified for cases where it doesn't yet exist - // (such as loading iframes in IE - #4833) - var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement; - - return documentElement ? documentElement.nodeName !== "HTML" : false; -}; - -var posProcess = function( selector, context, seed ) { - var match, - tmpSet = [], - later = "", - root = context.nodeType ? [context] : context; - - // Position selectors must be done after the filter - // And so must :not(positional) so we move all PSEUDOs to the end - while ( (match = Expr.match.PSEUDO.exec( selector )) ) { - later += match[0]; - selector = selector.replace( Expr.match.PSEUDO, "" ); - } - - selector = Expr.relative[selector] ? selector + "*" : selector; - - for ( var i = 0, l = root.length; i < l; i++ ) { - Sizzle( selector, root[i], tmpSet, seed ); - } - - return Sizzle.filter( later, tmpSet ); -}; - -// EXPOSE -// Override sizzle attribute retrieval -Sizzle.attr = jQuery.attr; -Sizzle.selectors.attrMap = {}; -jQuery.find = Sizzle; -jQuery.expr = Sizzle.selectors; -jQuery.expr[":"] = jQuery.expr.filters; -jQuery.unique = Sizzle.uniqueSort; -jQuery.text = Sizzle.getText; -jQuery.isXMLDoc = Sizzle.isXML; -jQuery.contains = Sizzle.contains; - - -})(); - - -var runtil = /Until$/, - rparentsprev = /^(?:parents|prevUntil|prevAll)/, - // Note: This RegExp should be improved, or likely pulled from Sizzle - rmultiselector = /,/, - isSimple = /^.[^:#\[\.,]*$/, - slice = Array.prototype.slice, - POS = jQuery.expr.match.globalPOS, - // methods guaranteed to produce a unique set when starting from a unique set - guaranteedUnique = { - children: true, - contents: true, - next: true, - prev: true - }; - -jQuery.fn.extend({ - find: function( selector ) { - var self = this, - i, l; - - if ( typeof selector !== "string" ) { - return jQuery( selector ).filter(function() { - for ( i = 0, l = self.length; i < l; i++ ) { - if ( jQuery.contains( self[ i ], this ) ) { - return true; - } - } - }); - } - - var ret = this.pushStack( "", "find", selector ), - length, n, r; - - for ( i = 0, l = this.length; i < l; i++ ) { - length = ret.length; - jQuery.find( selector, this[i], ret ); - - if ( i > 0 ) { - // Make sure that the results are unique - for ( n = length; n < ret.length; n++ ) { - for ( r = 0; r < length; r++ ) { - if ( ret[r] === ret[n] ) { - ret.splice(n--, 1); - break; - } - } - } - } - } - - return ret; - }, - - has: function( target ) { - var targets = jQuery( target ); - return this.filter(function() { - for ( var i = 0, l = targets.length; i < l; i++ ) { - if ( jQuery.contains( this, targets[i] ) ) { - return true; - } - } - }); - }, - - not: function( selector ) { - return this.pushStack( winnow(this, selector, false), "not", selector); - }, - - filter: function( selector ) { - return this.pushStack( winnow(this, selector, true), "filter", selector ); - }, - - is: function( selector ) { - return !!selector && ( - typeof selector === "string" ? - // If this is a positional selector, check membership in the returned set - // so $("p:first").is("p:last") won't return true for a doc with two "p". - POS.test( selector ) ? - jQuery( selector, this.context ).index( this[0] ) >= 0 : - jQuery.filter( selector, this ).length > 0 : - this.filter( selector ).length > 0 ); - }, - - closest: function( selectors, context ) { - var ret = [], i, l, cur = this[0]; - - // Array (deprecated as of jQuery 1.7) - if ( jQuery.isArray( selectors ) ) { - var level = 1; - - while ( cur && cur.ownerDocument && cur !== context ) { - for ( i = 0; i < selectors.length; i++ ) { - - if ( jQuery( cur ).is( selectors[ i ] ) ) { - ret.push({ selector: selectors[ i ], elem: cur, level: level }); - } - } - - cur = cur.parentNode; - level++; - } - - return ret; - } - - // String - var pos = POS.test( selectors ) || typeof selectors !== "string" ? - jQuery( selectors, context || this.context ) : - 0; - - for ( i = 0, l = this.length; i < l; i++ ) { - cur = this[i]; - - while ( cur ) { - if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) { - ret.push( cur ); - break; - - } else { - cur = cur.parentNode; - if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) { - break; - } - } - } - } - - ret = ret.length > 1 ? jQuery.unique( ret ) : ret; - - return this.pushStack( ret, "closest", selectors ); - }, - - // Determine the position of an element within - // the matched set of elements - index: function( elem ) { - - // No argument, return index in parent - if ( !elem ) { - return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1; - } - - // index in selector - if ( typeof elem === "string" ) { - return jQuery.inArray( this[0], jQuery( elem ) ); - } - - // Locate the position of the desired element - return jQuery.inArray( - // If it receives a jQuery object, the first element is used - elem.jquery ? elem[0] : elem, this ); - }, - - add: function( selector, context ) { - var set = typeof selector === "string" ? - jQuery( selector, context ) : - jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), - all = jQuery.merge( this.get(), set ); - - return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ? - all : - jQuery.unique( all ) ); - }, - - andSelf: function() { - return this.add( this.prevObject ); - } -}); - -// A painfully simple check to see if an element is disconnected -// from a document (should be improved, where feasible). -function isDisconnected( node ) { - return !node || !node.parentNode || node.parentNode.nodeType === 11; -} - -jQuery.each({ - parent: function( elem ) { - var parent = elem.parentNode; - return parent && parent.nodeType !== 11 ? parent : null; - }, - parents: function( elem ) { - return jQuery.dir( elem, "parentNode" ); - }, - parentsUntil: function( elem, i, until ) { - return jQuery.dir( elem, "parentNode", until ); - }, - next: function( elem ) { - return jQuery.nth( elem, 2, "nextSibling" ); - }, - prev: function( elem ) { - return jQuery.nth( elem, 2, "previousSibling" ); - }, - nextAll: function( elem ) { - return jQuery.dir( elem, "nextSibling" ); - }, - prevAll: function( elem ) { - return jQuery.dir( elem, "previousSibling" ); - }, - nextUntil: function( elem, i, until ) { - return jQuery.dir( elem, "nextSibling", until ); - }, - prevUntil: function( elem, i, until ) { - return jQuery.dir( elem, "previousSibling", until ); - }, - siblings: function( elem ) { - return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); - }, - children: function( elem ) { - return jQuery.sibling( elem.firstChild ); - }, - contents: function( elem ) { - return jQuery.nodeName( elem, "iframe" ) ? - elem.contentDocument || elem.contentWindow.document : - jQuery.makeArray( elem.childNodes ); - } -}, function( name, fn ) { - jQuery.fn[ name ] = function( until, selector ) { - var ret = jQuery.map( this, fn, until ); - - if ( !runtil.test( name ) ) { - selector = until; - } - - if ( selector && typeof selector === "string" ) { - ret = jQuery.filter( selector, ret ); - } - - ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret; - - if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) { - ret = ret.reverse(); - } - - return this.pushStack( ret, name, slice.call( arguments ).join(",") ); - }; -}); - -jQuery.extend({ - filter: function( expr, elems, not ) { - if ( not ) { - expr = ":not(" + expr + ")"; - } - - return elems.length === 1 ? - jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] : - jQuery.find.matches(expr, elems); - }, - - dir: function( elem, dir, until ) { - var matched = [], - cur = elem[ dir ]; - - while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { - if ( cur.nodeType === 1 ) { - matched.push( cur ); - } - cur = cur[dir]; - } - return matched; - }, - - nth: function( cur, result, dir, elem ) { - result = result || 1; - var num = 0; - - for ( ; cur; cur = cur[dir] ) { - if ( cur.nodeType === 1 && ++num === result ) { - break; - } - } - - return cur; - }, - - sibling: function( n, elem ) { - var r = []; - - for ( ; n; n = n.nextSibling ) { - if ( n.nodeType === 1 && n !== elem ) { - r.push( n ); - } - } - - return r; - } -}); - -// Implement the identical functionality for filter and not -function winnow( elements, qualifier, keep ) { - - // Can't pass null or undefined to indexOf in Firefox 4 - // Set to 0 to skip string check - qualifier = qualifier || 0; - - if ( jQuery.isFunction( qualifier ) ) { - return jQuery.grep(elements, function( elem, i ) { - var retVal = !!qualifier.call( elem, i, elem ); - return retVal === keep; - }); - - } else if ( qualifier.nodeType ) { - return jQuery.grep(elements, function( elem, i ) { - return ( elem === qualifier ) === keep; - }); - - } else if ( typeof qualifier === "string" ) { - var filtered = jQuery.grep(elements, function( elem ) { - return elem.nodeType === 1; - }); - - if ( isSimple.test( qualifier ) ) { - return jQuery.filter(qualifier, filtered, !keep); - } else { - qualifier = jQuery.filter( qualifier, filtered ); - } - } - - return jQuery.grep(elements, function( elem, i ) { - return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep; - }); -} - - - - -function createSafeFragment( document ) { - var list = nodeNames.split( "|" ), - safeFrag = document.createDocumentFragment(); - - if ( safeFrag.createElement ) { - while ( list.length ) { - safeFrag.createElement( - list.pop() - ); - } - } - return safeFrag; -} - -var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" + - "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", - rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g, - rleadingWhitespace = /^\s+/, - rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig, - rtagName = /<([\w:]+)/, - rtbody = /
    ", "
    " ], - tr: [ 2, "", "
    " ], - td: [ 3, "", "
    " ], - col: [ 2, "", "
    " ], - area: [ 1, "", "" ], - _default: [ 0, "", "" ] - }, - safeFragment = createSafeFragment( document ); - -wrapMap.optgroup = wrapMap.option; -wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; -wrapMap.th = wrapMap.td; - -// IE can't serialize and - - - - - - - - -
    -
    -
    -
    - - -

    Index

    - -
    - A - | B - | C - | D - | F - | G - | I - | L - | M - | N - | O - | P - | Q - | R - | S - | T - | V - | W - -
    -

    A

    - - - -
    - -
    AC_KERBEROS (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - - -
    AC_PASSWORD (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - - -
    AC_SMARTCARD (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - - -
    AC_UNSPECIFIED (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - - -
    AC_X509 (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - - -
    add_sign() (saml2.utils.OneLogin_Saml2_Utils static method) -
    - -
    - -
    add_x509_key_descriptors() (saml2.metadata.OneLogin_Saml2_Metadata static method) -
    - - -
    ALOWED_CLOCK_DRIFT (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - - -
    ATTRNAME_FORMAT_BASIC (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - - -
    ATTRNAME_FORMAT_UNSPECIFIED (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - - -
    ATTRNAME_FORMAT_URI (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - -
    - -

    B

    - - - -
    - -
    BINDING_DEFLATE (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - - -
    BINDING_HTTP_ARTIFACT (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - - -
    BINDING_HTTP_POST (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - - -
    BINDING_HTTP_REDIRECT (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - - -
    BINDING_SOAP (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - -
    - -
    build() (saml2.logout_response.OneLogin_Saml2_Logout_Response method) -
    - - -
    build_request_signature() (saml2.auth.OneLogin_Saml2_Auth method) -
    - - -
    build_response_signature() (saml2.auth.OneLogin_Saml2_Auth method) -
    - - -
    builder() (saml2.metadata.OneLogin_Saml2_Metadata static method) -
    - -
    - -

    C

    - - - -
    - -
    calculate_x509_fingerprint() (saml2.utils.OneLogin_Saml2_Utils static method) -
    - - -
    check_settings() (saml2.settings.OneLogin_Saml2_Settings method) -
    - - -
    check_sp_certs() (saml2.settings.OneLogin_Saml2_Settings method) -
    - - -
    check_status() (saml2.response.OneLogin_Saml2_Response method) -
    - -
    - -
    CM_BEARER (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - - -
    CM_HOLDER_KEY (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - - -
    CM_SENDER_VOUCHES (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - -
    - -

    D

    - - - -
    - -
    decode_base64_and_inflate() (saml2.utils.OneLogin_Saml2_Utils static method) -
    - - -
    decrypt_element() (saml2.utils.OneLogin_Saml2_Utils static method) -
    - -
    - -
    deflate_and_base64_encode() (saml2.utils.OneLogin_Saml2_Utils static method) -
    - - -
    delete_local_session() (saml2.utils.OneLogin_Saml2_Utils static method) -
    - -
    - -

    F

    - - - -
    - -
    format_cert() (saml2.utils.OneLogin_Saml2_Utils static method) -
    - - -
    format_finger_print() (saml2.utils.OneLogin_Saml2_Utils static method) -
    - -
    - -
    format_idp_cert() (saml2.settings.OneLogin_Saml2_Settings method) -
    - -
    - -

    G

    - - - -
    - -
    generate_name_id() (saml2.utils.OneLogin_Saml2_Utils static method) -
    - - -
    generate_unique_id() (saml2.utils.OneLogin_Saml2_Utils static method) -
    - - -
    get_attribute() (saml2.auth.OneLogin_Saml2_Auth method) -
    - - -
    get_attributes() (saml2.auth.OneLogin_Saml2_Auth method) -
    - -
    - -
    (saml2.response.OneLogin_Saml2_Response method) -
    - -
    - -
    get_audiences() (saml2.response.OneLogin_Saml2_Response method) -
    - - -
    get_base_path() (saml2.settings.OneLogin_Saml2_Settings method) -
    - - -
    get_cert_path() (saml2.settings.OneLogin_Saml2_Settings method) -
    - - -
    get_contacts() (saml2.settings.OneLogin_Saml2_Settings method) -
    - - -
    get_errors() (saml2.auth.OneLogin_Saml2_Auth method) -
    - -
    - -
    (saml2.settings.OneLogin_Saml2_Settings method) -
    - -
    - -
    get_expire_time() (saml2.utils.OneLogin_Saml2_Utils static method) -
    - - -
    get_ext_lib_path() (saml2.settings.OneLogin_Saml2_Settings method) -
    - - -
    get_id() (saml2.logout_request.OneLogin_Saml2_Logout_Request static method) -
    - - -
    get_idp_data() (saml2.settings.OneLogin_Saml2_Settings method) -
    - - -
    get_issuer() (saml2.logout_request.OneLogin_Saml2_Logout_Request static method) -
    - -
    - -
    (saml2.logout_response.OneLogin_Saml2_Logout_Response method) -
    - -
    - -
    get_issuers() (saml2.response.OneLogin_Saml2_Response method) -
    - - -
    get_lib_path() (saml2.settings.OneLogin_Saml2_Settings method) -
    - - -
    get_name_id() (saml2.logout_request.OneLogin_Saml2_Logout_Request static method) -
    - - -
    get_name_id_data() (saml2.logout_request.OneLogin_Saml2_Logout_Request static method) -
    - - -
    get_nameid() (saml2.auth.OneLogin_Saml2_Auth method) -
    - -
    - -
    (saml2.response.OneLogin_Saml2_Response method) -
    - -
    - -
    get_nameid_data() (saml2.response.OneLogin_Saml2_Response method) -
    - -
    - -
    get_organization() (saml2.settings.OneLogin_Saml2_Settings method) -
    - - -
    get_request() (saml2.authn_request.OneLogin_Saml2_Authn_Request method) -
    - -
    - -
    (saml2.logout_request.OneLogin_Saml2_Logout_Request method) -
    - -
    - -
    get_response() (saml2.logout_response.OneLogin_Saml2_Logout_Response method) -
    - - -
    get_schemas_path() (saml2.settings.OneLogin_Saml2_Settings method) -
    - - -
    get_security_data() (saml2.settings.OneLogin_Saml2_Settings method) -
    - - -
    get_self_host() (saml2.utils.OneLogin_Saml2_Utils static method) -
    - - -
    get_self_url() (saml2.utils.OneLogin_Saml2_Utils static method) -
    - - -
    get_self_url_host() (saml2.utils.OneLogin_Saml2_Utils static method) -
    - - -
    get_self_url_no_query() (saml2.utils.OneLogin_Saml2_Utils static method) -
    - - -
    get_session_index() (saml2.response.OneLogin_Saml2_Response method) -
    - - -
    get_session_indexes() (saml2.logout_request.OneLogin_Saml2_Logout_Request static method) -
    - - -
    get_session_not_on_or_after() (saml2.response.OneLogin_Saml2_Response method) -
    - - -
    get_settings() (saml2.auth.OneLogin_Saml2_Auth method) -
    - - -
    get_slo_url() (saml2.auth.OneLogin_Saml2_Auth method) -
    - - -
    get_sp_cert() (saml2.settings.OneLogin_Saml2_Settings method) -
    - - -
    get_sp_data() (saml2.settings.OneLogin_Saml2_Settings method) -
    - - -
    get_sp_key() (saml2.settings.OneLogin_Saml2_Settings method) -
    - - -
    get_sp_metadata() (saml2.settings.OneLogin_Saml2_Settings method) -
    - - -
    get_sso_url() (saml2.auth.OneLogin_Saml2_Auth method) -
    - - -
    get_status() (saml2.logout_response.OneLogin_Saml2_Logout_Response method) -
    - -
    - -
    (saml2.utils.OneLogin_Saml2_Utils static method) -
    - -
    -
    - -

    I

    - - - -
    - -
    is_authenticated() (saml2.auth.OneLogin_Saml2_Auth method) -
    - - -
    is_debug_active() (saml2.settings.OneLogin_Saml2_Settings method) -
    - - -
    is_https() (saml2.utils.OneLogin_Saml2_Utils static method) -
    - -
    - -
    is_strict() (saml2.settings.OneLogin_Saml2_Settings method) -
    - - -
    is_valid() (saml2.logout_request.OneLogin_Saml2_Logout_Request static method) -
    - -
    - -
    (saml2.logout_response.OneLogin_Saml2_Logout_Response method) -
    - - -
    (saml2.response.OneLogin_Saml2_Response method) -
    - -
    -
    - -

    L

    - - - -
    - -
    login() (saml2.auth.OneLogin_Saml2_Auth method) -
    - -
    - -
    logout() (saml2.auth.OneLogin_Saml2_Auth method) -
    - -
    - -

    M

    - - -
    - -
    METADATA_SP_INVALID (saml2.errors.OneLogin_Saml2_Error attribute) -
    - -
    - -

    N

    - - - -
    - -
    NAMEID_EMAIL_ADDRESS (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - - -
    NAMEID_ENCRYPTED (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - - -
    NAMEID_ENTITY (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - - -
    NAMEID_KERBEROS (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - - -
    NAMEID_PERSISTENT (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - - -
    NAMEID_TRANSIENT (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - - -
    NAMEID_WINDOWS_DOMAIN_QUALIFIED_NAME (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - - -
    NAMEID_X509_SUBJECT_NAME (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - - -
    NS_DS (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - -
    - -
    NS_MD (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - - -
    NS_SAML (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - - -
    NS_SAMLP (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - - -
    NS_SOAP (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - - -
    NS_XENC (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - - -
    NS_XS (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - - -
    NS_XSI (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - - -
    NSMAP (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - -
    - -

    O

    - - - -
    - -
    OneLogin_Saml2_Auth (class in saml2.auth) -
    - - -
    OneLogin_Saml2_Authn_Request (class in saml2.authn_request) -
    - - -
    OneLogin_Saml2_Constants (class in saml2.constants) -
    - - -
    OneLogin_Saml2_Error -
    - - -
    OneLogin_Saml2_Logout_Request (class in saml2.logout_request) -
    - -
    - -
    OneLogin_Saml2_Logout_Response (class in saml2.logout_response) -
    - - -
    OneLogin_Saml2_Metadata (class in saml2.metadata) -
    - - -
    OneLogin_Saml2_Response (class in saml2.response) -
    - - -
    OneLogin_Saml2_Settings (class in saml2.settings) -
    - - -
    OneLogin_Saml2_Utils (class in saml2.utils) -
    - -
    - -

    P

    - - - -
    - -
    parse_duration() (saml2.utils.OneLogin_Saml2_Utils static method) -
    - - -
    parse_SAML_to_time() (saml2.utils.OneLogin_Saml2_Utils static method) -
    - - -
    parse_time_to_SAML() (saml2.utils.OneLogin_Saml2_Utils static method) -
    - - -
    PRIVATE_KEY_FILE_NOT_FOUND (saml2.errors.OneLogin_Saml2_Error attribute) -
    - -
    - -
    process_response() (saml2.auth.OneLogin_Saml2_Auth method) -
    - - -
    process_slo() (saml2.auth.OneLogin_Saml2_Auth method) -
    - - -
    PUBLIC_CERT_FILE_NOT_FOUND (saml2.errors.OneLogin_Saml2_Error attribute) -
    - -
    - -

    Q

    - - -
    - -
    query() (saml2.utils.OneLogin_Saml2_Utils static method) -
    - -
    - -

    R

    - - - -
    - -
    redirect() (saml2.utils.OneLogin_Saml2_Utils static method) -
    - - -
    REDIRECT_INVALID_URL (saml2.errors.OneLogin_Saml2_Error attribute) -
    - -
    - -
    redirect_to() (saml2.auth.OneLogin_Saml2_Auth method) -
    - - -
    RSA_SHA1 (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - -
    - -

    S

    - - - -
    - -
    saml2.auth (module) -
    - - -
    saml2.authn_request (module) -
    - - -
    saml2.constants (module) -
    - - -
    saml2.errors (module) -
    - - -
    saml2.logout_request (module) -
    - - -
    saml2.logout_response (module) -
    - - -
    saml2.metadata (module) -
    - - -
    saml2.response (module) -
    - - -
    saml2.settings (module) -
    - - -
    saml2.utils (module) -
    - - -
    SAML_LOGOUTMESSAGE_NOT_FOUND (saml2.errors.OneLogin_Saml2_Error attribute) -
    - - -
    SAML_LOGOUTREQUEST_INVALID (saml2.errors.OneLogin_Saml2_Error attribute) -
    - - -
    SAML_LOGOUTRESPONSE_INVALID (saml2.errors.OneLogin_Saml2_Error attribute) -
    - - -
    SAML_RESPONSE_NOT_FOUND (saml2.errors.OneLogin_Saml2_Error attribute) -
    - -
    - -
    SAML_SINGLE_LOGOUT_NOT_SUPPORTED (saml2.errors.OneLogin_Saml2_Error attribute) -
    - - -
    set_strict() (saml2.auth.OneLogin_Saml2_Auth method) -
    - -
    - -
    (saml2.settings.OneLogin_Saml2_Settings method) -
    - -
    - -
    SETTINGS_FILE_NOT_FOUND (saml2.errors.OneLogin_Saml2_Error attribute) -
    - - -
    SETTINGS_INVALID (saml2.errors.OneLogin_Saml2_Error attribute) -
    - - -
    SETTINGS_INVALID_SYNTAX (saml2.errors.OneLogin_Saml2_Error attribute) -
    - - -
    sign_metadata() (saml2.metadata.OneLogin_Saml2_Metadata static method) -
    - - -
    SP_CERTS_NOT_FOUND (saml2.errors.OneLogin_Saml2_Error attribute) -
    - - -
    STATUS_NO_PASSIVE (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - - -
    STATUS_PARTIAL_LOGOUT (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - - -
    STATUS_PROXY_COUNT_EXCEEDED (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - - -
    STATUS_REQUESTER (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - - -
    STATUS_RESPONDER (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - - -
    STATUS_SUCCESS (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - - -
    STATUS_VERSION_MISMATCH (saml2.constants.OneLogin_Saml2_Constants attribute) -
    - -
    - -

    T

    - - - -
    - -
    TIME_CACHED (saml2.metadata.OneLogin_Saml2_Metadata attribute) -
    - -
    - -
    TIME_VALID (saml2.metadata.OneLogin_Saml2_Metadata attribute) -
    - -
    - -

    V

    - - - -
    - -
    validate_metadata() (saml2.settings.OneLogin_Saml2_Settings method) -
    - - -
    validate_num_assertions() (saml2.response.OneLogin_Saml2_Response method) -
    - - -
    validate_sign() (saml2.utils.OneLogin_Saml2_Utils static method) -
    - -
    - -
    validate_timestamps() (saml2.response.OneLogin_Saml2_Response method) -
    - - -
    validate_url() (in module saml2.settings) -
    - - -
    validate_xml() (saml2.utils.OneLogin_Saml2_Utils static method) -
    - -
    - -

    W

    - - -
    - -
    write_temp_file() (saml2.utils.OneLogin_Saml2_Utils static method) -
    - -
    - - - -
    -
    -
    -
    -
    - - - - - -
    -
    -
    -
    - - - - diff --git a/docs/saml2/index.html b/docs/saml2/index.html deleted file mode 100644 index 1015fa57..00000000 --- a/docs/saml2/index.html +++ /dev/null @@ -1,141 +0,0 @@ - - - - - - - - - - Welcome to SAML Python library documentation - - - - - - - - - - - - - - -
    -
    - -
    -
    -
    -

    Table Of Contents

    - - -

    Next topic

    -

    onelogin.saml2 Module

    -

    This Page

    - - - -
    -
    -
    -
    - - - - diff --git a/docs/saml2/objects.inv b/docs/saml2/objects.inv deleted file mode 100644 index f4df5df0cf8a1203e12c4a54b64e6f611ff0f28d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1959 zcmV;Y2Uz$cAX9K?X>NERX>N99Zgg*Qc_4OWa&u{KZXhxWBOp+6Z)#;@bUGkUZe>hw zXK8LAQ$bBkAW(U9Xm4&HY-wV0VRCsOV{Bn_b7gZNVQyp~ZDn+5Z)9@{BOq2~a&u{K zZaN?eBOp|0Wgv28ZDDC{WMy(7Z)PBLXlZjGW@&6?AZc?TV{dJ6a%FRKWn>_Ab7^j8 zAbM?s|2V9Tf1|@6!eqtX@uVz%yH4whi=+}}pI!S5+dIDwDmJfIc?DEG0DJqur|?K!B{31A&7V8LW*>Hi?r-`~ zkfqjEOaSLKbZyaZjfr)g9g~9G6Y*`-rh2^}idyuG8Cw>KV31T9JSUv}@jI|8t+QT# z@>3#DPj7WFfkWX$tzL(bdvC=AzLF=nb>{^9Acosu;X9~pZ_M~mINhu(ofi2sG|A{w zla*?D#C3r9u^T_3_PFn%hmz;R(`bCE_M2x|&7zLa(={s-j#R-3Lr}ulOOv+@L|&q= z|ERq`cX^R*^a4CzRf*_YHffJ7%jEu2f!!S9#%&L7h!v3tb=wGmG`8Xe1-h_pLg`fZ zT-I%w2uN9rKD4$)R!PY;%<_1wm;`1tutIaFz?-ekF1EIeDIDCOq5{ag>cJALsgtq` zCQ-Mj)1&~-IM3?}D|Vy#L&?$3jig1w`GgC(aec@2zkow-=MupUJ4ZzRS`Zo|o$geG zp_Vs?dGIAz#GZ6F>x*uTIT^2iylo=5SaBSw03&x7c~%SypN7PCZ(ZV;((mTlj5*U; zTS9r`4o#dAo;WL(ia$YBgM_d4)qo>@Pl`NK1n8wKOi&(5;#l4Qy@c=RI1mj3-$6PM zR;}xrn+&c0v-ilI?C&0lEys#2pdq;ay)}0o4VmX-)md}SIdJ){bXsU>l9mrV3q%bH z#3u8tO(Y)z`Dr1Wr1emcw^@%}d2P03-C;$o}bb#r+D)5=uc|SWH6?Y51(}1Y2+YB~uA{o6-*DU=8N>qo~-l_YK zh~q}Y-rl+-l=JE3&$NAQWQJ0(QhI?YIG>(2jcP;RD+-2HNd#FbM0v|txRCjn0Qpn5 zkSv8EytF;I3+6J#QPtz0Q;OYezi%B49&g0tdm!nuoK-0XM^N&HNTd_x&;{vjjGIh@ z6+jQEDK%c}A1k4GhOO*rFhV3P(NV35BAg8Ip+)IK5aD$AMlaGF6~ZDo;#?7x`TL#q z!DR8m0AE^c2R(#Qel@CHGqk(QpHrTd|6@ijH?xF14u9OgPGF zHmx3+y_gDqbtPmT&5d4$XSs8*94Er|dhn@D)s-OeLt>qbQQRam53MM6tqqs{Ik9y& ze>AjhKj`7my(be*-vl;~1`tP+p7qA*wnZw!J#+Tkcw!%r!>q3supM z#@K*%3`xwijn(9Z!PzdlZM5O(4hr1Pp7to<@;bx{Zr_x#GWaNK+qD~iK z7)02tLW>RVdeFo?BTb65>&F#)EM7`=u(oOQN*Ehg8*Wr*UQHLD6#2fjY?JIpD`p&3 zLDSZ$WOe(KU6;>(E^g+o-SR=9_99ybN?&g7BHp>vvb?UvaaY4G+V75MwTV7}(YqONT$4;}cxgG8?2 zL8>+IEZ2v>7V-d!2ZDNTlnml63*93e3(-d1Y8aE&89TqA)`~}9P2qi%n>I!w<>^++ zjf@D}9#Eo2-rzo1*ms0Z?y diff --git a/docs/saml2/py-modindex.html b/docs/saml2/py-modindex.html deleted file mode 100644 index 5f2d4393..00000000 --- a/docs/saml2/py-modindex.html +++ /dev/null @@ -1,153 +0,0 @@ - - - - - - - - - - Python Class Index — SAML Python library classes and methods - - - - - - - - - - - - - - - - -
    -
    -
    -
    - - -

    Python Class Index

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - saml2 -
        - saml2.auth -
        - saml2.authn_request -
        - saml2.constants -
        - saml2.errors -
        - saml2.logout_request -
        - saml2.logout_response -
        - saml2.metadata -
        - saml2.response -
        - saml2.settings -
        - saml2.utils -
    - - -
    -
    -
    -
    -
    - - -
    -
    -
    -
    - - - - diff --git a/docs/saml2/saml2.html b/docs/saml2/saml2.html deleted file mode 100644 index 6b9631f0..00000000 --- a/docs/saml2/saml2.html +++ /dev/null @@ -1,2043 +0,0 @@ - - - - - - - - - - OneLogin saml2 Module — SAML Python library classes and methods - - - - - - - - - - - - - - -
    -
    -
    -
    - -
    -

    OneLogin saml2 Module

    -
    -

    auth Class

    -
    -
    -class onelogin.saml2.auth.OneLogin_Saml2_Auth(request_data, old_settings=None)[source]
    -

    Bases: object

    -
    -
    -build_request_signature(saml_request, relay_state)[source]
    -

    Builds the Signature of the SAML Request.

    - --- - - - -
    Parameters:
      -
    • saml_request (string) – The SAML Request
    • -
    • relay_state (string) – The target URL the user should be redirected to
    • -
    -
    -
    - -
    -
    -build_response_signature(saml_response, relay_state)[source]
    -

    Builds the Signature of the SAML Response. -:param saml_request: The SAML Response -:type saml_request: string

    - --- - - - -
    Parameters:relay_state (string) – The target URL the user should be redirected to
    -
    - -
    -
    -get_attribute(name)[source]
    -

    Returns the requested SAML attribute.

    - --- - - - - - - - -
    Parameters:name (string) – Name of the attribute
    Returns:Attribute value if exists or None
    Return type:string
    -
    - -
    -
    -get_attributes()[source]
    -

    Returns the set of SAML attributes.

    - --- - - - - - -
    Returns:SAML attributes
    Return type:dict
    -
    - -
    -
    -get_errors()[source]
    -

    Returns a list with code errors if something went wrong

    - --- - - - - - -
    Returns:List of errors
    Return type:list
    -
    - -
    -
    -get_last_error_reason()[source]
    -

    Returns the reason for the last error

    - --- - - - - - -
    Returns:Error
    Return type:string
    -
    - -
    -
    -get_nameid()[source]
    -

    Returns the nameID.

    - --- - - - - - -
    Returns:NameID
    Return type:string
    -
    - -
    -
    -get_settings()[source]
    -

    Returns the settings info -:return: Setting info -:rtype: OneLogin_Saml2_Setting object

    -
    - -
    -
    -get_slo_url()[source]
    -

    Gets the SLO url.

    - --- - - - - - -
    Returns:An URL, the SLO endpoint of the IdP
    Return type:string
    -
    - -
    -
    -get_sso_url()[source]
    -

    Gets the SSO url.

    - --- - - - - - -
    Returns:An URL, the SSO endpoint of the IdP
    Return type:string
    -
    - -
    -
    -is_authenticated()[source]
    -

    Checks if the user is authenticated or not.

    - --- - - - - - -
    Returns:True if is authenticated, False if not
    Return type:bool
    -
    - -
    -
    -login(return_to=None, force_authn=False, is_passive=False)[source]
    -

    Initiates the SSO process.

    - --- - - - - - -
    Parameters:
      -
    • return_to (string) – Optional argument. The target URL the user should be redirected to after login.
    • -
    • force_authn (bool) – Optional argument. When true the AuthNReuqest will set the ForceAuthn='true'.
    • -
    • is_passive (bool) – Optional argument. When true the AuthNReuqest will set the Ispassive='true'.
    • -
    -
    Returns:Redirection url
    -
    - -
    -
    -logout(return_to=None, name_id=None, session_index=None)[source]
    -

    Initiates the SLO process.

    - --- - - - - - - -
    Parameters:
      -
    • return_to (string) – Optional argument. The target URL the user should be redirected to after logout.
    • -
    • name_id (string) – Optional argument. The NameID that will be set in the LogoutRequest.
    • -
    • session_index (string) – Optional argument. SessionIndex that identifies the session of the user.
    • -
    Returns:Redirection url
    -
    - -
    -
    -process_response(request_id=None)[source]
    -

    Process the SAML Response sent by the IdP.

    - --- - - - - - -
    Parameters:request_id (string) – Is an optional argumen. Is the ID of the AuthNRequest sent by this SP to the IdP.
    Raises :OneLogin_Saml2_Error.SAML_RESPONSE_NOT_FOUND, when a POST with a SAMLResponse is not found
    -
    - -
    -
    -process_slo(keep_local_session=False, request_id=None, delete_session_cb=None)[source]
    -

    Process the SAML Logout Response / Logout Request sent by the IdP.

    - --- - - - - - -
    Parameters:
      -
    • keep_local_session (bool) – When false will destroy the local session, otherwise will destroy it
    • -
    • request_id (string) – The ID of the LogoutRequest sent by this SP to the IdP
    • -
    -
    Returns:

    Redirection url

    -
    -
    - -
    -
    -redirect_to(url=None, parameters={})[source]
    -

    Redirects the user to the url past by parameter or to the url that we defined in our SSO Request.

    - --- - - - - - -
    Parameters:
      -
    • url (string) – The target URL to redirect the user
    • -
    • parameters (dict) – Extra parameters to be passed as part of the url
    • -
    -
    Returns:

    Redirection url

    -
    -
    - -
    -
    -set_strict(value)[source]
    -

    Set the strict mode active/disable

    - --- - - - -
    Parameters:value (bool) –
    -
    - -
    - -
    -
    -

    authn_request Class

    -
    -
    -class onelogin.saml2.authn_request.OneLogin_Saml2_Authn_Request(settings, force_authn=False, is_passive=False)[source]
    -
    -
    -get_request()[source]
    -

    Returns unsigned AuthnRequest. -:return: Unsigned AuthnRequest -:rtype: str object

    -
    - -
    - -
    -
    -

    constants Class

    -
    -
    -class onelogin.saml2.constants.OneLogin_Saml2_Constants[source]
    -
    -
    -AC_KERBEROS = 'urn:oasis:names:tc:SAML:2.0:ac:classes:Kerberos'
    -
    - -
    -
    -AC_PASSWORD = 'urn:oasis:names:tc:SAML:2.0:ac:classes:Password'
    -
    - -
    -
    -AC_SMARTCARD = 'urn:oasis:names:tc:SAML:2.0:ac:classes:Smartcard'
    -
    - -
    -
    -AC_UNSPECIFIED = 'urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified'
    -
    - -
    -
    -AC_X509 = 'urn:oasis:names:tc:SAML:2.0:ac:classes:X509'
    -
    - -
    -
    -ALOWED_CLOCK_DRIFT = 180
    -
    - -
    -
    -ATTRNAME_FORMAT_BASIC = 'urn:oasis:names:tc:SAML:2.0:attrname-format:basic'
    -
    - -
    -
    -ATTRNAME_FORMAT_UNSPECIFIED = 'urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified'
    -
    - -
    -
    -ATTRNAME_FORMAT_URI = 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri'
    -
    - -
    -
    -BINDING_DEFLATE = 'urn:oasis:names:tc:SAML:2.0:bindings:URL-Encoding:DEFLATE'
    -
    - -
    -
    -BINDING_HTTP_ARTIFACT = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact'
    -
    - -
    -
    -BINDING_HTTP_POST = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
    -
    - -
    -
    -BINDING_HTTP_REDIRECT = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'
    -
    - -
    -
    -BINDING_SOAP = 'urn:oasis:names:tc:SAML:2.0:bindings:SOAP'
    -
    - -
    -
    -CM_BEARER = 'urn:oasis:names:tc:SAML:2.0:cm:bearer'
    -
    - -
    -
    -CM_HOLDER_KEY = 'urn:oasis:names:tc:SAML:2.0:cm:holder-of-key'
    -
    - -
    -
    -CM_SENDER_VOUCHES = 'urn:oasis:names:tc:SAML:2.0:cm:sender-vouches'
    -
    - -
    -
    -NAMEID_EMAIL_ADDRESS = 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress'
    -
    - -
    -
    -NAMEID_ENCRYPTED = 'urn:oasis:names:tc:SAML:2.0:nameid-format:encrypted'
    -
    - -
    -
    -NAMEID_ENTITY = 'urn:oasis:names:tc:SAML:2.0:nameid-format:entity'
    -
    - -
    -
    -NAMEID_KERBEROS = 'urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos'
    -
    - -
    -
    -NAMEID_PERSISTENT = 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent'
    -
    - -
    -
    -NAMEID_TRANSIENT = 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient'
    -
    - -
    -
    -NAMEID_WINDOWS_DOMAIN_QUALIFIED_NAME = 'urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName'
    -
    - -
    -
    -NAMEID_X509_SUBJECT_NAME = 'urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName'
    -
    - -
    -
    -NSMAP = {'xenc': 'http://www.w3.org/2001/04/xmlenc#', 'samlp': 'urn:oasis:names:tc:SAML:2.0:protocol', 'ds': 'http://www.w3.org/2000/09/xmldsig#', 'saml': 'urn:oasis:names:tc:SAML:2.0:assertion'}
    -
    - -
    -
    -NS_DS = 'http://www.w3.org/2000/09/xmldsig#'
    -
    - -
    -
    -NS_MD = 'urn:oasis:names:tc:SAML:2.0:metadata'
    -
    - -
    -
    -NS_SAML = 'urn:oasis:names:tc:SAML:2.0:assertion'
    -
    - -
    -
    -NS_SAMLP = 'urn:oasis:names:tc:SAML:2.0:protocol'
    -
    - -
    -
    -NS_SOAP = 'http://schemas.xmlsoap.org/soap/envelope/'
    -
    - -
    -
    -NS_XENC = 'http://www.w3.org/2001/04/xmlenc#'
    -
    - -
    -
    -NS_XS = 'http://www.w3.org/2001/XMLSchema'
    -
    - -
    -
    -NS_XSI = 'http://www.w3.org/2001/XMLSchema-instance'
    -
    - -
    -
    -RSA_SHA1 = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1'
    -
    - -
    -
    -STATUS_NO_PASSIVE = 'urn:oasis:names:tc:SAML:2.0:status:NoPassive'
    -
    - -
    -
    -STATUS_PARTIAL_LOGOUT = 'urn:oasis:names:tc:SAML:2.0:status:PartialLogout'
    -
    - -
    -
    -STATUS_PROXY_COUNT_EXCEEDED = 'urn:oasis:names:tc:SAML:2.0:status:ProxyCountExceeded'
    -
    - -
    -
    -STATUS_REQUESTER = 'urn:oasis:names:tc:SAML:2.0:status:Requester'
    -
    - -
    -
    -STATUS_RESPONDER = 'urn:oasis:names:tc:SAML:2.0:status:Responder'
    -
    - -
    -
    -STATUS_SUCCESS = 'urn:oasis:names:tc:SAML:2.0:status:Success'
    -
    - -
    -
    -STATUS_VERSION_MISMATCH = 'urn:oasis:names:tc:SAML:2.0:status:VersionMismatch'
    -
    - -
    - -
    -
    -

    errors Class

    -
    -
    -exception onelogin.saml2.errors.OneLogin_Saml2_Error(message, code=0, errors=None)[source]
    -

    Bases: exceptions.Exception

    -
    -
    -METADATA_SP_INVALID = 3
    -
    - -
    -
    -PRIVATE_KEY_FILE_NOT_FOUND = 7
    -
    - -
    -
    -PUBLIC_CERT_FILE_NOT_FOUND = 6
    -
    - -
    -
    -REDIRECT_INVALID_URL = 5
    -
    - -
    -
    -SAML_LOGOUTMESSAGE_NOT_FOUND = 9
    -
    - -
    -
    -SAML_LOGOUTREQUEST_INVALID = 10
    -
    - -
    -
    -SAML_LOGOUTRESPONSE_INVALID = 11
    -
    - -
    -
    -SAML_RESPONSE_NOT_FOUND = 8
    -
    - -
    -
    -SAML_SINGLE_LOGOUT_NOT_SUPPORTED = 12
    -
    - -
    -
    -SETTINGS_FILE_NOT_FOUND = 0
    -
    - -
    -
    -SETTINGS_INVALID = 2
    -
    - -
    -
    -SETTINGS_INVALID_SYNTAX = 1
    -
    - -
    -
    -SP_CERTS_NOT_FOUND = 4
    -
    - -
    - -
    -
    -

    logout_request Class

    -
    -
    -class onelogin.saml2.logout_request.OneLogin_Saml2_Logout_Request(settings, request=None, name_id=None, session_index=None)[source]
    -
    -
    -static get_id(request)[source]
    -

    Returns the ID of the Logout Request -:param request: Logout Request Message -:type request: string|DOMDocument -:return: string ID -:rtype: str object

    -
    - -
    -
    -static get_issuer(request)[source]
    -

    Gets the Issuer of the Logout Request Message -:param request: Logout Request Message -:type request: string|DOMDocument -:return: The Issuer -:rtype: string

    -
    - -
    -
    -static get_name_id(request, key=None)[source]
    -

    Gets the NameID of the Logout Request Message -:param request: Logout Request Message -:type request: string|DOMDocument -:param key: The SP key -:type key: string -:return: Name ID Value -:rtype: string

    -
    - -
    -
    -static get_name_id_data(request, key=None)[source]
    -

    Gets the NameID Data of the the Logout Request -:param request: Logout Request Message -:type request: string|DOMDocument -:param key: The SP key -:type key: string -:return: Name ID Data (Value, Format, NameQualifier, SPNameQualifier) -:rtype: dict

    -
    - -
    -
    -get_request()[source]
    -

    Returns the Logout Request defated, base64encoded -:return: Deflated base64 encoded Logout Request -:rtype: str object

    -
    - -
    -
    -static get_session_indexes(request)[source]
    -

    Gets the SessionIndexes from the Logout Request -:param request: Logout Request Message -:type request: string|DOMDocument -:return: The SessionIndex value -:rtype: list

    -
    - -
    -
    -static is_valid(settings, request, get_data, debug=False)[source]
    -

    Checks if the Logout Request recieved is valid -:param settings: Settings -:type settings: OneLogin_Saml2_Settings -:param request: Logout Request Message -:type request: string|DOMDocument -:return: If the Logout Request is or not valid -:rtype: boolean

    -
    - -
    -
    -get_error()[source]
    -

    After execute a validation process, if fails this method returns the cause -:rtype: str object

    -
    - -
    - -
    -
    -

    logout_response Class

    -
    -
    -class onelogin.saml2.logout_response.OneLogin_Saml2_Logout_Response(settings, response=None)[source]
    -
    -
    -build(in_response_to)[source]
    -

    Creates a Logout Response object. -:param in_response_to: InResponseTo value for the Logout Response. -:type in_response_to: string

    -
    - -
    -
    -get_issuer()[source]
    -

    Gets the Issuer of the Logout Response Message -:return: The Issuer -:rtype: string

    -
    - -
    -
    -get_response()[source]
    -

    Returns a Logout Response object. -:return: Logout Response deflated and base64 encoded -:rtype: string

    -
    - -
    -
    -get_status()[source]
    -

    Gets the Status -:return: The Status -:rtype: string

    -
    - -
    -
    -is_valid(request_data, request_id=None)[source]
    -

    Determines if the SAML LogoutResponse is valid -:param request_id: The ID of the LogoutRequest sent by this SP to the IdP -:type request_id: string -:return: Returns if the SAML LogoutResponse is or not valid -:rtype: boolean

    -
    - -
    -
    -get_error()[source]
    -

    After execute a validation process, if fails this method returns the cause -:rtype: str object

    -
    - -
    - -
    -
    -

    metadata Class

    -
    -
    -class onelogin.saml2.metadata.OneLogin_Saml2_Metadata[source]
    -
    -
    -TIME_CACHED = 604800
    -
    - -
    -
    -TIME_VALID = 172800
    -
    - -
    -
    -static add_x509_key_descriptors(metadata, cert)[source]
    -

    Add the x509 descriptors (sign/encriptation to the metadata -The same cert will be used for sign/encrypt

    - --- - - - - - - - -
    Parameters:
      -
    • metadata (string) – SAML Metadata XML
    • -
    • cert (string) – x509 cert
    • -
    -
    Returns:

    Metadata with KeyDescriptors

    -
    Return type:

    string

    -
    -
    - -
    -
    -static builder(sp, authnsign=False, wsign=False, valid_until=None, cache_duration=None, contacts=None, organization=None)[source]
    -

    Build the metadata of the SP

    - --- - - - -
    Parameters:
      -
    • sp (string) – The SP data
    • -
    • authnsign (string) – authnRequestsSigned attribute
    • -
    • wsign (string) – wantAssertionsSigned attribute
    • -
    • valid_until (DateTime) – Metadata’s valid time
    • -
    • cache_duration (Timestamp) – Duration of the cache in seconds
    • -
    • contacts (dict) – Contacts info
    • -
    • organization (dict) – Organization ingo
    • -
    -
    -
    - -
    -
    -static sign_metadata(metadata, key, cert)[source]
    -

    Sign the metadata with the key/cert provided

    - --- - - - - - - - -
    Parameters:
      -
    • metadata (string) – SAML Metadata XML
    • -
    • key (string) – x509 key
    • -
    • cert (string) – x509 cert
    • -
    -
    Returns:

    Signed Metadata

    -
    Return type:

    string

    -
    -
    - -
    - -
    -
    -

    response Class

    -
    -
    -class onelogin.saml2.response.OneLogin_Saml2_Response(settings, response)[source]
    -

    Bases: object

    -
    -
    -check_status()[source]
    -

    Check if the status of the response is success or not

    - --- - - - -
    Raises :Exception. If the status is not success
    -
    - -
    -
    -get_attributes()[source]
    -

    Gets the Attributes from the AttributeStatement element. -EncryptedAttributes are not supported

    -
    - -
    -
    -get_audiences()[source]
    -

    Gets the audiences

    - --- - - - - - -
    Returns:The valid audiences for the SAML Response
    Return type:list
    -
    - -
    -
    -get_issuers()[source]
    -

    Gets the issuers (from message and from assertion)

    - --- - - - - - -
    Returns:The issuers
    Return type:list
    -
    - -
    -
    -get_nameid()[source]
    -

    Gets the NameID provided by the SAML Response from the IdP

    - --- - - - - - -
    Returns:NameID (value)
    Return type:string
    -
    - -
    -
    -get_nameid_data()[source]
    -

    Gets the NameID Data provided by the SAML Response from the IdP

    - --- - - - - - -
    Returns:Name ID Data (Value, Format, NameQualifier, SPNameQualifier)
    Return type:dict
    -
    - -
    -
    -get_session_index()[source]
    -

    Gets the SessionIndex from the AuthnStatement -Could be used to be stored in the local session in order -to be used in a future Logout Request that the SP could -send to the SP, to set what specific session must be deleted

    - --- - - - - - -
    Returns:The SessionIndex value
    Return type:string|None
    -
    - -
    -
    -get_session_not_on_or_after()[source]
    -

    Gets the SessionNotOnOrAfter from the AuthnStatement -Could be used to set the local session expiration

    - --- - - - - - -
    Returns:The SessionNotOnOrAfter value
    Return type:time|None
    -
    - -
    -
    -is_valid(request_data, request_id=None)[source]
    -

    Constructs the response object.

    - --- - - - - - - - -
    Parameters:request_id (string) – Optional argument. The ID of the AuthNRequest sent by this SP to the IdP
    Returns:True if the SAML Response is valid, False if not
    Return type:bool
    -
    - -
    -
    -validate_num_assertions()[source]
    -

    Verifies that the document only contains a single Assertion (encrypted or not)

    - --- - - - - - -
    Returns:True if only 1 assertion encrypted or not
    Return type:bool
    -
    - -
    -
    -validate_timestamps()[source]
    -

    Verifies that the document is valid according to Conditions Element

    - --- - - - - - -
    Returns:True if the condition is valid, False otherwise
    Return type:bool
    -
    - -
    - -
    -
    -

    settings Class

    -
    -
    -class onelogin.saml2.settings.OneLogin_Saml2_Settings(settings=None, custom_base_path=None)[source]
    -
    -
    -check_settings(settings)[source]
    -

    Checks the settings info.

    - --- - - - - - - - -
    Parameters:settings (dict) – Dict with settings data
    Returns:Errors found on the settings data
    Return type:list
    -
    - -
    -
    -check_sp_certs()[source]
    -

    Checks if the x509 certs of the SP exists and are valid.

    - --- - - - - - -
    Returns:If the x509 certs of the SP exists and are valid
    Return type:boolean
    -
    - -
    -
    -format_idp_cert()[source]
    -

    Formats the IdP cert.

    -
    - -
    -
    -get_base_path()[source]
    -

    Returns base path

    - --- - - - - - -
    Returns:The base toolkit folder path
    Return type:string
    -
    - -
    -
    -get_cert_path()[source]
    -

    Returns cert path

    - --- - - - - - -
    Returns:The cert folder path
    Return type:string
    -
    - -
    -
    -get_contacts()[source]
    -

    Gets contact data.

    - --- - - - - - -
    Returns:Contacts info
    Return type:dict
    -
    - -
    -
    -get_errors()[source]
    -

    Returns an array with the errors, the array is empty when the settings is ok.

    - --- - - - - - -
    Returns:Errors
    Return type:list
    -
    - -
    -
    -get_ext_lib_path()[source]
    -

    Returns external lib path

    - --- - - - - - -
    Returns:The external library folder path
    Return type:string
    -
    - -
    -
    -get_idp_data()[source]
    -

    Gets the IdP data.

    - --- - - - - - -
    Returns:IdP info
    Return type:dict
    -
    - -
    -
    -get_lib_path()[source]
    -

    Returns lib path

    - --- - - - - - -
    Returns:The library folder path
    Return type:string
    -
    - -
    -
    -get_organization()[source]
    -

    Gets organization data.

    - --- - - - - - -
    Returns:Organization info
    Return type:dict
    -
    - -
    -
    -get_schemas_path()[source]
    -

    Returns schema path

    - --- - - - - - -
    Returns:The schema folder path
    Return type:string
    -
    - -
    -
    -get_security_data()[source]
    -

    Gets security data.

    - --- - - - - - -
    Returns:Security info
    Return type:dict
    -
    - -
    -
    -get_sp_cert()[source]
    -

    Returns the x509 public cert of the SP.

    - --- - - - - - -
    Returns:SP public cert
    Return type:string
    -
    - -
    -
    -get_sp_data()[source]
    -

    Gets the SP data.

    - --- - - - - - -
    Returns:SP info
    Return type:dict
    -
    - -
    -
    -get_sp_key()[source]
    -

    Returns the x509 private key of the SP.

    - --- - - - - - -
    Returns:SP private key
    Return type:string
    -
    - -
    -
    -get_sp_metadata()[source]
    -

    Gets the SP metadata. The XML representation.

    - --- - - - - - -
    Returns:SP metadata (xml)
    Return type:string
    -
    - -
    -
    -is_debug_active()[source]
    -

    Returns if the debug is active.

    - --- - - - - - -
    Returns:Debug parameter
    Return type:boolean
    -
    - -
    -
    -is_strict()[source]
    -

    Returns if the ‘strict’ mode is active.

    - --- - - - - - -
    Returns:Strict parameter
    Return type:boolean
    -
    - -
    -
    -set_strict(value)[source]
    -

    Activates or deactivates the strict mode.

    - --- - - - -
    Parameters:xml (boolean) – Strict parameter
    -
    - -
    -
    -validate_metadata(xml)[source]
    -

    Validates an XML SP Metadata.

    - --- - - - - - - - -
    Parameters:xml (string) – Metadata’s XML that will be validate
    Returns:The list of found errors
    Return type:list
    -
    - -
    - -
    -
    -onelogin.saml2.settings.validate_url(url)[source]
    -
    - -
    -
    -

    utils Class

    -
    -
    -class onelogin.saml2.utils.OneLogin_Saml2_Utils[source]
    -
    -
    -static add_sign(xml, key, cert)[source]
    -

    Adds signature key and senders certificate to an element (Message or -Assertion).

    - --- - - - - - - - - - -
    Parameters:
      -
    • xml – The element we should sign
    • -
    • key – The private key
    • -
    • cert – The public
    • -
    -
    Type :

    string | Document

    -
    Type :

    string

    -
    Type :

    string

    -
    -
    - -
    -
    -static calculate_x509_fingerprint(x509_cert)[source]
    -

    Calculates the fingerprint of a x509cert.

    - --- - - - - - - - - - -
    Parameters:x509_cert – x509 cert
    Type :string
    Returns:Formated fingerprint
    Return type:string
    -
    - -
    -
    -static decode_base64_and_inflate(value)[source]
    -

    base64 decodes and then inflates according to RFC1951 -:param value: a deflated and encoded string -:return: the string after decoding and inflating

    -
    - -
    -
    -static decrypt_element(encrypted_data, enc_ctx)[source]
    -

    Decrypts an encrypted element.

    - --- - - - - - - - - - - - -
    Parameters:
      -
    • encrypted_data – The encrypted data.
    • -
    • enc_ctx – The encryption context.
    • -
    -
    Type :

    DOMElement

    -
    Type :

    Encryption Context

    -
    Returns:

    The decrypted element.

    -
    Return type:

    DOMElement

    -
    -
    - -
    -
    -static deflate_and_base64_encode(value)[source]
    -

    Deflates and the base64 encodes a string -:param value: The string to deflate and encode -:return: The deflated and encoded string

    -
    - -
    -
    -static delete_local_session(callback=None)[source]
    -

    Deletes the local session.

    -
    - -
    -
    -static format_cert(cert, heads=True)[source]
    -

    Returns a x509 cert (adding header & footer if required).

    - --- - - - - - - - - - - - -
    Parameters:
      -
    • cert – A x509 unformated cert
    • -
    • heads – True if we want to include head and footer
    • -
    -
    Type :

    string

    -
    Type :

    boolean

    -
    Returns:

    Formated cert

    -
    Return type:

    string

    -
    -
    - -
    -
    -static format_finger_print(fingerprint)[source]
    -

    Formates a fingerprint.

    - --- - - - - - - - - - -
    Parameters:fingerprint – fingerprint
    Type :string
    Returns:Formated fingerprint
    Return type:string
    -
    - -
    -
    -static generate_name_id(value, sp_nq, sp_format, key=None)[source]
    -

    Generates a nameID.

    - --- - - - - - - - - - - - - - - - -
    Parameters:
      -
    • value – fingerprint
    • -
    • sp_nq – SP Name Qualifier
    • -
    • sp_format – SP Format
    • -
    • key – SP Key to encrypt the nameID
    • -
    -
    Type :

    string

    -
    Type :

    string

    -
    Type :

    string

    -
    Type :

    string

    -
    Returns:

    DOMElement | XMLSec nameID

    -
    Return type:

    string

    -
    -
    - -
    -
    -static generate_unique_id()[source]
    -

    Generates an unique string (used for example as ID for assertions).

    - --- - - - - - -
    Returns:A unique string
    Return type:string
    -
    - -
    -
    -static get_expire_time(cache_duration=None, valid_until=None)[source]
    -

    Compares 2 dates and returns the earliest.

    - --- - - - - - - - - - - - -
    Parameters:
      -
    • cache_duration – The duration, as a string.
    • -
    • valid_until – The valid until date, as a string or as a timestamp
    • -
    -
    Type :

    string

    -
    Type :

    string

    -
    Returns:

    The expiration time.

    -
    Return type:

    int

    -
    -
    - -
    -
    -static get_self_host(request_data)[source]
    -

    Returns the current host.

    - --- - - - - - - - - - -
    Parameters:request_data – The request as a dict
    Type :dict
    Returns:The current host
    Return type:string
    -
    - -
    -
    -static get_self_url(request_data)[source]
    -

    Returns the URL of the current host + current view + query.

    - --- - - - - - - - - - -
    Parameters:request_data – The request as a dict
    Type :dict
    Returns:The url of current host + current view + query
    Return type:string
    -
    - -
    -
    -static get_self_url_host(request_data)[source]
    -

    Returns the protocol + the current host + the port (if different than -common ports).

    - --- - - - - - - - - - -
    Parameters:request_data – The request as a dict
    Type :dict
    Returns:Url
    Return type:string
    -
    - -
    -
    -static get_self_url_no_query(request_data)[source]
    -

    Returns the URL of the current host + current view.

    - --- - - - - - - - - - -
    Parameters:request_data – The request as a dict
    Type :dict
    Returns:The url of current host + current view
    Return type:string
    -
    - -
    -
    -static get_status(dom)[source]
    -

    Gets Status from a Response.

    - --- - - - - - - - - - -
    Parameters:dom – The Response as XML
    Type :Document
    Returns:The Status, an array with the code and a message.
    Return type:dict
    -
    - -
    -
    -static is_https(request_data)[source]
    -

    Checks if https or http.

    - --- - - - - - - - - - -
    Parameters:request_data – The request as a dict
    Type :dict
    Returns:False if https is not active
    Return type:boolean
    -
    - -
    -
    -static parse_SAML_to_time(timestr)[source]
    -

    Converts a SAML2 timestamp on the form yyyy-mm-ddThh:mm:ss(.s+)?Z -to a UNIX timestamp. The sub-second part is ignored.

    - --- - - - - - - - - - -
    Parameters:time – The time we should convert (SAML Timestamp).
    Type :string
    Returns:Converted to a unix timestamp.
    Return type:int
    -
    - -
    -
    -static parse_duration(duration, timestamp=None)[source]
    -

    Interprets a ISO8601 duration value relative to a given timestamp.

    - --- - - - - - - - - - - - -
    Parameters:
      -
    • duration – The duration, as a string.
    • -
    • timestamp – The unix timestamp we should apply the duration to. -Optional, default to the current time.
    • -
    -
    Type :

    string

    -
    Type :

    string

    -
    Returns:

    The new timestamp, after the duration is applied.

    -
    Return type:

    int

    -
    -
    - -
    -
    -static parse_time_to_SAML(time)[source]
    -

    Converts a UNIX timestamp to SAML2 timestamp on the form -yyyy-mm-ddThh:mm:ss(.s+)?Z.

    - --- - - - - - - - - - -
    Parameters:time – The time we should convert (DateTime).
    Type :string
    Returns:SAML2 timestamp.
    Return type:string
    -
    - -
    -
    -static query(dom, query, context=None)[source]
    -

    Extracts nodes that match the query from the Element

    - --- - - - - - - - - - - - - - -
    Parameters:
      -
    • dom – The root of the lxml objet
    • -
    • query – Xpath Expresion
    • -
    • context – Context Node
    • -
    -
    Type :

    Element

    -
    Type :

    string

    -
    Type :

    DOMElement

    -
    Returns:

    The queried nodes

    -
    Return type:

    list

    -
    -
    - -
    -
    -static redirect(url, parameters={}, request_data={})[source]
    -

    Executes a redirection to the provided url (or return the target url).

    - --- - - - - - - - - - - - - - -
    Parameters:
      -
    • url – The target url
    • -
    • parameters – Extra parameters to be passed as part of the url
    • -
    • request_data – The request as a dict
    • -
    -
    Type :

    string

    -
    Type :

    dict

    -
    Type :

    dict

    -
    Returns:

    Url

    -
    Return type:

    string

    -
    -
    - -
    -
    -static validate_sign(xml, cert=None, fingerprint=None)[source]
    -

    Validates a signature (Message or Assertion).

    - --- - - - - - - - - - -
    Parameters:
      -
    • xml – The element we should validate
    • -
    • cert – The pubic cert
    • -
    • fingerprint – The fingerprint of the public cert
    • -
    -
    Type :

    string | Document

    -
    Type :

    string

    -
    Type :

    string

    -
    -
    - -
    -
    -static validate_xml(xml, schema, debug=False)[source]
    -
    - -
    -
    -static write_temp_file(content)[source]
    -

    Writes some content into a temporary file and returns it.

    - --- - - - - - - - - - -
    Parameters:content – The file content
    Type :string
    Returns:The temporary file
    Return type:file-like object
    -
    - -
    - -
    -
    - - -
    -
    -
    -
    -
    -

    Table Of Contents

    - - -

    Previous topic

    -

    Welcome to SAML Python library documentation

    -

    This Page

    - - - -
    -
    -
    -
    - - - - diff --git a/docs/saml2/search.html b/docs/saml2/search.html deleted file mode 100644 index 933fde97..00000000 --- a/docs/saml2/search.html +++ /dev/null @@ -1,106 +0,0 @@ - - - - - - - - - - Search — SAML Python library classes and methods - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Search

    -
    - -

    - Please activate JavaScript to enable the search - functionality. -

    -
    -

    - From here you can search these documents. Enter your search - words into the box below and click "search". Note that the search - function will automatically search for all of the words. Pages - containing fewer words won't appear in the result list. -

    -
    - - - -
    - -
    - -
    - -
    -
    -
    -
    -
    -
    -
    -
    -
    - - - - diff --git a/docs/saml2/searchindex.js b/docs/saml2/searchindex.js deleted file mode 100644 index cf3c42ca..00000000 --- a/docs/saml2/searchindex.js +++ /dev/null @@ -1 +0,0 @@ -Search.setIndex({objects:{"saml2.logout_response.OneLogin_Saml2_Logout_Response":{is_valid:[1,2,1,""],get_response:[1,2,1,""],get_status:[1,2,1,""],get_issuer:[1,2,1,""],build:[1,2,1,""]},"saml2.response.OneLogin_Saml2_Response":{get_audiences:[1,2,1,""],validate_num_assertions:[1,2,1,""],get_nameid_data:[1,2,1,""],get_session_index:[1,2,1,""],get_issuers:[1,2,1,""],is_valid:[1,2,1,""],check_status:[1,2,1,""],validate_timestamps:[1,2,1,""],get_nameid:[1,2,1,""],get_attributes:[1,2,1,""],get_session_not_on_or_after:[1,2,1,""]},"saml2.errors.OneLogin_Saml2_Error":{PUBLIC_CERT_FILE_NOT_FOUND:[1,1,1,""],SETTINGS_FILE_NOT_FOUND:[1,1,1,""],SAML_LOGOUTREQUEST_INVALID:[1,1,1,""],REDIRECT_INVALID_URL:[1,1,1,""],PRIVATE_KEY_FILE_NOT_FOUND:[1,1,1,""],SAML_LOGOUTMESSAGE_NOT_FOUND:[1,1,1,""],SAML_RESPONSE_NOT_FOUND:[1,1,1,""],METADATA_SP_INVALID:[1,1,1,""],SAML_LOGOUTRESPONSE_INVALID:[1,1,1,""],SETTINGS_INVALID:[1,1,1,""],SP_CERTS_NOT_FOUND:[1,1,1,""],SETTINGS_INVALID_SYNTAX:[1,1,1,""],SAML_SINGLE_LOGOUT_NOT_SUPPORTED:[1,1,1,""]},"saml2.errors":{OneLogin_Saml2_Error:[1,6,1,""]},"saml2.metadata.OneLogin_Saml2_Metadata":{sign_metadata:[1,3,1,""],builder:[1,3,1,""],add_x509_key_descriptors:[1,3,1,""],TIME_VALID:[1,1,1,""],TIME_CACHED:[1,1,1,""]},"saml2.response":{OneLogin_Saml2_Response:[1,4,1,""]},"saml2.settings.OneLogin_Saml2_Settings":{get_contacts:[1,2,1,""],get_security_data:[1,2,1,""],validate_metadata:[1,2,1,""],get_errors:[1,2,1,""],check_settings:[1,2,1,""],get_sp_data:[1,2,1,""],get_idp_data:[1,2,1,""],get_cert_path:[1,2,1,""],get_schemas_path:[1,2,1,""],set_strict:[1,2,1,""],get_base_path:[1,2,1,""],is_strict:[1,2,1,""],get_lib_path:[1,2,1,""],get_sp_key:[1,2,1,""],get_sp_metadata:[1,2,1,""],is_debug_active:[1,2,1,""],get_ext_lib_path:[1,2,1,""],get_sp_cert:[1,2,1,""],get_organization:[1,2,1,""],check_sp_certs:[1,2,1,""],format_idp_cert:[1,2,1,""]},"saml2.settings":{OneLogin_Saml2_Settings:[1,4,1,""],validate_url:[1,5,1,""]},"saml2.logout_response":{OneLogin_Saml2_Logout_Response:[1,4,1,""]},"saml2.authn_request.OneLogin_Saml2_Authn_Request":{get_request:[1,2,1,""]},"saml2.constants.OneLogin_Saml2_Constants":{NAMEID_EMAIL_ADDRESS:[1,1,1,""],CM_SENDER_VOUCHES:[1,1,1,""],CM_HOLDER_KEY:[1,1,1,""],NS_SAML:[1,1,1,""],RSA_SHA1:[1,1,1,""],NS_XS:[1,1,1,""],STATUS_PROXY_COUNT_EXCEEDED:[1,1,1,""],NS_SOAP:[1,1,1,""],NAMEID_ENCRYPTED:[1,1,1,""],STATUS_REQUESTER:[1,1,1,""],STATUS_NO_PASSIVE:[1,1,1,""],STATUS_PARTIAL_LOGOUT:[1,1,1,""],BINDING_HTTP_REDIRECT:[1,1,1,""],NAMEID_X509_SUBJECT_NAME:[1,1,1,""],AC_KERBEROS:[1,1,1,""],NAMEID_KERBEROS:[1,1,1,""],BINDING_HTTP_ARTIFACT:[1,1,1,""],NS_XENC:[1,1,1,""],BINDING_HTTP_POST:[1,1,1,""],CM_BEARER:[1,1,1,""],ALOWED_CLOCK_DRIFT:[1,1,1,""],BINDING_DEFLATE:[1,1,1,""],NAMEID_ENTITY:[1,1,1,""],AC_SMARTCARD:[1,1,1,""],AC_UNSPECIFIED:[1,1,1,""],NS_XSI:[1,1,1,""],NSMAP:[1,1,1,""],STATUS_RESPONDER:[1,1,1,""],AC_PASSWORD:[1,1,1,""],NS_SAMLP:[1,1,1,""],NS_DS:[1,1,1,""],STATUS_SUCCESS:[1,1,1,""],AC_X509:[1,1,1,""],NAMEID_TRANSIENT:[1,1,1,""],BINDING_SOAP:[1,1,1,""],ATTRNAME_FORMAT_UNSPECIFIED:[1,1,1,""],ATTRNAME_FORMAT_BASIC:[1,1,1,""],NS_MD:[1,1,1,""],ATTRNAME_FORMAT_URI:[1,1,1,""],NAMEID_PERSISTENT:[1,1,1,""],STATUS_VERSION_MISMATCH:[1,1,1,""],NAMEID_WINDOWS_DOMAIN_QUALIFIED_NAME:[1,1,1,""]},"saml2.authn_request":{OneLogin_Saml2_Authn_Request:[1,4,1,""]},"saml2.metadata":{OneLogin_Saml2_Metadata:[1,4,1,""]},"saml2.utils.OneLogin_Saml2_Utils":{generate_unique_id:[1,3,1,""],add_sign:[1,3,1,""],deflate_and_base64_encode:[1,3,1,""],get_status:[1,3,1,""],query:[1,3,1,""],redirect:[1,3,1,""],get_expire_time:[1,3,1,""],decode_base64_and_inflate:[1,3,1,""],parse_SAML_to_time:[1,3,1,""],parse_duration:[1,3,1,""],generate_name_id:[1,3,1,""],validate_xml:[1,3,1,""],get_self_host:[1,3,1,""],parse_time_to_SAML:[1,3,1,""],format_finger_print:[1,3,1,""],decrypt_element:[1,3,1,""],get_self_url_host:[1,3,1,""],get_self_url:[1,3,1,""],delete_local_session:[1,3,1,""],format_cert:[1,3,1,""],is_https:[1,3,1,""],calculate_x509_fingerprint:[1,3,1,""],get_self_url_no_query:[1,3,1,""],write_temp_file:[1,3,1,""],validate_sign:[1,3,1,""]},"saml2.logout_request.OneLogin_Saml2_Logout_Request":{get_issuer:[1,3,1,""],get_name_id:[1,3,1,""],get_request:[1,2,1,""],get_id:[1,3,1,""],is_valid:[1,3,1,""],get_session_indexes:[1,3,1,""],get_name_id_data:[1,3,1,""]},"saml2.utils":{OneLogin_Saml2_Utils:[1,4,1,""]},"saml2.constants":{OneLogin_Saml2_Constants:[1,4,1,""]},"saml2.auth.OneLogin_Saml2_Auth":{get_settings:[1,2,1,""],process_response:[1,2,1,""],get_errors:[1,2,1,""],build_request_signature:[1,2,1,""],redirect_to:[1,2,1,""],is_authenticated:[1,2,1,""],get_attribute:[1,2,1,""],build_response_signature:[1,2,1,""],set_strict:[1,2,1,""],process_slo:[1,2,1,""],get_sso_url:[1,2,1,""],logout:[1,2,1,""],login:[1,2,1,""],get_slo_url:[1,2,1,""],get_attributes:[1,2,1,""],get_nameid:[1,2,1,""]},"saml2.auth":{OneLogin_Saml2_Auth:[1,4,1,""]},saml2:{errors:[1,0,1,""],settings:[1,0,1,""],utils:[1,0,1,""],auth:[1,0,1,""],logout_request:[1,0,1,""],authn_request:[1,0,1,""],logout_response:[1,0,1,""],response:[1,0,1,""],constants:[1,0,1,""],metadata:[1,0,1,""]},"saml2.logout_request":{OneLogin_Saml2_Logout_Request:[1,4,1,""]}},terms:{represent:1,code:1,queri:1,issuer:1,privat:1,encryptedattribut:1,base64:1,ac_smartcard:1,specif:1,send:1,binding_defl:1,must:1,sent:1,deactiv:1,sourc:1,string:1,fals:1,parse_saml_to_tim:1,util:[0,1],xmlschema:1,public_cert_file_not_found:1,get_self_url_no_queri:1,settings_file_not_found:1,list:1,onelogin_saml2_util:1,sign_metadata:1,pubic:1,sign:1,past:1,second:1,pass:1,port:1,index:0,what:1,get_name_id:1,compar:1,get_idp_data:1,sp_nq:1,current:1,delet:1,x509subjectnam:1,"new":1,status_no_pass:1,"public":1,metadata:[0,1],redirect:1,keep_local_sess:1,gener:1,windowsdomainqualifiednam:1,logout:1,path:1,valu:1,search:0,sender:1,datetim:1,cert:1,is_debug_act:1,redirect_invalid_url:1,extra:1,appli:1,modul:[0,1],metadata_sp_invalid:1,is_authent:1,unix:1,"boolean":1,onelogin_saml2_respons:1,org:1,post:1,authnstat:1,from:1,ddthh:1,nameid_persist:1,emailaddress:1,status_request:1,type:1,until:1,keydescriptor:1,attrname_format_bas:1,iso8601:1,"transient":1,get_sp_cert:1,cach:1,status_proxy_count_exceed:1,none:1,endpoint:1,redirect_to:1,uniqu:1,descriptor:1,time_valid:1,root:1,status_success:1,request_data:1,objet:1,process:1,onelogin_saml2_auth:1,unform:1,indic:0,ac_unspecifi:1,want:1,unsign:1,lxml:1,secur:1,check_statu:1,status_respond:1,write:1,verifi:1,decrypt_el:1,ns_samlp:1,ac_x509:1,x509:1,after:1,validate_xml:1,nameid_x509_subject_nam:1,callback:1,date:1,data:1,domdocu:1,footer:1,bind:1,element:1,onelogin_saml2_logout_request:1,authn_request:[0,1],nameid:1,order:1,alowed_clock_drift:1,deflate_and_base64_encod:1,process_respons:1,paramet:1,settings_invalid_syntax:1,persist:1,get_slo_url:1,"return":1,timestamp:1,auth:[0,1],authnrequest:1,get_respons:1,get_self_url:1,format_idp_cert:1,namequalifi:1,authent:1,onelogin_saml2_authn_request:1,mode:1,request_id:1,debug:1,found:1,went:1,oasi:1,authnsign:1,"static":1,rsa_sha1:1,our:1,extract:1,check_sp_cert:1,content:[0,1],validate_metadata:1,rel:1,qualifi:1,generate_name_id:1,envelop:1,given:1,base:1,get_lib_path:1,format_finger_print:1,get_session_index:1,earliest:1,get_name_id_data:1,ns_x:1,could:1,wrong:1,domel:1,ns_d:1,time_cach:1,validate_sign:1,smartcard:1,arrai:1,messag:1,attrname_format_uri:1,kerbero:1,saml_single_logout_not_support:1,differ:1,construct:1,ns_xsi:1,parse_time_to_saml:1,store:1,schema:1,option:1,sessionnotonoraft:1,rsa:1,artifact:1,wantassertionssign:1,encrypted_data:1,vouch:1,part:1,is_strict:1,holder:1,than:1,target:1,provid:1,defat:1,str:1,get_nameid:1,initi:1,argument:1,packag:[0,1],expir:1,onelogin_saml2_error:1,tabl:0,onelogin_saml2_set:1,lib:1,build_response_signatur:1,destroi:1,contact:1,build:1,soap:1,singl:1,validate_timestamp:1,decode_base64_and_infl:1,object:1,nameid_kerbero:1,logout_request:[0,1],return_to:1,"class":1,sub:1,ns_saml:1,saml_logoutrequest_invalid:1,dom:1,url:1,urn:1,nsmap:1,uri:1,determin:1,saml_response_not_found:1,add_sign:1,session:1,nameid_ent:1,onelogin_saml2_const:1,xml:1,onli:1,get_sp_kei:1,timestr:1,activ:1,set_strict:1,should:1,get_sso_url:1,dict:1,folder:1,local:1,valid_until:1,nameid_transi:1,get:1,authnrequestssign:1,sso:1,expres:1,get_nameid_data:1,requir:1,organ:1,onelogin:[0,1],binding_http_redirect:1,common:1,contain:1,xmlenc:1,view:1,respond:1,certif:1,set:[0,1],get_set:1,respons:[0,1],statu:1,ingo:1,logoutrequest:1,check_set:1,someth:1,get_organ:1,saml_logoutresponse_invalid:1,entiti:1,attribut:1,signatur:1,accord:1,kei:1,samlrespons:1,old_set:1,wsign:1,sp_format:1,delete_session_cb:1,rtype:1,get_cert_path:1,format_cert:1,audienc:1,instanc:1,get_error:1,attrnam:1,login:1,validate_num_assert:1,generate_unique_id:1,settings_invalid:1,write_temp_fil:1,status_partial_logout:1,header:1,rfc1951:1,reciev:1,empti:1,interpret:1,basic:1,nameid_email_address:1,partiallogout:1,convert:1,assert:1,get_id:1,versionmismatch:1,saml_request:1,durat:1,defin:1,calcul:1,slo:1,error:[0,1],ns_xenc:1,cm_holder_kei:1,get_attribut:1,binding_soap:1,toolkit:1,sessionindex:1,cache_dur:1,sp_certs_not_found:1,get_statu:1,status_version_mismatch:1,welcom:0,saml:1,inresponseto:1,process_slo:1,same:1,decod:1,document:[0,1],get_security_data:1,http:1,context:1,inflat:1,onelogin_saml2_logout_respons:1,rais:1,temporari:1,user:1,binding_http_artifact:1,extern:1,get_self_url_host:1,sha1:1,builder:1,relay_st:1,calculate_x509_fingerprint:1,exampl:1,thi:1,get_contact:1,protocol:1,bearer:1,execut:1,private_key_file_not_found:1,fingerprint:1,ac_kerbero:1,get_audi:1,get_sp_data:1,except:1,param:1,proxycountexceed:1,add:1,is_valid:1,xenc:1,match:1,logoutrespons:1,nameid_encrypt:1,format:1,add_x509_key_descriptor:1,cm_sender_vouch:1,get_sp_metadata:1,password:1,enc_ctx:1,name:1,like:1,success:1,xmlsoap:1,nameid_windows_domain_qualified_nam:1,page:0,yyyi:1,is_http:1,www:1,some:1,unspecifi:1,librari:1,xmldsig:1,condit:1,get_ext_lib_path:1,get_expire_tim:1,cm_bearer:1,build_request_signatur:1,logout_respons:[0,1],host:1,get_base_path:1,x509cert:1,deflat:1,idp:1,get_request:1,disabl:1,encod:1,get_issu:1,validate_url:1,samlp:1,support:1,strict:1,encript:1,includ:1,delete_local_sess:1,xpath:1,head:1,form:1,spnamequalifi:1,saml_logoutmessage_not_found:1,parse_dur:1,ac_password:1,"true":1,info:1,binding_http_post:1,get_session_not_on_or_aft:1,"default":1,ns_md:1,otherwis:1,constant:[0,1],creat:1,"int":1,request:1,decrypt:1,onelogin_saml2_metadata:1,exist:1,file:1,check:1,saml2:[0,1],encrypt:1,attrname_format_unspecifi:1,get_schemas_path:1,get_data:1,when:1,valid:1,bool:1,futur:1,saml_respons:1,argumen:1,node:1,attributestat:1,get_self_host:1,x509_cert:1,nopass:1,ns_soap:1,xmlsec:1,ignor:1,base64encod:1,time:1,custom_base_path:1,in_response_to:1},objtypes:{"0":"py:module","1":"py:attribute","2":"py:method","3":"py:staticmethod","4":"py:class","5":"py:function","6":"py:exception"},titles:["Welcome to saml2’s documentation!","OneLogin saml2 Module"],objnames:{"0":["py","module","Python module"],"1":["py","attribute","Python attribute"],"2":["py","method","Python method"],"3":["py","staticmethod","Python static method"],"4":["py","class","Python class"],"5":["py","function","Python function"],"6":["py","exception","Python exception"]},filenames:["index","saml2"]}) \ No newline at end of file From 325833137c047e3a467a5559cebf2ffe39d7a925 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Sun, 1 Oct 2023 03:11:48 +0200 Subject: [PATCH 179/205] Add updated HTML doc --- docs/saml2/_modules/index.html | 116 + docs/saml2/_modules/onelogin/saml2/auth.html | 865 ++++ .../onelogin/saml2/authn_request.html | 265 ++ .../saml2/_modules/onelogin/saml2/compat.html | 166 + .../_modules/onelogin/saml2/constants.html | 218 + .../saml2/_modules/onelogin/saml2/errors.html | 228 + .../onelogin/saml2/idp_metadata_parser.html | 380 ++ .../onelogin/saml2/logout_request.html | 465 ++ .../onelogin/saml2/logout_response.html | 321 ++ .../_modules/onelogin/saml2/metadata.html | 365 ++ .../_modules/onelogin/saml2/response.html | 1054 +++++ .../_modules/onelogin/saml2/settings.html | 961 +++++ docs/saml2/_modules/onelogin/saml2/utils.html | 1173 +++++ .../onelogin/saml2/xml_templates.html | 256 ++ .../_modules/onelogin/saml2/xml_utils.html | 276 ++ .../_modules/onelogin/saml2/xmlparser.html | 285 ++ docs/saml2/_sources/index.rst.txt | 14 + docs/saml2/_sources/modules.rst.txt | 7 + docs/saml2/_sources/onelogin.rst.txt | 18 + docs/saml2/_sources/onelogin.saml2.rst.txt | 133 + .../_sphinx_javascript_frameworks_compat.js | 123 + docs/saml2/_static/basic.css | 921 ++++ docs/saml2/_static/css/badge_only.css | 1 + .../_static/css/fonts/Roboto-Slab-Bold.woff | Bin 0 -> 87624 bytes .../_static/css/fonts/Roboto-Slab-Bold.woff2 | Bin 0 -> 67312 bytes .../css/fonts/Roboto-Slab-Regular.woff | Bin 0 -> 86288 bytes .../css/fonts/Roboto-Slab-Regular.woff2 | Bin 0 -> 66444 bytes .../_static/css/fonts/fontawesome-webfont.eot | Bin 0 -> 165742 bytes .../_static/css/fonts/fontawesome-webfont.svg | 2671 ++++++++++++ .../_static/css/fonts/fontawesome-webfont.ttf | Bin 0 -> 165548 bytes .../css/fonts/fontawesome-webfont.woff | Bin 0 -> 98024 bytes .../css/fonts/fontawesome-webfont.woff2 | Bin 0 -> 77160 bytes .../_static/css/fonts/lato-bold-italic.woff | Bin 0 -> 323344 bytes .../_static/css/fonts/lato-bold-italic.woff2 | Bin 0 -> 193308 bytes docs/saml2/_static/css/fonts/lato-bold.woff | Bin 0 -> 309728 bytes docs/saml2/_static/css/fonts/lato-bold.woff2 | Bin 0 -> 184912 bytes .../_static/css/fonts/lato-normal-italic.woff | Bin 0 -> 328412 bytes .../css/fonts/lato-normal-italic.woff2 | Bin 0 -> 195704 bytes docs/saml2/_static/css/fonts/lato-normal.woff | Bin 0 -> 309192 bytes .../saml2/_static/css/fonts/lato-normal.woff2 | Bin 0 -> 182708 bytes docs/saml2/_static/css/theme.css | 4 + docs/saml2/_static/doctools.js | 156 + docs/saml2/_static/documentation_options.js | 14 + docs/saml2/_static/file.png | Bin 0 -> 286 bytes docs/saml2/_static/jquery.js | 2 + docs/saml2/_static/js/badge_only.js | 1 + .../_static/js/html5shiv-printshiv.min.js | 4 + docs/saml2/_static/js/html5shiv.min.js | 4 + docs/saml2/_static/js/theme.js | 1 + docs/saml2/_static/language_data.js | 199 + docs/saml2/_static/minus.png | Bin 0 -> 90 bytes docs/saml2/_static/plus.png | Bin 0 -> 90 bytes docs/saml2/_static/pygments.css | 75 + docs/saml2/_static/searchtools.js | 566 +++ docs/saml2/_static/sphinx_highlight.js | 144 + docs/saml2/genindex.html | 1223 ++++++ docs/saml2/index.html | 139 + docs/saml2/modules.html | 135 + docs/saml2/objects.inv | Bin 0 -> 3433 bytes docs/saml2/onelogin.html | 585 +++ docs/saml2/onelogin.saml2.html | 3780 +++++++++++++++++ docs/saml2/py-modindex.html | 200 + docs/saml2/search.html | 120 + docs/saml2/searchindex.js | 1 + 64 files changed, 18635 insertions(+) create mode 100644 docs/saml2/_modules/index.html create mode 100644 docs/saml2/_modules/onelogin/saml2/auth.html create mode 100644 docs/saml2/_modules/onelogin/saml2/authn_request.html create mode 100644 docs/saml2/_modules/onelogin/saml2/compat.html create mode 100644 docs/saml2/_modules/onelogin/saml2/constants.html create mode 100644 docs/saml2/_modules/onelogin/saml2/errors.html create mode 100644 docs/saml2/_modules/onelogin/saml2/idp_metadata_parser.html create mode 100644 docs/saml2/_modules/onelogin/saml2/logout_request.html create mode 100644 docs/saml2/_modules/onelogin/saml2/logout_response.html create mode 100644 docs/saml2/_modules/onelogin/saml2/metadata.html create mode 100644 docs/saml2/_modules/onelogin/saml2/response.html create mode 100644 docs/saml2/_modules/onelogin/saml2/settings.html create mode 100644 docs/saml2/_modules/onelogin/saml2/utils.html create mode 100644 docs/saml2/_modules/onelogin/saml2/xml_templates.html create mode 100644 docs/saml2/_modules/onelogin/saml2/xml_utils.html create mode 100644 docs/saml2/_modules/onelogin/saml2/xmlparser.html create mode 100644 docs/saml2/_sources/index.rst.txt create mode 100644 docs/saml2/_sources/modules.rst.txt create mode 100644 docs/saml2/_sources/onelogin.rst.txt create mode 100644 docs/saml2/_sources/onelogin.saml2.rst.txt create mode 100644 docs/saml2/_static/_sphinx_javascript_frameworks_compat.js create mode 100644 docs/saml2/_static/basic.css create mode 100644 docs/saml2/_static/css/badge_only.css create mode 100644 docs/saml2/_static/css/fonts/Roboto-Slab-Bold.woff create mode 100644 docs/saml2/_static/css/fonts/Roboto-Slab-Bold.woff2 create mode 100644 docs/saml2/_static/css/fonts/Roboto-Slab-Regular.woff create mode 100644 docs/saml2/_static/css/fonts/Roboto-Slab-Regular.woff2 create mode 100644 docs/saml2/_static/css/fonts/fontawesome-webfont.eot create mode 100644 docs/saml2/_static/css/fonts/fontawesome-webfont.svg create mode 100644 docs/saml2/_static/css/fonts/fontawesome-webfont.ttf create mode 100644 docs/saml2/_static/css/fonts/fontawesome-webfont.woff create mode 100644 docs/saml2/_static/css/fonts/fontawesome-webfont.woff2 create mode 100644 docs/saml2/_static/css/fonts/lato-bold-italic.woff create mode 100644 docs/saml2/_static/css/fonts/lato-bold-italic.woff2 create mode 100644 docs/saml2/_static/css/fonts/lato-bold.woff create mode 100644 docs/saml2/_static/css/fonts/lato-bold.woff2 create mode 100644 docs/saml2/_static/css/fonts/lato-normal-italic.woff create mode 100644 docs/saml2/_static/css/fonts/lato-normal-italic.woff2 create mode 100644 docs/saml2/_static/css/fonts/lato-normal.woff create mode 100644 docs/saml2/_static/css/fonts/lato-normal.woff2 create mode 100644 docs/saml2/_static/css/theme.css create mode 100644 docs/saml2/_static/doctools.js create mode 100644 docs/saml2/_static/documentation_options.js create mode 100644 docs/saml2/_static/file.png create mode 100644 docs/saml2/_static/jquery.js create mode 100644 docs/saml2/_static/js/badge_only.js create mode 100644 docs/saml2/_static/js/html5shiv-printshiv.min.js create mode 100644 docs/saml2/_static/js/html5shiv.min.js create mode 100644 docs/saml2/_static/js/theme.js create mode 100644 docs/saml2/_static/language_data.js create mode 100644 docs/saml2/_static/minus.png create mode 100644 docs/saml2/_static/plus.png create mode 100644 docs/saml2/_static/pygments.css create mode 100644 docs/saml2/_static/searchtools.js create mode 100644 docs/saml2/_static/sphinx_highlight.js create mode 100644 docs/saml2/genindex.html create mode 100644 docs/saml2/index.html create mode 100644 docs/saml2/modules.html create mode 100644 docs/saml2/objects.inv create mode 100644 docs/saml2/onelogin.html create mode 100644 docs/saml2/onelogin.saml2.html create mode 100644 docs/saml2/py-modindex.html create mode 100644 docs/saml2/search.html create mode 100644 docs/saml2/searchindex.js diff --git a/docs/saml2/_modules/index.html b/docs/saml2/_modules/index.html new file mode 100644 index 00000000..decb1d07 --- /dev/null +++ b/docs/saml2/_modules/index.html @@ -0,0 +1,116 @@ + + + + + + Overview: module code — SAML Python2/3 Toolkit 1 documentation + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/saml2/_modules/onelogin/saml2/auth.html b/docs/saml2/_modules/onelogin/saml2/auth.html new file mode 100644 index 00000000..b202bed0 --- /dev/null +++ b/docs/saml2/_modules/onelogin/saml2/auth.html @@ -0,0 +1,865 @@ + + + + + + onelogin.saml2.auth — SAML Python2/3 Toolkit 1 documentation + + + + + + + + + + + + + + + +
    + + +
    + +
    +
    +
    + +
    +
    +
    +
    + +

    Source code for onelogin.saml2.auth

    +# -*- coding: utf-8 -*-
    +
    +""" OneLogin_Saml2_Auth class
    +
    +
    +Main class of SAML Python Toolkit.
    +
    +Initializes the SP SAML instance
    +
    +"""
    +
    +import xmlsec
    +
    +from onelogin.saml2 import compat
    +from onelogin.saml2.authn_request import OneLogin_Saml2_Authn_Request
    +from onelogin.saml2.constants import OneLogin_Saml2_Constants
    +from onelogin.saml2.logout_request import OneLogin_Saml2_Logout_Request
    +from onelogin.saml2.logout_response import OneLogin_Saml2_Logout_Response
    +from onelogin.saml2.response import OneLogin_Saml2_Response
    +from onelogin.saml2.settings import OneLogin_Saml2_Settings
    +from onelogin.saml2.utils import OneLogin_Saml2_Utils, OneLogin_Saml2_Error, OneLogin_Saml2_ValidationError
    +from onelogin.saml2.xmlparser import tostring
    +
    +
    +
    [docs]class OneLogin_Saml2_Auth(object): + """ + + This class implements the SP SAML instance. + + Defines the methods that you can invoke in your application in + order to add SAML support (initiates SSO, initiates SLO, processes a + SAML Response, a Logout Request or a Logout Response). + """ + + authn_request_class = OneLogin_Saml2_Authn_Request + logout_request_class = OneLogin_Saml2_Logout_Request + logout_response_class = OneLogin_Saml2_Logout_Response + response_class = OneLogin_Saml2_Response + + def __init__(self, request_data, old_settings=None, custom_base_path=None): + """ + Initializes the SP SAML instance. + + :param request_data: Request Data + :type request_data: dict + + :param old_settings: Optional. SAML Toolkit Settings + :type old_settings: dict + + :param custom_base_path: Optional. Path where are stored the settings file and the cert folder + :type custom_base_path: string + """ + self._request_data = request_data + if isinstance(old_settings, OneLogin_Saml2_Settings): + self._settings = old_settings + else: + self._settings = OneLogin_Saml2_Settings(old_settings, custom_base_path) + self._attributes = dict() + self._friendlyname_attributes = dict() + self._nameid = None + self._nameid_format = None + self._nameid_nq = None + self._nameid_spnq = None + self._session_index = None + self._session_expiration = None + self._authenticated = False + self._errors = [] + self._error_reason = None + self._last_request_id = None + self._last_message_id = None + self._last_assertion_id = None + self._last_assertion_issue_instant = None + self._last_authn_contexts = [] + self._last_request = None + self._last_response = None + self._last_response_in_response_to = None + self._last_assertion_not_on_or_after = None + +
    [docs] def get_settings(self): + """ + Returns the settings info + :return: Setting info + :rtype: OneLogin_Saml2_Setting object + """ + return self._settings
    + +
    [docs] def set_strict(self, value): + """ + Set the strict mode active/disable + + :param value: + :type value: bool + """ + assert isinstance(value, bool) + self._settings.set_strict(value)
    + +
    [docs] def store_valid_response(self, response): + self._attributes = response.get_attributes() + self._friendlyname_attributes = response.get_friendlyname_attributes() + self._nameid = response.get_nameid() + self._nameid_format = response.get_nameid_format() + self._nameid_nq = response.get_nameid_nq() + self._nameid_spnq = response.get_nameid_spnq() + self._session_index = response.get_session_index() + self._session_expiration = response.get_session_not_on_or_after() + self._last_message_id = response.get_id() + self._last_assertion_id = response.get_assertion_id() + self._last_assertion_issue_instant = response.get_assertion_issue_instant() + self._last_authn_contexts = response.get_authn_contexts() + self._authenticated = True + self._last_response_in_response_to = response.get_in_response_to() + self._last_assertion_not_on_or_after = response.get_assertion_not_on_or_after()
    + +
    [docs] def process_response(self, request_id=None): + """ + Process the SAML Response sent by the IdP. + + :param request_id: Is an optional argument. Is the ID of the AuthNRequest sent by this SP to the IdP. + :type request_id: string + + :raises: OneLogin_Saml2_Error.SAML_RESPONSE_NOT_FOUND, when a POST with a SAMLResponse is not found + """ + self._errors = [] + self._error_reason = None + + if 'post_data' in self._request_data and 'SAMLResponse' in self._request_data['post_data']: + # AuthnResponse -- HTTP_POST Binding + response = self.response_class(self._settings, self._request_data['post_data']['SAMLResponse']) + self._last_response = response.get_xml_document() + + if response.is_valid(self._request_data, request_id): + self.store_valid_response(response) + else: + self._errors.append('invalid_response') + self._error_reason = response.get_error() + + else: + self._errors.append('invalid_binding') + raise OneLogin_Saml2_Error( + 'SAML Response not found, Only supported HTTP_POST Binding', + OneLogin_Saml2_Error.SAML_RESPONSE_NOT_FOUND + )
    + +
    [docs] def process_slo(self, keep_local_session=False, request_id=None, delete_session_cb=None): + """ + Process the SAML Logout Response / Logout Request sent by the IdP. + + :param keep_local_session: When false will destroy the local session, otherwise will destroy it + :type keep_local_session: bool + + :param request_id: The ID of the LogoutRequest sent by this SP to the IdP + :type request_id: string + + :returns: Redirection url + """ + self._errors = [] + self._error_reason = None + + get_data = 'get_data' in self._request_data and self._request_data['get_data'] + if get_data and 'SAMLResponse' in get_data: + logout_response = self.logout_response_class(self._settings, get_data['SAMLResponse']) + self._last_response = logout_response.get_xml() + if not self.validate_response_signature(get_data): + self._errors.append('invalid_logout_response_signature') + self._errors.append('Signature validation failed. Logout Response rejected') + elif not logout_response.is_valid(self._request_data, request_id): + self._errors.append('invalid_logout_response') + elif logout_response.get_status() != OneLogin_Saml2_Constants.STATUS_SUCCESS: + self._errors.append('logout_not_success') + else: + self._last_message_id = logout_response.id + if not keep_local_session: + OneLogin_Saml2_Utils.delete_local_session(delete_session_cb) + + elif get_data and 'SAMLRequest' in get_data: + logout_request = self.logout_request_class(self._settings, get_data['SAMLRequest']) + self._last_request = logout_request.get_xml() + if not self.validate_request_signature(get_data): + self._errors.append("invalid_logout_request_signature") + self._errors.append('Signature validation failed. Logout Request rejected') + elif not logout_request.is_valid(self._request_data): + self._errors.append('invalid_logout_request') + else: + if not keep_local_session: + OneLogin_Saml2_Utils.delete_local_session(delete_session_cb) + + in_response_to = logout_request.id + self._last_message_id = logout_request.id + response_builder = self.logout_response_class(self._settings) + response_builder.build(in_response_to) + self._last_response = response_builder.get_xml() + logout_response = response_builder.get_response() + + parameters = {'SAMLResponse': logout_response} + if 'RelayState' in self._request_data['get_data']: + parameters['RelayState'] = self._request_data['get_data']['RelayState'] + + security = self._settings.get_security_data() + if security['logoutResponseSigned']: + self.add_response_signature(parameters, security['signatureAlgorithm']) + + return self.redirect_to(self.get_slo_response_url(), parameters) + else: + self._errors.append('invalid_binding') + raise OneLogin_Saml2_Error( + 'SAML LogoutRequest/LogoutResponse not found. Only supported HTTP_REDIRECT Binding', + OneLogin_Saml2_Error.SAML_LOGOUTMESSAGE_NOT_FOUND + )
    + +
    [docs] def redirect_to(self, url=None, parameters={}): + """ + Redirects the user to the URL passed by parameter or to the URL that we defined in our SSO Request. + + :param url: The target URL to redirect the user + :type url: string + :param parameters: Extra parameters to be passed as part of the URL + :type parameters: dict + + :returns: Redirection URL + """ + if url is None and 'RelayState' in self._request_data['get_data']: + url = self._request_data['get_data']['RelayState'] + return OneLogin_Saml2_Utils.redirect(url, parameters, request_data=self._request_data)
    + +
    [docs] def is_authenticated(self): + """ + Checks if the user is authenticated or not. + + :returns: True if is authenticated, False if not + :rtype: bool + """ + return self._authenticated
    + +
    [docs] def get_attributes(self): + """ + Returns the set of SAML attributes. + + :returns: SAML attributes + :rtype: dict + """ + return self._attributes
    + +
    [docs] def get_friendlyname_attributes(self): + """ + Returns the set of SAML attributes indexed by FiendlyName. + + :returns: SAML attributes + :rtype: dict + """ + return self._friendlyname_attributes
    + +
    [docs] def get_nameid(self): + """ + Returns the nameID. + + :returns: NameID + :rtype: string|None + """ + return self._nameid
    + +
    [docs] def get_nameid_format(self): + """ + Returns the nameID Format. + + :returns: NameID Format + :rtype: string|None + """ + return self._nameid_format
    + +
    [docs] def get_nameid_nq(self): + """ + Returns the nameID NameQualifier of the Assertion. + + :returns: NameID NameQualifier + :rtype: string|None + """ + return self._nameid_nq
    + +
    [docs] def get_nameid_spnq(self): + """ + Returns the nameID SP NameQualifier of the Assertion. + + :returns: NameID SP NameQualifier + :rtype: string|None + """ + return self._nameid_spnq
    + +
    [docs] def get_session_index(self): + """ + Returns the SessionIndex from the AuthnStatement. + :returns: The SessionIndex of the assertion + :rtype: string + """ + return self._session_index
    + +
    [docs] def get_session_expiration(self): + """ + Returns the SessionNotOnOrAfter from the AuthnStatement. + :returns: The SessionNotOnOrAfter of the assertion + :rtype: unix/posix timestamp|None + """ + return self._session_expiration
    + +
    [docs] def get_last_assertion_not_on_or_after(self): + """ + The NotOnOrAfter value of the valid SubjectConfirmationData node + (if any) of the last assertion processed + """ + return self._last_assertion_not_on_or_after
    + +
    [docs] def get_errors(self): + """ + Returns a list with code errors if something went wrong + + :returns: List of errors + :rtype: list + """ + return self._errors
    + +
    [docs] def get_last_error_reason(self): + """ + Returns the reason for the last error + + :returns: Reason of the last error + :rtype: None | string + """ + return self._error_reason
    + +
    [docs] def get_attribute(self, name): + """ + Returns the requested SAML attribute. + + :param name: Name of the attribute + :type name: string + + :returns: Attribute value(s) if exists or None + :rtype: list + """ + assert isinstance(name, compat.str_type) + return self._attributes.get(name)
    + +
    [docs] def get_friendlyname_attribute(self, friendlyname): + """ + Returns the requested SAML attribute searched by FriendlyName. + + :param friendlyname: FriendlyName of the attribute + :type friendlyname: string + + :returns: Attribute value(s) if exists or None + :rtype: list + """ + assert isinstance(friendlyname, compat.str_type) + return self._friendlyname_attributes.get(friendlyname)
    + +
    [docs] def get_last_request_id(self): + """ + :returns: The ID of the last Request SAML message generated. + :rtype: string + """ + return self._last_request_id
    + +
    [docs] def get_last_message_id(self): + """ + :returns: The ID of the last Response SAML message processed. + :rtype: string + """ + return self._last_message_id
    + +
    [docs] def get_last_assertion_id(self): + """ + :returns: The ID of the last assertion processed. + :rtype: string + """ + return self._last_assertion_id
    + +
    [docs] def get_last_assertion_issue_instant(self): + """ + :returns: The IssueInstant of the last assertion processed. + :rtype: unix/posix timestamp|None + """ + return self._last_assertion_issue_instant
    + +
    [docs] def get_last_authn_contexts(self): + """ + :returns: The list of authentication contexts sent in the last SAML Response. + :rtype: list + """ + return self._last_authn_contexts
    + +
    [docs] def get_last_response_in_response_to(self): + """ + :returns: InResponseTo attribute of the last Response SAML processed or None if it is not present. + :rtype: string + """ + return self._last_response_in_response_to
    + +
    [docs] def login(self, return_to=None, force_authn=False, is_passive=False, set_nameid_policy=True, name_id_value_req=None): + """ + Initiates the SSO process. + + :param return_to: Optional argument. The target URL the user should be redirected to after login. + :type return_to: string + + :param force_authn: Optional argument. When true the AuthNRequest will set the ForceAuthn='true'. + :type force_authn: bool + + :param is_passive: Optional argument. When true the AuthNRequest will set the Ispassive='true'. + :type is_passive: bool + + :param set_nameid_policy: Optional argument. When true the AuthNRequest will set a nameIdPolicy element. + :type set_nameid_policy: bool + + :param name_id_value_req: Optional argument. Indicates to the IdP the subject that should be authenticated + :type name_id_value_req: string + + :returns: Redirection URL + :rtype: string + """ + authn_request = self.authn_request_class(self._settings, force_authn, is_passive, set_nameid_policy, name_id_value_req) + self._last_request = authn_request.get_xml() + self._last_request_id = authn_request.get_id() + + saml_request = authn_request.get_request() + parameters = {'SAMLRequest': saml_request} + + if return_to is not None: + parameters['RelayState'] = return_to + else: + parameters['RelayState'] = OneLogin_Saml2_Utils.get_self_url_no_query(self._request_data) + + security = self._settings.get_security_data() + if security.get('authnRequestsSigned', False): + self.add_request_signature(parameters, security['signatureAlgorithm']) + return self.redirect_to(self.get_sso_url(), parameters)
    + +
    [docs] def logout(self, return_to=None, name_id=None, session_index=None, nq=None, name_id_format=None, spnq=None): + """ + Initiates the SLO process. + + :param return_to: Optional argument. The target URL the user should be redirected to after logout. + :type return_to: string + + :param name_id: The NameID that will be set in the LogoutRequest. + :type name_id: string + + :param session_index: SessionIndex that identifies the session of the user. + :type session_index: string + + :param nq: IDP Name Qualifier + :type: string + + :param name_id_format: The NameID Format that will be set in the LogoutRequest. + :type: string + + :param spnq: SP Name Qualifier + :type: string + + :returns: Redirection URL + """ + slo_url = self.get_slo_url() + if slo_url is None: + raise OneLogin_Saml2_Error( + 'The IdP does not support Single Log Out', + OneLogin_Saml2_Error.SAML_SINGLE_LOGOUT_NOT_SUPPORTED + ) + + if name_id is None and self._nameid is not None: + name_id = self._nameid + + if name_id_format is None and self._nameid_format is not None: + name_id_format = self._nameid_format + + logout_request = self.logout_request_class( + self._settings, + name_id=name_id, + session_index=session_index, + nq=nq, + name_id_format=name_id_format, + spnq=spnq + ) + self._last_request = logout_request.get_xml() + self._last_request_id = logout_request.id + + parameters = {'SAMLRequest': logout_request.get_request()} + if return_to is not None: + parameters['RelayState'] = return_to + else: + parameters['RelayState'] = OneLogin_Saml2_Utils.get_self_url_no_query(self._request_data) + + security = self._settings.get_security_data() + if security.get('logoutRequestSigned', False): + self.add_request_signature(parameters, security['signatureAlgorithm']) + return self.redirect_to(slo_url, parameters)
    + +
    [docs] def get_sso_url(self): + """ + Gets the SSO URL. + + :returns: An URL, the SSO endpoint of the IdP + :rtype: string + """ + return self._settings.get_idp_sso_url()
    + +
    [docs] def get_slo_url(self): + """ + Gets the SLO URL. + + :returns: An URL, the SLO endpoint of the IdP + :rtype: string + """ + return self._settings.get_idp_slo_url()
    + +
    [docs] def get_slo_response_url(self): + """ + Gets the SLO return URL for IdP-initiated logout. + + :returns: an URL, the SLO return endpoint of the IdP + :rtype: string + """ + return self._settings.get_idp_slo_response_url()
    + +
    [docs] def add_request_signature(self, request_data, sign_algorithm=OneLogin_Saml2_Constants.RSA_SHA256): + """ + Builds the Signature of the SAML Request. + + :param request_data: The Request parameters + :type request_data: dict + + :param sign_algorithm: Signature algorithm method + :type sign_algorithm: string + """ + return self._build_signature(request_data, 'SAMLRequest', sign_algorithm)
    + +
    [docs] def add_response_signature(self, response_data, sign_algorithm=OneLogin_Saml2_Constants.RSA_SHA256): + """ + Builds the Signature of the SAML Response. + :param response_data: The Response parameters + :type response_data: dict + + :param sign_algorithm: Signature algorithm method + :type sign_algorithm: string + """ + return self._build_signature(response_data, 'SAMLResponse', sign_algorithm)
    + + @staticmethod + def _build_sign_query_from_qs(query_string, saml_type): + """ + Build sign query from query string + + :param query_string: The query string + :type query_string: str + + :param saml_type: The target URL the user should be redirected to + :type saml_type: string SAMLRequest | SAMLResponse + """ + args = ('%s=' % saml_type, 'RelayState=', 'SigAlg=') + parts = query_string.split('&') + # Join in the order of arguments rather than the original order of parts. + return '&'.join(part for arg in args for part in parts if part.startswith(arg)) + + @staticmethod + def _build_sign_query(saml_data, relay_state, algorithm, saml_type, lowercase_urlencoding=False): + """ + Build sign query + + :param saml_data: The Request data + :type saml_data: str + + :param relay_state: The Relay State + :type relay_state: str + + :param algorithm: The Signature Algorithm + :type algorithm: str + + :param saml_type: The target URL the user should be redirected to + :type saml_type: string SAMLRequest | SAMLResponse + + :param lowercase_urlencoding: lowercase or no + :type lowercase_urlencoding: boolean + """ + sign_data = ['%s=%s' % (saml_type, OneLogin_Saml2_Utils.escape_url(saml_data, lowercase_urlencoding))] + if relay_state is not None: + sign_data.append('RelayState=%s' % OneLogin_Saml2_Utils.escape_url(relay_state, lowercase_urlencoding)) + sign_data.append('SigAlg=%s' % OneLogin_Saml2_Utils.escape_url(algorithm, lowercase_urlencoding)) + return '&'.join(sign_data) + + def _build_signature(self, data, saml_type, sign_algorithm=OneLogin_Saml2_Constants.RSA_SHA256): + """ + Builds the Signature + :param data: The Request data + :type data: dict + + :param saml_type: The target URL the user should be redirected to + :type saml_type: string SAMLRequest | SAMLResponse + + :param sign_algorithm: Signature algorithm method + :type sign_algorithm: string + """ + assert saml_type in ('SAMLRequest', 'SAMLResponse') + key = self.get_settings().get_sp_key() + + if not key: + raise OneLogin_Saml2_Error( + "Trying to sign the %s but can't load the SP private key." % saml_type, + OneLogin_Saml2_Error.PRIVATE_KEY_NOT_FOUND + ) + + msg = self._build_sign_query(data[saml_type], + data.get('RelayState', None), + sign_algorithm, + saml_type) + + sign_algorithm_transform_map = { + OneLogin_Saml2_Constants.DSA_SHA1: xmlsec.Transform.DSA_SHA1, + OneLogin_Saml2_Constants.RSA_SHA1: xmlsec.Transform.RSA_SHA1, + OneLogin_Saml2_Constants.RSA_SHA256: xmlsec.Transform.RSA_SHA256, + OneLogin_Saml2_Constants.RSA_SHA384: xmlsec.Transform.RSA_SHA384, + OneLogin_Saml2_Constants.RSA_SHA512: xmlsec.Transform.RSA_SHA512 + } + sign_algorithm_transform = sign_algorithm_transform_map.get(sign_algorithm, xmlsec.Transform.RSA_SHA256) + + signature = OneLogin_Saml2_Utils.sign_binary(msg, key, sign_algorithm_transform, self._settings.is_debug_active()) + data['Signature'] = OneLogin_Saml2_Utils.b64encode(signature) + data['SigAlg'] = sign_algorithm + +
    [docs] def validate_request_signature(self, request_data): + """ + Validate Request Signature + + :param request_data: The Request data + :type request_data: dict + + """ + + return self._validate_signature(request_data, 'SAMLRequest')
    + +
    [docs] def validate_response_signature(self, request_data): + """ + Validate Response Signature + + :param request_data: The Request data + :type request_data: dict + + """ + + return self._validate_signature(request_data, 'SAMLResponse')
    + + def _validate_signature(self, data, saml_type, raise_exceptions=False): + """ + Validate Signature + + :param data: The Request data + :type data: dict + + :param cert: The certificate to check signature + :type cert: str + + :param saml_type: The target URL the user should be redirected to + :type saml_type: string SAMLRequest | SAMLResponse + + :param raise_exceptions: Whether to return false on failure or raise an exception + :type raise_exceptions: Boolean + """ + try: + signature = data.get('Signature', None) + if signature is None: + if self._settings.is_strict() and self._settings.get_security_data().get('wantMessagesSigned', False): + raise OneLogin_Saml2_ValidationError( + 'The %s is not signed. Rejected.' % saml_type, + OneLogin_Saml2_ValidationError.NO_SIGNED_MESSAGE + ) + return True + + idp_data = self.get_settings().get_idp_data() + + exists_x509cert = self.get_settings().get_idp_cert() is not None + exists_multix509sign = 'x509certMulti' in idp_data and \ + 'signing' in idp_data['x509certMulti'] and \ + idp_data['x509certMulti']['signing'] + + if not (exists_x509cert or exists_multix509sign): + error_msg = 'In order to validate the sign on the %s, the x509cert of the IdP is required' % saml_type + self._errors.append(error_msg) + raise OneLogin_Saml2_Error( + error_msg, + OneLogin_Saml2_Error.CERT_NOT_FOUND + ) + + sign_alg = data.get('SigAlg', OneLogin_Saml2_Constants.RSA_SHA1) + if isinstance(sign_alg, bytes): + sign_alg = sign_alg.decode('utf8') + + security = self._settings.get_security_data() + reject_deprecated_alg = security.get('rejectDeprecatedAlgorithm', False) + if reject_deprecated_alg: + if sign_alg in OneLogin_Saml2_Constants.DEPRECATED_ALGORITHMS: + raise OneLogin_Saml2_ValidationError( + 'Deprecated signature algorithm found: %s' % sign_alg, + OneLogin_Saml2_ValidationError.DEPRECATED_SIGNATURE_METHOD + ) + + query_string = self._request_data.get('query_string') + if query_string and self._request_data.get('validate_signature_from_qs'): + signed_query = self._build_sign_query_from_qs(query_string, saml_type) + else: + lowercase_urlencoding = self._request_data.get('lowercase_urlencoding', False) + signed_query = self._build_sign_query(data[saml_type], + data.get('RelayState'), + sign_alg, + saml_type, + lowercase_urlencoding) + + if exists_multix509sign: + for cert in idp_data['x509certMulti']['signing']: + if OneLogin_Saml2_Utils.validate_binary_sign(signed_query, + OneLogin_Saml2_Utils.b64decode(signature), + cert, + sign_alg): + return True + raise OneLogin_Saml2_ValidationError( + 'Signature validation failed. %s rejected' % saml_type, + OneLogin_Saml2_ValidationError.INVALID_SIGNATURE + ) + else: + cert = self.get_settings().get_idp_cert() + + if not OneLogin_Saml2_Utils.validate_binary_sign(signed_query, + OneLogin_Saml2_Utils.b64decode(signature), + cert, + sign_alg, + self._settings.is_debug_active()): + raise OneLogin_Saml2_ValidationError( + 'Signature validation failed. %s rejected' % saml_type, + OneLogin_Saml2_ValidationError.INVALID_SIGNATURE + ) + return True + except Exception as e: + self._error_reason = str(e) + if raise_exceptions: + raise e + return False + +
    [docs] def get_last_response_xml(self, pretty_print_if_possible=False): + """ + Retrieves the raw XML (decrypted) of the last SAML response, + or the last Logout Response generated or processed + :returns: SAML response XML + :rtype: string|None + """ + response = None + if self._last_response is not None: + if isinstance(self._last_response, compat.str_type): + response = self._last_response + else: + response = tostring(self._last_response, encoding='unicode', pretty_print=pretty_print_if_possible) + return response
    + +
    [docs] def get_last_request_xml(self): + """ + Retrieves the raw XML sent in the last SAML request + :returns: SAML request XML + :rtype: string|None + """ + return self._last_request or None
    +
    + +
    +
    + +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/docs/saml2/_modules/onelogin/saml2/authn_request.html b/docs/saml2/_modules/onelogin/saml2/authn_request.html new file mode 100644 index 00000000..b72bd675 --- /dev/null +++ b/docs/saml2/_modules/onelogin/saml2/authn_request.html @@ -0,0 +1,265 @@ + + + + + + onelogin.saml2.authn_request — SAML Python2/3 Toolkit 1 documentation + + + + + + + + + + + + + + + +
    + + +
    + +
    +
    +
    + +
    +
    +
    +
    + +

    Source code for onelogin.saml2.authn_request

    +# -*- coding: utf-8 -*-
    +
    +""" OneLogin_Saml2_Authn_Request class
    +
    +
    +AuthNRequest class of SAML Python Toolkit.
    +
    +"""
    +
    +from onelogin.saml2.constants import OneLogin_Saml2_Constants
    +from onelogin.saml2.utils import OneLogin_Saml2_Utils
    +from onelogin.saml2.xml_templates import OneLogin_Saml2_Templates
    +
    +
    +
    [docs]class OneLogin_Saml2_Authn_Request(object): + """ + + This class handles an AuthNRequest. It builds an + AuthNRequest object. + + """ + + def __init__(self, settings, force_authn=False, is_passive=False, set_nameid_policy=True, name_id_value_req=None): + """ + Constructs the AuthnRequest object. + + :param settings: OSetting data + :type settings: OneLogin_Saml2_Settings + + :param force_authn: Optional argument. When true the AuthNRequest will set the ForceAuthn='true'. + :type force_authn: bool + + :param is_passive: Optional argument. When true the AuthNRequest will set the Ispassive='true'. + :type is_passive: bool + + :param set_nameid_policy: Optional argument. When true the AuthNRequest will set a nameIdPolicy element. + :type set_nameid_policy: bool + + :param name_id_value_req: Optional argument. Indicates to the IdP the subject that should be authenticated + :type name_id_value_req: string + """ + self._settings = settings + + sp_data = self._settings.get_sp_data() + idp_data = self._settings.get_idp_data() + security = self._settings.get_security_data() + + self._id = self._generate_request_id() + issue_instant = OneLogin_Saml2_Utils.parse_time_to_SAML(OneLogin_Saml2_Utils.now()) + + destination = idp_data['singleSignOnService']['url'] + + provider_name_str = '' + organization_data = settings.get_organization() + if isinstance(organization_data, dict) and organization_data: + langs = organization_data + if 'en-US' in langs: + lang = 'en-US' + else: + lang = sorted(langs)[0] + + display_name = 'displayname' in organization_data[lang] and organization_data[lang]['displayname'] + if display_name: + provider_name_str = "\n" + ' ProviderName="%s"' % organization_data[lang]['displayname'] + + force_authn_str = '' + if force_authn is True: + force_authn_str = "\n" + ' ForceAuthn="true"' + + is_passive_str = '' + if is_passive is True: + is_passive_str = "\n" + ' IsPassive="true"' + + subject_str = '' + if name_id_value_req: + subject_str = """ + <saml:Subject> + <saml:NameID Format="%s">%s</saml:NameID> + <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"></saml:SubjectConfirmation> + </saml:Subject>""" % (sp_data['NameIDFormat'], name_id_value_req) + + nameid_policy_str = '' + if set_nameid_policy: + name_id_policy_format = sp_data['NameIDFormat'] + if security['wantNameIdEncrypted']: + name_id_policy_format = OneLogin_Saml2_Constants.NAMEID_ENCRYPTED + + nameid_policy_str = """ + <samlp:NameIDPolicy + Format="%s" + AllowCreate="true" />""" % name_id_policy_format + + requested_authn_context_str = '' + if security['requestedAuthnContext'] is not False: + authn_comparison = security['requestedAuthnContextComparison'] + + if security['requestedAuthnContext'] is True: + requested_authn_context_str = """ <samlp:RequestedAuthnContext Comparison="%s"> + <saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef> + </samlp:RequestedAuthnContext>""" % authn_comparison + else: + requested_authn_context_str = ' <samlp:RequestedAuthnContext Comparison="%s">' % authn_comparison + for authn_context in security['requestedAuthnContext']: + requested_authn_context_str += '<saml:AuthnContextClassRef>%s</saml:AuthnContextClassRef>' % authn_context + requested_authn_context_str += ' </samlp:RequestedAuthnContext>' + + attr_consuming_service_str = '' + if 'attributeConsumingService' in sp_data and sp_data['attributeConsumingService']: + attr_consuming_service_str = "\n AttributeConsumingServiceIndex=\"%s\"" % sp_data['attributeConsumingService'].get('index', '1') + + request = OneLogin_Saml2_Templates.AUTHN_REQUEST % \ + { + 'id': self._id, + 'provider_name': provider_name_str, + 'force_authn_str': force_authn_str, + 'is_passive_str': is_passive_str, + 'issue_instant': issue_instant, + 'destination': destination, + 'assertion_url': sp_data['assertionConsumerService']['url'], + 'entity_id': sp_data['entityId'], + 'subject_str': subject_str, + 'nameid_policy_str': nameid_policy_str, + 'requested_authn_context_str': requested_authn_context_str, + 'attr_consuming_service_str': attr_consuming_service_str, + 'acs_binding': sp_data['assertionConsumerService'].get('binding', 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST') + } + + self._authn_request = request + + def _generate_request_id(self): + """ + Generate an unique request ID. + """ + return OneLogin_Saml2_Utils.generate_unique_id() + +
    [docs] def get_request(self, deflate=True): + """ + Returns unsigned AuthnRequest. + :param deflate: It makes the deflate process optional + :type: bool + :return: AuthnRequest maybe deflated and base64 encoded + :rtype: str object + """ + if deflate: + request = OneLogin_Saml2_Utils.deflate_and_base64_encode(self._authn_request) + else: + request = OneLogin_Saml2_Utils.b64encode(self._authn_request) + return request
    + +
    [docs] def get_id(self): + """ + Returns the AuthNRequest ID. + :return: AuthNRequest ID + :rtype: string + """ + return self._id
    + +
    [docs] def get_xml(self): + """ + Returns the XML that will be sent as part of the request + :return: XML request body + :rtype: string + """ + return self._authn_request
    +
    + +
    +
    + +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/docs/saml2/_modules/onelogin/saml2/compat.html b/docs/saml2/_modules/onelogin/saml2/compat.html new file mode 100644 index 00000000..0497cf19 --- /dev/null +++ b/docs/saml2/_modules/onelogin/saml2/compat.html @@ -0,0 +1,166 @@ + + + + + + onelogin.saml2.compat — SAML Python2/3 Toolkit 1 documentation + + + + + + + + + + + + + + + +
    + + +
    + +
    +
    +
    + +
    +
    +
    +
    + +

    Source code for onelogin.saml2.compat

    +# -*- coding: utf-8 -*-
    +
    +""" py3 compatibility class
    +
    +
    +"""
    +
    +from __future__ import absolute_import, print_function, with_statement
    +
    +try:
    +    basestring
    +except NameError:
    +    basestring = str
    +
    +try:
    +    unicode
    +except NameError:
    +    unicode = str
    +
    +
    +if isinstance(b'', type('')):  # py 2.x
    +    text_types = (basestring,)  # noqa
    +    bytes_type = bytes
    +    str_type = basestring  # noqa
    +
    +    def utf8(data):
    +        """  return utf8-encoded string """
    +        if isinstance(data, basestring):
    +            return data.decode("utf8")
    +        return unicode(data)
    +
    +    def to_string(data):
    +        """ return string """
    +        if isinstance(data, unicode):
    +            return data.encode("utf8")
    +        return str(data)
    +
    +    def to_bytes(data):
    +        """ return bytes """
    +        if isinstance(data, unicode):
    +            return data.encode("utf8")
    +        return str(data)
    +
    +else:  # py 3.x
    +    text_types = (bytes, str)
    +    bytes_type = bytes
    +    str_type = str
    +
    +
    [docs] def utf8(data): + """ return utf8-encoded string """ + if isinstance(data, bytes): + return data.decode("utf8") + return str(data)
    + +
    [docs] def to_string(data): + """convert to string""" + if isinstance(data, bytes): + return data.decode("utf8") + return str(data)
    + +
    [docs] def to_bytes(data): + """return bytes""" + if isinstance(data, str): + return data.encode("utf8") + return bytes(data)
    +
    + +
    +
    + +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/docs/saml2/_modules/onelogin/saml2/constants.html b/docs/saml2/_modules/onelogin/saml2/constants.html new file mode 100644 index 00000000..299831bb --- /dev/null +++ b/docs/saml2/_modules/onelogin/saml2/constants.html @@ -0,0 +1,218 @@ + + + + + + onelogin.saml2.constants — SAML Python2/3 Toolkit 1 documentation + + + + + + + + + + + + + + + +
    + + +
    + +
    +
    +
    + +
    +
    +
    +
    + +

    Source code for onelogin.saml2.constants

    +# -*- coding: utf-8 -*-
    +
    +""" OneLogin_Saml2_Constants class
    +
    +
    +Constants class of SAML Python Toolkit.
    +
    +"""
    +
    +
    +
    [docs]class OneLogin_Saml2_Constants(object): + """ + + This class defines all the constants that will be used + in the SAML Python Toolkit. + + """ + + # Value added to the current time in time condition validations + ALLOWED_CLOCK_DRIFT = 300 + + # NameID Formats + NAMEID_EMAIL_ADDRESS = 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress' + NAMEID_X509_SUBJECT_NAME = 'urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName' + NAMEID_WINDOWS_DOMAIN_QUALIFIED_NAME = 'urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName' + NAMEID_UNSPECIFIED = 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified' + NAMEID_KERBEROS = 'urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos' + NAMEID_ENTITY = 'urn:oasis:names:tc:SAML:2.0:nameid-format:entity' + NAMEID_TRANSIENT = 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient' + NAMEID_PERSISTENT = 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent' + NAMEID_ENCRYPTED = 'urn:oasis:names:tc:SAML:2.0:nameid-format:encrypted' + + # Attribute Name Formats + ATTRNAME_FORMAT_UNSPECIFIED = 'urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified' + ATTRNAME_FORMAT_URI = 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri' + ATTRNAME_FORMAT_BASIC = 'urn:oasis:names:tc:SAML:2.0:attrname-format:basic' + + # Namespaces + NS_SAML = 'urn:oasis:names:tc:SAML:2.0:assertion' + NS_SAMLP = 'urn:oasis:names:tc:SAML:2.0:protocol' + NS_SOAP = 'http://schemas.xmlsoap.org/soap/envelope/' + NS_MD = 'urn:oasis:names:tc:SAML:2.0:metadata' + NS_XS = 'http://www.w3.org/2001/XMLSchema' + NS_XSI = 'http://www.w3.org/2001/XMLSchema-instance' + NS_XENC = 'http://www.w3.org/2001/04/xmlenc#' + NS_DS = 'http://www.w3.org/2000/09/xmldsig#' + + # Namespace Prefixes + NS_PREFIX_SAML = 'saml' + NS_PREFIX_SAMLP = 'samlp' + NS_PREFIX_MD = 'md' + NS_PREFIX_XS = 'xs' + NS_PREFIX_XSI = 'xsi' + NS_PREFIX_XSD = 'xsd' + NS_PREFIX_XENC = 'xenc' + NS_PREFIX_DS = 'ds' + + # Prefix:Namespace Mappings + NSMAP = { + NS_PREFIX_SAMLP: NS_SAMLP, + NS_PREFIX_SAML: NS_SAML, + NS_PREFIX_DS: NS_DS, + NS_PREFIX_XENC: NS_XENC, + NS_PREFIX_MD: NS_MD + } + + # Bindings + BINDING_HTTP_POST = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST' + BINDING_HTTP_REDIRECT = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect' + BINDING_HTTP_ARTIFACT = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact' + BINDING_SOAP = 'urn:oasis:names:tc:SAML:2.0:bindings:SOAP' + BINDING_DEFLATE = 'urn:oasis:names:tc:SAML:2.0:bindings:URL-Encoding:DEFLATE' + + # Auth Context Class + AC_UNSPECIFIED = 'urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified' + AC_PASSWORD = 'urn:oasis:names:tc:SAML:2.0:ac:classes:Password' + AC_PASSWORD_PROTECTED = 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport' + AC_X509 = 'urn:oasis:names:tc:SAML:2.0:ac:classes:X509' + AC_SMARTCARD = 'urn:oasis:names:tc:SAML:2.0:ac:classes:Smartcard' + AC_KERBEROS = 'urn:oasis:names:tc:SAML:2.0:ac:classes:Kerberos' + + # Subject Confirmation + CM_BEARER = 'urn:oasis:names:tc:SAML:2.0:cm:bearer' + CM_HOLDER_KEY = 'urn:oasis:names:tc:SAML:2.0:cm:holder-of-key' + CM_SENDER_VOUCHES = 'urn:oasis:names:tc:SAML:2.0:cm:sender-vouches' + + # Status Codes + STATUS_SUCCESS = 'urn:oasis:names:tc:SAML:2.0:status:Success' + STATUS_REQUESTER = 'urn:oasis:names:tc:SAML:2.0:status:Requester' + STATUS_RESPONDER = 'urn:oasis:names:tc:SAML:2.0:status:Responder' + STATUS_VERSION_MISMATCH = 'urn:oasis:names:tc:SAML:2.0:status:VersionMismatch' + STATUS_NO_PASSIVE = 'urn:oasis:names:tc:SAML:2.0:status:NoPassive' + STATUS_PARTIAL_LOGOUT = 'urn:oasis:names:tc:SAML:2.0:status:PartialLogout' + STATUS_PROXY_COUNT_EXCEEDED = 'urn:oasis:names:tc:SAML:2.0:status:ProxyCountExceeded' + + # Sign & Crypto + SHA1 = 'http://www.w3.org/2000/09/xmldsig#sha1' + SHA256 = 'http://www.w3.org/2001/04/xmlenc#sha256' + SHA384 = 'http://www.w3.org/2001/04/xmldsig-more#sha384' + SHA512 = 'http://www.w3.org/2001/04/xmlenc#sha512' + + DSA_SHA1 = 'http://www.w3.org/2000/09/xmldsig#dsa-sha1' + RSA_SHA1 = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1' + RSA_SHA256 = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256' + RSA_SHA384 = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384' + RSA_SHA512 = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512' + + # Enc + TRIPLEDES_CBC = 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc' + AES128_CBC = 'http://www.w3.org/2001/04/xmlenc#aes128-cbc' + AES192_CBC = 'http://www.w3.org/2001/04/xmlenc#aes192-cbc' + AES256_CBC = 'http://www.w3.org/2001/04/xmlenc#aes256-cbc' + RSA_1_5 = 'http://www.w3.org/2001/04/xmlenc#rsa-1_5' + RSA_OAEP_MGF1P = 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p' + + # Define here the deprecated algorithms + DEPRECATED_ALGORITHMS = [DSA_SHA1, RSA_SHA1, SHA1]
    +
    + +
    +
    + +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/docs/saml2/_modules/onelogin/saml2/errors.html b/docs/saml2/_modules/onelogin/saml2/errors.html new file mode 100644 index 00000000..77b1c1f7 --- /dev/null +++ b/docs/saml2/_modules/onelogin/saml2/errors.html @@ -0,0 +1,228 @@ + + + + + + onelogin.saml2.errors — SAML Python2/3 Toolkit 1 documentation + + + + + + + + + + + + + + + +
    + + +
    + +
    +
    +
    + +
    +
    +
    +
    + +

    Source code for onelogin.saml2.errors

    +# -*- coding: utf-8 -*-
    +
    +""" OneLogin_Saml2_Error class
    +
    +
    +Error class of SAML Python Toolkit.
    +
    +Defines common Error codes and has a custom initializator.
    +
    +"""
    +
    +
    +
    [docs]class OneLogin_Saml2_Error(Exception): + """ + + This class implements a custom Exception handler. + Defines custom error codes. + + """ + + # Errors + SETTINGS_FILE_NOT_FOUND = 0 + SETTINGS_INVALID_SYNTAX = 1 + SETTINGS_INVALID = 2 + METADATA_SP_INVALID = 3 + # SP_CERTS_NOT_FOUND is deprecated, use CERT_NOT_FOUND instead + SP_CERTS_NOT_FOUND = 4 + CERT_NOT_FOUND = 4 + REDIRECT_INVALID_URL = 5 + PUBLIC_CERT_FILE_NOT_FOUND = 6 + PRIVATE_KEY_FILE_NOT_FOUND = 7 + SAML_RESPONSE_NOT_FOUND = 8 + SAML_LOGOUTMESSAGE_NOT_FOUND = 9 + SAML_LOGOUTREQUEST_INVALID = 10 + SAML_LOGOUTRESPONSE_INVALID = 11 + SAML_SINGLE_LOGOUT_NOT_SUPPORTED = 12 + PRIVATE_KEY_NOT_FOUND = 13 + UNSUPPORTED_SETTINGS_OBJECT = 14 + + def __init__(self, message, code=0, errors=None): + """ + Initializes the Exception instance. + + Arguments are: + * (str) message. Describes the error. + * (int) code. The code error (defined in the error class). + """ + assert isinstance(code, int) + + if errors is not None: + message = message % errors + + Exception.__init__(self, message) + self.code = code
    + + +
    [docs]class OneLogin_Saml2_ValidationError(Exception): + """ + This class implements another custom Exception handler, related + to exceptions that happens during validation process. + Defines custom error codes . + """ + + # Validation Errors + UNSUPPORTED_SAML_VERSION = 0 + MISSING_ID = 1 + WRONG_NUMBER_OF_ASSERTIONS = 2 + MISSING_STATUS = 3 + MISSING_STATUS_CODE = 4 + STATUS_CODE_IS_NOT_SUCCESS = 5 + WRONG_SIGNED_ELEMENT = 6 + ID_NOT_FOUND_IN_SIGNED_ELEMENT = 7 + DUPLICATED_ID_IN_SIGNED_ELEMENTS = 8 + INVALID_SIGNED_ELEMENT = 9 + DUPLICATED_REFERENCE_IN_SIGNED_ELEMENTS = 10 + UNEXPECTED_SIGNED_ELEMENTS = 11 + WRONG_NUMBER_OF_SIGNATURES_IN_RESPONSE = 12 + WRONG_NUMBER_OF_SIGNATURES_IN_ASSERTION = 13 + INVALID_XML_FORMAT = 14 + WRONG_INRESPONSETO = 15 + NO_ENCRYPTED_ASSERTION = 16 + NO_ENCRYPTED_NAMEID = 17 + MISSING_CONDITIONS = 18 + ASSERTION_TOO_EARLY = 19 + ASSERTION_EXPIRED = 20 + WRONG_NUMBER_OF_AUTHSTATEMENTS = 21 + NO_ATTRIBUTESTATEMENT = 22 + ENCRYPTED_ATTRIBUTES = 23 + WRONG_DESTINATION = 24 + EMPTY_DESTINATION = 25 + WRONG_AUDIENCE = 26 + ISSUER_MULTIPLE_IN_RESPONSE = 27 + ISSUER_NOT_FOUND_IN_ASSERTION = 28 + WRONG_ISSUER = 29 + SESSION_EXPIRED = 30 + WRONG_SUBJECTCONFIRMATION = 31 + NO_SIGNED_MESSAGE = 32 + NO_SIGNED_ASSERTION = 33 + NO_SIGNATURE_FOUND = 34 + KEYINFO_NOT_FOUND_IN_ENCRYPTED_DATA = 35 + CHILDREN_NODE_NOT_FOUND_IN_KEYINFO = 36 + UNSUPPORTED_RETRIEVAL_METHOD = 37 + NO_NAMEID = 38 + EMPTY_NAMEID = 39 + SP_NAME_QUALIFIER_NAME_MISMATCH = 40 + DUPLICATED_ATTRIBUTE_NAME_FOUND = 41 + INVALID_SIGNATURE = 42 + WRONG_NUMBER_OF_SIGNATURES = 43 + RESPONSE_EXPIRED = 44 + AUTHN_CONTEXT_MISMATCH = 45 + DEPRECATED_SIGNATURE_METHOD = 46 + DEPRECATED_DIGEST_METHOD = 47 + + def __init__(self, message, code=0, errors=None): + """ + Initializes the Exception instance. + Arguments are: + * (str) message. Describes the error. + * (int) code. The code error (defined in the error class). + """ + assert isinstance(code, int) + + if errors is not None: + message = message % errors + + Exception.__init__(self, message) + self.code = code
    +
    + +
    +
    + +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/docs/saml2/_modules/onelogin/saml2/idp_metadata_parser.html b/docs/saml2/_modules/onelogin/saml2/idp_metadata_parser.html new file mode 100644 index 00000000..a4ee1098 --- /dev/null +++ b/docs/saml2/_modules/onelogin/saml2/idp_metadata_parser.html @@ -0,0 +1,380 @@ + + + + + + onelogin.saml2.idp_metadata_parser — SAML Python2/3 Toolkit 1 documentation + + + + + + + + + + + + + + + +
    + + +
    + +
    +
    +
    +
      +
    • + + +
    • +
    • +
    +
    +
    +
    +
    + +

    Source code for onelogin.saml2.idp_metadata_parser

    +# -*- coding: utf-8 -*-
    +
    +""" OneLogin_Saml2_IdPMetadataParser class
    +Metadata class of SAML Python Toolkit.
    +"""
    +
    +
    +from copy import deepcopy
    +
    +try:
    +    import urllib.request as urllib2
    +except ImportError:
    +    import urllib2
    +
    +import ssl
    +
    +from onelogin.saml2.constants import OneLogin_Saml2_Constants
    +from onelogin.saml2.xml_utils import OneLogin_Saml2_XML
    +
    +
    +
    [docs]class OneLogin_Saml2_IdPMetadataParser(object): + """ + A class that contain methods related to obtaining and parsing metadata from IdP + + This class does not validate in any way the URL that is introduced, + make sure to validate it properly before use it in a get_metadata method. + """ + +
    [docs] @classmethod + def get_metadata(cls, url, validate_cert=True, timeout=None, headers=None): + """ + Gets the metadata XML from the provided URL + :param url: Url where the XML of the Identity Provider Metadata is published. + :type url: string + + :param validate_cert: If the url uses https schema, that flag enables or not the verification of the associated certificate. + :type validate_cert: bool + + :param timeout: Timeout in seconds to wait for metadata response + :type timeout: int + :param headers: Extra headers to send in the request + :type headers: dict + + :returns: metadata XML + :rtype: string + """ + valid = False + + request = urllib2.Request(url, headers=headers or {}) + + if validate_cert: + response = urllib2.urlopen(request, timeout=timeout) + else: + ctx = ssl.create_default_context() + ctx.check_hostname = False + ctx.verify_mode = ssl.CERT_NONE + response = urllib2.urlopen(request, context=ctx, timeout=timeout) + xml = response.read() + + if xml: + try: + dom = OneLogin_Saml2_XML.to_etree(xml) + idp_descriptor_nodes = OneLogin_Saml2_XML.query(dom, '//md:IDPSSODescriptor') + if idp_descriptor_nodes: + valid = True + except Exception: + pass + + if not valid: + raise Exception('Not valid IdP XML found from URL: %s' % (url)) + + return xml
    + +
    [docs] @classmethod + def parse_remote(cls, url, validate_cert=True, entity_id=None, timeout=None, **kwargs): + """ + Gets the metadata XML from the provided URL and parse it, returning a dict with extracted data + :param url: Url where the XML of the Identity Provider Metadata is published. + :type url: string + + :param validate_cert: If the url uses https schema, that flag enables or not the verification of the associated certificate. + :type validate_cert: bool + + :param entity_id: Specify the entity_id of the EntityDescriptor that you want to parse a XML + that contains multiple EntityDescriptor. + :type entity_id: string + + :param timeout: Timeout in seconds to wait for metadata response + :type timeout: int + + :returns: settings dict with extracted data + :rtype: dict + """ + idp_metadata = cls.get_metadata(url, validate_cert, timeout, headers=kwargs.pop('headers', None)) + return cls.parse(idp_metadata, entity_id=entity_id, **kwargs)
    + +
    [docs] @classmethod + def parse( + cls, + idp_metadata, + required_sso_binding=OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT, + required_slo_binding=OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT, + entity_id=None): + """ + Parses the Identity Provider metadata and return a dict with extracted data. + + If there are multiple <IDPSSODescriptor> tags, parse only the first. + + Parses only those SSO endpoints with the same binding as given by + the `required_sso_binding` parameter. + + Parses only those SLO endpoints with the same binding as given by + the `required_slo_binding` parameter. + + If the metadata specifies multiple SSO endpoints with the required + binding, extract only the first (the same holds true for SLO + endpoints). + + :param idp_metadata: XML of the Identity Provider Metadata. + :type idp_metadata: string + + :param required_sso_binding: Parse only POST or REDIRECT SSO endpoints. + :type required_sso_binding: one of OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT + or OneLogin_Saml2_Constants.BINDING_HTTP_POST + + :param required_slo_binding: Parse only POST or REDIRECT SLO endpoints. + :type required_slo_binding: one of OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT + or OneLogin_Saml2_Constants.BINDING_HTTP_POST + + :param entity_id: Specify the entity_id of the EntityDescriptor that you want to parse a XML + that contains multiple EntityDescriptor. + :type entity_id: string + + :returns: settings dict with extracted data + :rtype: dict + """ + data = {} + + dom = OneLogin_Saml2_XML.to_etree(idp_metadata) + idp_entity_id = want_authn_requests_signed = idp_name_id_format = idp_sso_url = idp_slo_url = certs = None + + entity_desc_path = '//md:EntityDescriptor' + if entity_id: + entity_desc_path += "[@entityID='%s']" % entity_id + entity_descriptor_nodes = OneLogin_Saml2_XML.query(dom, entity_desc_path) + + if len(entity_descriptor_nodes) > 0: + entity_descriptor_node = entity_descriptor_nodes[0] + idp_descriptor_nodes = OneLogin_Saml2_XML.query(entity_descriptor_node, './md:IDPSSODescriptor') + if len(idp_descriptor_nodes) > 0: + idp_descriptor_node = idp_descriptor_nodes[0] + + idp_entity_id = entity_descriptor_node.get('entityID', None) + + want_authn_requests_signed = idp_descriptor_node.get('WantAuthnRequestsSigned', None) + + name_id_format_nodes = OneLogin_Saml2_XML.query(idp_descriptor_node, './md:NameIDFormat') + if len(name_id_format_nodes) > 0: + idp_name_id_format = OneLogin_Saml2_XML.element_text(name_id_format_nodes[0]) + + sso_nodes = OneLogin_Saml2_XML.query( + idp_descriptor_node, + "./md:SingleSignOnService[@Binding='%s']" % required_sso_binding + ) + + if len(sso_nodes) > 0: + idp_sso_url = sso_nodes[0].get('Location', None) + + slo_nodes = OneLogin_Saml2_XML.query( + idp_descriptor_node, + "./md:SingleLogoutService[@Binding='%s']" % required_slo_binding + ) + + if len(slo_nodes) > 0: + idp_slo_url = slo_nodes[0].get('Location', None) + + signing_nodes = OneLogin_Saml2_XML.query(idp_descriptor_node, "./md:KeyDescriptor[not(contains(@use, 'encryption'))]/ds:KeyInfo/ds:X509Data/ds:X509Certificate") + encryption_nodes = OneLogin_Saml2_XML.query(idp_descriptor_node, "./md:KeyDescriptor[not(contains(@use, 'signing'))]/ds:KeyInfo/ds:X509Data/ds:X509Certificate") + + if len(signing_nodes) > 0 or len(encryption_nodes) > 0: + certs = {} + if len(signing_nodes) > 0: + certs['signing'] = [] + for cert_node in signing_nodes: + certs['signing'].append(''.join(OneLogin_Saml2_XML.element_text(cert_node).split())) + if len(encryption_nodes) > 0: + certs['encryption'] = [] + for cert_node in encryption_nodes: + certs['encryption'].append(''.join(OneLogin_Saml2_XML.element_text(cert_node).split())) + + data['idp'] = {} + + if idp_entity_id is not None: + data['idp']['entityId'] = idp_entity_id + + if idp_sso_url is not None: + data['idp']['singleSignOnService'] = {} + data['idp']['singleSignOnService']['url'] = idp_sso_url + data['idp']['singleSignOnService']['binding'] = required_sso_binding + + if idp_slo_url is not None: + data['idp']['singleLogoutService'] = {} + data['idp']['singleLogoutService']['url'] = idp_slo_url + data['idp']['singleLogoutService']['binding'] = required_slo_binding + + if want_authn_requests_signed is not None: + data['security'] = {} + data['security']['authnRequestsSigned'] = want_authn_requests_signed == "true" + + if idp_name_id_format: + data['sp'] = {} + data['sp']['NameIDFormat'] = idp_name_id_format + + if certs is not None: + if (len(certs) == 1 and + (('signing' in certs and len(certs['signing']) == 1) or + ('encryption' in certs and len(certs['encryption']) == 1))) or \ + (('signing' in certs and len(certs['signing']) == 1) and + ('encryption' in certs and len(certs['encryption']) == 1 and + certs['signing'][0] == certs['encryption'][0])): + if 'signing' in certs: + data['idp']['x509cert'] = certs['signing'][0] + else: + data['idp']['x509cert'] = certs['encryption'][0] + else: + data['idp']['x509certMulti'] = certs + return data
    + +
    [docs] @staticmethod + def merge_settings(settings, new_metadata_settings): + """ + Will update the settings with the provided new settings data extracted from the IdP metadata + :param settings: Current settings dict data + :type settings: dict + :param new_metadata_settings: Settings to be merged (extracted from IdP metadata after parsing) + :type new_metadata_settings: dict + :returns: merged settings + :rtype: dict + """ + for d in (settings, new_metadata_settings): + if not isinstance(d, dict): + raise TypeError('Both arguments must be dictionaries.') + + # Guarantee to not modify original data (`settings.copy()` would not + # be sufficient, as it's just a shallow copy). + result_settings = deepcopy(settings) + + # previously I will take care of cert stuff + if 'idp' in new_metadata_settings and 'idp' in result_settings: + if new_metadata_settings['idp'].get('x509cert', None) and result_settings['idp'].get('x509certMulti', None): + del result_settings['idp']['x509certMulti'] + if new_metadata_settings['idp'].get('x509certMulti', None) and result_settings['idp'].get('x509cert', None): + del result_settings['idp']['x509cert'] + + # Merge `new_metadata_settings` into `result_settings`. + dict_deep_merge(result_settings, new_metadata_settings) + return result_settings
    + + +
    [docs]def dict_deep_merge(a, b, path=None): + """Deep-merge dictionary `b` into dictionary `a`. + + Kudos to http://stackoverflow.com/a/7205107/145400 + """ + if path is None: + path = [] + for key in b: + if key in a: + if isinstance(a[key], dict) and isinstance(b[key], dict): + dict_deep_merge(a[key], b[key], path + [str(key)]) + elif a[key] == b[key]: + # Key conflict, but equal value. + pass + else: + # Key/value conflict. Prioritize b over a. + a[key] = b[key] + else: + a[key] = b[key] + return a
    +
    + +
    +
    + +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/docs/saml2/_modules/onelogin/saml2/logout_request.html b/docs/saml2/_modules/onelogin/saml2/logout_request.html new file mode 100644 index 00000000..bdc48e77 --- /dev/null +++ b/docs/saml2/_modules/onelogin/saml2/logout_request.html @@ -0,0 +1,465 @@ + + + + + + onelogin.saml2.logout_request — SAML Python2/3 Toolkit 1 documentation + + + + + + + + + + + + + + + +
    + + +
    + +
    +
    +
    +
      +
    • + + +
    • +
    • +
    +
    +
    +
    +
    + +

    Source code for onelogin.saml2.logout_request

    +# -*- coding: utf-8 -*-
    +
    +""" OneLogin_Saml2_Logout_Request class
    +
    +
    +Logout Request class of SAML Python Toolkit.
    +
    +"""
    +
    +from onelogin.saml2 import compat
    +from onelogin.saml2.constants import OneLogin_Saml2_Constants
    +from onelogin.saml2.utils import OneLogin_Saml2_Utils, OneLogin_Saml2_Error, OneLogin_Saml2_ValidationError
    +from onelogin.saml2.xml_templates import OneLogin_Saml2_Templates
    +from onelogin.saml2.xml_utils import OneLogin_Saml2_XML
    +
    +
    +
    [docs]class OneLogin_Saml2_Logout_Request(object): + """ + + This class handles a Logout Request. + + Builds a Logout Response object and validates it. + + """ + + def __init__(self, settings, request=None, name_id=None, session_index=None, nq=None, name_id_format=None, spnq=None): + """ + Constructs the Logout Request object. + + :param settings: Setting data + :type settings: OneLogin_Saml2_Settings + + :param request: Optional. A LogoutRequest to be loaded instead build one. + :type request: string + + :param name_id: The NameID that will be set in the LogoutRequest. + :type name_id: string + + :param session_index: SessionIndex that identifies the session of the user. + :type session_index: string + + :param nq: IDP Name Qualifier + :type: string + + :param name_id_format: The NameID Format that will be set in the LogoutRequest. + :type: string + + :param spnq: SP Name Qualifier + :type: string + """ + self._settings = settings + self._error = None + self.id = None + + if request is None: + sp_data = self._settings.get_sp_data() + idp_data = self._settings.get_idp_data() + security = self._settings.get_security_data() + + self.id = self._generate_request_id() + + issue_instant = OneLogin_Saml2_Utils.parse_time_to_SAML(OneLogin_Saml2_Utils.now()) + + cert = None + if security['nameIdEncrypted']: + exists_multix509enc = 'x509certMulti' in idp_data and \ + 'encryption' in idp_data['x509certMulti'] and \ + idp_data['x509certMulti']['encryption'] + if exists_multix509enc: + cert = idp_data['x509certMulti']['encryption'][0] + else: + cert = self._settings.get_idp_cert() + + if name_id is not None: + if not name_id_format and sp_data['NameIDFormat'] != OneLogin_Saml2_Constants.NAMEID_UNSPECIFIED: + name_id_format = sp_data['NameIDFormat'] + else: + name_id = idp_data['entityId'] + name_id_format = OneLogin_Saml2_Constants.NAMEID_ENTITY + + # From saml-core-2.0-os 8.3.6, when the entity Format is used: + # "The NameQualifier, SPNameQualifier, and SPProvidedID attributes + # MUST be omitted. + if name_id_format and name_id_format == OneLogin_Saml2_Constants.NAMEID_ENTITY: + nq = None + spnq = None + + # NameID Format UNSPECIFIED omitted + if name_id_format and name_id_format == OneLogin_Saml2_Constants.NAMEID_UNSPECIFIED: + name_id_format = None + + name_id_obj = OneLogin_Saml2_Utils.generate_name_id( + name_id, + spnq, + name_id_format, + cert, + False, + nq + ) + + if session_index: + session_index_str = '<samlp:SessionIndex>%s</samlp:SessionIndex>' % session_index + else: + session_index_str = '' + + logout_request = OneLogin_Saml2_Templates.LOGOUT_REQUEST % \ + { + 'id': self.id, + 'issue_instant': issue_instant, + 'single_logout_url': self._settings.get_idp_slo_url(), + 'entity_id': sp_data['entityId'], + 'name_id': name_id_obj, + 'session_index': session_index_str, + } + else: + logout_request = OneLogin_Saml2_Utils.decode_base64_and_inflate(request, ignore_zip=True) + self.id = self.get_id(logout_request) + + self._logout_request = compat.to_string(logout_request) + +
    [docs] def get_request(self, deflate=True): + """ + Returns the Logout Request deflated, base64encoded + :param deflate: It makes the deflate process optional + :type: bool + :return: Logout Request maybe deflated and base64 encoded + :rtype: str object + """ + if deflate: + request = OneLogin_Saml2_Utils.deflate_and_base64_encode(self._logout_request) + else: + request = OneLogin_Saml2_Utils.b64encode(self._logout_request) + return request
    + +
    [docs] def get_xml(self): + """ + Returns the XML that will be sent as part of the request + or that was received at the SP + :return: XML request body + :rtype: string + """ + return self._logout_request
    + +
    [docs] @classmethod + def get_id(cls, request): + """ + Returns the ID of the Logout Request + :param request: Logout Request Message + :type request: string|DOMDocument + :return: string ID + :rtype: str object + """ + + elem = OneLogin_Saml2_XML.to_etree(request) + return elem.get('ID', None)
    + +
    [docs] @classmethod + def get_nameid_data(cls, request, key=None): + """ + Gets the NameID Data of the the Logout Request + :param request: Logout Request Message + :type request: string|DOMDocument + :param key: The SP key + :type key: string + :return: Name ID Data (Value, Format, NameQualifier, SPNameQualifier) + :rtype: dict + """ + elem = OneLogin_Saml2_XML.to_etree(request) + name_id = None + encrypted_entries = OneLogin_Saml2_XML.query(elem, '/samlp:LogoutRequest/saml:EncryptedID') + + if len(encrypted_entries) == 1: + if key is None: + raise OneLogin_Saml2_Error( + 'Private Key is required in order to decrypt the NameID, check settings', + OneLogin_Saml2_Error.PRIVATE_KEY_NOT_FOUND + ) + + encrypted_data_nodes = OneLogin_Saml2_XML.query(elem, '/samlp:LogoutRequest/saml:EncryptedID/xenc:EncryptedData') + if len(encrypted_data_nodes) == 1: + encrypted_data = encrypted_data_nodes[0] + name_id = OneLogin_Saml2_Utils.decrypt_element(encrypted_data, key) + else: + entries = OneLogin_Saml2_XML.query(elem, '/samlp:LogoutRequest/saml:NameID') + if len(entries) == 1: + name_id = entries[0] + + if name_id is None: + raise OneLogin_Saml2_ValidationError( + 'NameID not found in the Logout Request', + OneLogin_Saml2_ValidationError.NO_NAMEID + ) + + name_id_data = { + 'Value': OneLogin_Saml2_XML.element_text(name_id) + } + for attr in ['Format', 'SPNameQualifier', 'NameQualifier']: + if attr in name_id.attrib: + name_id_data[attr] = name_id.attrib[attr] + + return name_id_data
    + +
    [docs] @classmethod + def get_nameid(cls, request, key=None): + """ + Gets the NameID of the Logout Request Message + :param request: Logout Request Message + :type request: string|DOMDocument + :param key: The SP key + :type key: string + :return: Name ID Value + :rtype: string + """ + name_id = cls.get_nameid_data(request, key) + return name_id['Value']
    + +
    [docs] @classmethod + def get_nameid_format(cls, request, key=None): + """ + Gets the NameID Format of the Logout Request Message + :param request: Logout Request Message + :type request: string|DOMDocument + :param key: The SP key + :type key: string + :return: Name ID Format + :rtype: string + """ + name_id_format = None + name_id_data = cls.get_nameid_data(request, key) + if name_id_data and 'Format' in name_id_data.keys(): + name_id_format = name_id_data['Format'] + return name_id_format
    + +
    [docs] @classmethod + def get_issuer(cls, request): + """ + Gets the Issuer of the Logout Request Message + :param request: Logout Request Message + :type request: string|DOMDocument + :return: The Issuer + :rtype: string + """ + + elem = OneLogin_Saml2_XML.to_etree(request) + issuer = None + issuer_nodes = OneLogin_Saml2_XML.query(elem, '/samlp:LogoutRequest/saml:Issuer') + if len(issuer_nodes) == 1: + issuer = OneLogin_Saml2_XML.element_text(issuer_nodes[0]) + return issuer
    + +
    [docs] @classmethod + def get_session_indexes(cls, request): + """ + Gets the SessionIndexes from the Logout Request + :param request: Logout Request Message + :type request: string|DOMDocument + :return: The SessionIndex value + :rtype: list + """ + + elem = OneLogin_Saml2_XML.to_etree(request) + session_indexes = [] + session_index_nodes = OneLogin_Saml2_XML.query(elem, '/samlp:LogoutRequest/samlp:SessionIndex') + for session_index_node in session_index_nodes: + session_indexes.append(OneLogin_Saml2_XML.element_text(session_index_node)) + return session_indexes
    + +
    [docs] def is_valid(self, request_data, raise_exceptions=False): + """ + Checks if the Logout Request received is valid + :param request_data: Request Data + :type request_data: dict + + :param raise_exceptions: Whether to return false on failure or raise an exception + :type raise_exceptions: Boolean + + :return: If the Logout Request is or not valid + :rtype: boolean + """ + self._error = None + try: + root = OneLogin_Saml2_XML.to_etree(self._logout_request) + + idp_data = self._settings.get_idp_data() + idp_entity_id = idp_data['entityId'] + + get_data = ('get_data' in request_data and request_data['get_data']) or dict() + + if self._settings.is_strict(): + res = OneLogin_Saml2_XML.validate_xml(root, 'saml-schema-protocol-2.0.xsd', self._settings.is_debug_active()) + if isinstance(res, str): + raise OneLogin_Saml2_ValidationError( + 'Invalid SAML Logout Request. Not match the saml-schema-protocol-2.0.xsd', + OneLogin_Saml2_ValidationError.INVALID_XML_FORMAT + ) + + security = self._settings.get_security_data() + + current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data) + + # Check NotOnOrAfter + if root.get('NotOnOrAfter', None): + na = OneLogin_Saml2_Utils.parse_SAML_to_time(root.get('NotOnOrAfter')) + if na <= OneLogin_Saml2_Utils.now(): + raise OneLogin_Saml2_ValidationError( + 'Could not validate timestamp: expired. Check system clock.)', + OneLogin_Saml2_ValidationError.RESPONSE_EXPIRED + ) + + # Check destination + destination = root.get('Destination', None) + if destination: + if not OneLogin_Saml2_Utils.normalize_url(url=destination).startswith(OneLogin_Saml2_Utils.normalize_url(url=current_url)): + raise OneLogin_Saml2_ValidationError( + 'The LogoutRequest was received at ' + '%(currentURL)s instead of %(destination)s' % + { + 'currentURL': current_url, + 'destination': destination, + }, + OneLogin_Saml2_ValidationError.WRONG_DESTINATION + ) + + # Check issuer + issuer = self.get_issuer(root) + if issuer is not None and issuer != idp_entity_id: + raise OneLogin_Saml2_ValidationError( + 'Invalid issuer in the Logout Request (expected %(idpEntityId)s, got %(issuer)s)' % + { + 'idpEntityId': idp_entity_id, + 'issuer': issuer + }, + OneLogin_Saml2_ValidationError.WRONG_ISSUER + ) + + if security['wantMessagesSigned']: + if 'Signature' not in get_data: + raise OneLogin_Saml2_ValidationError( + 'The Message of the Logout Request is not signed and the SP require it', + OneLogin_Saml2_ValidationError.NO_SIGNED_MESSAGE + ) + + return True + except Exception as err: + # pylint: disable=R0801 + self._error = str(err) + debug = self._settings.is_debug_active() + if debug: + print(err) + if raise_exceptions: + raise + return False
    + +
    [docs] def get_error(self): + """ + After executing a validation process, if it fails this method returns the cause + """ + return self._error
    + + def _generate_request_id(self): + """ + Generate an unique logout request ID. + """ + return OneLogin_Saml2_Utils.generate_unique_id()
    +
    + +
    +
    + +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/docs/saml2/_modules/onelogin/saml2/logout_response.html b/docs/saml2/_modules/onelogin/saml2/logout_response.html new file mode 100644 index 00000000..a403dc35 --- /dev/null +++ b/docs/saml2/_modules/onelogin/saml2/logout_response.html @@ -0,0 +1,321 @@ + + + + + + onelogin.saml2.logout_response — SAML Python2/3 Toolkit 1 documentation + + + + + + + + + + + + + + + +
    + + +
    + +
    +
    +
    +
      +
    • + + +
    • +
    • +
    +
    +
    +
    +
    + +

    Source code for onelogin.saml2.logout_response

    +# -*- coding: utf-8 -*-
    +
    +""" OneLogin_Saml2_Logout_Response class
    +
    +
    +Logout Response class of SAML Python Toolkit.
    +
    +"""
    +
    +from onelogin.saml2 import compat
    +from onelogin.saml2.constants import OneLogin_Saml2_Constants
    +from onelogin.saml2.utils import OneLogin_Saml2_Utils, OneLogin_Saml2_ValidationError
    +from onelogin.saml2.xml_templates import OneLogin_Saml2_Templates
    +from onelogin.saml2.xml_utils import OneLogin_Saml2_XML
    +
    +
    +
    [docs]class OneLogin_Saml2_Logout_Response(object): + """ + + This class handles a Logout Response. It Builds or parses a Logout Response object + and validates it. + + """ + + def __init__(self, settings, response=None): + """ + Constructs a Logout Response object (Initialize params from settings + and if provided load the Logout Response. + + Arguments are: + * (OneLogin_Saml2_Settings) settings. Setting data + * (string) response. An UUEncoded SAML Logout + response from the IdP. + """ + self._settings = settings + self._error = None + self.id = None + + if response is not None: + self._logout_response = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(response, ignore_zip=True)) + self.document = OneLogin_Saml2_XML.to_etree(self._logout_response) + self.id = self.document.get('ID', None) + +
    [docs] def get_issuer(self): + """ + Gets the Issuer of the Logout Response Message + :return: The Issuer + :rtype: string + """ + issuer = None + issuer_nodes = self._query('/samlp:LogoutResponse/saml:Issuer') + if len(issuer_nodes) == 1: + issuer = OneLogin_Saml2_XML.element_text(issuer_nodes[0]) + return issuer
    + +
    [docs] def get_status(self): + """ + Gets the Status + :return: The Status + :rtype: string + """ + entries = self._query('/samlp:LogoutResponse/samlp:Status/samlp:StatusCode') + if len(entries) == 0: + return None + status = entries[0].attrib['Value'] + return status
    + +
    [docs] def is_valid(self, request_data, request_id=None, raise_exceptions=False): + """ + Determines if the SAML LogoutResponse is valid + :param request_id: The ID of the LogoutRequest sent by this SP to the IdP + :type request_id: string + + :param raise_exceptions: Whether to return false on failure or raise an exception + :type raise_exceptions: Boolean + + :return: Returns if the SAML LogoutResponse is or not valid + :rtype: boolean + """ + self._error = None + try: + idp_data = self._settings.get_idp_data() + idp_entity_id = idp_data['entityId'] + get_data = request_data['get_data'] + + if self._settings.is_strict(): + res = OneLogin_Saml2_XML.validate_xml(self.document, 'saml-schema-protocol-2.0.xsd', self._settings.is_debug_active()) + if isinstance(res, str): + raise OneLogin_Saml2_ValidationError( + 'Invalid SAML Logout Request. Not match the saml-schema-protocol-2.0.xsd', + OneLogin_Saml2_ValidationError.INVALID_XML_FORMAT + ) + + security = self._settings.get_security_data() + + # Check if the InResponseTo of the Logout Response matches the ID of the Logout Request (requestId) if provided + in_response_to = self.get_in_response_to() + if request_id is not None and in_response_to and in_response_to != request_id: + raise OneLogin_Saml2_ValidationError( + 'The InResponseTo of the Logout Response: %s, does not match the ID of the Logout request sent by the SP: %s' % (in_response_to, request_id), + OneLogin_Saml2_ValidationError.WRONG_INRESPONSETO + ) + + # Check issuer + issuer = self.get_issuer() + if issuer is not None and issuer != idp_entity_id: + raise OneLogin_Saml2_ValidationError( + 'Invalid issuer in the Logout Response (expected %(idpEntityId)s, got %(issuer)s)' % + { + 'idpEntityId': idp_entity_id, + 'issuer': issuer + }, + OneLogin_Saml2_ValidationError.WRONG_ISSUER + ) + + current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data) + + # Check destination + destination = self.document.get('Destination', None) + if destination: + if not OneLogin_Saml2_Utils.normalize_url(url=destination).startswith(OneLogin_Saml2_Utils.normalize_url(url=current_url)): + raise OneLogin_Saml2_ValidationError( + 'The LogoutResponse was received at %s instead of %s' % (current_url, destination), + OneLogin_Saml2_ValidationError.WRONG_DESTINATION + ) + + if security['wantMessagesSigned']: + if 'Signature' not in get_data: + raise OneLogin_Saml2_ValidationError( + 'The Message of the Logout Response is not signed and the SP require it', + OneLogin_Saml2_ValidationError.NO_SIGNED_MESSAGE + ) + return True + # pylint: disable=R0801 + except Exception as err: + self._error = str(err) + debug = self._settings.is_debug_active() + if debug: + print(err) + if raise_exceptions: + raise + return False
    + + def _query(self, query): + """ + Extracts a node from the Etree (Logout Response Message) + :param query: Xpath Expression + :type query: string + :return: The queried node + :rtype: Element + """ + return OneLogin_Saml2_XML.query(self.document, query) + +
    [docs] def build(self, in_response_to, status=OneLogin_Saml2_Constants.STATUS_SUCCESS): + """ + Creates a Logout Response object. + :param in_response_to: InResponseTo value for the Logout Response. + :type in_response_to: string + :param: status: The status of the response + :type: status: string + """ + sp_data = self._settings.get_sp_data() + + self.id = self._generate_request_id() + + issue_instant = OneLogin_Saml2_Utils.parse_time_to_SAML(OneLogin_Saml2_Utils.now()) + + logout_response = OneLogin_Saml2_Templates.LOGOUT_RESPONSE % { + "id": self.id, + "issue_instant": issue_instant, + "destination": self._settings.get_idp_slo_response_url(), + "in_response_to": in_response_to, + "entity_id": sp_data["entityId"], + "status": status, + } + + self._logout_response = logout_response
    + +
    [docs] def get_in_response_to(self): + """ + Gets the ID of the LogoutRequest which this response is in response to + :returns: ID of LogoutRequest this LogoutResponse is in response to or None if it is not present + :rtype: str + """ + return self.document.get('InResponseTo')
    + +
    [docs] def get_response(self, deflate=True): + """ + Returns a Logout Response object. + :param deflate: It makes the deflate process optional + :type: bool + :return: Logout Response maybe deflated and base64 encoded + :rtype: string + """ + if deflate: + response = OneLogin_Saml2_Utils.deflate_and_base64_encode(self._logout_response) + else: + response = OneLogin_Saml2_Utils.b64encode(self._logout_response) + return response
    + +
    [docs] def get_error(self): + """ + After executing a validation process, if it fails this method returns the cause + """ + return self._error
    + +
    [docs] def get_xml(self): + """ + Returns the XML that will be sent as part of the response + or that was received at the SP + :return: XML response body + :rtype: string + """ + return self._logout_response
    + + def _generate_request_id(self): + """ + Generate an unique logout response ID. + """ + return OneLogin_Saml2_Utils.generate_unique_id()
    +
    + +
    +
    + +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/docs/saml2/_modules/onelogin/saml2/metadata.html b/docs/saml2/_modules/onelogin/saml2/metadata.html new file mode 100644 index 00000000..cf74b78c --- /dev/null +++ b/docs/saml2/_modules/onelogin/saml2/metadata.html @@ -0,0 +1,365 @@ + + + + + + onelogin.saml2.metadata — SAML Python2/3 Toolkit 1 documentation + + + + + + + + + + + + + + + +
    + + +
    + +
    +
    +
    + +
    +
    +
    +
    + +

    Source code for onelogin.saml2.metadata

    +# -*- coding: utf-8 -*-
    +
    +""" OneLoginSaml2Metadata class
    +
    +
    +Metadata class of SAML Python Toolkit.
    +
    +"""
    +
    +from time import gmtime, strftime, time
    +from datetime import datetime
    +
    +from onelogin.saml2 import compat
    +from onelogin.saml2.constants import OneLogin_Saml2_Constants
    +from onelogin.saml2.utils import OneLogin_Saml2_Utils
    +from onelogin.saml2.xml_templates import OneLogin_Saml2_Templates
    +from onelogin.saml2.xml_utils import OneLogin_Saml2_XML
    +
    +try:
    +    basestring
    +except NameError:
    +    basestring = str
    +
    +
    +
    [docs]class OneLogin_Saml2_Metadata(object): + """ + + A class that contains methods related to the metadata of the SP + + """ + + TIME_VALID = 172800 # 2 days + TIME_CACHED = 604800 # 1 week + +
    [docs] @classmethod + def builder(cls, sp, authnsign=False, wsign=False, valid_until=None, cache_duration=None, contacts=None, organization=None): + """ + Builds the metadata of the SP + + :param sp: The SP data + :type sp: string + + :param authnsign: authnRequestsSigned attribute + :type authnsign: string + + :param wsign: wantAssertionsSigned attribute + :type wsign: string + + :param valid_until: Metadata's expiry date + :type valid_until: string|DateTime|Timestamp + + :param cache_duration: Duration of the cache in seconds + :type cache_duration: int|string + + :param contacts: Contacts info + :type contacts: dict + + :param organization: Organization info + :type organization: dict + """ + if valid_until is None: + valid_until = int(time()) + cls.TIME_VALID + if not isinstance(valid_until, basestring): + if isinstance(valid_until, datetime): + valid_until_time = valid_until.timetuple() + else: + valid_until_time = gmtime(valid_until) + valid_until_str = strftime(r'%Y-%m-%dT%H:%M:%SZ', valid_until_time) + else: + valid_until_str = valid_until + + if cache_duration is None: + cache_duration = cls.TIME_CACHED + if not isinstance(cache_duration, compat.str_type): + cache_duration_str = 'PT%sS' % cache_duration # Period of Time x Seconds + else: + cache_duration_str = cache_duration + + if contacts is None: + contacts = {} + if organization is None: + organization = {} + + sls = '' + if 'singleLogoutService' in sp and 'url' in sp['singleLogoutService']: + sls = OneLogin_Saml2_Templates.MD_SLS % \ + { + 'binding': sp['singleLogoutService']['binding'], + 'location': sp['singleLogoutService']['url'], + } + + str_authnsign = 'true' if authnsign else 'false' + str_wsign = 'true' if wsign else 'false' + + str_organization = '' + if len(organization) > 0: + organization_names = [] + organization_displaynames = [] + organization_urls = [] + for (lang, info) in organization.items(): + organization_names.append(""" <md:OrganizationName xml:lang="%s">%s</md:OrganizationName>""" % (lang, info['name'])) + organization_displaynames.append(""" <md:OrganizationDisplayName xml:lang="%s">%s</md:OrganizationDisplayName>""" % (lang, info['displayname'])) + organization_urls.append(""" <md:OrganizationURL xml:lang="%s">%s</md:OrganizationURL>""" % (lang, info['url'])) + org_data = '\n'.join(organization_names) + '\n' + '\n'.join(organization_displaynames) + '\n' + '\n'.join(organization_urls) + str_organization = """ <md:Organization>\n%(org)s\n </md:Organization>""" % {'org': org_data} + + str_contacts = '' + if len(contacts) > 0: + contacts_info = [] + for (ctype, info) in contacts.items(): + contact = OneLogin_Saml2_Templates.MD_CONTACT_PERSON % \ + { + 'type': ctype, + 'name': info['givenName'], + 'email': info['emailAddress'], + } + contacts_info.append(contact) + str_contacts = '\n'.join(contacts_info) + + str_attribute_consuming_service = '' + if 'attributeConsumingService' in sp and len(sp['attributeConsumingService']): + attr_cs_desc_str = '' + if "serviceDescription" in sp['attributeConsumingService']: + attr_cs_desc_str = """ <md:ServiceDescription xml:lang="en">%s</md:ServiceDescription> +""" % sp['attributeConsumingService']['serviceDescription'] + + requested_attribute_data = [] + for req_attribs in sp['attributeConsumingService']['requestedAttributes']: + req_attr_nameformat_str = req_attr_friendlyname_str = req_attr_isrequired_str = '' + req_attr_aux_str = ' />' + + if 'nameFormat' in req_attribs.keys() and req_attribs['nameFormat']: + req_attr_nameformat_str = " NameFormat=\"%s\"" % req_attribs['nameFormat'] + if 'friendlyName' in req_attribs.keys() and req_attribs['friendlyName']: + req_attr_friendlyname_str = " FriendlyName=\"%s\"" % req_attribs['friendlyName'] + if 'isRequired' in req_attribs.keys() and req_attribs['isRequired']: + req_attr_isrequired_str = " isRequired=\"%s\"" % 'true' if req_attribs['isRequired'] else 'false' + if 'attributeValue' in req_attribs.keys() and req_attribs['attributeValue']: + if isinstance(req_attribs['attributeValue'], basestring): + req_attribs['attributeValue'] = [req_attribs['attributeValue']] + + req_attr_aux_str = ">" + for attrValue in req_attribs['attributeValue']: + req_attr_aux_str += """ + <saml:AttributeValue xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">%(attributeValue)s</saml:AttributeValue>""" % \ + { + 'attributeValue': attrValue + } + req_attr_aux_str += """ + </md:RequestedAttribute>""" + + requested_attribute = """ <md:RequestedAttribute Name="%(req_attr_name)s"%(req_attr_nameformat_str)s%(req_attr_friendlyname_str)s%(req_attr_isrequired_str)s%(req_attr_aux_str)s""" % \ + { + 'req_attr_name': req_attribs['name'], + 'req_attr_nameformat_str': req_attr_nameformat_str, + 'req_attr_friendlyname_str': req_attr_friendlyname_str, + 'req_attr_isrequired_str': req_attr_isrequired_str, + 'req_attr_aux_str': req_attr_aux_str + } + + requested_attribute_data.append(requested_attribute) + + str_attribute_consuming_service = """ <md:AttributeConsumingService index="%(attribute_consuming_service_index)s"> + <md:ServiceName xml:lang="en">%(service_name)s</md:ServiceName> +%(attr_cs_desc)s%(requested_attribute_str)s + </md:AttributeConsumingService> +""" % \ + { + 'service_name': sp['attributeConsumingService']['serviceName'], + 'attr_cs_desc': attr_cs_desc_str, + 'attribute_consuming_service_index': sp['attributeConsumingService'].get('index', '1'), + 'requested_attribute_str': '\n'.join(requested_attribute_data) + } + + metadata = OneLogin_Saml2_Templates.MD_ENTITY_DESCRIPTOR % \ + { + 'valid': ('validUntil="%s"' % valid_until_str) if valid_until_str else '', + 'cache': ('cacheDuration="%s"' % cache_duration_str) if cache_duration_str else '', + 'entity_id': sp['entityId'], + 'authnsign': str_authnsign, + 'wsign': str_wsign, + 'name_id_format': sp['NameIDFormat'], + 'binding': sp['assertionConsumerService']['binding'], + 'location': sp['assertionConsumerService']['url'], + 'sls': sls, + 'organization': str_organization, + 'contacts': str_contacts, + 'attribute_consuming_service': str_attribute_consuming_service + } + + return metadata
    + +
    [docs] @staticmethod + def sign_metadata(metadata, key, cert, sign_algorithm=OneLogin_Saml2_Constants.RSA_SHA256, digest_algorithm=OneLogin_Saml2_Constants.SHA256): + """ + Signs the metadata with the key/cert provided + + :param metadata: SAML Metadata XML + :type metadata: string + + :param key: x509 key + :type key: string + + :param cert: x509 cert + :type cert: string + + :returns: Signed Metadata + :rtype: string + + :param sign_algorithm: Signature algorithm method + :type sign_algorithm: string + + :param digest_algorithm: Digest algorithm method + :type digest_algorithm: string + """ + return OneLogin_Saml2_Utils.add_sign(metadata, key, cert, False, sign_algorithm, digest_algorithm)
    + + @staticmethod + def _add_x509_key_descriptors(root, cert, signing): + key_descriptor = OneLogin_Saml2_XML.make_child(root, '{%s}KeyDescriptor' % OneLogin_Saml2_Constants.NS_MD) + root.remove(key_descriptor) + root.insert(0, key_descriptor) + key_info = OneLogin_Saml2_XML.make_child(key_descriptor, '{%s}KeyInfo' % OneLogin_Saml2_Constants.NS_DS) + key_data = OneLogin_Saml2_XML.make_child(key_info, '{%s}X509Data' % OneLogin_Saml2_Constants.NS_DS) + + x509_certificate = OneLogin_Saml2_XML.make_child(key_data, '{%s}X509Certificate' % OneLogin_Saml2_Constants.NS_DS) + x509_certificate.text = OneLogin_Saml2_Utils.format_cert(cert, False) + key_descriptor.set('use', ('encryption', 'signing')[signing]) + +
    [docs] @classmethod + def add_x509_key_descriptors(cls, metadata, cert=None, add_encryption=True): + """ + Adds the x509 descriptors (sign/encryption) to the metadata + The same cert will be used for sign/encrypt + + :param metadata: SAML Metadata XML + :type metadata: string + + :param cert: x509 cert + :type cert: string + + :param add_encryption: Determines if the KeyDescriptor[use="encryption"] should be added. + :type add_encryption: boolean + + :returns: Metadata with KeyDescriptors + :rtype: string + """ + if cert is None or cert == '': + return metadata + try: + root = OneLogin_Saml2_XML.to_etree(metadata) + except Exception as e: + raise Exception('Error parsing metadata. ' + str(e)) + + assert root.tag == '{%s}EntityDescriptor' % OneLogin_Saml2_Constants.NS_MD + try: + sp_sso_descriptor = next(root.iterfind('.//md:SPSSODescriptor', namespaces=OneLogin_Saml2_Constants.NSMAP)) + except StopIteration: + raise Exception('Malformed metadata.') + + if add_encryption: + cls._add_x509_key_descriptors(sp_sso_descriptor, cert, False) + cls._add_x509_key_descriptors(sp_sso_descriptor, cert, True) + return OneLogin_Saml2_XML.to_string(root)
    +
    + +
    +
    + +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/docs/saml2/_modules/onelogin/saml2/response.html b/docs/saml2/_modules/onelogin/saml2/response.html new file mode 100644 index 00000000..5babc977 --- /dev/null +++ b/docs/saml2/_modules/onelogin/saml2/response.html @@ -0,0 +1,1054 @@ + + + + + + onelogin.saml2.response — SAML Python2/3 Toolkit 1 documentation + + + + + + + + + + + + + + + +
    + + +
    + +
    +
    +
    + +
    +
    +
    +
    + +

    Source code for onelogin.saml2.response

    +# -*- coding: utf-8 -*-
    +
    +""" OneLogin_Saml2_Response class
    +
    +
    +SAML Response class of SAML Python Toolkit.
    +
    +"""
    +
    +from copy import deepcopy
    +from onelogin.saml2.constants import OneLogin_Saml2_Constants
    +from onelogin.saml2.utils import OneLogin_Saml2_Utils, OneLogin_Saml2_Error, OneLogin_Saml2_ValidationError, return_false_on_exception
    +from onelogin.saml2.xml_utils import OneLogin_Saml2_XML
    +
    +
    +
    [docs]class OneLogin_Saml2_Response(object): + """ + + This class handles a SAML Response. It parses or validates + a Logout Response object. + + """ + + def __init__(self, settings, response): + """ + Constructs the response object. + + :param settings: The setting info + :type settings: OneLogin_Saml2_Setting object + + :param response: The base64 encoded, XML string containing the samlp:Response + :type response: string + """ + self._settings = settings + self._error = None + self.response = OneLogin_Saml2_Utils.b64decode(response) + self.document = OneLogin_Saml2_XML.to_etree(self.response) + self.decrypted_document = None + self.encrypted = None + self.valid_scd_not_on_or_after = None + + # Quick check for the presence of EncryptedAssertion + encrypted_assertion_nodes = self._query('/samlp:Response/saml:EncryptedAssertion') + if encrypted_assertion_nodes: + decrypted_document = deepcopy(self.document) + self.encrypted = True + self.decrypted_document = self._decrypt_assertion(decrypted_document) + +
    [docs] def is_valid(self, request_data, request_id=None, raise_exceptions=False): + """ + Validates the response object. + + :param request_data: Request Data + :type request_data: dict + + :param request_id: Optional argument. The ID of the AuthNRequest sent by this SP to the IdP + :type request_id: string + + :param raise_exceptions: Whether to return false on failure or raise an exception + :type raise_exceptions: Boolean + + :returns: True if the SAML Response is valid, False if not + :rtype: bool + """ + self._error = None + try: + # Checks SAML version + if self.document.get('Version', None) != '2.0': + raise OneLogin_Saml2_ValidationError( + 'Unsupported SAML version', + OneLogin_Saml2_ValidationError.UNSUPPORTED_SAML_VERSION + ) + + # Checks that ID exists + if self.document.get('ID', None) is None: + raise OneLogin_Saml2_ValidationError( + 'Missing ID attribute on SAML Response', + OneLogin_Saml2_ValidationError.MISSING_ID + ) + + # Checks that the response has the SUCCESS status + self.check_status() + + # Checks that the response only has one assertion + if not self.validate_num_assertions(): + raise OneLogin_Saml2_ValidationError( + 'SAML Response must contain 1 assertion', + OneLogin_Saml2_ValidationError.WRONG_NUMBER_OF_ASSERTIONS + ) + + idp_data = self._settings.get_idp_data() + idp_entity_id = idp_data['entityId'] + sp_data = self._settings.get_sp_data() + sp_entity_id = sp_data['entityId'] + + signed_elements = self.process_signed_elements() + + has_signed_response = '{%s}Response' % OneLogin_Saml2_Constants.NS_SAMLP in signed_elements + has_signed_assertion = '{%s}Assertion' % OneLogin_Saml2_Constants.NS_SAML in signed_elements + + if self._settings.is_strict(): + no_valid_xml_msg = 'Invalid SAML Response. Not match the saml-schema-protocol-2.0.xsd' + res = OneLogin_Saml2_XML.validate_xml(self.document, 'saml-schema-protocol-2.0.xsd', self._settings.is_debug_active()) + if isinstance(res, str): + raise OneLogin_Saml2_ValidationError( + no_valid_xml_msg, + OneLogin_Saml2_ValidationError.INVALID_XML_FORMAT + ) + + # If encrypted, check also the decrypted document + if self.encrypted: + res = OneLogin_Saml2_XML.validate_xml(self.decrypted_document, 'saml-schema-protocol-2.0.xsd', self._settings.is_debug_active()) + if isinstance(res, str): + raise OneLogin_Saml2_ValidationError( + no_valid_xml_msg, + OneLogin_Saml2_ValidationError.INVALID_XML_FORMAT + ) + + security = self._settings.get_security_data() + current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data) + + # Check if the InResponseTo of the Response matchs the ID of the AuthNRequest (requestId) if provided + in_response_to = self.get_in_response_to() + if in_response_to is not None and request_id is not None: + if in_response_to != request_id: + raise OneLogin_Saml2_ValidationError( + 'The InResponseTo of the Response: %s, does not match the ID of the AuthNRequest sent by the SP: %s' % (in_response_to, request_id), + OneLogin_Saml2_ValidationError.WRONG_INRESPONSETO + ) + + if not self.encrypted and security['wantAssertionsEncrypted']: + raise OneLogin_Saml2_ValidationError( + 'The assertion of the Response is not encrypted and the SP require it', + OneLogin_Saml2_ValidationError.NO_ENCRYPTED_ASSERTION + ) + + if security['wantNameIdEncrypted']: + encrypted_nameid_nodes = self._query_assertion('/saml:Subject/saml:EncryptedID/xenc:EncryptedData') + if len(encrypted_nameid_nodes) != 1: + raise OneLogin_Saml2_ValidationError( + 'The NameID of the Response is not encrypted and the SP require it', + OneLogin_Saml2_ValidationError.NO_ENCRYPTED_NAMEID + ) + + # Checks that a Conditions element exists + if not self.check_one_condition(): + raise OneLogin_Saml2_ValidationError( + 'The Assertion must include a Conditions element', + OneLogin_Saml2_ValidationError.MISSING_CONDITIONS + ) + + # Validates Assertion timestamps + self.validate_timestamps(raise_exceptions=True) + + # Checks that an AuthnStatement element exists and is unique + if not self.check_one_authnstatement(): + raise OneLogin_Saml2_ValidationError( + 'The Assertion must include an AuthnStatement element', + OneLogin_Saml2_ValidationError.WRONG_NUMBER_OF_AUTHSTATEMENTS + ) + + # Checks that the response has all of the AuthnContexts that we provided in the request. + # Only check if failOnAuthnContextMismatch is true and requestedAuthnContext is set to a list. + requested_authn_contexts = security['requestedAuthnContext'] + if security['failOnAuthnContextMismatch'] and requested_authn_contexts and requested_authn_contexts is not True: + authn_contexts = self.get_authn_contexts() + unmatched_contexts = set(authn_contexts).difference(requested_authn_contexts) + if unmatched_contexts: + raise OneLogin_Saml2_ValidationError( + 'The AuthnContext "%s" was not a requested context "%s"' % (', '.join(unmatched_contexts), ', '.join(requested_authn_contexts)), + OneLogin_Saml2_ValidationError.AUTHN_CONTEXT_MISMATCH + ) + + # Checks that there is at least one AttributeStatement if required + attribute_statement_nodes = self._query_assertion('/saml:AttributeStatement') + if security.get('wantAttributeStatement', True) and not attribute_statement_nodes: + raise OneLogin_Saml2_ValidationError( + 'There is no AttributeStatement on the Response', + OneLogin_Saml2_ValidationError.NO_ATTRIBUTESTATEMENT + ) + + encrypted_attributes_nodes = self._query_assertion('/saml:AttributeStatement/saml:EncryptedAttribute') + if encrypted_attributes_nodes: + raise OneLogin_Saml2_ValidationError( + 'There is an EncryptedAttribute in the Response and this SP not support them', + OneLogin_Saml2_ValidationError.ENCRYPTED_ATTRIBUTES + ) + + # Checks destination + destination = self.document.get('Destination', None) + if destination: + if not OneLogin_Saml2_Utils.normalize_url(url=destination).startswith(OneLogin_Saml2_Utils.normalize_url(url=current_url)): + # TODO: Review if following lines are required, since we can control the + # request_data + # current_url_routed = OneLogin_Saml2_Utils.get_self_routed_url_no_query(request_data) + # if not destination.startswith(current_url_routed): + raise OneLogin_Saml2_ValidationError( + 'The response was received at %s instead of %s' % (current_url, destination), + OneLogin_Saml2_ValidationError.WRONG_DESTINATION + ) + elif destination == '': + raise OneLogin_Saml2_ValidationError( + 'The response has an empty Destination value', + OneLogin_Saml2_ValidationError.EMPTY_DESTINATION + ) + # Checks audience + valid_audiences = self.get_audiences() + if valid_audiences and sp_entity_id not in valid_audiences: + raise OneLogin_Saml2_ValidationError( + '%s is not a valid audience for this Response' % sp_entity_id, + OneLogin_Saml2_ValidationError.WRONG_AUDIENCE + ) + + # Checks the issuers + issuers = self.get_issuers() + for issuer in issuers: + if issuer is None or issuer != idp_entity_id: + raise OneLogin_Saml2_ValidationError( + 'Invalid issuer in the Assertion/Response (expected %(idpEntityId)s, got %(issuer)s)' % + { + 'idpEntityId': idp_entity_id, + 'issuer': issuer + }, + OneLogin_Saml2_ValidationError.WRONG_ISSUER + ) + + # Checks the session Expiration + session_expiration = self.get_session_not_on_or_after() + if session_expiration and session_expiration <= OneLogin_Saml2_Utils.now(): + raise OneLogin_Saml2_ValidationError( + 'The attributes have expired, based on the SessionNotOnOrAfter of the AttributeStatement of this Response', + OneLogin_Saml2_ValidationError.SESSION_EXPIRED + ) + + # Checks the SubjectConfirmation, at least one SubjectConfirmation must be valid + any_subject_confirmation = False + subject_confirmation_nodes = self._query_assertion('/saml:Subject/saml:SubjectConfirmation') + + for scn in subject_confirmation_nodes: + method = scn.get('Method', None) + if method and method != OneLogin_Saml2_Constants.CM_BEARER: + continue + sc_data = scn.find('saml:SubjectConfirmationData', namespaces=OneLogin_Saml2_Constants.NSMAP) + if sc_data is None: + continue + else: + irt = sc_data.get('InResponseTo', None) + if in_response_to and irt and irt != in_response_to: + continue + recipient = sc_data.get('Recipient', None) + if recipient and current_url not in recipient: + continue + nooa = sc_data.get('NotOnOrAfter', None) + if nooa: + parsed_nooa = OneLogin_Saml2_Utils.parse_SAML_to_time(nooa) + if parsed_nooa <= OneLogin_Saml2_Utils.now(): + continue + nb = sc_data.get('NotBefore', None) + if nb: + parsed_nb = OneLogin_Saml2_Utils.parse_SAML_to_time(nb) + if parsed_nb > OneLogin_Saml2_Utils.now(): + continue + + if nooa: + self.valid_scd_not_on_or_after = OneLogin_Saml2_Utils.parse_SAML_to_time(nooa) + + any_subject_confirmation = True + break + + if not any_subject_confirmation: + raise OneLogin_Saml2_ValidationError( + 'A valid SubjectConfirmation was not found on this Response', + OneLogin_Saml2_ValidationError.WRONG_SUBJECTCONFIRMATION + ) + + if security['wantAssertionsSigned'] and not has_signed_assertion: + raise OneLogin_Saml2_ValidationError( + 'The Assertion of the Response is not signed and the SP require it', + OneLogin_Saml2_ValidationError.NO_SIGNED_ASSERTION + ) + + if security['wantMessagesSigned'] and not has_signed_response: + raise OneLogin_Saml2_ValidationError( + 'The Message of the Response is not signed and the SP require it', + OneLogin_Saml2_ValidationError.NO_SIGNED_MESSAGE + ) + + if not signed_elements or (not has_signed_response and not has_signed_assertion): + raise OneLogin_Saml2_ValidationError( + 'No Signature found. SAML Response rejected', + OneLogin_Saml2_ValidationError.NO_SIGNATURE_FOUND + ) + else: + cert = self._settings.get_idp_cert() + fingerprint = idp_data.get('certFingerprint', None) + if fingerprint: + fingerprint = OneLogin_Saml2_Utils.format_finger_print(fingerprint) + fingerprintalg = idp_data.get('certFingerprintAlgorithm', None) + + multicerts = None + if 'x509certMulti' in idp_data and 'signing' in idp_data['x509certMulti'] and idp_data['x509certMulti']['signing']: + multicerts = idp_data['x509certMulti']['signing'] + + # If find a Signature on the Response, validates it checking the original response + if has_signed_response and not OneLogin_Saml2_Utils.validate_sign(self.document, cert, fingerprint, fingerprintalg, xpath=OneLogin_Saml2_Utils.RESPONSE_SIGNATURE_XPATH, multicerts=multicerts, raise_exceptions=False): + raise OneLogin_Saml2_ValidationError( + 'Signature validation failed. SAML Response rejected', + OneLogin_Saml2_ValidationError.INVALID_SIGNATURE + ) + + document_check_assertion = self.decrypted_document if self.encrypted else self.document + if has_signed_assertion and not OneLogin_Saml2_Utils.validate_sign(document_check_assertion, cert, fingerprint, fingerprintalg, xpath=OneLogin_Saml2_Utils.ASSERTION_SIGNATURE_XPATH, multicerts=multicerts, raise_exceptions=False): + raise OneLogin_Saml2_ValidationError( + 'Signature validation failed. SAML Response rejected', + OneLogin_Saml2_ValidationError.INVALID_SIGNATURE + ) + + return True + except Exception as err: + self._error = str(err) + debug = self._settings.is_debug_active() + if debug: + print(err) + if raise_exceptions: + raise + return False
    + +
    [docs] def check_status(self): + """ + Check if the status of the response is success or not + + :raises: Exception. If the status is not success + """ + status = OneLogin_Saml2_Utils.get_status(self.document) + code = status.get('code', None) + if code and code != OneLogin_Saml2_Constants.STATUS_SUCCESS: + splited_code = code.split(':') + printable_code = splited_code.pop() + status_exception_msg = 'The status code of the Response was not Success, was %s' % printable_code + status_msg = status.get('msg', None) + if status_msg: + status_exception_msg += ' -> ' + status_msg + raise OneLogin_Saml2_ValidationError( + status_exception_msg, + OneLogin_Saml2_ValidationError.STATUS_CODE_IS_NOT_SUCCESS + )
    + +
    [docs] def check_one_condition(self): + """ + Checks that the samlp:Response/saml:Assertion/saml:Conditions element exists and is unique. + """ + condition_nodes = self._query_assertion('/saml:Conditions') + if len(condition_nodes) == 1: + return True + else: + return False
    + +
    [docs] def check_one_authnstatement(self): + """ + Checks that the samlp:Response/saml:Assertion/saml:AuthnStatement element exists and is unique. + """ + authnstatement_nodes = self._query_assertion('/saml:AuthnStatement') + if len(authnstatement_nodes) == 1: + return True + else: + return False
    + +
    [docs] def get_audiences(self): + """ + Gets the audiences + + :returns: The valid audiences for the SAML Response + :rtype: list + """ + audience_nodes = self._query_assertion('/saml:Conditions/saml:AudienceRestriction/saml:Audience') + return [OneLogin_Saml2_XML.element_text(node) for node in audience_nodes if OneLogin_Saml2_XML.element_text(node) is not None]
    + +
    [docs] def get_authn_contexts(self): + """ + Gets the authentication contexts + + :returns: The authentication classes for the SAML Response + :rtype: list + """ + authn_context_nodes = self._query_assertion('/saml:AuthnStatement/saml:AuthnContext/saml:AuthnContextClassRef') + return [OneLogin_Saml2_XML.element_text(node) for node in authn_context_nodes]
    + +
    [docs] def get_in_response_to(self): + """ + Gets the ID of the request which this response is in response to + :returns: ID of AuthNRequest this Response is in response to or None if it is not present + :rtype: str + """ + return self.document.get('InResponseTo')
    + +
    [docs] def get_issuers(self): + """ + Gets the issuers (from message and from assertion) + + :returns: The issuers + :rtype: list + """ + issuers = set() + + message_issuer_nodes = OneLogin_Saml2_XML.query(self.document, '/samlp:Response/saml:Issuer') + if len(message_issuer_nodes) > 0: + if len(message_issuer_nodes) == 1: + issuer_value = OneLogin_Saml2_XML.element_text(message_issuer_nodes[0]) + if issuer_value: + issuers.add(issuer_value) + else: + raise OneLogin_Saml2_ValidationError( + 'Issuer of the Response is multiple.', + OneLogin_Saml2_ValidationError.ISSUER_MULTIPLE_IN_RESPONSE + ) + + assertion_issuer_nodes = self._query_assertion('/saml:Issuer') + if len(assertion_issuer_nodes) == 1: + issuer_value = OneLogin_Saml2_XML.element_text(assertion_issuer_nodes[0]) + if issuer_value: + issuers.add(issuer_value) + else: + raise OneLogin_Saml2_ValidationError( + 'Issuer of the Assertion not found or multiple.', + OneLogin_Saml2_ValidationError.ISSUER_NOT_FOUND_IN_ASSERTION + ) + + return list(set(issuers))
    + +
    [docs] def get_nameid_data(self): + """ + Gets the NameID Data provided by the SAML Response from the IdP + + :returns: Name ID Data (Value, Format, NameQualifier, SPNameQualifier) + :rtype: dict + """ + nameid = None + nameid_data = {} + + encrypted_id_data_nodes = self._query_assertion('/saml:Subject/saml:EncryptedID/xenc:EncryptedData') + if encrypted_id_data_nodes: + encrypted_data = encrypted_id_data_nodes[0] + key = self._settings.get_sp_key() + nameid = OneLogin_Saml2_Utils.decrypt_element(encrypted_data, key) + else: + nameid_nodes = self._query_assertion('/saml:Subject/saml:NameID') + if nameid_nodes: + nameid = nameid_nodes[0] + + is_strict = self._settings.is_strict() + want_nameid = self._settings.get_security_data().get('wantNameId', True) + if nameid is None: + if is_strict and want_nameid: + raise OneLogin_Saml2_ValidationError( + 'NameID not found in the assertion of the Response', + OneLogin_Saml2_ValidationError.NO_NAMEID + ) + else: + if is_strict and want_nameid and not OneLogin_Saml2_XML.element_text(nameid): + raise OneLogin_Saml2_ValidationError( + 'An empty NameID value found', + OneLogin_Saml2_ValidationError.EMPTY_NAMEID + ) + + nameid_data = {'Value': OneLogin_Saml2_XML.element_text(nameid)} + for attr in ['Format', 'SPNameQualifier', 'NameQualifier']: + value = nameid.get(attr, None) + if value: + if is_strict and attr == 'SPNameQualifier': + sp_data = self._settings.get_sp_data() + sp_entity_id = sp_data.get('entityId', '') + if sp_entity_id != value: + raise OneLogin_Saml2_ValidationError( + 'The SPNameQualifier value mistmatch the SP entityID value.', + OneLogin_Saml2_ValidationError.SP_NAME_QUALIFIER_NAME_MISMATCH + ) + + nameid_data[attr] = value + return nameid_data
    + +
    [docs] def get_nameid(self): + """ + Gets the NameID provided by the SAML Response from the IdP + + :returns: NameID (value) + :rtype: string|None + """ + nameid_value = None + nameid_data = self.get_nameid_data() + if nameid_data and 'Value' in nameid_data.keys(): + nameid_value = nameid_data['Value'] + return nameid_value
    + +
    [docs] def get_nameid_format(self): + """ + Gets the NameID Format provided by the SAML Response from the IdP + + :returns: NameID Format + :rtype: string|None + """ + nameid_format = None + nameid_data = self.get_nameid_data() + if nameid_data and 'Format' in nameid_data.keys(): + nameid_format = nameid_data['Format'] + return nameid_format
    + +
    [docs] def get_nameid_nq(self): + """ + Gets the NameID NameQualifier provided by the SAML Response from the IdP + + :returns: NameID NameQualifier + :rtype: string|None + """ + nameid_nq = None + nameid_data = self.get_nameid_data() + if nameid_data and 'NameQualifier' in nameid_data.keys(): + nameid_nq = nameid_data['NameQualifier'] + return nameid_nq
    + +
    [docs] def get_nameid_spnq(self): + """ + Gets the NameID SP NameQualifier provided by the SAML response from the IdP. + + :returns: NameID SP NameQualifier + :rtype: string|None + """ + nameid_spnq = None + nameid_data = self.get_nameid_data() + if nameid_data and 'SPNameQualifier' in nameid_data.keys(): + nameid_spnq = nameid_data['SPNameQualifier'] + return nameid_spnq
    + +
    [docs] def get_session_not_on_or_after(self): + """ + Gets the SessionNotOnOrAfter from the AuthnStatement + Could be used to set the local session expiration + + :returns: The SessionNotOnOrAfter value + :rtype: time|None + """ + not_on_or_after = None + authn_statement_nodes = self._query_assertion('/saml:AuthnStatement[@SessionNotOnOrAfter]') + if authn_statement_nodes: + not_on_or_after = OneLogin_Saml2_Utils.parse_SAML_to_time(authn_statement_nodes[0].get('SessionNotOnOrAfter')) + return not_on_or_after
    + +
    [docs] def get_assertion_not_on_or_after(self): + """ + Returns the NotOnOrAfter value of the valid SubjectConfirmationData node if any + """ + return self.valid_scd_not_on_or_after
    + +
    [docs] def get_session_index(self): + """ + Gets the SessionIndex from the AuthnStatement + Could be used to be stored in the local session in order + to be used in a future Logout Request that the SP could + send to the SP, to set what specific session must be deleted + + :returns: The SessionIndex value + :rtype: string|None + """ + session_index = None + authn_statement_nodes = self._query_assertion('/saml:AuthnStatement[@SessionIndex]') + if authn_statement_nodes: + session_index = authn_statement_nodes[0].get('SessionIndex') + return session_index
    + +
    [docs] def get_attributes(self): + """ + Gets the Attributes from the AttributeStatement element. + EncryptedAttributes are not supported + """ + return self._get_attributes('Name')
    + +
    [docs] def get_friendlyname_attributes(self): + """ + Gets the Attributes from the AttributeStatement element indexed by FiendlyName. + EncryptedAttributes are not supported + """ + return self._get_attributes('FriendlyName')
    + + def _get_attributes(self, attr_name): + allow_duplicates = self._settings.get_security_data().get('allowRepeatAttributeName', False) + attributes = {} + attribute_nodes = self._query_assertion('/saml:AttributeStatement/saml:Attribute') + for attribute_node in attribute_nodes: + attr_key = attribute_node.get(attr_name) + if attr_key: + if not allow_duplicates and attr_key in attributes: + raise OneLogin_Saml2_ValidationError( + 'Found an Attribute element with duplicated ' + attr_name, + OneLogin_Saml2_ValidationError.DUPLICATED_ATTRIBUTE_NAME_FOUND + ) + + values = [] + for attr in attribute_node.iterchildren('{%s}AttributeValue' % OneLogin_Saml2_Constants.NSMAP['saml']): + attr_text = OneLogin_Saml2_XML.element_text(attr) + if attr_text: + attr_text = attr_text.strip() + if attr_text: + values.append(attr_text) + + # Parse any nested NameID children + for nameid in attr.iterchildren('{%s}NameID' % OneLogin_Saml2_Constants.NSMAP['saml']): + values.append({ + 'NameID': { + 'Format': nameid.get('Format'), + 'NameQualifier': nameid.get('NameQualifier'), + 'value': nameid.text + } + }) + if attr_key in attributes: + attributes[attr_key].extend(values) + else: + attributes[attr_key] = values + return attributes + +
    [docs] def validate_num_assertions(self): + """ + Verifies that the document only contains a single Assertion (encrypted or not) + + :returns: True if only 1 assertion encrypted or not + :rtype: bool + """ + encrypted_assertion_nodes = OneLogin_Saml2_XML.query(self.document, '//saml:EncryptedAssertion') + assertion_nodes = OneLogin_Saml2_XML.query(self.document, '//saml:Assertion') + + valid = len(encrypted_assertion_nodes) + len(assertion_nodes) == 1 + + if (self.encrypted): + assertion_nodes = OneLogin_Saml2_XML.query(self.decrypted_document, '//saml:Assertion') + valid = valid and len(assertion_nodes) == 1 + + return valid
    + +
    [docs] def process_signed_elements(self): + """ + Verifies the signature nodes: + - Checks that are Response or Assertion + - Check that IDs and reference URI are unique and consistent. + + :returns: The signed elements tag names + :rtype: list + """ + sign_nodes = self._query('//ds:Signature') + + signed_elements = [] + verified_seis = [] + verified_ids = [] + response_tag = '{%s}Response' % OneLogin_Saml2_Constants.NS_SAMLP + assertion_tag = '{%s}Assertion' % OneLogin_Saml2_Constants.NS_SAML + + security = self._settings.get_security_data() + reject_deprecated_alg = security.get('rejectDeprecatedAlgorithm', False) + + for sign_node in sign_nodes: + signed_element = sign_node.getparent().tag + if signed_element != response_tag and signed_element != assertion_tag: + raise OneLogin_Saml2_ValidationError( + 'Invalid Signature Element %s SAML Response rejected' % signed_element, + OneLogin_Saml2_ValidationError.WRONG_SIGNED_ELEMENT + ) + + if not sign_node.getparent().get('ID'): + raise OneLogin_Saml2_ValidationError( + 'Signed Element must contain an ID. SAML Response rejected', + OneLogin_Saml2_ValidationError.ID_NOT_FOUND_IN_SIGNED_ELEMENT + ) + + id_value = sign_node.getparent().get('ID') + if id_value in verified_ids: + raise OneLogin_Saml2_ValidationError( + 'Duplicated ID. SAML Response rejected', + OneLogin_Saml2_ValidationError.DUPLICATED_ID_IN_SIGNED_ELEMENTS + ) + verified_ids.append(id_value) + + # Check that reference URI matches the parent ID and no duplicate References or IDs + ref = OneLogin_Saml2_XML.query(sign_node, './/ds:Reference') + if ref: + ref = ref[0] + if ref.get('URI'): + sei = ref.get('URI')[1:] + + if sei != id_value: + raise OneLogin_Saml2_ValidationError( + 'Found an invalid Signed Element. SAML Response rejected', + OneLogin_Saml2_ValidationError.INVALID_SIGNED_ELEMENT + ) + + if sei in verified_seis: + raise OneLogin_Saml2_ValidationError( + 'Duplicated Reference URI. SAML Response rejected', + OneLogin_Saml2_ValidationError.DUPLICATED_REFERENCE_IN_SIGNED_ELEMENTS + ) + verified_seis.append(sei) + + # Check the signature and digest algorithm + if reject_deprecated_alg: + sig_method_node = OneLogin_Saml2_XML.query(sign_node, './/ds:SignatureMethod') + if sig_method_node: + sig_method = sig_method_node[0].get("Algorithm") + if sig_method in OneLogin_Saml2_Constants.DEPRECATED_ALGORITHMS: + raise OneLogin_Saml2_ValidationError( + 'Deprecated signature algorithm found: %s' % sig_method, + OneLogin_Saml2_ValidationError.DEPRECATED_SIGNATURE_METHOD + ) + + dig_method_node = OneLogin_Saml2_XML.query(sign_node, './/ds:DigestMethod') + if dig_method_node: + dig_method = dig_method_node[0].get("Algorithm") + if dig_method in OneLogin_Saml2_Constants.DEPRECATED_ALGORITHMS: + raise OneLogin_Saml2_ValidationError( + 'Deprecated digest algorithm found: %s' % dig_method, + OneLogin_Saml2_ValidationError.DEPRECATED_DIGEST_METHOD + ) + + signed_elements.append(signed_element) + + if signed_elements: + if not self.validate_signed_elements(signed_elements, raise_exceptions=True): + raise OneLogin_Saml2_ValidationError( + 'Found an unexpected Signature Element. SAML Response rejected', + OneLogin_Saml2_ValidationError.UNEXPECTED_SIGNED_ELEMENTS + ) + return signed_elements
    + +
    [docs] @return_false_on_exception + def validate_signed_elements(self, signed_elements): + """ + Verifies that the document has the expected signed nodes. + + :param signed_elements: The signed elements to be checked + :type signed_elements: list + :param raise_exceptions: Whether to return false on failure or raise an exception + :type raise_exceptions: Boolean + """ + if len(signed_elements) > 2: + return False + + response_tag = '{%s}Response' % OneLogin_Saml2_Constants.NS_SAMLP + assertion_tag = '{%s}Assertion' % OneLogin_Saml2_Constants.NS_SAML + + if (response_tag in signed_elements and signed_elements.count(response_tag) > 1) or \ + (assertion_tag in signed_elements and signed_elements.count(assertion_tag) > 1) or \ + (response_tag not in signed_elements and assertion_tag not in signed_elements): + return False + + # Check that the signed elements found here, are the ones that will be verified + # by OneLogin_Saml2_Utils.validate_sign + if response_tag in signed_elements: + expected_signature_nodes = OneLogin_Saml2_XML.query(self.document, OneLogin_Saml2_Utils.RESPONSE_SIGNATURE_XPATH) + if len(expected_signature_nodes) != 1: + raise OneLogin_Saml2_ValidationError( + 'Unexpected number of Response signatures found. SAML Response rejected.', + OneLogin_Saml2_ValidationError.WRONG_NUMBER_OF_SIGNATURES_IN_RESPONSE + ) + + if assertion_tag in signed_elements: + expected_signature_nodes = self._query(OneLogin_Saml2_Utils.ASSERTION_SIGNATURE_XPATH) + if len(expected_signature_nodes) != 1: + raise OneLogin_Saml2_ValidationError( + 'Unexpected number of Assertion signatures found. SAML Response rejected.', + OneLogin_Saml2_ValidationError.WRONG_NUMBER_OF_SIGNATURES_IN_ASSERTION + ) + + return True
    + +
    [docs] @return_false_on_exception + def validate_timestamps(self): + """ + Verifies that the document is valid according to Conditions Element + + :returns: True if the condition is valid, False otherwise + :rtype: bool + """ + conditions_nodes = self._query_assertion('/saml:Conditions') + + for conditions_node in conditions_nodes: + nb_attr = conditions_node.get('NotBefore') + nooa_attr = conditions_node.get('NotOnOrAfter') + if nb_attr and OneLogin_Saml2_Utils.parse_SAML_to_time(nb_attr) > OneLogin_Saml2_Utils.now() + OneLogin_Saml2_Constants.ALLOWED_CLOCK_DRIFT: + raise OneLogin_Saml2_ValidationError( + 'Could not validate timestamp: not yet valid. Check system clock.', + OneLogin_Saml2_ValidationError.ASSERTION_TOO_EARLY + ) + if nooa_attr and OneLogin_Saml2_Utils.parse_SAML_to_time(nooa_attr) + OneLogin_Saml2_Constants.ALLOWED_CLOCK_DRIFT <= OneLogin_Saml2_Utils.now(): + raise OneLogin_Saml2_ValidationError( + 'Could not validate timestamp: expired. Check system clock.', + OneLogin_Saml2_ValidationError.ASSERTION_EXPIRED + ) + return True
    + + def _query_assertion(self, xpath_expr): + """ + Extracts nodes that match the query from the Assertion + + :param xpath_expr: Xpath Expresion + :type xpath_expr: String + + :returns: The queried nodes + :rtype: list + """ + + assertion_expr = '/saml:Assertion' + signature_expr = '/ds:Signature/ds:SignedInfo/ds:Reference' + signed_assertion_query = '/samlp:Response' + assertion_expr + signature_expr + assertion_reference_nodes = self._query(signed_assertion_query) + tagid = None + + if not assertion_reference_nodes: + # Check if the message is signed + signed_message_query = '/samlp:Response' + signature_expr + message_reference_nodes = self._query(signed_message_query) + if message_reference_nodes: + message_id = message_reference_nodes[0].get('URI') + final_query = "/samlp:Response[@ID=$tagid]/" + tagid = message_id[1:] + else: + final_query = "/samlp:Response" + final_query += assertion_expr + else: + assertion_id = assertion_reference_nodes[0].get('URI') + final_query = '/samlp:Response' + assertion_expr + "[@ID=$tagid]" + tagid = assertion_id[1:] + final_query += xpath_expr + return self._query(final_query, tagid) + + def _query(self, query, tagid=None): + """ + Extracts nodes that match the query from the Response + + :param query: Xpath Expresion + :type query: String + + :param tagid: Tag ID + :type query: String + + :returns: The queried nodes + :rtype: list + """ + if self.encrypted: + document = self.decrypted_document + else: + document = self.document + return OneLogin_Saml2_XML.query(document, query, None, tagid) + + def _decrypt_assertion(self, xml): + """ + Decrypts the Assertion + + :raises: Exception if no private key available + :param xml: Encrypted Assertion + :type xml: Element + :returns: Decrypted Assertion + :rtype: Element + """ + key = self._settings.get_sp_key() + debug = self._settings.is_debug_active() + + if not key: + raise OneLogin_Saml2_Error( + 'No private key available to decrypt the assertion, check settings', + OneLogin_Saml2_Error.PRIVATE_KEY_NOT_FOUND + ) + + encrypted_assertion_nodes = OneLogin_Saml2_XML.query(xml, '/samlp:Response/saml:EncryptedAssertion') + if encrypted_assertion_nodes: + encrypted_data_nodes = OneLogin_Saml2_XML.query(encrypted_assertion_nodes[0], '//saml:EncryptedAssertion/xenc:EncryptedData') + if encrypted_data_nodes: + keyinfo = OneLogin_Saml2_XML.query(encrypted_assertion_nodes[0], '//saml:EncryptedAssertion/xenc:EncryptedData/ds:KeyInfo') + if not keyinfo: + raise OneLogin_Saml2_ValidationError( + 'No KeyInfo present, invalid Assertion', + OneLogin_Saml2_ValidationError.KEYINFO_NOT_FOUND_IN_ENCRYPTED_DATA + ) + keyinfo = keyinfo[0] + children = keyinfo.getchildren() + if not children: + raise OneLogin_Saml2_ValidationError( + 'KeyInfo has no children nodes, invalid Assertion', + OneLogin_Saml2_ValidationError.CHILDREN_NODE_NOT_FOUND_IN_KEYINFO + ) + for child in children: + if 'RetrievalMethod' in child.tag: + if child.attrib['Type'] != 'http://www.w3.org/2001/04/xmlenc#EncryptedKey': + raise OneLogin_Saml2_ValidationError( + 'Unsupported Retrieval Method found', + OneLogin_Saml2_ValidationError.UNSUPPORTED_RETRIEVAL_METHOD + ) + uri = child.attrib['URI'] + if not uri.startswith('#'): + break + uri = uri.split('#')[1] + encrypted_key = OneLogin_Saml2_XML.query(encrypted_assertion_nodes[0], './xenc:EncryptedKey[@Id=$tagid]', None, uri) + if encrypted_key: + keyinfo.append(encrypted_key[0]) + + encrypted_data = encrypted_data_nodes[0] + decrypted = OneLogin_Saml2_Utils.decrypt_element(encrypted_data, key, debug=debug, inplace=True) + xml.replace(encrypted_assertion_nodes[0], decrypted) + return xml + +
    [docs] def get_error(self): + """ + After executing a validation process, if it fails this method returns the cause + """ + return self._error
    + +
    [docs] def get_xml_document(self): + """ + Returns the SAML Response document (If contains an encrypted assertion, decrypts it) + + :return: Decrypted XML response document + :rtype: DOMDocument + """ + if self.encrypted: + return self.decrypted_document + else: + return self.document
    + +
    [docs] def get_id(self): + """ + :returns: the ID of the response + :rtype: string + """ + return self.document.get('ID', None)
    + +
    [docs] def get_assertion_id(self): + """ + :returns: the ID of the assertion in the response + :rtype: string + """ + if not self.validate_num_assertions(): + raise OneLogin_Saml2_ValidationError( + 'SAML Response must contain 1 assertion', + OneLogin_Saml2_ValidationError.WRONG_NUMBER_OF_ASSERTIONS + ) + return self._query_assertion('')[0].get('ID', None)
    + +
    [docs] def get_assertion_issue_instant(self): + """ + :returns: the IssueInstant of the assertion in the response + :rtype: unix/posix timestamp|None + """ + if not self.validate_num_assertions(): + raise OneLogin_Saml2_ValidationError( + 'SAML Response must contain 1 assertion', + OneLogin_Saml2_ValidationError.WRONG_NUMBER_OF_ASSERTIONS + ) + issue_instant = self._query_assertion('')[0].get('IssueInstant', None) + return OneLogin_Saml2_Utils.parse_SAML_to_time(issue_instant)
    +
    + +
    +
    + +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/docs/saml2/_modules/onelogin/saml2/settings.html b/docs/saml2/_modules/onelogin/saml2/settings.html new file mode 100644 index 00000000..70185c17 --- /dev/null +++ b/docs/saml2/_modules/onelogin/saml2/settings.html @@ -0,0 +1,961 @@ + + + + + + onelogin.saml2.settings — SAML Python2/3 Toolkit 1 documentation + + + + + + + + + + + + + + + +
    + + +
    + +
    +
    +
    + +
    +
    +
    +
    + +

    Source code for onelogin.saml2.settings

    +# -*- coding: utf-8 -*-
    +
    +""" OneLogin_Saml2_Settings class
    +
    +Copyright (c) 2010-2021 OneLogin, Inc.
    +MIT License
    +
    +Setting class of OneLogin's Python Toolkit.
    +
    +"""
    +from time import time
    +import re
    +from os.path import dirname, exists, join, sep
    +
    +from onelogin.saml2 import compat
    +from onelogin.saml2.constants import OneLogin_Saml2_Constants
    +from onelogin.saml2.errors import OneLogin_Saml2_Error
    +from onelogin.saml2.metadata import OneLogin_Saml2_Metadata
    +from onelogin.saml2.utils import OneLogin_Saml2_Utils
    +from onelogin.saml2.xml_utils import OneLogin_Saml2_XML
    +
    +try:
    +    import ujson as json
    +except ImportError:
    +    import json
    +
    +try:
    +    basestring
    +except NameError:
    +    basestring = str
    +
    +# Regex from Django Software Foundation and individual contributors.
    +# Released under a BSD 3-Clause License
    +url_regex = re.compile(
    +    r'^(?:[a-z0-9\.\-]*)://'  # scheme is validated separately
    +    r'(?:(?:[A-Z0-9_](?:[A-Z0-9-_]{0,61}[A-Z0-9_])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|'  # domain...
    +    r'localhost|'  # localhost...
    +    r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|'  # ...or ipv4
    +    r'\[?[A-F0-9]*:[A-F0-9:]+\]?)'  # ...or ipv6
    +    r'(?::\d+)?'  # optional port
    +    r'(?:/?|[/?]\S+)$', re.IGNORECASE)
    +url_regex_single_label_domain = re.compile(
    +    r'^(?:[a-z0-9\.\-]*)://'  # scheme is validated separately
    +    r'(?:(?:[A-Z0-9_](?:[A-Z0-9-_]{0,61}[A-Z0-9_])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|'  # domain...
    +    r'(?:[A-Z0-9_](?:[A-Z0-9-_]{0,61}[A-Z0-9_]))|'  # single-label-domain
    +    r'localhost|'  # localhost...
    +    r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|'  # ...or ipv4
    +    r'\[?[A-F0-9]*:[A-F0-9:]+\]?)'  # ...or ipv6
    +    r'(?::\d+)?'  # optional port
    +    r'(?:/?|[/?]\S+)$', re.IGNORECASE)
    +url_schemes = ['http', 'https', 'ftp', 'ftps']
    +
    +
    +
    [docs]def validate_url(url, allow_single_label_domain=False): + """ + Auxiliary method to validate an urllib + :param url: An url to be validated + :type url: string + :param allow_single_label_domain: In order to allow or not single label domain + :type url: bool + :returns: True if the url is valid + :rtype: bool + """ + + scheme = url.split('://')[0].lower() + if scheme not in url_schemes: + return False + if allow_single_label_domain: + if not bool(url_regex_single_label_domain.search(url)): + return False + else: + if not bool(url_regex.search(url)): + return False + return True
    + + +
    [docs]class OneLogin_Saml2_Settings(object): + """ + + Handles the settings of the Python toolkits. + + """ + + metadata_class = OneLogin_Saml2_Metadata + + def __init__(self, settings=None, custom_base_path=None, sp_validation_only=False): + """ + Initializes the settings: + - Sets the paths of the different folders + - Loads settings info from settings file or array/object provided + + :param settings: SAML Toolkit Settings + :type settings: dict + + :param custom_base_path: Path where are stored the settings file and the cert folder + :type custom_base_path: string + + :param sp_validation_only: Avoid the IdP validation + :type sp_validation_only: boolean + """ + self._sp_validation_only = sp_validation_only + self._paths = {} + self._strict = True + self._debug = False + self._sp = {} + self._idp = {} + self._security = {} + self._contacts = {} + self._organization = {} + self._errors = [] + + self._load_paths(base_path=custom_base_path) + self._update_paths(settings) + + if settings is None: + try: + valid = self._load_settings_from_file() + except Exception as e: + raise e + if not valid: + raise OneLogin_Saml2_Error( + 'Invalid dict settings at the file: %s', + OneLogin_Saml2_Error.SETTINGS_INVALID, + ','.join(self._errors) + ) + elif isinstance(settings, dict): + if not self._load_settings_from_dict(settings): + raise OneLogin_Saml2_Error( + 'Invalid dict settings: %s', + OneLogin_Saml2_Error.SETTINGS_INVALID, + ','.join(self._errors) + ) + else: + raise OneLogin_Saml2_Error( + 'Unsupported settings object', + OneLogin_Saml2_Error.UNSUPPORTED_SETTINGS_OBJECT + ) + + self.format_idp_cert() + if 'x509certMulti' in self._idp: + self.format_idp_cert_multi() + self.format_sp_cert() + if 'x509certNew' in self._sp: + self.format_sp_cert_new() + self.format_sp_key() + + def _load_paths(self, base_path=None): + """ + Set the paths of the different folders + """ + if base_path is None: + base_path = dirname(dirname(dirname(__file__))) + if not base_path.endswith(sep): + base_path += sep + self._paths = { + 'base': base_path, + 'cert': base_path + 'certs' + sep, + 'lib': dirname(__file__) + sep + } + + def _update_paths(self, settings): + """ + Set custom paths if necessary + """ + if not isinstance(settings, dict): + return + + if 'custom_base_path' in settings: + base_path = settings['custom_base_path'] + base_path = join(dirname(__file__), base_path) + self._load_paths(base_path) + +
    [docs] def get_base_path(self): + """ + Returns base path + + :return: The base toolkit folder path + :rtype: string + """ + return self._paths['base']
    + +
    [docs] def get_cert_path(self): + """ + Returns cert path + + :return: The cert folder path + :rtype: string + """ + return self._paths['cert']
    + +
    [docs] def set_cert_path(self, path): + """ + Set a new cert path + """ + self._paths['cert'] = path
    + +
    [docs] def get_lib_path(self): + """ + Returns lib path + + :return: The library folder path + :rtype: string + """ + return self._paths['lib']
    + +
    [docs] def get_schemas_path(self): + """ + Returns schema path + + :return: The schema folder path + :rtype: string + """ + return self._paths['lib'] + 'schemas/'
    + + def _load_settings_from_dict(self, settings): + """ + Loads settings info from a settings Dict + + :param settings: SAML Toolkit Settings + :type settings: dict + + :returns: True if the settings info is valid + :rtype: boolean + """ + errors = self.check_settings(settings) + if len(errors) == 0: + self._errors = [] + self._sp = settings['sp'] + self._idp = settings.get('idp', {}) + self._strict = settings.get('strict', True) + self._debug = settings.get('debug', False) + self._security = settings.get('security', {}) + self._contacts = settings.get('contactPerson', {}) + self._organization = settings.get('organization', {}) + + self._add_default_values() + return True + + self._errors = errors + return False + + def _load_settings_from_file(self): + """ + Loads settings info from the settings json file + + :returns: True if the settings info is valid + :rtype: boolean + """ + filename = self.get_base_path() + 'settings.json' + + if not exists(filename): + raise OneLogin_Saml2_Error( + 'Settings file not found: %s', + OneLogin_Saml2_Error.SETTINGS_FILE_NOT_FOUND, + filename + ) + + # In the php toolkit instead of being a json file it is a php file and + # it is directly included + with open(filename, 'r') as json_data: + settings = json.loads(json_data.read()) + + advanced_filename = self.get_base_path() + 'advanced_settings.json' + if exists(advanced_filename): + with open(advanced_filename, 'r') as json_data: + settings.update(json.loads(json_data.read())) # Merge settings + + return self._load_settings_from_dict(settings) + + def _add_default_values(self): + """ + Add default values if the settings info is not complete + """ + self._sp.setdefault('assertionConsumerService', {}) + self._sp['assertionConsumerService'].setdefault('binding', OneLogin_Saml2_Constants.BINDING_HTTP_POST) + + self._sp.setdefault('attributeConsumingService', {}) + + self._sp.setdefault('singleLogoutService', {}) + self._sp['singleLogoutService'].setdefault('binding', OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT) + + self._idp.setdefault('singleLogoutService', {}) + + # Related to nameID + self._sp.setdefault('NameIDFormat', OneLogin_Saml2_Constants.NAMEID_UNSPECIFIED) + self._security.setdefault('nameIdEncrypted', False) + + # Metadata format + self._security.setdefault('metadataValidUntil', None) # None means use default + self._security.setdefault('metadataCacheDuration', None) # None means use default + + # Sign provided + self._security.setdefault('authnRequestsSigned', False) + self._security.setdefault('logoutRequestSigned', False) + self._security.setdefault('logoutResponseSigned', False) + self._security.setdefault('signMetadata', False) + + # Sign expected + self._security.setdefault('wantMessagesSigned', False) + self._security.setdefault('wantAssertionsSigned', False) + + # NameID element expected + self._security.setdefault('wantNameId', True) + + # Encrypt expected + self._security.setdefault('wantAssertionsEncrypted', False) + self._security.setdefault('wantNameIdEncrypted', False) + + # Signature Algorithm + self._security.setdefault('signatureAlgorithm', OneLogin_Saml2_Constants.RSA_SHA256) + + # Digest Algorithm + self._security.setdefault('digestAlgorithm', OneLogin_Saml2_Constants.SHA256) + + # Reject Deprecated Algorithms + self._security.setdefault('rejectDeprecatedAlgorithm', False) + + # AttributeStatement required by default + self._security.setdefault('wantAttributeStatement', True) + + # Disallow duplicate attribute names by default + self._security.setdefault('allowRepeatAttributeName', False) + + self._idp.setdefault('x509cert', '') + self._idp.setdefault('certFingerprint', '') + self._idp.setdefault('certFingerprintAlgorithm', 'sha1') + + self._sp.setdefault('x509cert', '') + self._sp.setdefault('privateKey', '') + + self._security.setdefault('requestedAuthnContext', True) + self._security.setdefault('requestedAuthnContextComparison', 'exact') + self._security.setdefault('failOnAuthnContextMismatch', False) + +
    [docs] def check_settings(self, settings): + """ + Checks the settings info. + + :param settings: Dict with settings data + :type settings: dict + + :returns: Errors found on the settings data + :rtype: list + """ + assert isinstance(settings, dict) + + errors = [] + if not isinstance(settings, dict) or len(settings) == 0: + errors.append('invalid_syntax') + else: + if not self._sp_validation_only: + errors += self.check_idp_settings(settings) + sp_errors = self.check_sp_settings(settings) + errors += sp_errors + + return errors
    + +
    [docs] def check_idp_settings(self, settings): + """ + Checks the IdP settings info. + :param settings: Dict with settings data + :type settings: dict + :returns: Errors found on the IdP settings data + :rtype: list + """ + assert isinstance(settings, dict) + + errors = [] + if not isinstance(settings, dict) or len(settings) == 0: + errors.append('invalid_syntax') + else: + if not settings.get('idp'): + errors.append('idp_not_found') + else: + allow_single_domain_urls = self._get_allow_single_label_domain(settings) + idp = settings['idp'] + if not idp.get('entityId'): + errors.append('idp_entityId_not_found') + + if not idp.get('singleSignOnService', {}).get('url'): + errors.append('idp_sso_not_found') + elif not validate_url(idp['singleSignOnService']['url'], allow_single_domain_urls): + errors.append('idp_sso_url_invalid') + + slo_url = idp.get('singleLogoutService', {}).get('url') + if slo_url and not validate_url(slo_url, allow_single_domain_urls): + errors.append('idp_slo_url_invalid') + + if 'security' in settings: + security = settings['security'] + + exists_x509 = bool(idp.get('x509cert')) + exists_fingerprint = bool(idp.get('certFingerprint')) + + exists_multix509sign = 'x509certMulti' in idp and \ + 'signing' in idp['x509certMulti'] and \ + idp['x509certMulti']['signing'] + exists_multix509enc = 'x509certMulti' in idp and \ + 'encryption' in idp['x509certMulti'] and \ + idp['x509certMulti']['encryption'] + + want_assert_sign = bool(security.get('wantAssertionsSigned')) + want_mes_signed = bool(security.get('wantMessagesSigned')) + nameid_enc = bool(security.get('nameIdEncrypted')) + + if (want_assert_sign or want_mes_signed) and \ + not (exists_x509 or exists_fingerprint or exists_multix509sign): + errors.append('idp_cert_or_fingerprint_not_found_and_required') + if nameid_enc and not (exists_x509 or exists_multix509enc): + errors.append('idp_cert_not_found_and_required') + return errors
    + +
    [docs] def check_sp_settings(self, settings): + """ + Checks the SP settings info. + :param settings: Dict with settings data + :type settings: dict + :returns: Errors found on the SP settings data + :rtype: list + """ + assert isinstance(settings, dict) + + errors = [] + if not isinstance(settings, dict) or not settings: + errors.append('invalid_syntax') + else: + if not settings.get('sp'): + errors.append('sp_not_found') + else: + allow_single_domain_urls = self._get_allow_single_label_domain(settings) + # check_sp_certs uses self._sp so I add it + old_sp = self._sp + self._sp = settings['sp'] + + sp = settings['sp'] + security = settings.get('security', {}) + + if not sp.get('entityId'): + errors.append('sp_entityId_not_found') + + if not sp.get('assertionConsumerService', {}).get('url'): + errors.append('sp_acs_not_found') + elif not validate_url(sp['assertionConsumerService']['url'], allow_single_domain_urls): + errors.append('sp_acs_url_invalid') + + if sp.get('attributeConsumingService'): + attributeConsumingService = sp['attributeConsumingService'] + if 'serviceName' not in attributeConsumingService: + errors.append('sp_attributeConsumingService_serviceName_not_found') + elif not isinstance(attributeConsumingService['serviceName'], basestring): + errors.append('sp_attributeConsumingService_serviceName_type_invalid') + + if 'requestedAttributes' not in attributeConsumingService: + errors.append('sp_attributeConsumingService_requestedAttributes_not_found') + elif not isinstance(attributeConsumingService['requestedAttributes'], list): + errors.append('sp_attributeConsumingService_serviceName_type_invalid') + else: + for req_attrib in attributeConsumingService['requestedAttributes']: + if 'name' not in req_attrib: + errors.append('sp_attributeConsumingService_requestedAttributes_name_not_found') + if 'name' in req_attrib and not req_attrib['name'].strip(): + errors.append('sp_attributeConsumingService_requestedAttributes_name_invalid') + if 'attributeValue' in req_attrib and type(req_attrib['attributeValue']) != list: + errors.append('sp_attributeConsumingService_requestedAttributes_attributeValue_type_invalid') + if 'isRequired' in req_attrib and type(req_attrib['isRequired']) != bool: + errors.append('sp_attributeConsumingService_requestedAttributes_isRequired_type_invalid') + + if "serviceDescription" in attributeConsumingService and not isinstance(attributeConsumingService['serviceDescription'], basestring): + errors.append('sp_attributeConsumingService_serviceDescription_type_invalid') + + slo_url = sp.get('singleLogoutService', {}).get('url') + if slo_url and not validate_url(slo_url, allow_single_domain_urls): + errors.append('sp_sls_url_invalid') + + if 'signMetadata' in security and isinstance(security['signMetadata'], dict): + if 'keyFileName' not in security['signMetadata'] or \ + 'certFileName' not in security['signMetadata']: + errors.append('sp_signMetadata_invalid') + + authn_sign = bool(security.get('authnRequestsSigned')) + logout_req_sign = bool(security.get('logoutRequestSigned')) + logout_res_sign = bool(security.get('logoutResponseSigned')) + want_assert_enc = bool(security.get('wantAssertionsEncrypted')) + want_nameid_enc = bool(security.get('wantNameIdEncrypted')) + + if not self.check_sp_certs(): + if authn_sign or logout_req_sign or logout_res_sign or \ + want_assert_enc or want_nameid_enc: + errors.append('sp_cert_not_found_and_required') + + if 'contactPerson' in settings: + types = settings['contactPerson'] + valid_types = ['technical', 'support', 'administrative', 'billing', 'other'] + for c_type in types: + if c_type not in valid_types: + errors.append('contact_type_invalid') + break + + for c_type in settings['contactPerson']: + contact = settings['contactPerson'][c_type] + if ('givenName' not in contact or len(contact['givenName']) == 0) or \ + ('emailAddress' not in contact or len(contact['emailAddress']) == 0): + errors.append('contact_not_enought_data') + break + + if 'organization' in settings: + for org in settings['organization']: + organization = settings['organization'][org] + if ('name' not in organization or len(organization['name']) == 0) or \ + ('displayname' not in organization or len(organization['displayname']) == 0) or \ + ('url' not in organization or len(organization['url']) == 0): + errors.append('organization_not_enought_data') + break + # Restores the value that had the self._sp + if 'old_sp' in locals(): + self._sp = old_sp + + return errors
    + +
    [docs] def check_sp_certs(self): + """ + Checks if the x509 certs of the SP exists and are valid. + :returns: If the x509 certs of the SP exists and are valid + :rtype: boolean + """ + key = self.get_sp_key() + cert = self.get_sp_cert() + return key is not None and cert is not None
    + +
    [docs] def get_idp_sso_url(self): + """ + Gets the IdP SSO URL. + + :returns: An URL, the SSO endpoint of the IdP + :rtype: string + """ + idp_data = self.get_idp_data() + return idp_data['singleSignOnService']['url']
    + +
    [docs] def get_idp_slo_url(self): + """ + Gets the IdP SLO URL. + + :returns: An URL, the SLO endpoint of the IdP + :rtype: string + """ + idp_data = self.get_idp_data() + if 'url' in idp_data['singleLogoutService']: + return idp_data['singleLogoutService']['url']
    + +
    [docs] def get_idp_slo_response_url(self): + """ + Gets the IdP SLO return URL for IdP-initiated logout. + + :returns: an URL, the SLO return endpoint of the IdP + :rtype: string + """ + idp_data = self.get_idp_data() + if 'url' in idp_data['singleLogoutService']: + return idp_data['singleLogoutService'].get('responseUrl', self.get_idp_slo_url())
    + +
    [docs] def get_sp_key(self): + """ + Returns the x509 private key of the SP. + :returns: SP private key + :rtype: string or None + """ + key = self._sp.get('privateKey') + key_file_name = self._paths['cert'] + 'sp.key' + + if not key and exists(key_file_name): + with open(key_file_name) as f: + key = f.read() + + return key or None
    + +
    [docs] def get_sp_cert(self): + """ + Returns the x509 public cert of the SP. + :returns: SP public cert + :rtype: string or None + """ + cert = self._sp.get('x509cert') + cert_file_name = self._paths['cert'] + 'sp.crt' + + if not cert and exists(cert_file_name): + with open(cert_file_name) as f: + cert = f.read() + + return cert or None
    + +
    [docs] def get_sp_cert_new(self): + """ + Returns the x509 public of the SP planned + to be used soon instead the other public cert + :returns: SP public cert new + :rtype: string or None + """ + cert = self._sp.get('x509certNew') + cert_file_name = self._paths['cert'] + 'sp_new.crt' + + if not cert and exists(cert_file_name): + with open(cert_file_name) as f: + cert = f.read() + + return cert or None
    + +
    [docs] def get_idp_cert(self): + """ + Returns the x509 public cert of the IdP. + :returns: IdP public cert + :rtype: string + """ + cert = self._idp.get('x509cert') + cert_file_name = self.get_cert_path() + 'idp.crt' + if not cert and exists(cert_file_name): + with open(cert_file_name) as f: + cert = f.read() + return cert or None
    + +
    [docs] def get_idp_data(self): + """ + Gets the IdP data. + + :returns: IdP info + :rtype: dict + """ + return self._idp
    + +
    [docs] def get_sp_data(self): + """ + Gets the SP data. + + :returns: SP info + :rtype: dict + """ + return self._sp
    + +
    [docs] def get_security_data(self): + """ + Gets security data. + + :returns: Security info + :rtype: dict + """ + return self._security
    + +
    [docs] def get_contacts(self): + """ + Gets contact data. + + :returns: Contacts info + :rtype: dict + """ + return self._contacts
    + +
    [docs] def get_organization(self): + """ + Gets organization data. + + :returns: Organization info + :rtype: dict + """ + return self._organization
    + +
    [docs] def get_sp_metadata(self): + """ + Gets the SP metadata. The XML representation. + :returns: SP metadata (xml) + :rtype: string + """ + metadata = self.metadata_class.builder( + self._sp, self._security['authnRequestsSigned'], + self._security['wantAssertionsSigned'], + self._security['metadataValidUntil'], + self._security['metadataCacheDuration'], + self.get_contacts(), self.get_organization() + ) + + add_encryption = self._security['wantNameIdEncrypted'] or self._security['wantAssertionsEncrypted'] + + cert_new = self.get_sp_cert_new() + metadata = self.metadata_class.add_x509_key_descriptors(metadata, cert_new, add_encryption) + + cert = self.get_sp_cert() + metadata = self.metadata_class.add_x509_key_descriptors(metadata, cert, add_encryption) + + # Sign metadata + if 'signMetadata' in self._security and self._security['signMetadata'] is not False: + if self._security['signMetadata'] is True: + # Use the SP's normal key to sign the metadata: + if not cert: + raise OneLogin_Saml2_Error( + 'Cannot sign metadata: missing SP public key certificate.', + OneLogin_Saml2_Error.PUBLIC_CERT_FILE_NOT_FOUND + ) + cert_metadata = cert + key_metadata = self.get_sp_key() + if not key_metadata: + raise OneLogin_Saml2_Error( + 'Cannot sign metadata: missing SP private key.', + OneLogin_Saml2_Error.PRIVATE_KEY_FILE_NOT_FOUND + ) + else: + # Use a custom key to sign the metadata: + if ('keyFileName' not in self._security['signMetadata'] or + 'certFileName' not in self._security['signMetadata']): + raise OneLogin_Saml2_Error( + 'Invalid Setting: signMetadata value of the sp is not valid', + OneLogin_Saml2_Error.SETTINGS_INVALID_SYNTAX + ) + key_file_name = self._security['signMetadata']['keyFileName'] + cert_file_name = self._security['signMetadata']['certFileName'] + key_metadata_file = self._paths['cert'] + key_file_name + cert_metadata_file = self._paths['cert'] + cert_file_name + + try: + with open(key_metadata_file, 'r') as f_metadata_key: + key_metadata = f_metadata_key.read() + except IOError: + raise OneLogin_Saml2_Error( + 'Private key file not readable: %s', + OneLogin_Saml2_Error.PRIVATE_KEY_FILE_NOT_FOUND, + key_metadata_file + ) + + try: + with open(cert_metadata_file, 'r') as f_metadata_cert: + cert_metadata = f_metadata_cert.read() + except IOError: + raise OneLogin_Saml2_Error( + 'Public cert file not readable: %s', + OneLogin_Saml2_Error.PUBLIC_CERT_FILE_NOT_FOUND, + cert_metadata_file + ) + + signature_algorithm = self._security['signatureAlgorithm'] + digest_algorithm = self._security['digestAlgorithm'] + + metadata = self.metadata_class.sign_metadata(metadata, key_metadata, cert_metadata, signature_algorithm, digest_algorithm) + + return metadata
    + +
    [docs] def validate_metadata(self, xml): + """ + Validates an XML SP Metadata. + + :param xml: Metadata's XML that will be validate + :type xml: string + + :returns: The list of found errors + :rtype: list + """ + + assert isinstance(xml, compat.text_types) + + if len(xml) == 0: + raise Exception('Empty string supplied as input') + + errors = [] + root = OneLogin_Saml2_XML.validate_xml(xml, 'saml-schema-metadata-2.0.xsd', self._debug) + if isinstance(root, str): + errors.append(root) + else: + if root.tag != '{%s}EntityDescriptor' % OneLogin_Saml2_Constants.NS_MD: + errors.append('noEntityDescriptor_xml') + else: + if (len(root.findall('.//md:SPSSODescriptor', namespaces=OneLogin_Saml2_Constants.NSMAP))) != 1: + errors.append('onlySPSSODescriptor_allowed_xml') + else: + valid_until, cache_duration = root.get('validUntil'), root.get('cacheDuration') + + if valid_until: + valid_until = OneLogin_Saml2_Utils.parse_SAML_to_time(valid_until) + expire_time = OneLogin_Saml2_Utils.get_expire_time(cache_duration, valid_until) + if expire_time is not None and int(time()) > int(expire_time): + errors.append('expired_xml') + + # TODO: Validate Sign + + return errors
    + +
    [docs] def format_idp_cert(self): + """ + Formats the IdP cert. + """ + self._idp['x509cert'] = OneLogin_Saml2_Utils.format_cert(self._idp['x509cert'])
    + +
    [docs] def format_idp_cert_multi(self): + """ + Formats the Multple IdP certs. + """ + if 'x509certMulti' in self._idp: + if 'signing' in self._idp['x509certMulti']: + for idx in range(len(self._idp['x509certMulti']['signing'])): + self._idp['x509certMulti']['signing'][idx] = OneLogin_Saml2_Utils.format_cert(self._idp['x509certMulti']['signing'][idx]) + + if 'encryption' in self._idp['x509certMulti']: + for idx in range(len(self._idp['x509certMulti']['encryption'])): + self._idp['x509certMulti']['encryption'][idx] = OneLogin_Saml2_Utils.format_cert(self._idp['x509certMulti']['encryption'][idx])
    + +
    [docs] def format_sp_cert(self): + """ + Formats the SP cert. + """ + self._sp['x509cert'] = OneLogin_Saml2_Utils.format_cert(self._sp['x509cert'])
    + +
    [docs] def format_sp_cert_new(self): + """ + Formats the SP cert. + """ + self._sp['x509certNew'] = OneLogin_Saml2_Utils.format_cert(self._sp['x509certNew'])
    + +
    [docs] def format_sp_key(self): + """ + Formats the private key. + """ + self._sp['privateKey'] = OneLogin_Saml2_Utils.format_private_key(self._sp['privateKey'])
    + +
    [docs] def get_errors(self): + """ + Returns an array with the errors, the array is empty when the settings is ok. + + :returns: Errors + :rtype: list + """ + return self._errors
    + +
    [docs] def set_strict(self, value): + """ + Activates or deactivates the strict mode. + + :param value: Strict parameter + :type value: boolean + """ + assert isinstance(value, bool) + + self._strict = value
    + +
    [docs] def is_strict(self): + """ + Returns if the 'strict' mode is active. + + :returns: Strict parameter + :rtype: boolean + """ + return self._strict
    + +
    [docs] def is_debug_active(self): + """ + Returns if the debug is active. + + :returns: Debug parameter + :rtype: boolean + """ + return self._debug
    + + def _get_allow_single_label_domain(self, settings): + security = settings.get('security', {}) + return 'allowSingleLabelDomains' in security.keys() and security['allowSingleLabelDomains']
    +
    + +
    +
    + +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/docs/saml2/_modules/onelogin/saml2/utils.html b/docs/saml2/_modules/onelogin/saml2/utils.html new file mode 100644 index 00000000..623a06f6 --- /dev/null +++ b/docs/saml2/_modules/onelogin/saml2/utils.html @@ -0,0 +1,1173 @@ + + + + + + onelogin.saml2.utils — SAML Python2/3 Toolkit 1 documentation + + + + + + + + + + + + + + + +
    + + +
    + +
    +
    +
    + +
    +
    +
    +
    + +

    Source code for onelogin.saml2.utils

    +# -*- coding: utf-8 -*-
    +
    +""" OneLogin_Saml2_Utils class
    +
    +
    +Auxiliary class of SAML Python Toolkit.
    +
    +"""
    +
    +import base64
    +import warnings
    +from copy import deepcopy
    +import calendar
    +from datetime import datetime
    +from hashlib import sha1, sha256, sha384, sha512
    +from isodate import parse_duration as duration_parser
    +import re
    +from textwrap import wrap
    +from functools import wraps
    +from uuid import uuid4
    +from xml.dom.minidom import Element
    +import zlib
    +import xmlsec
    +
    +from onelogin.saml2 import compat
    +from onelogin.saml2.constants import OneLogin_Saml2_Constants
    +from onelogin.saml2.errors import OneLogin_Saml2_Error, OneLogin_Saml2_ValidationError
    +from onelogin.saml2.xml_utils import OneLogin_Saml2_XML
    +
    +
    +try:
    +    from urllib.parse import quote_plus, urlsplit, urlunsplit  # py3
    +except ImportError:
    +    from urlparse import urlsplit, urlunsplit
    +    from urllib import quote_plus  # py2
    +
    +
    +
    [docs]def return_false_on_exception(func): + """ + Decorator. When applied to a function, it will, by default, suppress any exceptions + raised by that function and return False. It may be overridden by passing a + "raise_exceptions" keyword argument when calling the wrapped function. + """ + @wraps(func) + def exceptfalse(*args, **kwargs): + if not kwargs.pop('raise_exceptions', False): + try: + return func(*args, **kwargs) + except Exception: + return False + else: + return func(*args, **kwargs) + return exceptfalse
    + + +
    [docs]class OneLogin_Saml2_Utils(object): + """ + + Auxiliary class that contains several utility methods to parse time, + urls, add sign, encrypt, decrypt, sign validation, handle xml ... + + """ + + RESPONSE_SIGNATURE_XPATH = '/samlp:Response/ds:Signature' + ASSERTION_SIGNATURE_XPATH = '/samlp:Response/saml:Assertion/ds:Signature' + + TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ" + TIME_FORMAT_2 = "%Y-%m-%dT%H:%M:%S.%fZ" + TIME_FORMAT_WITH_FRAGMENT = re.compile(r'^(\d{4,4}-\d{2,2}-\d{2,2}T\d{2,2}:\d{2,2}:\d{2,2})(\.\d*)?Z?$') + +
    [docs] @staticmethod + def escape_url(url, lowercase_urlencoding=False): + """ + escape the non-safe symbols in url + The encoding used by ADFS 3.0 is not compatible with + python's quote_plus (ADFS produces lower case hex numbers and quote_plus produces + upper case hex numbers) + :param url: the url to escape + :type url: str + + :param lowercase_urlencoding: lowercase or no + :type lowercase_urlencoding: boolean + + :return: the escaped url + :rtype str + """ + encoded = quote_plus(url) + return re.sub(r"%[A-F0-9]{2}", lambda m: m.group(0).lower(), encoded) if lowercase_urlencoding else encoded
    + +
    [docs] @staticmethod + def b64encode(data): + """base64 encode""" + return compat.to_string(base64.b64encode(compat.to_bytes(data)))
    + +
    [docs] @staticmethod + def b64decode(data): + """base64 decode""" + return base64.b64decode(data)
    + +
    [docs] @staticmethod + def decode_base64_and_inflate(value, ignore_zip=False): + """ + base64 decodes and then inflates according to RFC1951 + :param value: a deflated and encoded string + :type value: string + :param ignore_zip: ignore zip errors + :returns: the string after decoding and inflating + :rtype: string + """ + encoded = OneLogin_Saml2_Utils.b64decode(value) + try: + return zlib.decompress(encoded, -15) + except zlib.error: + if not ignore_zip: + raise + return encoded
    + +
    [docs] @staticmethod + def deflate_and_base64_encode(value): + """ + Deflates and then base64 encodes a string + :param value: The string to deflate and encode + :type value: string + :returns: The deflated and encoded string + :rtype: string + """ + return OneLogin_Saml2_Utils.b64encode(zlib.compress(compat.to_bytes(value))[2:-4])
    + +
    [docs] @staticmethod + def format_cert(cert, heads=True): + """ + Returns a x509 cert (adding header & footer if required). + + :param cert: A x509 unformatted cert + :type: string + + :param heads: True if we want to include head and footer + :type: boolean + + :returns: Formatted cert + :rtype: string + """ + x509_cert = cert.replace('\x0D', '') + x509_cert = x509_cert.replace('\r', '') + x509_cert = x509_cert.replace('\n', '') + if len(x509_cert) > 0: + x509_cert = x509_cert.replace('-----BEGIN CERTIFICATE-----', '') + x509_cert = x509_cert.replace('-----END CERTIFICATE-----', '') + x509_cert = x509_cert.replace(' ', '') + + if heads: + x509_cert = "-----BEGIN CERTIFICATE-----\n" + "\n".join(wrap(x509_cert, 64)) + "\n-----END CERTIFICATE-----\n" + + return x509_cert
    + +
    [docs] @staticmethod + def format_private_key(key, heads=True): + """ + Returns a private key (adding header & footer if required). + + :param key A private key + :type: string + + :param heads: True if we want to include head and footer + :type: boolean + + :returns: Formated private key + :rtype: string + """ + private_key = key.replace('\x0D', '') + private_key = private_key.replace('\r', '') + private_key = private_key.replace('\n', '') + if len(private_key) > 0: + if private_key.find('-----BEGIN PRIVATE KEY-----') != -1: + private_key = private_key.replace('-----BEGIN PRIVATE KEY-----', '') + private_key = private_key.replace('-----END PRIVATE KEY-----', '') + private_key = private_key.replace(' ', '') + if heads: + private_key = "-----BEGIN PRIVATE KEY-----\n" + "\n".join(wrap(private_key, 64)) + "\n-----END PRIVATE KEY-----\n" + else: + private_key = private_key.replace('-----BEGIN RSA PRIVATE KEY-----', '') + private_key = private_key.replace('-----END RSA PRIVATE KEY-----', '') + private_key = private_key.replace(' ', '') + if heads: + private_key = "-----BEGIN RSA PRIVATE KEY-----\n" + "\n".join(wrap(private_key, 64)) + "\n-----END RSA PRIVATE KEY-----\n" + return private_key
    + +
    [docs] @staticmethod + def redirect(url, parameters={}, request_data={}): + """ + Executes a redirection to the provided url (or return the target url). + + :param url: The target url + :type: string + + :param parameters: Extra parameters to be passed as part of the url + :type: dict + + :param request_data: The request as a dict + :type: dict + + :returns: Url + :rtype: string + """ + assert isinstance(url, compat.str_type) + assert isinstance(parameters, dict) + + if url.startswith('/'): + url = '%s%s' % (OneLogin_Saml2_Utils.get_self_url_host(request_data), url) + + # Verify that the URL is to a http or https site. + if re.search('^https?://', url, flags=re.IGNORECASE) is None: + raise OneLogin_Saml2_Error( + 'Redirect to invalid URL: ' + url, + OneLogin_Saml2_Error.REDIRECT_INVALID_URL + ) + + # Add encoded parameters + if url.find('?') < 0: + param_prefix = '?' + else: + param_prefix = '&' + + for name, value in parameters.items(): + + if value is None: + param = OneLogin_Saml2_Utils.escape_url(name) + elif isinstance(value, list): + param = '' + for val in value: + param += OneLogin_Saml2_Utils.escape_url(name) + '[]=' + OneLogin_Saml2_Utils.escape_url(val) + '&' + if len(param) > 0: + param = param[0:-1] + else: + param = OneLogin_Saml2_Utils.escape_url(name) + '=' + OneLogin_Saml2_Utils.escape_url(value) + + if param: + url += param_prefix + param + param_prefix = '&' + + return url
    + +
    [docs] @staticmethod + def get_self_url_host(request_data): + """ + Returns the protocol + the current host + the port (if different than + common ports). + + :param request_data: The request as a dict + :type: dict + + :return: Url + :rtype: string + """ + current_host = OneLogin_Saml2_Utils.get_self_host(request_data) + protocol = 'https' if OneLogin_Saml2_Utils.is_https(request_data) else 'http' + + if request_data.get('server_port') is not None: + warnings.warn( + 'The server_port key in request data is deprecated. ' + 'The http_host key should include a port, if required.', + category=DeprecationWarning, + ) + port_suffix = ':%s' % request_data['server_port'] + if not current_host.endswith(port_suffix): + if not ((protocol == 'https' and port_suffix == ':443') or (protocol == 'http' and port_suffix == ':80')): + current_host += port_suffix + + return '%s://%s' % (protocol, current_host)
    + +
    [docs] @staticmethod + def get_self_host(request_data): + """ + Returns the current host (which may include a port number part). + + :param request_data: The request as a dict + :type: dict + + :return: The current host + :rtype: string + """ + if 'http_host' in request_data: + return request_data['http_host'] + elif 'server_name' in request_data: + warnings.warn("The server_name key in request data is undocumented & deprecated.", category=DeprecationWarning) + return request_data['server_name'] + raise Exception('No hostname defined')
    + +
    [docs] @staticmethod + def is_https(request_data): + """ + Checks if https or http. + + :param request_data: The request as a dict + :type: dict + + :return: False if https is not active + :rtype: boolean + """ + is_https = 'https' in request_data and request_data['https'] != 'off' + # TODO: this use of server_port should be removed too + is_https = is_https or ('server_port' in request_data and str(request_data['server_port']) == '443') + return is_https
    + +
    [docs] @staticmethod + def get_self_url_no_query(request_data): + """ + Returns the URL of the current host + current view. + + :param request_data: The request as a dict + :type: dict + + :return: The url of current host + current view + :rtype: string + """ + self_url_host = OneLogin_Saml2_Utils.get_self_url_host(request_data) + script_name = request_data['script_name'] + if script_name: + if script_name[0] != '/': + script_name = '/' + script_name + else: + script_name = '' + self_url_no_query = self_url_host + script_name + if 'path_info' in request_data: + self_url_no_query += request_data['path_info'] + + return self_url_no_query
    + +
    [docs] @staticmethod + def get_self_routed_url_no_query(request_data): + """ + Returns the routed URL of the current host + current view. + + :param request_data: The request as a dict + :type: dict + + :return: The url of current host + current view + :rtype: string + """ + self_url_host = OneLogin_Saml2_Utils.get_self_url_host(request_data) + route = '' + if 'request_uri' in request_data and request_data['request_uri']: + route = request_data['request_uri'] + if 'query_string' in request_data and request_data['query_string']: + route = route.replace(request_data['query_string'], '') + + return self_url_host + route
    + +
    [docs] @staticmethod + def get_self_url(request_data): + """ + Returns the URL of the current host + current view + query. + + :param request_data: The request as a dict + :type: dict + + :return: The url of current host + current view + query + :rtype: string + """ + self_url_host = OneLogin_Saml2_Utils.get_self_url_host(request_data) + + request_uri = '' + if 'request_uri' in request_data: + request_uri = request_data['request_uri'] + if not request_uri.startswith('/'): + match = re.search('^https?://[^/]*(/.*)', request_uri) + if match is not None: + request_uri = match.groups()[0] + + return self_url_host + request_uri
    + +
    [docs] @staticmethod + def generate_unique_id(): + """ + Generates an unique string (used for example as ID for assertions). + + :return: A unique string + :rtype: string + """ + return 'ONELOGIN_%s' % sha1(compat.to_bytes(uuid4().hex)).hexdigest()
    + +
    [docs] @staticmethod + def parse_time_to_SAML(time): + r""" + Converts a UNIX timestamp to SAML2 timestamp on the form + yyyy-mm-ddThh:mm:ss(\.s+)?Z. + + :param time: The time we should convert (DateTime). + :type: string + + :return: SAML2 timestamp. + :rtype: string + """ + data = datetime.utcfromtimestamp(float(time)) + return data.strftime(OneLogin_Saml2_Utils.TIME_FORMAT)
    + +
    [docs] @staticmethod + def parse_SAML_to_time(timestr): + r""" + Converts a SAML2 timestamp on the form yyyy-mm-ddThh:mm:ss(\.s+)?Z + to a UNIX timestamp. The sub-second part is ignored. + + :param timestr: The time we should convert (SAML Timestamp). + :type: string + + :return: Converted to a unix timestamp. + :rtype: int + """ + try: + data = datetime.strptime(timestr, OneLogin_Saml2_Utils.TIME_FORMAT) + except ValueError: + try: + data = datetime.strptime(timestr, OneLogin_Saml2_Utils.TIME_FORMAT_2) + except ValueError: + elem = OneLogin_Saml2_Utils.TIME_FORMAT_WITH_FRAGMENT.match(timestr) + if not elem: + raise Exception("time data %s does not match format %s" % (timestr, r'yyyy-mm-ddThh:mm:ss(\.s+)?Z')) + data = datetime.strptime(elem.groups()[0] + "Z", OneLogin_Saml2_Utils.TIME_FORMAT) + + return calendar.timegm(data.utctimetuple())
    + +
    [docs] @staticmethod + def now(): + """ + :return: unix timestamp of actual time. + :rtype: int + """ + return calendar.timegm(datetime.utcnow().utctimetuple())
    + +
    [docs] @staticmethod + def parse_duration(duration, timestamp=None): + """ + Interprets a ISO8601 duration value relative to a given timestamp. + + :param duration: The duration, as a string. + :type: string + + :param timestamp: The unix timestamp we should apply the duration to. + Optional, default to the current time. + :type: string + + :return: The new timestamp, after the duration is applied. + :rtype: int + """ + assert isinstance(duration, compat.str_type) + assert timestamp is None or isinstance(timestamp, int) + + timedelta = duration_parser(duration) + if timestamp is None: + data = datetime.utcnow() + timedelta + else: + data = datetime.utcfromtimestamp(timestamp) + timedelta + return calendar.timegm(data.utctimetuple())
    + +
    [docs] @staticmethod + def get_expire_time(cache_duration=None, valid_until=None): + """ + Compares 2 dates and returns the earliest. + + :param cache_duration: The duration, as a string. + :type: string + + :param valid_until: The valid until date, as a string or as a timestamp + :type: string + + :return: The expiration time. + :rtype: int + """ + expire_time = None + + if cache_duration is not None: + expire_time = OneLogin_Saml2_Utils.parse_duration(cache_duration) + + if valid_until is not None: + if isinstance(valid_until, int): + valid_until_time = valid_until + else: + valid_until_time = OneLogin_Saml2_Utils.parse_SAML_to_time(valid_until) + if expire_time is None or expire_time > valid_until_time: + expire_time = valid_until_time + + if expire_time is not None: + return '%d' % expire_time + return None
    + +
    [docs] @staticmethod + def delete_local_session(callback=None): + """ + Deletes the local session. + """ + + if callback is not None: + callback()
    + +
    [docs] @staticmethod + def calculate_x509_fingerprint(x509_cert, alg='sha1'): + """ + Calculates the fingerprint of a formatted x509cert. + + :param x509_cert: x509 cert formatted + :type: string + + :param alg: The algorithm to build the fingerprint + :type: string + + :returns: fingerprint + :rtype: string + """ + assert isinstance(x509_cert, compat.str_type) + + lines = x509_cert.split('\n') + data = '' + inData = False + + for line in lines: + # Remove '\r' from end of line if present. + line = line.rstrip() + if not inData: + if line == '-----BEGIN CERTIFICATE-----': + inData = True + elif line == '-----BEGIN PUBLIC KEY-----' or line == '-----BEGIN RSA PRIVATE KEY-----': + # This isn't an X509 certificate. + return None + else: + if line == '-----END CERTIFICATE-----': + break + + # Append the current line to the certificate data. + data += line + + if not data: + return None + + decoded_data = base64.b64decode(compat.to_bytes(data)) + + if alg == 'sha512': + fingerprint = sha512(decoded_data) + elif alg == 'sha384': + fingerprint = sha384(decoded_data) + elif alg == 'sha256': + fingerprint = sha256(decoded_data) + else: + fingerprint = sha1(decoded_data) + + return fingerprint.hexdigest().lower()
    + +
    [docs] @staticmethod + def format_finger_print(fingerprint): + """ + Formats a fingerprint. + + :param fingerprint: fingerprint + :type: string + + :returns: Formatted fingerprint + :rtype: string + """ + formatted_fingerprint = fingerprint.replace(':', '') + return formatted_fingerprint.lower()
    + +
    [docs] @staticmethod + def generate_name_id(value, sp_nq, sp_format=None, cert=None, debug=False, nq=None): + """ + Generates a nameID. + + :param value: fingerprint + :type: string + + :param sp_nq: SP Name Qualifier + :type: string + + :param sp_format: SP Format + :type: string + + :param cert: IdP Public Cert to encrypt the nameID + :type: string + + :param debug: Activate the xmlsec debug + :type: bool + + :returns: DOMElement | XMLSec nameID + :rtype: string + + :param nq: IDP Name Qualifier + :type: string + """ + + root = OneLogin_Saml2_XML.make_root("{%s}container" % OneLogin_Saml2_Constants.NS_SAML) + name_id = OneLogin_Saml2_XML.make_child(root, '{%s}NameID' % OneLogin_Saml2_Constants.NS_SAML) + if sp_nq is not None: + name_id.set('SPNameQualifier', sp_nq) + if sp_format is not None: + name_id.set('Format', sp_format) + if nq is not None: + name_id.set('NameQualifier', nq) + name_id.text = value + + if cert is not None: + xmlsec.enable_debug_trace(debug) + + # Load the public cert + manager = xmlsec.KeysManager() + manager.add_key(xmlsec.Key.from_memory(cert, xmlsec.KeyFormat.CERT_PEM, None)) + + # Prepare for encryption + enc_data = xmlsec.template.encrypted_data_create( + root, xmlsec.Transform.AES128, type=xmlsec.EncryptionType.ELEMENT, ns="xenc") + + xmlsec.template.encrypted_data_ensure_cipher_value(enc_data) + key_info = xmlsec.template.encrypted_data_ensure_key_info(enc_data, ns="dsig") + enc_key = xmlsec.template.add_encrypted_key(key_info, xmlsec.Transform.RSA_OAEP) + xmlsec.template.encrypted_data_ensure_cipher_value(enc_key) + + # Encrypt! + enc_ctx = xmlsec.EncryptionContext(manager) + enc_ctx.key = xmlsec.Key.generate(xmlsec.KeyData.AES, 128, xmlsec.KeyDataType.SESSION) + enc_data = enc_ctx.encrypt_xml(enc_data, name_id) + return '<saml:EncryptedID>' + compat.to_string(OneLogin_Saml2_XML.to_string(enc_data)) + '</saml:EncryptedID>' + else: + return OneLogin_Saml2_XML.extract_tag_text(root, "saml:NameID")
    + +
    [docs] @staticmethod + def get_status(dom): + """ + Gets Status from a Response. + + :param dom: The Response as XML + :type: Document + + :returns: The Status, an array with the code and a message. + :rtype: dict + """ + status = {} + + status_entry = OneLogin_Saml2_XML.query(dom, '/samlp:Response/samlp:Status') + if len(status_entry) != 1: + raise OneLogin_Saml2_ValidationError( + 'Missing Status on response', + OneLogin_Saml2_ValidationError.MISSING_STATUS + ) + + code_entry = OneLogin_Saml2_XML.query(dom, '/samlp:Response/samlp:Status/samlp:StatusCode', status_entry[0]) + if len(code_entry) != 1: + raise OneLogin_Saml2_ValidationError( + 'Missing Status Code on response', + OneLogin_Saml2_ValidationError.MISSING_STATUS_CODE + ) + code = code_entry[0].values()[0] + status['code'] = code + + status['msg'] = '' + message_entry = OneLogin_Saml2_XML.query(dom, '/samlp:Response/samlp:Status/samlp:StatusMessage', status_entry[0]) + if len(message_entry) == 0: + subcode_entry = OneLogin_Saml2_XML.query(dom, '/samlp:Response/samlp:Status/samlp:StatusCode/samlp:StatusCode', status_entry[0]) + if len(subcode_entry) == 1: + status['msg'] = subcode_entry[0].values()[0] + elif len(message_entry) == 1: + status['msg'] = OneLogin_Saml2_XML.element_text(message_entry[0]) + + return status
    + +
    [docs] @staticmethod + def decrypt_element(encrypted_data, key, debug=False, inplace=False): + """ + Decrypts an encrypted element. + + :param encrypted_data: The encrypted data. + :type: lxml.etree.Element | DOMElement | basestring + + :param key: The key. + :type: string + + :param debug: Activate the xmlsec debug + :type: bool + + :param inplace: update passed data with decrypted result + :type: bool + + :returns: The decrypted element. + :rtype: lxml.etree.Element + """ + + if isinstance(encrypted_data, Element): + encrypted_data = OneLogin_Saml2_XML.to_etree(str(encrypted_data.toxml())) + if not inplace and isinstance(encrypted_data, OneLogin_Saml2_XML._element_class): + encrypted_data = deepcopy(encrypted_data) + elif isinstance(encrypted_data, OneLogin_Saml2_XML._text_class): + encrypted_data = OneLogin_Saml2_XML._parse_etree(encrypted_data) + + xmlsec.enable_debug_trace(debug) + manager = xmlsec.KeysManager() + + manager.add_key(xmlsec.Key.from_memory(key, xmlsec.KeyFormat.PEM, None)) + enc_ctx = xmlsec.EncryptionContext(manager) + return enc_ctx.decrypt(encrypted_data)
    + +
    [docs] @staticmethod + def add_sign(xml, key, cert, debug=False, sign_algorithm=OneLogin_Saml2_Constants.RSA_SHA256, digest_algorithm=OneLogin_Saml2_Constants.SHA256): + """ + Adds signature key and senders certificate to an element (Message or + Assertion). + + :param xml: The element we should sign + :type: string | Document + + :param key: The private key + :type: string + + :param cert: The public + :type: string + + :param debug: Activate the xmlsec debug + :type: bool + + :param sign_algorithm: Signature algorithm method + :type sign_algorithm: string + + :param digest_algorithm: Digest algorithm method + :type digest_algorithm: string + + :returns: Signed XML + :rtype: string + """ + if xml is None or xml == '': + raise Exception('Empty string supplied as input') + + elem = OneLogin_Saml2_XML.to_etree(xml) + + sign_algorithm_transform_map = { + OneLogin_Saml2_Constants.DSA_SHA1: xmlsec.Transform.DSA_SHA1, + OneLogin_Saml2_Constants.RSA_SHA1: xmlsec.Transform.RSA_SHA1, + OneLogin_Saml2_Constants.RSA_SHA256: xmlsec.Transform.RSA_SHA256, + OneLogin_Saml2_Constants.RSA_SHA384: xmlsec.Transform.RSA_SHA384, + OneLogin_Saml2_Constants.RSA_SHA512: xmlsec.Transform.RSA_SHA512 + } + sign_algorithm_transform = sign_algorithm_transform_map.get(sign_algorithm, xmlsec.Transform.RSA_SHA256) + + signature = xmlsec.template.create(elem, xmlsec.Transform.EXCL_C14N, sign_algorithm_transform, ns='ds') + + issuer = OneLogin_Saml2_XML.query(elem, '//saml:Issuer') + if len(issuer) > 0: + issuer = issuer[0] + issuer.addnext(signature) + elem_to_sign = issuer.getparent() + else: + entity_descriptor = OneLogin_Saml2_XML.query(elem, '//md:EntityDescriptor') + if len(entity_descriptor) > 0: + elem.insert(0, signature) + else: + elem[0].insert(0, signature) + elem_to_sign = elem + + elem_id = elem_to_sign.get('ID', None) + if elem_id is not None: + if elem_id: + elem_id = '#' + elem_id + else: + generated_id = generated_id = OneLogin_Saml2_Utils.generate_unique_id() + elem_id = '#' + generated_id + elem_to_sign.attrib['ID'] = generated_id + + xmlsec.enable_debug_trace(debug) + xmlsec.tree.add_ids(elem_to_sign, ["ID"]) + + digest_algorithm_transform_map = { + OneLogin_Saml2_Constants.SHA1: xmlsec.Transform.SHA1, + OneLogin_Saml2_Constants.SHA256: xmlsec.Transform.SHA256, + OneLogin_Saml2_Constants.SHA384: xmlsec.Transform.SHA384, + OneLogin_Saml2_Constants.SHA512: xmlsec.Transform.SHA512 + } + digest_algorithm_transform = digest_algorithm_transform_map.get(digest_algorithm, xmlsec.Transform.SHA256) + + ref = xmlsec.template.add_reference(signature, digest_algorithm_transform, uri=elem_id) + xmlsec.template.add_transform(ref, xmlsec.Transform.ENVELOPED) + xmlsec.template.add_transform(ref, xmlsec.Transform.EXCL_C14N) + key_info = xmlsec.template.ensure_key_info(signature) + xmlsec.template.add_x509_data(key_info) + + dsig_ctx = xmlsec.SignatureContext() + sign_key = xmlsec.Key.from_memory(key, xmlsec.KeyFormat.PEM, None) + sign_key.load_cert_from_memory(cert, xmlsec.KeyFormat.PEM) + + dsig_ctx.key = sign_key + dsig_ctx.sign(signature) + + return OneLogin_Saml2_XML.to_string(elem)
    + +
    [docs] @staticmethod + @return_false_on_exception + def validate_sign(xml, cert=None, fingerprint=None, fingerprintalg='sha1', validatecert=False, debug=False, xpath=None, multicerts=None): + """ + Validates a signature (Message or Assertion). + + :param xml: The element we should validate + :type: string | Document + + :param cert: The public cert + :type: string + + :param fingerprint: The fingerprint of the public cert + :type: string + + :param fingerprintalg: The algorithm used to build the fingerprint + :type: string + + :param validatecert: If true, will verify the signature and if the cert is valid. + :type: bool + + :param debug: Activate the xmlsec debug + :type: bool + + :param xpath: The xpath of the signed element + :type: string + + :param multicerts: Multiple public certs + :type: list + + :param raise_exceptions: Whether to return false on failure or raise an exception + :type raise_exceptions: Boolean + """ + if xml is None or xml == '': + raise Exception('Empty string supplied as input') + + elem = OneLogin_Saml2_XML.to_etree(xml) + xmlsec.enable_debug_trace(debug) + xmlsec.tree.add_ids(elem, ["ID"]) + + if xpath: + signature_nodes = OneLogin_Saml2_XML.query(elem, xpath) + else: + signature_nodes = OneLogin_Saml2_XML.query(elem, OneLogin_Saml2_Utils.RESPONSE_SIGNATURE_XPATH) + + if len(signature_nodes) == 0: + signature_nodes = OneLogin_Saml2_XML.query(elem, OneLogin_Saml2_Utils.ASSERTION_SIGNATURE_XPATH) + + if len(signature_nodes) == 1: + signature_node = signature_nodes[0] + + if not multicerts: + return OneLogin_Saml2_Utils.validate_node_sign(signature_node, elem, cert, fingerprint, fingerprintalg, validatecert, debug, raise_exceptions=True) + else: + # If multiple certs are provided, I may ignore cert and + # fingerprint provided by the method and just check the + # certs multicerts + fingerprint = fingerprintalg = None + for cert in multicerts: + if OneLogin_Saml2_Utils.validate_node_sign(signature_node, elem, cert, fingerprint, fingerprintalg, validatecert, False, raise_exceptions=False): + return True + raise OneLogin_Saml2_ValidationError( + 'Signature validation failed. SAML Response rejected.', + OneLogin_Saml2_ValidationError.INVALID_SIGNATURE + ) + else: + raise OneLogin_Saml2_ValidationError( + 'Expected exactly one signature node; got {}.'.format(len(signature_nodes)), + OneLogin_Saml2_ValidationError.WRONG_NUMBER_OF_SIGNATURES + )
    + +
    [docs] @staticmethod + @return_false_on_exception + def validate_metadata_sign(xml, cert=None, fingerprint=None, fingerprintalg='sha1', validatecert=False, debug=False): + """ + Validates a signature of a EntityDescriptor. + + :param xml: The element we should validate + :type: string | Document + + :param cert: The public cert + :type: string + + :param fingerprint: The fingerprint of the public cert + :type: string + + :param fingerprintalg: The algorithm used to build the fingerprint + :type: string + + :param validatecert: If true, will verify the signature and if the cert is valid. + :type: bool + + :param debug: Activate the xmlsec debug + :type: bool + + :param raise_exceptions: Whether to return false on failure or raise an exception + :type raise_exceptions: Boolean + """ + if xml is None or xml == '': + raise Exception('Empty string supplied as input') + + elem = OneLogin_Saml2_XML.to_etree(xml) + xmlsec.enable_debug_trace(debug) + xmlsec.tree.add_ids(elem, ["ID"]) + + signature_nodes = OneLogin_Saml2_XML.query(elem, '/md:EntitiesDescriptor/ds:Signature') + + if len(signature_nodes) == 0: + signature_nodes += OneLogin_Saml2_XML.query(elem, '/md:EntityDescriptor/ds:Signature') + + if len(signature_nodes) == 0: + signature_nodes += OneLogin_Saml2_XML.query(elem, '/md:EntityDescriptor/md:SPSSODescriptor/ds:Signature') + signature_nodes += OneLogin_Saml2_XML.query(elem, '/md:EntityDescriptor/md:IDPSSODescriptor/ds:Signature') + + if len(signature_nodes) > 0: + for signature_node in signature_nodes: + # Raises exception if invalid + OneLogin_Saml2_Utils.validate_node_sign(signature_node, elem, cert, fingerprint, fingerprintalg, validatecert, debug, raise_exceptions=True) + return True + else: + raise Exception('Could not validate metadata signature: No signature nodes found.')
    + +
    [docs] @staticmethod + @return_false_on_exception + def validate_node_sign(signature_node, elem, cert=None, fingerprint=None, fingerprintalg='sha1', validatecert=False, debug=False): + """ + Validates a signature node. + + :param signature_node: The signature node + :type: Node + + :param xml: The element we should validate + :type: Document + + :param cert: The public cert + :type: string + + :param fingerprint: The fingerprint of the public cert + :type: string + + :param fingerprintalg: The algorithm used to build the fingerprint + :type: string + + :param validatecert: If true, will verify the signature and if the cert is valid. + :type: bool + + :param debug: Activate the xmlsec debug + :type: bool + + :param raise_exceptions: Whether to return false on failure or raise an exception + :type raise_exceptions: Boolean + """ + if (cert is None or cert == '') and fingerprint: + x509_certificate_nodes = OneLogin_Saml2_XML.query(signature_node, '//ds:Signature/ds:KeyInfo/ds:X509Data/ds:X509Certificate') + if len(x509_certificate_nodes) > 0: + x509_certificate_node = x509_certificate_nodes[0] + x509_cert_value = OneLogin_Saml2_XML.element_text(x509_certificate_node) + x509_cert_value_formatted = OneLogin_Saml2_Utils.format_cert(x509_cert_value) + x509_fingerprint_value = OneLogin_Saml2_Utils.calculate_x509_fingerprint(x509_cert_value_formatted, fingerprintalg) + if fingerprint == x509_fingerprint_value: + cert = x509_cert_value_formatted + + if cert is None or cert == '': + raise OneLogin_Saml2_Error( + 'Could not validate node signature: No certificate provided.', + OneLogin_Saml2_Error.CERT_NOT_FOUND + ) + + # Check if Reference URI is empty + # reference_elem = OneLogin_Saml2_XML.query(signature_node, '//ds:Reference') + # if len(reference_elem) > 0: + # if reference_elem[0].get('URI') == '': + # reference_elem[0].set('URI', '#%s' % signature_node.getparent().get('ID')) + + if validatecert: + manager = xmlsec.KeysManager() + manager.load_cert_from_memory(cert, xmlsec.KeyFormat.CERT_PEM, xmlsec.KeyDataType.TRUSTED) + dsig_ctx = xmlsec.SignatureContext(manager) + else: + dsig_ctx = xmlsec.SignatureContext() + dsig_ctx.key = xmlsec.Key.from_memory(cert, xmlsec.KeyFormat.CERT_PEM, None) + + dsig_ctx.set_enabled_key_data([xmlsec.KeyData.X509]) + + try: + dsig_ctx.verify(signature_node) + except Exception as err: + raise OneLogin_Saml2_ValidationError( + 'Signature validation failed. SAML Response rejected. %s', + OneLogin_Saml2_ValidationError.INVALID_SIGNATURE, + str(err) + ) + + return True
    + +
    [docs] @staticmethod + def sign_binary(msg, key, algorithm=xmlsec.Transform.RSA_SHA256, debug=False): + """ + Sign binary message + + :param msg: The element we should validate + :type: bytes + + :param key: The private key + :type: string + + :param debug: Activate the xmlsec debug + :type: bool + + :return signed message + :rtype str + """ + + if isinstance(msg, str): + msg = msg.encode('utf8') + + xmlsec.enable_debug_trace(debug) + dsig_ctx = xmlsec.SignatureContext() + dsig_ctx.key = xmlsec.Key.from_memory(key, xmlsec.KeyFormat.PEM, None) + return dsig_ctx.sign_binary(compat.to_bytes(msg), algorithm)
    + +
    [docs] @staticmethod + def validate_binary_sign(signed_query, signature, cert=None, algorithm=OneLogin_Saml2_Constants.RSA_SHA256, debug=False): + """ + Validates signed binary data (Used to validate GET Signature). + + :param signed_query: The element we should validate + :type: string + + + :param signature: The signature that will be validate + :type: string + + :param cert: The public cert + :type: string + + :param algorithm: Signature algorithm + :type: string + + :param debug: Activate the xmlsec debug + :type: bool + """ + try: + xmlsec.enable_debug_trace(debug) + dsig_ctx = xmlsec.SignatureContext() + dsig_ctx.key = xmlsec.Key.from_memory(cert, xmlsec.KeyFormat.CERT_PEM, None) + + sign_algorithm_transform_map = { + OneLogin_Saml2_Constants.DSA_SHA1: xmlsec.Transform.DSA_SHA1, + OneLogin_Saml2_Constants.RSA_SHA1: xmlsec.Transform.RSA_SHA1, + OneLogin_Saml2_Constants.RSA_SHA256: xmlsec.Transform.RSA_SHA256, + OneLogin_Saml2_Constants.RSA_SHA384: xmlsec.Transform.RSA_SHA384, + OneLogin_Saml2_Constants.RSA_SHA512: xmlsec.Transform.RSA_SHA512 + } + sign_algorithm_transform = sign_algorithm_transform_map.get(algorithm, xmlsec.Transform.RSA_SHA256) + + dsig_ctx.verify_binary(compat.to_bytes(signed_query), + sign_algorithm_transform, + compat.to_bytes(signature)) + return True + except xmlsec.Error as e: + if debug: + print(e) + return False
    + +
    [docs] @staticmethod + def normalize_url(url): + """ + Returns normalized URL for comparison. + This method converts the netloc to lowercase, as it should be case-insensitive (per RFC 4343, RFC 7617) + If standardization fails, the original URL is returned + Python documentation indicates that URL split also normalizes query strings if empty query fields are present + + :param url: URL + :type url: String + + :returns: A normalized URL, or the given URL string if parsing fails + :rtype: String + """ + try: + scheme, netloc, path, query, fragment = urlsplit(url) + normalized_url = urlunsplit((scheme.lower(), netloc.lower(), path, query, fragment)) + return normalized_url + except Exception: + return url
    +
    + +
    +
    + +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/docs/saml2/_modules/onelogin/saml2/xml_templates.html b/docs/saml2/_modules/onelogin/saml2/xml_templates.html new file mode 100644 index 00000000..13dd9b94 --- /dev/null +++ b/docs/saml2/_modules/onelogin/saml2/xml_templates.html @@ -0,0 +1,256 @@ + + + + + + onelogin.saml2.xml_templates — SAML Python2/3 Toolkit 1 documentation + + + + + + + + + + + + + + + +
    + + +
    + +
    +
    +
    + +
    +
    +
    +
    + +

    Source code for onelogin.saml2.xml_templates

    +# -*- coding: utf-8 -*-
    +
    +""" OneLogin_Saml2_Auth class
    +
    +
    +Main class of SAML Python Toolkit.
    +
    +Initializes the SP SAML instance
    +
    +"""
    +
    +
    +
    [docs]class OneLogin_Saml2_Templates(object): + + ATTRIBUTE = """ + <saml:Attribute Name="%s" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"> + <saml:AttributeValue xsi:type="xs:string">%s</saml:AttributeValue> + </saml:Attribute>""" + + AUTHN_REQUEST = """\ +<samlp:AuthnRequest + xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" + xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" + ID="%(id)s" + Version="2.0"%(provider_name)s%(force_authn_str)s%(is_passive_str)s + IssueInstant="%(issue_instant)s" + Destination="%(destination)s" + ProtocolBinding="%(acs_binding)s" + AssertionConsumerServiceURL="%(assertion_url)s"%(attr_consuming_service_str)s> + <saml:Issuer>%(entity_id)s</saml:Issuer>%(subject_str)s%(nameid_policy_str)s +%(requested_authn_context_str)s +</samlp:AuthnRequest>""" + + LOGOUT_REQUEST = """\ +<samlp:LogoutRequest + xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" + xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" + ID="%(id)s" + Version="2.0" + IssueInstant="%(issue_instant)s" + Destination="%(single_logout_url)s"> + <saml:Issuer>%(entity_id)s</saml:Issuer> + %(name_id)s + %(session_index)s +</samlp:LogoutRequest>""" + + LOGOUT_RESPONSE = """\ +<samlp:LogoutResponse + xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" + xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" + ID="%(id)s" + Version="2.0" + IssueInstant="%(issue_instant)s" + Destination="%(destination)s" + InResponseTo="%(in_response_to)s"> + <saml:Issuer>%(entity_id)s</saml:Issuer> + <samlp:Status> + <samlp:StatusCode Value="%(status)s" /> + </samlp:Status> +</samlp:LogoutResponse>""" + + MD_CONTACT_PERSON = """\ + <md:ContactPerson contactType="%(type)s"> + <md:GivenName>%(name)s</md:GivenName> + <md:EmailAddress>%(email)s</md:EmailAddress> + </md:ContactPerson>""" + + MD_SLS = """\ + <md:SingleLogoutService Binding="%(binding)s" + Location="%(location)s" />\n""" + + MD_REQUESTED_ATTRIBUTE = """\ + <md:RequestedAttribute Name="%(req_attr_name)s"%(req_attr_nameformat_str)s%(req_attr_isrequired_str)s%(req_attr_aux_str)s""" + + MD_ATTR_CONSUMER_SERVICE = """\ + <md:AttributeConsumingService index="1"> + <md:ServiceName xml:lang="en">%(service_name)s</md:ServiceName> +%(attr_cs_desc)s%(requested_attribute_str)s + </md:AttributeConsumingService>\n""" + + MD_ENTITY_DESCRIPTOR = """\ +<?xml version="1.0"?> +<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" + %(valid)s + %(cache)s + entityID="%(entity_id)s"> + <md:SPSSODescriptor AuthnRequestsSigned="%(authnsign)s" WantAssertionsSigned="%(wsign)s" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> +%(sls)s <md:NameIDFormat>%(name_id_format)s</md:NameIDFormat> + <md:AssertionConsumerService Binding="%(binding)s" + Location="%(location)s" + index="1" /> +%(attribute_consuming_service)s </md:SPSSODescriptor> +%(organization)s +%(contacts)s +</md:EntityDescriptor>""" + + MD_ORGANISATION = """\ + <md:Organization> + <md:OrganizationName xml:lang="%(lang)s">%(name)s</md:OrganizationName> + <md:OrganizationDisplayName xml:lang="%(lang)s">%(display_name)s</md:OrganizationDisplayName> + <md:OrganizationURL xml:lang="%(lang)s">%(url)s</md:OrganizationURL> + </md:Organization>""" + + RESPONSE = """\ +<samlp:Response + xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" + xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" + ID="%(id)s" + InResponseTo="%(in_response_to)s" + Version="2.0" + IssueInstant="%(issue_instant)s" + Destination="%(destination)s"> + <saml:Issuer>%(entity_id)s</saml:Issuer> + <samlp:Status xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"> + <samlp:StatusCode + xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" + Value="%(status)s"> + </samlp:StatusCode> + </samlp:Status> + <saml:Assertion + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:xs="http://www.w3.org/2001/XMLSchema" + Version="2.0" + ID="%(assertion_id)s" + IssueInstant="%(issue_instant)s"> + <saml:Issuer>%(entity_id)s</saml:Issuer> + <saml:Subject> + <saml:NameID + NameQualifier="%(entity_id)s" + SPNameQualifier="%(requester)s" + Format="%(name_id_policy)s">%(name_id)s</saml:NameID> + <saml:SubjectConfirmation Method="%(cm)s"> + <saml:SubjectConfirmationData + NotOnOrAfter="%(not_after)s" + InResponseTo="%(in_response_to)s" + Recipient="%(destination)s"> + </saml:SubjectConfirmationData> + </saml:SubjectConfirmation> + </saml:Subject> + <saml:Conditions NotBefore="%(not_before)s" NotOnOrAfter="%(not_after)s"> + <saml:AudienceRestriction> + <saml:Audience>%(requester)s</saml:Audience> + </saml:AudienceRestriction> + </saml:Conditions> + <saml:AuthnStatement + AuthnInstant="%(issue_instant)s" + SessionIndex="%(session_index)s" + SessionNotOnOrAfter="%(not_after)s"> +%(authn_context)s + </saml:AuthnStatement> + <saml:AttributeStatement> +%(attributes)s + </saml:AttributeStatement> + </saml:Assertion> +</samlp:Response>"""
    +
    + +
    +
    + +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/docs/saml2/_modules/onelogin/saml2/xml_utils.html b/docs/saml2/_modules/onelogin/saml2/xml_utils.html new file mode 100644 index 00000000..3c26761b --- /dev/null +++ b/docs/saml2/_modules/onelogin/saml2/xml_utils.html @@ -0,0 +1,276 @@ + + + + + + onelogin.saml2.xml_utils — SAML Python2/3 Toolkit 1 documentation + + + + + + + + + + + + + + + +
    + + +
    + +
    +
    +
    + +
    +
    +
    +
    + +

    Source code for onelogin.saml2.xml_utils

    +# -*- coding: utf-8 -*-
    +
    +""" OneLogin_Saml2_XML class
    +
    +
    +Auxiliary class of SAML Python Toolkit.
    +
    +"""
    +
    +from os.path import join, dirname
    +from lxml import etree
    +from onelogin.saml2 import compat
    +from onelogin.saml2.constants import OneLogin_Saml2_Constants
    +from onelogin.saml2.xmlparser import tostring, fromstring
    +
    +
    +for prefix, url in OneLogin_Saml2_Constants.NSMAP.items():
    +    etree.register_namespace(prefix, url)
    +
    +
    +
    [docs]class OneLogin_Saml2_XML(object): + _element_class = type(etree.Element('root')) + _parse_etree = staticmethod(fromstring) + _schema_class = etree.XMLSchema + _text_class = compat.text_types + _bytes_class = compat.bytes_type + _unparse_etree = staticmethod(tostring) + + dump = staticmethod(etree.dump) + make_root = staticmethod(etree.Element) + make_child = staticmethod(etree.SubElement) + +
    [docs] @staticmethod + def to_string(xml, **kwargs): + """ + Serialize an element to an encoded string representation of its XML tree. + :param xml: The root node + :type xml: str|bytes|xml.dom.minidom.Document|etree.Element + :returns: string representation of xml + :rtype: string + """ + + if isinstance(xml, OneLogin_Saml2_XML._text_class): + return xml + + if isinstance(xml, OneLogin_Saml2_XML._element_class): + OneLogin_Saml2_XML.cleanup_namespaces(xml) + return OneLogin_Saml2_XML._unparse_etree(xml, **kwargs) + + raise ValueError("unsupported type %r" % type(xml))
    + +
    [docs] @staticmethod + def to_etree(xml): + """ + Parses an XML document or fragment from a string. + :param xml: the string to parse + :type xml: str|bytes|xml.dom.minidom.Document|etree.Element + :returns: the root node + :rtype: OneLogin_Saml2_XML._element_class + """ + if isinstance(xml, OneLogin_Saml2_XML._element_class): + return xml + if isinstance(xml, OneLogin_Saml2_XML._bytes_class): + return OneLogin_Saml2_XML._parse_etree(xml, forbid_dtd=True, forbid_entities=True) + if isinstance(xml, OneLogin_Saml2_XML._text_class): + return OneLogin_Saml2_XML._parse_etree(compat.to_bytes(xml), forbid_dtd=True, forbid_entities=True) + + raise ValueError('unsupported type %r' % type(xml))
    + +
    [docs] @staticmethod + def validate_xml(xml, schema, debug=False): + """ + Validates a xml against a schema + :param xml: The xml that will be validated + :type xml: str|bytes|xml.dom.minidom.Document|etree.Element + :param schema: The schema + :type schema: string + :param debug: If debug is active, the parse-errors will be showed + :type debug: bool + :returns: Error code or the DomDocument of the xml + :rtype: xml.dom.minidom.Document + """ + + assert isinstance(schema, compat.str_type) + try: + xml = OneLogin_Saml2_XML.to_etree(xml) + except Exception as e: + if debug: + print(e) + return 'unloaded_xml' + + schema_file = join(dirname(__file__), 'schemas', schema) + with open(schema_file, 'r') as f_schema: + xmlschema = OneLogin_Saml2_XML._schema_class(etree.parse(f_schema)) + + if not xmlschema.validate(xml): + if debug: + print('Errors validating the metadata: ') + for error in xmlschema.error_log: + print(error.message) + return 'invalid_xml' + return xml
    + +
    [docs] @staticmethod + def query(dom, query, context=None, tagid=None): + """ + Extracts nodes that match the query from the Element + + :param dom: The root of the lxml objet + :type: Element + + :param query: Xpath Expresion + :type: string + + :param context: Context Node + :type: DOMElement + + :param tagid: Tag ID + :type query: String + + :returns: The queried nodes + :rtype: list + """ + if context is None: + source = dom + else: + source = context + + if tagid is None: + return source.xpath(query, namespaces=OneLogin_Saml2_Constants.NSMAP) + else: + return source.xpath(query, tagid=tagid, namespaces=OneLogin_Saml2_Constants.NSMAP)
    + +
    [docs] @staticmethod + def cleanup_namespaces(tree_or_element, top_nsmap=None, keep_ns_prefixes=None): + """ + Keeps the xmlns:xs namespace intact when etree.cleanup_namespaces is invoked. + :param tree_or_element: An XML tree or element + :type tree_or_element: etree.Element + :param top_nsmap: A mapping from namespace prefixes to namespace URIs + :type top_nsmap: dict + :param keep_ns_prefixes: List of prefixes that should not be removed as part of the cleanup + :type keep_ns_prefixes: list + :returns: An XML tree or element + :rtype: etree.Element + """ + all_prefixes_to_keep = [ + OneLogin_Saml2_Constants.NS_PREFIX_XS, + OneLogin_Saml2_Constants.NS_PREFIX_XSI, + OneLogin_Saml2_Constants.NS_PREFIX_XSD + ] + + if keep_ns_prefixes: + all_prefixes_to_keep = list(set(all_prefixes_to_keep.extend(keep_ns_prefixes))) + + return etree.cleanup_namespaces(tree_or_element, keep_ns_prefixes=all_prefixes_to_keep)
    + +
    [docs] @staticmethod + def extract_tag_text(xml, tagname): + open_tag = compat.to_bytes("<%s" % tagname) + close_tag = compat.to_bytes("</%s>" % tagname) + + xml = OneLogin_Saml2_XML.to_string(xml) + start = xml.find(open_tag) + assert start != -1 + + end = xml.find(close_tag, start) + len(close_tag) + assert end != -1 + return compat.to_string(xml[start:end])
    + +
    [docs] @staticmethod + def element_text(node): + # Double check, the LXML Parser already removes comments + etree.strip_tags(node, etree.Comment) + return node.text
    +
    + +
    +
    + +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/docs/saml2/_modules/onelogin/saml2/xmlparser.html b/docs/saml2/_modules/onelogin/saml2/xmlparser.html new file mode 100644 index 00000000..7058367c --- /dev/null +++ b/docs/saml2/_modules/onelogin/saml2/xmlparser.html @@ -0,0 +1,285 @@ + + + + + + onelogin.saml2.xmlparser — SAML Python2/3 Toolkit 1 documentation + + + + + + + + + + + + + + + +
    + + +
    + +
    +
    +
    + +
    +
    +
    +
    + +

    Source code for onelogin.saml2.xmlparser

    +# -*- coding: utf-8 -*-
    +
    +# Based on the lxml example from defusedxml
    +# DTDForbidden, EntitiesForbidden, NotSupportedError are clones of the classes defined at defusedxml
    +#
    +# Copyright (c) 2013 by Christian Heimes <christian@python.org>
    +# Licensed to PSF under a Contributor Agreement.
    +# See https://www.python.org/psf/license for licensing details.
    +"""lxml.etree protection"""
    +
    +from __future__ import print_function, absolute_import
    +
    +import threading
    +
    +from lxml import etree as _etree
    +
    +LXML3 = _etree.LXML_VERSION[0] >= 3
    +
    +__origin__ = "lxml.etree"
    +
    +tostring = _etree.tostring
    +
    +
    +
    [docs]class DTDForbidden(ValueError): + """Document type definition is forbidden + """ + + def __init__(self, name, sysid, pubid): + super(DTDForbidden, self).__init__() + self.name = name + self.sysid = sysid + self.pubid = pubid + + def __str__(self): + tpl = "DTDForbidden(name='{}', system_id={!r}, public_id={!r})" + return tpl.format(self.name, self.sysid, self.pubid)
    + + +
    [docs]class EntitiesForbidden(ValueError): + """Entity definition is forbidden + """ + + def __init__(self, name, value, base, sysid, pubid, notation_name): + super(EntitiesForbidden, self).__init__() + self.name = name + self.value = value + self.base = base + self.sysid = sysid + self.pubid = pubid + self.notation_name = notation_name + + def __str__(self): + tpl = "EntitiesForbidden(name='{}', system_id={!r}, public_id={!r})" + return tpl.format(self.name, self.sysid, self.pubid)
    + + +
    [docs]class NotSupportedError(ValueError): + """The operation is not supported + """
    + + +
    [docs]class RestrictedElement(_etree.ElementBase): + """A restricted Element class that filters out instances of some classes + """ + + __slots__ = () + blacklist = (_etree._Entity, _etree._ProcessingInstruction, _etree._Comment) + + def _filter(self, iterator): + blacklist = self.blacklist + for child in iterator: + if isinstance(child, blacklist): + continue + yield child + + def __iter__(self): + iterator = super(RestrictedElement, self).__iter__() + return self._filter(iterator) + +
    [docs] def iterchildren(self, tag=None, reversed=False): + iterator = super(RestrictedElement, self).iterchildren(tag=tag, reversed=reversed) + return self._filter(iterator)
    + +
    [docs] def iter(self, tag=None, *tags): + iterator = super(RestrictedElement, self).iter(tag=tag, *tags) + return self._filter(iterator)
    + +
    [docs] def iterdescendants(self, tag=None, *tags): + iterator = super(RestrictedElement, self).iterdescendants(tag=tag, *tags) + return self._filter(iterator)
    + +
    [docs] def itersiblings(self, tag=None, preceding=False): + iterator = super(RestrictedElement, self).itersiblings(tag=tag, preceding=preceding) + return self._filter(iterator)
    + +
    [docs] def getchildren(self): + iterator = super(RestrictedElement, self).__iter__() + return list(self._filter(iterator))
    + +
    [docs] def getiterator(self, tag=None): + iterator = super(RestrictedElement, self).getiterator(tag) + return self._filter(iterator)
    + + +
    [docs]class GlobalParserTLS(threading.local): + """Thread local context for custom parser instances + """ + + parser_config = { + "resolve_entities": False, + 'remove_comments': True, + 'no_network': True, + 'remove_pis': True, + 'huge_tree': False + } + + element_class = RestrictedElement + +
    [docs] def createDefaultParser(self): + parser = _etree.XMLParser(**self.parser_config) + element_class = self.element_class + if self.element_class is not None: + lookup = _etree.ElementDefaultClassLookup(element=element_class) + parser.set_element_class_lookup(lookup) + return parser
    + +
    [docs] def setDefaultParser(self, parser): + self._default_parser = parser
    + +
    [docs] def getDefaultParser(self): + parser = getattr(self, "_default_parser", None) + if parser is None: + parser = self.createDefaultParser() + self.setDefaultParser(parser) + return parser
    + + +_parser_tls = GlobalParserTLS() +getDefaultParser = _parser_tls.getDefaultParser + + +
    [docs]def check_docinfo(elementtree, forbid_dtd=False, forbid_entities=True): + """Check docinfo of an element tree for DTD and entity declarations + The check for entity declarations needs lxml 3 or newer. lxml 2.x does + not support dtd.iterentities(). + """ + docinfo = elementtree.docinfo + if docinfo.doctype: + if forbid_dtd: + raise DTDForbidden(docinfo.doctype, docinfo.system_url, docinfo.public_id) + if forbid_entities and not LXML3: + # lxml < 3 has no iterentities() + raise NotSupportedError("Unable to check for entity declarations " "in lxml 2.x") + + if forbid_entities: + for dtd in docinfo.internalDTD, docinfo.externalDTD: + if dtd is None: + continue + for entity in dtd.iterentities(): + raise EntitiesForbidden(entity.name, entity.content, None, None, None, None)
    + + +
    [docs]def parse(source, parser=None, base_url=None, forbid_dtd=True, forbid_entities=True): + if parser is None: + parser = getDefaultParser() + elementtree = _etree.parse(source, parser, base_url=base_url) + check_docinfo(elementtree, forbid_dtd, forbid_entities) + return elementtree
    + + +
    [docs]def fromstring(text, parser=None, base_url=None, forbid_dtd=True, forbid_entities=True): + if parser is None: + parser = getDefaultParser() + rootelement = _etree.fromstring(text, parser, base_url=base_url) + elementtree = rootelement.getroottree() + check_docinfo(elementtree, forbid_dtd, forbid_entities) + return rootelement
    + + +XML = fromstring + + +
    [docs]def iterparse(*args, **kwargs): + raise NotSupportedError("iterparse not available")
    +
    + +
    +
    + +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/docs/saml2/_sources/index.rst.txt b/docs/saml2/_sources/index.rst.txt new file mode 100644 index 00000000..020156af --- /dev/null +++ b/docs/saml2/_sources/index.rst.txt @@ -0,0 +1,14 @@ +.. SAML Python2/3 Toolkit documentation master file, created by + sphinx-quickstart on Sun Oct 1 03:00:42 2023. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to SAML Python2/3 Toolkit's documentation! +================================================== + +.. toctree:: + :maxdepth: 4 + :caption: Contents: + + onelogin + diff --git a/docs/saml2/_sources/modules.rst.txt b/docs/saml2/_sources/modules.rst.txt new file mode 100644 index 00000000..529aa5bd --- /dev/null +++ b/docs/saml2/_sources/modules.rst.txt @@ -0,0 +1,7 @@ +onelogin +======== + +.. toctree:: + :maxdepth: 4 + + onelogin diff --git a/docs/saml2/_sources/onelogin.rst.txt b/docs/saml2/_sources/onelogin.rst.txt new file mode 100644 index 00000000..33f8a252 --- /dev/null +++ b/docs/saml2/_sources/onelogin.rst.txt @@ -0,0 +1,18 @@ +onelogin package +================ + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + onelogin.saml2 + +Module contents +--------------- + +.. automodule:: onelogin + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/saml2/_sources/onelogin.saml2.rst.txt b/docs/saml2/_sources/onelogin.saml2.rst.txt new file mode 100644 index 00000000..3ba9d62b --- /dev/null +++ b/docs/saml2/_sources/onelogin.saml2.rst.txt @@ -0,0 +1,133 @@ +onelogin.saml2 package +====================== + +Submodules +---------- + +onelogin.saml2.auth module +-------------------------- + +.. automodule:: onelogin.saml2.auth + :members: + :undoc-members: + :show-inheritance: + +onelogin.saml2.authn\_request module +------------------------------------ + +.. automodule:: onelogin.saml2.authn_request + :members: + :undoc-members: + :show-inheritance: + +onelogin.saml2.compat module +---------------------------- + +.. automodule:: onelogin.saml2.compat + :members: + :undoc-members: + :show-inheritance: + +onelogin.saml2.constants module +------------------------------- + +.. automodule:: onelogin.saml2.constants + :members: + :undoc-members: + :show-inheritance: + +onelogin.saml2.errors module +---------------------------- + +.. automodule:: onelogin.saml2.errors + :members: + :undoc-members: + :show-inheritance: + +onelogin.saml2.idp\_metadata\_parser module +------------------------------------------- + +.. automodule:: onelogin.saml2.idp_metadata_parser + :members: + :undoc-members: + :show-inheritance: + +onelogin.saml2.logout\_request module +------------------------------------- + +.. automodule:: onelogin.saml2.logout_request + :members: + :undoc-members: + :show-inheritance: + +onelogin.saml2.logout\_response module +-------------------------------------- + +.. automodule:: onelogin.saml2.logout_response + :members: + :undoc-members: + :show-inheritance: + +onelogin.saml2.metadata module +------------------------------ + +.. automodule:: onelogin.saml2.metadata + :members: + :undoc-members: + :show-inheritance: + +onelogin.saml2.response module +------------------------------ + +.. automodule:: onelogin.saml2.response + :members: + :undoc-members: + :show-inheritance: + +onelogin.saml2.settings module +------------------------------ + +.. automodule:: onelogin.saml2.settings + :members: + :undoc-members: + :show-inheritance: + +onelogin.saml2.utils module +--------------------------- + +.. automodule:: onelogin.saml2.utils + :members: + :undoc-members: + :show-inheritance: + +onelogin.saml2.xml\_templates module +------------------------------------ + +.. automodule:: onelogin.saml2.xml_templates + :members: + :undoc-members: + :show-inheritance: + +onelogin.saml2.xml\_utils module +-------------------------------- + +.. automodule:: onelogin.saml2.xml_utils + :members: + :undoc-members: + :show-inheritance: + +onelogin.saml2.xmlparser module +------------------------------- + +.. automodule:: onelogin.saml2.xmlparser + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: onelogin.saml2 + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/saml2/_static/_sphinx_javascript_frameworks_compat.js b/docs/saml2/_static/_sphinx_javascript_frameworks_compat.js new file mode 100644 index 00000000..81415803 --- /dev/null +++ b/docs/saml2/_static/_sphinx_javascript_frameworks_compat.js @@ -0,0 +1,123 @@ +/* Compatability shim for jQuery and underscores.js. + * + * Copyright Sphinx contributors + * Released under the two clause BSD licence + */ + +/** + * small helper function to urldecode strings + * + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL + */ +jQuery.urldecode = function(x) { + if (!x) { + return x + } + return decodeURIComponent(x.replace(/\+/g, ' ')); +}; + +/** + * small helper function to urlencode strings + */ +jQuery.urlencode = encodeURIComponent; + +/** + * This function returns the parsed url parameters of the + * current request. Multiple values per key are supported, + * it will always return arrays of strings for the value parts. + */ +jQuery.getQueryParameters = function(s) { + if (typeof s === 'undefined') + s = document.location.search; + var parts = s.substr(s.indexOf('?') + 1).split('&'); + var result = {}; + for (var i = 0; i < parts.length; i++) { + var tmp = parts[i].split('=', 2); + var key = jQuery.urldecode(tmp[0]); + var value = jQuery.urldecode(tmp[1]); + if (key in result) + result[key].push(value); + else + result[key] = [value]; + } + return result; +}; + +/** + * highlight a given string on a jquery object by wrapping it in + * span elements with the given class name. + */ +jQuery.fn.highlightText = function(text, className) { + function highlight(node, addItems) { + if (node.nodeType === 3) { + var val = node.nodeValue; + var pos = val.toLowerCase().indexOf(text); + if (pos >= 0 && + !jQuery(node.parentNode).hasClass(className) && + !jQuery(node.parentNode).hasClass("nohighlight")) { + var span; + var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.className = className; + } + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + node.parentNode.insertBefore(span, node.parentNode.insertBefore( + document.createTextNode(val.substr(pos + text.length)), + node.nextSibling)); + node.nodeValue = val.substr(0, pos); + if (isInSVG) { + var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); + var bbox = node.parentElement.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute('class', className); + addItems.push({ + "parent": node.parentNode, + "target": rect}); + } + } + } + else if (!jQuery(node).is("button, select, textarea")) { + jQuery.each(node.childNodes, function() { + highlight(this, addItems); + }); + } + } + var addItems = []; + var result = this.each(function() { + highlight(this, addItems); + }); + for (var i = 0; i < addItems.length; ++i) { + jQuery(addItems[i].parent).before(addItems[i].target); + } + return result; +}; + +/* + * backward compatibility for jQuery.browser + * This will be supported until firefox bug is fixed. + */ +if (!jQuery.browser) { + jQuery.uaMatch = function(ua) { + ua = ua.toLowerCase(); + + var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || + /(webkit)[ \/]([\w.]+)/.exec(ua) || + /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || + /(msie) ([\w.]+)/.exec(ua) || + ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || + []; + + return { + browser: match[ 1 ] || "", + version: match[ 2 ] || "0" + }; + }; + jQuery.browser = {}; + jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; +} diff --git a/docs/saml2/_static/basic.css b/docs/saml2/_static/basic.css new file mode 100644 index 00000000..cfc60b86 --- /dev/null +++ b/docs/saml2/_static/basic.css @@ -0,0 +1,921 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +div.section::after { + display: block; + content: ''; + clear: left; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li p.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: 360px; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, figure.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, figure.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, figure.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, figure.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar, +aside.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px; + background-color: #ffe; + width: 40%; + float: right; + clear: right; + overflow-x: auto; +} + +p.sidebar-title { + font-weight: bold; +} + +nav.contents, +aside.topic, +div.admonition, div.topic, blockquote { + clear: left; +} + +/* -- topics ---------------------------------------------------------------- */ + +nav.contents, +aside.topic, +div.topic { + border: 1px solid #ccc; + padding: 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- content of sidebars/topics/admonitions -------------------------------- */ + +div.sidebar > :last-child, +aside.sidebar > :last-child, +nav.contents > :last-child, +aside.topic > :last-child, +div.topic > :last-child, +div.admonition > :last-child { + margin-bottom: 0; +} + +div.sidebar::after, +aside.sidebar::after, +nav.contents::after, +aside.topic::after, +div.topic::after, +div.admonition::after, +blockquote::after { + display: block; + content: ''; + clear: both; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + margin-top: 10px; + margin-bottom: 10px; + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > :first-child, +td > :first-child { + margin-top: 0px; +} + +th > :last-child, +td > :last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure, figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption, figcaption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number, +figcaption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text, +figcaption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist { + margin: 1em 0; +} + +table.hlist td { + vertical-align: top; +} + +/* -- object description styles --------------------------------------------- */ + +.sig { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; +} + +.sig-name, code.descname { + background-color: transparent; + font-weight: bold; +} + +.sig-name { + font-size: 1.1em; +} + +code.descname { + font-size: 1.2em; +} + +.sig-prename, code.descclassname { + background-color: transparent; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.sig-param.n { + font-style: italic; +} + +/* C++ specific styling */ + +.sig-inline.c-texpr, +.sig-inline.cpp-texpr { + font-family: unset; +} + +.sig.c .k, .sig.c .kt, +.sig.cpp .k, .sig.cpp .kt { + color: #0033B3; +} + +.sig.c .m, +.sig.cpp .m { + color: #1750EB; +} + +.sig.c .s, .sig.c .sc, +.sig.cpp .s, .sig.cpp .sc { + color: #067D17; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +:not(li) > ol > li:first-child > :first-child, +:not(li) > ul > li:first-child > :first-child { + margin-top: 0px; +} + +:not(li) > ol > li:last-child > :last-child, +:not(li) > ul > li:last-child > :last-child { + margin-bottom: 0px; +} + +ol.simple ol p, +ol.simple ul p, +ul.simple ol p, +ul.simple ul p { + margin-top: 0; +} + +ol.simple > li:not(:first-child) > p, +ul.simple > li:not(:first-child) > p { + margin-top: 0; +} + +ol.simple p, +ul.simple p { + margin-bottom: 0; +} + +aside.footnote > span, +div.citation > span { + float: left; +} +aside.footnote > span:last-of-type, +div.citation > span:last-of-type { + padding-right: 0.5em; +} +aside.footnote > p { + margin-left: 2em; +} +div.citation > p { + margin-left: 4em; +} +aside.footnote > p:last-of-type, +div.citation > p:last-of-type { + margin-bottom: 0em; +} +aside.footnote > p:last-of-type:after, +div.citation > p:last-of-type:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > :first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +.sig dd { + margin-top: 0px; + margin-bottom: 0px; +} + +.sig dl { + margin-top: 0px; + margin-bottom: 0px; +} + +dl > dd:last-child, +dl > dd:last-child > :last-child { + margin-bottom: 0; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0 0.5em; + content: ":"; + display: inline-block; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +.translated { + background-color: rgba(207, 255, 207, 0.2) +} + +.untranslated { + background-color: rgba(255, 207, 207, 0.2) +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +pre, div[class*="highlight-"] { + clear: both; +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; + white-space: nowrap; +} + +div[class*="highlight-"] { + margin: 1em 0; +} + +td.linenos pre { + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + display: block; +} + +table.highlighttable tbody { + display: block; +} + +table.highlighttable tr { + display: flex; +} + +table.highlighttable td { + margin: 0; + padding: 0; +} + +table.highlighttable td.linenos { + padding-right: 0.5em; +} + +table.highlighttable td.code { + flex: 1; + overflow: hidden; +} + +.highlight .hll { + display: block; +} + +div.highlight pre, +table.highlighttable pre { + margin: 0; +} + +div.code-block-caption + div { + margin-top: 0; +} + +div.code-block-caption { + margin-top: 1em; + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +table.highlighttable td.linenos, +span.linenos, +div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; + -webkit-user-select: text; /* Safari fallback only */ + -webkit-user-select: none; /* Chrome/Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE10+ */ +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + margin: 1em 0; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: absolute; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/docs/saml2/_static/css/badge_only.css b/docs/saml2/_static/css/badge_only.css new file mode 100644 index 00000000..c718cee4 --- /dev/null +++ b/docs/saml2/_static/css/badge_only.css @@ -0,0 +1 @@ +.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}} \ No newline at end of file diff --git a/docs/saml2/_static/css/fonts/Roboto-Slab-Bold.woff b/docs/saml2/_static/css/fonts/Roboto-Slab-Bold.woff new file mode 100644 index 0000000000000000000000000000000000000000..6cb60000181dbd348963953ac8ac54afb46c63d5 GIT binary patch literal 87624 zcmaI71zemx*C#x!Tp zndIaoGr4k-bN9U&_Lhd8SbF`U&{aS5&tGC24eIF6>x)sAOb&v zfVhIZGKkgz05Gxu09p-Ln#TZfWmRDSfawxMKLh|EoVkQZ`Q(-Vma{B@>M4POeg`;B zkdcjnJpjN;2LM2A0syd<0h`_}My}4p000*vh=&mrIB6Qd!%gkYY(O+#0043i0Dy~+ zMDP?cGjIac*g%2((WW-Z z97F_wef;$GNYK zfxA5bOcYe@pSr|Q_wavg4Qxz6G!PGXCa5nlCp;7+_I6Ir05EaTdqH{!{e&2vHVd-7 zqY0?4Du@P%1cew_u&6xu6(fCPef=#1e*gtEa_Fq!$Vh2VDfAaI9A$rFawGD%3Zn{` zgy^VfK}VWhXJU_#D|iSpz)(AE6ae79l9T`z{7Dgec+=K{^=9K?!wUkQ%eaTrpjIC> zLC8Nb@pFsd7ck_Sk!=816dlWeVYWSNRMZzZ%}6%bZDUA)+~NZV;g9^cr|GFKyZe`} zidYTZm7dU!k6>K<5q`*>Dao$Y2>XfSh@4lX_chMROUufP07Bu;w~|>J@*~h z8aP=_3{}bwwX%57OdFGJj?3eh?_+r|_=znRWSa|kViSC$RK)Ok@HyQrquqh1QhUm2 zD#axlDzU|}+qJuj4PN`wdW1Q8w#UyVncX4X1;k;KqNy&nG-avs3m&sQqsS_7#K?e| z)9F;OQ!VEQ%1Qf(Y|eN+2lxU}?rMDt1nhIO>18ni9TBcQ4`8!U*6eXw%5OuafEU=M zwS%l$`22YQyA8YF*h3ZaT_6lZIAm*v7dFfhg1$5=H^f)z%E@iat(7w-QOoT{3(4)~ z>cHV9nMzvk=|x;0r~8FU5u%2{?xjU`UU^#WHgM&BANT1*`K1sX!83!8KiG*V82yx5 zBx8pm+K>F!(2D-b6Co81jUK2|S8E@zTp#2Ufm(hT5V@_Z#HZsaf1oaKyOv{%w0H5_ zF}kq>VThTK0kHmIOHDSb|MS6asI}pF(lz)h3>i=(L~5xZ5%ZO4hJ>e&6bqi1`$qxf zPTr?6Vz4nNi~<%Q37jRQ@=rM?^5Z;yB?B2Iqyy+#Lx?6~f+hnP79({gynD#{T|p)o zE{8-e&8OK-0s?8KfNj9tEK4B8RC$x-Zs2hl zAp%2Vnc`G2)ij&Z?P;4h& z%<3zlRmIRw#E=zlj%7Z@PCA7ZOO6$=cqgRTid)aJ*mNh^)QV8gDgbk<6Vn2X|4&Ik zY*WE@yAd}X`%_M8*u61)~Lr`Cu}mS*kN&o^z+?JT)oEtJwN%`de{ zVV>CI9!cW0fy3_Tw4QBdHKB)(uvSlLu?{uzk2GPAejn44UHjTur#xN#)V|xzS;r{v zJ#o#?|rTB2Nzj~0wg0)B;Y#eq$=S|I=H05;jlVrq)OouufyhCVv;G4ikyye zt9q-1t4$@If8|ZvNPa&3zQx9AskF&!-ihX(=c0qn&$u%+orBbFAUaBYypyzpbOL_c z*PY#7AjL9BvkGHSftjR&+ZpD8JnlJ%7|jvtVNhYUmpHpEvYE-CD(rE+XuXd|Z6hJS zvLj?n&L%}=GSS(ko?AI{$pWil$->0!=c5EonyH#sgUWN`B;tY^#&}h{imd_c1B(QD zL$vZnQa1XCB`RWbX7Y;dLZFM`?oO-fi%eE<8YCS1DAQN>V61MQONDas4iiV=ysO`y zPFV|%GlZ;SC>gCNUrg>lX8F zy_yfLhE{;u%TviO#PqKJrbQVu4`B*EUA6-0De%WuSFgR)+}qiuLfrYt)hnrA~eu9CPLJY7CI>6paS zWnopw;$U)dp^e}K+3}Y&a@*xhfM}R|{p`3LBacr-0@@jdb$DYK?&I$w^NSzRrP_ObwH`u$VHUzG=(mgYH-8BkFliqhRIf0BGd z#SQg;0fKNb#@807bm?Drgy!lpM{LR48+WFs5(5dCRWWRk+F6%u!UC!_e|G-NAr_e& zkhjKs_ucr>s~%Vl?bq@7jQ0$36KTUBuL?@_DYrCJsOXJ$Y<%D<#UD9hAiKhziB?l{ z+@3`ziu0ITPg|%c2ncA@g=VtiSPCbJ6n%WEhX-?xw^!rQv@vT8nwRq?U+&teVHix6 z?zsBZFV{XuCaJAy)0DX&{jBMtI-uo7(#+pMpP{GQ%-HqM&}N zunOPt%jUEFRew`XR|b8$R#@!TW*RD)Lmyx8k9~^#iXhmW%OvI9{o5gwvKwbxO-Pr9 zrHL8uC0;lh1-W>*+wP)oZXv-n8PSUr9c@>~L*?3tB`{h`R5jcBC;`!sC*ay}P3YF- zOHx+}f^xY%^qt^rl;*2M-0sVu0O`#xK|d|V2Qchx2z-oqKg=uf{9PTB-=1CMHOX}w z5ik+PI%J9ATOLj_NS9a6sqdVXRmc$`@|{FPde3Ld@i=>DCcZ9vU4_8Pt@60L=3{Ddonu+Kt8=A&#Vn{1Ypkd|)aXDP#76Yobb7n%}Flnxrd9qH4- zWI~QjPwDfrhjA9no=4E%bL6QaE@56ZdTHg}5v+wEZ3?%SxQv=RuOi0^w~E>&huyhO z<&aJvb9)GNqf>5R2$CP~*2xmZXFtOc67KBLCroJ-^HXR(Q@yh1Ym~M$BF}dXymZb; zk>B~hz{vA7M=mt7RyFZ=*!h6O412ly#z@538Xo1Q%QXY_ zis@n>*p35+Jl|D=G8Wpv4CII7V^V(+HiL=1WJu)d#D2=;A^ULM(34*W-VzRN^APY1 zkhD6f&96yS+dXkE;QJKLjtK>wH@ItlmA*cE0+Tn$X1zfoxOK#8Y4e!KSQ016l1FTS_y;IU~ zH0H@KQe66>d{J0yJF!U30V2p|jjwpP~RQcZ^;^$KDSF(j~Z6 zm9$oU#i>!atd#+3?Gb6T65}nLkV@-?ZE6^KF87fk5twD`FPTW)uDAlX;VGsO6izN0 z^Zi9Jr}G(4_W~ix1M(=E*;L`Y@`9b|Z2{J5;X$4yw4?MBL<>5=7ipHZ#He$Bqkw_h z>4)%&V#x$ZWi(mi=BQKlg!ONdCONiu3p90^w&(fiDJNQ-2N{i*E`OJRb7xXANuFPP zVjbTG#N|@OJ2&oQu6BsxlSt>6I9Z#v zu$#o3+v4i?$vn9P%7?nx1O>)%-!huKh1e5ei4WyQ{69$o*73?hEi4^L|PM6o3OZtv{obc z&^9FkXsSNueb?fEWGBaqZYg-?9Qn2HM|E(mEA%4SDm-WRD+CQ*>BTHu_sCe zEtvvN11~9xQ;IPTSwyAbnKP=K5s6$OK;z-+S*|Q88@U2xmolu#**gnC5nKlfGY9rV zdxpco&ZC=Fe*_EMZh4N~d?JoQ#(VzBeWE?`x)AH5mQ+t&+GVY#cpDR*Wj)tIR^67U z@gpgY)%J11x{_0J&>yI)?jUKBh@B%W@(Jru-XOn7F{-F=h%yW0k~4%?PM?xFNV_3@ zQBO0A%1qcBMv_GG(4vz&9`2MBS?2W3&B|N<#-pA?r2R$qY_ZR`(%eS0Df&C*ne~Mr zCAXI>*0SuqQO#R*?R4Wkk>x9HdeV}K4-Zj$_{=(WXD)GN$W#jAL$20vwPD&q*& zK6rc#Y2OZv}J>(0U_y@);yb6iiTJo;V=z!?!ju|Jm2_o zeZI|odXun6**3LT8a}ZYBi?#LNzbO*)~oWrGO0CemvCPDZ z1(^{WXJFJ+&azKH<)Mk2kSY^ zs_$-lh>7D>*`2%tSFhX~ToY9-EVe&G0ec~2T10UPwF?%n|KQT*k>M1Ur@yL($D8Or z)F&&Q#7w$_DuBlT{iTg?5>b6 zYI7KuM$~c#OI*9xvk7l^EQ^^VO}s~>Vp=v4zEY&#-xi|;?RGi;Uw&cd&HLDA)S{sP zLl=9j5^2bH^Z0$FOIYKAE;p%JVi&ebtG%nIoo|6Y?R;51!W5 z4I~R;7{UWIc#X+n?>@7NeJA#h*Ynkmm!{kFtik21{?@1+x$~ISgwU^f5GXgWP!$J8 z{M)Px*Ib#q><@;GJ4AEY*9kVy>MKsQ*YWO{TclTJc(4wN8)>!f7IE>_Yv%VIyuKyAL;H1Rq5w!h1 zZ3dp0Cd~Z8wS`U4=kabMC9TDrQ8r;rZ8iB5-o;#yOs)j(4EtM1y2|z!xr0x@nFO_l zJc9Kv{y7B*P}H^thk@Ac1kxpe%J z?z4G+@&$3N#InXh@s5(_Y-?iP@G{mVb#9muk>f`e)PcufO+CCAn~ZE{Ev->nohPzA zlx4J+d{4(6Tz^d`8ycfJq#EX&LaFt5Ce3hy`&{dh@GGeoG^PiaoHrUhiF7+SIVQ~0 zH>A>&yH#=faF(iV9xT895kg+G`^8ri?7pvWniK3zG7KE|c{-ysM&i7YaB~j>HMJ8( ze4R)A`qw+1W!|Bzf$>**n{P1x(GhyQas0UmEpf$HIL07TCx{)F&2+-ZVT_ zbJ&9`s^g||GWesGPnS(}}GYKk(r;UoZ) z6}B*CNMKeQ!>V>1^_kNMYD%T7US;bviJKM*_+9+&q|}#SzPjMqMWs!pNLuyZNU#V& zr9x#;O7*`*f{jwD<^Mz~$?z(rf+3(N27X>Kj~l4`lLiW?@Dm;sZDAxoe=FiwER$C` z{$I&0jL(nXpnBU7bLy{~-PF{dihLS7rkY4z?-{IG-#0fb=IXmH;zbHxkdNjUUgMpWGnm6Db@C4DYp!#4C1!9gGMp3NT0*>ixyB&R zBxMYWeQVdI!F;)%Ro|}{f`JXuqP|wL4sR=XI^7eNshR|^B72VTHjJc3FKW5BCy&{h zgqL2{Khn>yGb^a(19;`vZg8ex#fI3D7dg~FoLPqk@^3kZSXUqMjjCKEi8JvJ^O~$r zfu4C|O);X9ct+WGAAh_GGEc3%1dfh;S^NXm@JqflV~^LOT`i-$38<-)I@c6fQ!|L7jN(7}5EZUu~;6m0s| zrqi%14?Y3i18989elP}u(YTUBcw`E%E)Lcyd||y`&hJze?Y>9!iamRw-X<=>&yOm= z-wlQ@DZ^q^xOysESRvT^Tt}%s#A5bSlO}gNO5fP}0I?%(O7+U%pOrD%9{)45wtwNHWt9ByY zo#Zu}_4iV``Kb(@Jw=s0MVBiDCJ)AHe=_0#2>gu;zkG_XjolPWw(^XnH_Ur31cU_kp_LQ2fz5B%l*`Fo_a{Vnln~e>#6}#BP93D9^)@Yw zs_(KRF#8{kXQ0k?VWdZOhZ(ok;@p?LW?r`WB-t;yUPuy?5@^R9xW+zwEeoz3d z7Qf&*q3C8uyY&O+I}-wQO8P`JrdFRrny_lcy#&bd3zI~W9FmN(!Z(X%T22(7+>|RD zc}8fBryq5>Q;W}IGMHs}{tl0fHwRzL)dcfPo9Tu|Q_Ka$StHMk=7)S8?Rvutv&4&- z?eD3>4@-f8e?-0QA5lj>0lnd<=^LeaPK`exYra?Nywd@yzl|yr5%c|Cz4gGl&=Hg!_dL#Oj(iKoa^q7eX z11JO35!+{3*s)a|FVz})_8NO$wRx+oeV3~2W?T4PMq{eNZ1k3_;YYskJ>u@6Q_8gB zANpPM>R-k)wck-cOjVpy@0y36X&c|Wn%}$Bx1;{asUAg1nW=Cay`3Q&^>gK*W|er# zT1e8qqBO8xRv!Cdh@HrT6z*v|$aqGu`Ci^B*Dm1|c}tImJmUCKoe9rXMswZ=9ObUd zsfDgXE13;W8Cn@dzLt7Hn&BrM|BpUXX{iVHNoGw@s}!Y}m1BiuIXf}r71jRl769|r z>OZpfGGP*b1%H8|%IMCX8JtxZ>e}RLlX2Yq%TDy<(Pn0GN#AJkc(cMUvm={#w;*bB z)clP(s-HuWW!~41nLm?@cZwJD@=K(9sF~)(O~;6mnrE!4_W&+`QJl-C+5p7Nr9Qoz zC2_bZ*?kV>kk@ivwC>3DO^!f#`=^%K}HM`PYgIBz{T zbh0iz^YfaVr5Qy>AmG#VuG8%TzP!h2XycLB-mtJ>hWFO>%rT6T0I~%>zz7?CNE6fZ zzI)u&`w)>Qd7UxWBdh4GPh7pl5wvRzZ-x{%6n;Jr7k2af6cF%IQfB&RVWt@D~I23E1I$WZhcfCB}R>nOS&Es=nE5-K9_M6eF zT&nEIye;MG_{Ob4+`ImhTdnl5t5oSFpH4_3XS#B!6yGN2zj)AeEuBBIo-53Wi}74C zcN$7ZVzz~PJt}2mSE<^9Tzj0ouF@LRPKN7M_`wT*M&lsm1pq8WMDeJAh z(*GM=yIldV)+JXTkKG$~jDG#*OCyjN;#jFeDUd4a*tuvI_kAR4jf!J*vdJ!9`>y-? zse7BJsXlT2G;fLb!O0)~h7T=w%2NOA`$Z=2ONkXFfk@>qNe1S7^pKU4C{;byeaxoN z<7Br*7;DCd$xQg=GD{7cvJ~g7F$G$e0S%me{C(`mmEB2r_@ z-V!O}rU|&lgq3UIZt_gr@(wlJ6Iz&)O}ZOwTkE8EkX86r`bNo;KCCjXN}X=-$~e(9 zjZbJsh~S+cA6lB~Odi$ymkLx%lYM*3ktvqLU%bYH zYYNFt4tY|C!0QBsQr!W05G+<%Gsju%-bEglutKx9`4ter*<0VTb3(|c=6Ruu=u-!7wkn7h8 z7c8(wqt^NsS}5_uy_Bi7#2!v`aNIJEkXhGr{x&{LVA@6oXPk)fFTYXKY9jly&)p4n z)f*sog*|?B;@1a4{jMJCM*L|(uwykJrkg30BPZKA+YP}s9qXp)LHUGdrsf6HiH&LJ zScTgw?}=eO1N-0HWW^+>E$gn0X~!g@`WtV%jcGFt&J@I}uUh$pWtisY%u#k$O%sap z3FENzPhrodiWRP5lle=C_|eF<8a~J+!z2Gp&NX*VIAi6^g^kAQ38R1EuGumn102N8 zf;~AzD+wW@-8kPTtBchCrctz&Ewr4V_;weZ8Tv=eILUSv3K`ChMu>KM_dseRs3jh4 zh;Z+(%5XM4CQ32EUyO0EQllZ905Vu5oISp~Q86H>wlbuIkkG}Nls)ean*3{OJAe*L zHQR8UbY}5p(`|1H{B%-4BhmclkTpP3CNJ#`-#)5B;hcIU$R zMVs)BsQ=Rk`mLODM}2U~##|63KF`iGZ%_s1mVy4leD(Z2@h$C2{6 zqMjF}+wgp{d?Vf%MZ@elG0!LiV$pROTepwlTaC}qnE0OGzJ*J`o7xR??j?@ZQ`RQ` z=tjkbg{%9-Qz;J6F+{KV(f5xWis$wRU;q5|;$hng2t_--C0`4!mCjt0fS0u>Ha5TA zTB{5E3wTEn*p&Yo3}hmc&P`JL_B4%L(cE)Idfo!MxzJw=(LRPg`rn_|9t^9WAn04> zx+*QCy|`!68FYsBor`$*j%2_4-uSf%2tfFDUw^pL=7LF=_uzPg(jGjcV~0K0-*X@q zWk7b5Rersd_I|zoUx2|AwK>T53|c%;yt-?z(Vkw+`Yv8VSJNgMKQJcDNaU}+e)I@j z<1^L-r@Akn{4W51MfA3L>$%#kPnLPtJhsUzet*`+oOOL;HxyKsw8^ea;LubNN9nzo zWvR_!1^nV%0@K-&VKHHdLsXXlk*CHJ3;2=DCCC_x z{txVgC!H{BE>79Tl%$O_#J4v57G(mo%Jz6kYD`Go|Nnp@sgOm_u40--o#d*>i!c(p zlC_e$zFAp|A^c=p8MC(EvDzblVRoO&g%;i473~e9c5kud0){rXi?Kvw^<$z$>2(t0 zag*0Y;L(oP#m!{fO@94Om)7rNZ+%(L!ID?!$tDL*l`npj?5~MbSc3nC<4-A^{84>r zLsiV{yY;w9LFOJ;_RPBPK+_;UfYR~NoV*y5Z%p&q-B!n=Av&gsIa&NK?2+(ee8cJK z@jIBn)!%{-{4>{N6V@1*p=guoa9sMsDpTm6Q|zV1)P7^X4?;?h4^!6`r$`7JrDAz` zzfn#`GZ$)VQPD3j=er2UyReq%hI;y_#TggaeKCWw?m}5#a*jt1u^G6`Psn)DEDcS) zO4n$2Xbc8-==65bD28-jj3oVg{7~qaIW}JCvwTaKq47Y#aYBw6aC*p!H>9|#Br&AV zR=zoLhRD~QuE$aRZ(rhSc@D7YNfc{V#z`ENUP^-jPEX#fN4jPFjQMZ2YrUGR z1MPj6pJjK$JBW)1$;F_6PpkYENRf)e^y03{l0kRagIX zeubVE=Zo`?#?$$`xI|*`jCGx8HwY_7DqJYBgYAT;@x{9wSfb=r8Q!=;SRRb~N8p;} zKEDSogq717k++(ycA#drrgsT8rc{ictlGKAmMD3L>-=fDB<{SPdKDReJ1dmoo(f52 z0dT?nWKuFq`6&2{WDDACpGUq&dqPXd;e<{_#k>nXlIidd^O9nZthovvG%H2?iKxT( z?6AbD_Q)mR%!ps`8pMbm7$9WZ>EdF$`L7rpn%Y@3oiPn8H^nn&8jRGtaXV>Ugq5#F zG#@@hf7mPyg!}10d71XbYZ61E)qMM!K%xsaMJ0sOq0n1M+auo=D4?au#QiG*)wux6 zAg;=vU@4jk-@t*hcgG=y{14K>HyxAFmR==$1h@DfFLW3vnwW(3*1RTM?o*Ce3H#e@ zAe!V&O;=%1y?X_6#Ws8UN6$QAR{@`ba%g?RpeC;P1*#Ws|uD=b_R9Bc~@ zxABJ=VuEfW&bLPIx!3dvX0?#WI@PyEcnVxmgXXOao*wTFYopu*<;N-@TeM$@j}bQ;K2hj0MOP`2v_ zoCcEDA*75kXppq)7o7&GGDRzCu=p)8`z_T2IO_nxED#10=-U(EXcO?i!vi8T7El}} zkgqCG(Boh+BqzW}D;Q_e*;q6LjO*S<3}Z%2??()fM@;0X3>c_PY^jW@O7+i6O$k9e zeSVo~lo{!n>|4>u2SIWNA+`sRga;vd2PLX41~B!#45oQD?iP52E1{W9Xr(r3E1`V0 z%oxq-1m{c`Zt3+4hL-fj3+Sbdke2jKT9MTYJH?HV+ZYIaW(UQSkQS^$I+1w1NN)WE zZ%8N%!;#|=JFLIOzFJ3NSBINza8wt{TpesBImFe( z#+!xT=Wq)@&I+!dc%}JeYGBI6dexOgOZ4<~XITsr*Yz!=dTPmRO@e|DeL5VLLP(4* zKw}I(Y1V+L)bO)%sZoZ-Tv$}X*UaT8MD3*jI-cbqaIfVsd>GCx{xHrx?mo0d#Te32 z=9s)3IaX$Q`@T~djGIp-6LRd#)AmEB-WVg|kG!M|_Fxtaj=wtw$ZuFuCuwzuDrI79zZY#UZ>| z^6ta9p_ZRC9_uTT3!qb}F<{}lTQzFf~9F|^Moi;*E%F?zXS zCZ$|D+fW?8P+`hf$u&t*{7(eqh7(+Q5bTscY zHQ%wPv|(RxK;LY+aYIbxar;J@& zJ2dFap_C|1{8AEtwjXVte6PSfx1Ya@-~)!eMc&>$;xnb8n;F0N!BHevC}8UR3UN>zvy~$n;Aj)N?>07Uu*G zgUg632*7FbA>GKRLw~J6bpYh7BUAaxC>Rk70YNFIQLh64CnO^6( zIpsL3`|AmpPg7y^iP>tv)J9v;X1MWegM0IQBAV+-J`Q6K^gy@ny>(0u_->dA_+(O( z6q`Y&h)XcUg~iLGDOi6_(nwG${~47bNKaeTBt(EvChhYx;H_)z*AmOuJg#4T!dkcu zb#V*OHguxe<0aYqzc%WQ^hKQ;9Jw{mb6?g&as(NrXIFosyoLXjB4O4pfhaf#g=AT(9inJv;j_mEz>Y2Q|CXb7C}u1j;TF@o&r8jXWS}Up-)~j zCak7CfE(1P*2B#Xz^hD>#jIPFTJDq6PZd37UoryoT1N4c+94kH-_0W4DeR@D-TG?g zU!O9~g}`OE6WA*{eu%E_U#>RAW((kuU8_U4b%JTJ3R&9)yZp7o%i?aG>|uDBWH#Vo zJJ_+6{9qNtfqAvC(@~Fo@wD|8FW+Mpc{8|GKKL}`7KbK@KKO%LOh*%5Fi%+6gcfD* zzC;BI2oU`NyI)5l&45?;Sv^Y-jvO{w1wBb=jHmKzJjzCpu`zAGrA+t5Z+PCHn;Q8cQD z9kJUfpV%`;=~+S%W-x#}juf^Z+V9wJ(7MeuaOA-KgALlMLc^$L=zmWPcsLL`W)U5h zGlnC~VGV^GNA8f`4La05C$xO?vCsi_(b?*4nCU5P4OY>da;K(gM}JaTx1qJ5ZPM9T(LCm9rD>OZw@|l1b5hAc7&{DxS7p;r zj#stLw00Z1UHoCkc^7$wj>Ll^w5ksSi`yWiFx?VZRrTjf zU8WuFO|a5-B#=f<(a99S7tXWwS0iXY1zIhXa!wfZOp%${L+hWB$2h9+4Kb^v5OMXw z-4#y2WZKOg1WhhZ7j%a5icJM&r+U<7!SFDydMKZD#AI_A9)8XlQ&!aWYPdfRy-#Rd zY`p)`sD2}p6Jd}u=mf|acT!yS8+||7hw1>-fO~nMF)ED*9!tB!>7zB#_Zg$fZ6|lY z*C3QEh5XbVIVt^I;=*Z2V7f7)4LGT}WZwwQXud)2QR3}WlIh5FE4U=w2%7NnAkybK z2qjo;GO8mm;BmDct~!IA%2&(B+=D%Ir>7AI9*)M>kRf0>py|tETGbiJy0&J~f>rI% z%;`+dAG7HMt&B~mQMBfq%!3>^L-1PBmd`TQeBON}nes~GYCJB%@?P6CmB8G)8C6qNfs4WN zJ)rOJarGzFw>qpErHW@&MgtSgyA!+I8UOos!y+YPUSSDg8Q{ zG**gjt+T-q=Kmuh`2f)~G|A3jvu3?J^Z%b{P1c@YZ9xiIZPo1z;+f-VRql*zpCh^! zF}6y3O(QB7*rudPaTsT*qT5X|(Q)8#gIMe5JMLU7-x&5eep{>N?}0cD;w|ML5IOGW ztyq9ZOIBTP0w(^?2%|dz*lYJhZ@G&5nllp_!j~*?E#5PAzO=0S-uIm;y8Buk(r?%9 zc=L&;?>+QQcXgBCr9G2W7D_3ZW{#ah$?jNHdgJ(gu9{E+;*VsI+Ohi-LYYAa>BgVr_ezF+Ga?CQ9Y1q8aiH9MWxQc0 zx?vNzX$BxP5F<40VjAXQnr>qn#ABLJo)%`;&AL+L>V7|~$V$9%6k-@NX(d(P&(KN5 zb5yn+wP~e&*z4kr3%iyeT*Uyn-|w|{#HFIsGo#ZwpfkcHP)R1xQ76z?TubSV<9X0t z>~(s_#a>JDk7GSqZtk_e#u+gs341gEei4#wMa|CutcplBulR7U3fKfOtgLlwmfBYJ zE1C`13B0U}>4Tap8&o+htj>t#u-w0I@#UDjXXI;59hKxUNja8Jov#&lVo~WjzQ-Q_ z0HN<|G@={o+$%2Sw6U+)u#`zqbyg95YmiclytQLLgZDCy3e7=YDm~akEVw{nQ58{< z261u33DqNiVHrafj5306dADtCDi40jXcrftaW>L7Z5?a~3rG$KaKS8RhJYyG4L0o> zi5nX5MUuv;Amn=>J;;WIY&;R`DZQ;kNgEuh>8 z9B>kis{2=VqGBOFtxJ6poz6~IUzMSJ>i#be{C3?^o7FLCoAya*JDbx+SI!l{9;H!0 z4`dk_-1B8s;2xMg4j(FHBLFryG{QD5fL!CpxR`WR5=m}O35d^fv>fA|*KxcQ@?|YF ztz+ds%C&&4$ED9@BF7DmbF4&9eNUvg#>O1axUo@`L*<;JE)oQqcq=nk&hXhaMCPS==>GO03P_=zpXcgEg2INif8f+D94i9{r?_yBA(|5dE z$_1f=(X6cwI8FK-F6nmQPk_R7IGVL{bQw$8pi*dw~1y4k5-~5XRi^Zq2(d z_9vVhxWLW8Q9Ogw{dMYKXmFEF?R^gWJ#&bg2sZ+6(~+#Kbc400T}HH%K52FpQ302o zD>F@YWXNo{rWosYLCIL#R_WZu68fk z^X2&rVe8D&m5V1Vd+279Cp{MdusEg{>Fu&OpQ=u)>*LE7tc+$a#W|RrFPB4Gn?} z)aqZIrOycVDXOun7P%|nSP|hB8hCH+dV&Narxx-@C$Ih0age8qhA-9b)lxXvBHMnWX$3- zMY>@Ij%j+LwC#bWemr@~etha@HBF;zB=-HpTpQOVlUN_*PYQ7&F`Ng%${`iJii%6X zANE6BSUQe_jrnW#;{*@9mm?U{Kd7e=synWxU~;{w^S*4pUXBxea3(3?auB{k!lKt@ z%vTO$;?(Vp><)xr<-*g$B z-Ekj^?*YGodmEGgkmP+CUnj3ps&tdijr867ZqiNQ^)`}%zWXgtHjIJK?}%0Z4-wgr zOxp8wl)R@@DU^R7Q`^VQS^xMNWSxx{c(W2$*l#c2mw2&QRw?($m+w5nwpR<tdPs!! zX+}9vY{1bEudIyuo$fc3=C!BqA0=ujnuZ~&3mRr3HkOAuFilHpVcg3Gix_;8x~Cp2 zFZkw4$~ni>TDuZ>E58}|ZndaTSwR!^luvVutLaT<`ec&coCHC8ARp7~3oIcrKImeZ z|Gu9XNU24?4O{F?wxi^BdB0qpOn3YLjH?MRO=}4*OlpX#$m$5pPpJC=%xDWOPHBp% z%IONoi?59+OK%M+N^Xp<%-NUV=6&ukAnED!P&9AiT4r}93h zAegU#Ybm>4JXCK_xKQP%p-WL0WWm#vhU zifUI3YG;vaPlmZcx!#JW}j;caDK1!iq_xMJvXwtj1XlmoM)!A<_;qfT?jSUB^}a=+wslVgq~^QWGqGEs-rg z(zDN;t(;@_1*6^J2kg;CuqWf3SA3gA!j~#+0ZEMsTkn)F0ZDXuaz!a%!fY$iMPqqU z_J2I}Nab)PmRT-;St#-$OS&;oWGnRt;VH^-jeU*;W>Lh2RvJtYp0z3ykukSszQ2`3j-vCIvh<(JduLoImwdDV(tKBr`P znDzU}zraBih6HOp^sG>4w_?8AeFAgdQah{S^GJ(mxWrIF>{m48un3tymPF-n72xSL zx7vgFC)04aqsB=}Il^9BNX9e1-q=_LaAt&`#!ro*xoVoWK>9F6fzwedXFu83+!mHCWg?pj`G;M{x| zU9|u4fmv+%Q+aQ5tM-EIS?+_?Io}nwVF}kc?+Kz3dX}5qs#iz_9TVeXLJX_jaJN~vm2p9{5aH$69``7IYg0Jf6pcFn~weNhR9C3%e znh=i8n44#Xo*_E$NNvwj^hQM9*`nhhF!M#$CDgnfI^CX?j*oBhOW{a2=M%3SR$rYv zU5tYd%MeXC=33c^^rKTuUn7#fyI%h?m&k{yIMr!=u3tkYV*w0n`ADM?J))N88k2J~ zqGQ4G1;Sui;9%g+wL&!FxNr-i%$6t5{QP7^KSUamY)Rjb<)BmY$FlSIaXp$ZwQ~<> zh(3a`FA}bD0>R&boFUJKxQ1_7-LG3&Z749?LQ0D?#~kL6xUlSmh6Am5n&Hh>!GDUP z^%DH@1BJr;C(Q{qACYrZXkGDhE&x+x|GP$U@yx~6Q$xc}d;3DIF6AgEm!Bz0UOqxa zg^o3K&9Ww~IHshbCD@a$}USiQ)4(>4Fj6C?{ z!amQ;OQ_jR$#zL;L?nEe%qP|b=cq72MbLzn8iaF)n(MQm%Od{nL$yKt-Mjat4Ld)K zq=2=?5lh%ViBd(QM!`nRpvmh9*&e<(hN`0?e+uCbnjB<8Rf^H9=vmdN^{Un zaIlXL01C+V)q>GcQNQOuOyM9laaYDZ{=m4_G1rhEt3PK!DAF;jf`L(CgtupTwkzk>M=Mod$@BAVV`Hp+vTODvy zDWX@gjJ6c;5DDGkhj3s$81^mud6h=a4h^Xg(Fp(`Sz3uWTIu`+1syVmz6%FNOFAkt z)j1To zT?$Nfpac9x3{DQc;WrT^*>j7mPS&5 zR|RZQLCEf^_OvFZzD(q1ajFM&wtP`YI!=1*eKz0T!m#Kdo-t-);n(wEFjP3`{GgR|X%6QJ;C<{3vm>Euq(><=7- z2t-?n!jHAoV&zax32XvD*6>281ds|nL8X|)=(m;9`Q(ve!tCP|mUs95Zm{A8a_IqOa#J(tbk@@erDy7!iqH7PMPTiDsEj`!QC)t;i;e~uMjt}Ff zp9SAKxE)WO)N?<93n0kRe!5tK=(0+LhabATL^)gcL)~EavN|jD66bR{A zK34!Wu8sySJX|b}#CcgUD9K%_kFC81gsgVW$FAdgBE@4f3Yzr*4f$qw!;fl@@_82ED|oCyilg-*VCoMT1# z=hv;7@N=2kR=N$|U*)wg$n-6*>1HV~jRZSXIMK~$cKgqs%)+m1BD~~ca=O}*j+d?& z+TSgV~rEHzD}$&)>AC^Ctt3o6ATWOCn7;7TELAO zmvxaKAgKdx(JWkR1ON*M*$NP8m4s@v0#*f#Iazsbj=huDmfJzL0t^(j!I>Mysd^ie zl`+=X)GNHW@Uh0LuDW8(^|JJ6XnXxUCe6L(=EdUFh1bO?PB3%sq^YxRy8bbG`HU+k zOslKVww^wI;EJu?3!a&M;G<)Ew998)Uw6}V*KKyYnUaz$IOUE9OM@OideEc$E%eF9AKwspVq@g5$)pzZB5QDzmPXvW z*1DUT+-uj;y)DBhg2f{7FlI!6lavF12Ryn>`ZL$7x0BxVduT~XX^GA0Acp&V(tR-pTYPqpP6uR& zxg9&+IPk5HBVQg6=Q+W&YpWaT8?UaawhM7N9mKAx7h$&_sc8B1EphSv9X$EU8S7^* zy7#i6P7B0y%6~O-4HYsDGQbbRLqjMcFeI2D*%)ynXNnS7P;nd;08pi&(J(nTV!=tv zvoaC9o=kt1-)Xld#c7a%8FAJEEJ}4*@(i%964@~2I$~LFmybDPt09k$Sve@sZ`#0R z4N2nrBOX*;M#TQHa7I*=j7qng1x|N3RPiB%T0EsTTd7CVR}U+> zxYd^|K1j|vyF1dFaF%g)M0_do#M5`)iTz3XxpjDh!7s|B-@MXqF7QG(=oad1rG#)C zpjrXtqy`xK*MgTTe>&x>&})+!!QWT~Lt}eQ=g>CSjLe)m^N-@oQ>Vojx6W+1Q5-#r zAjC~IHP>HXTXREB?Wob!6Dvp6u&y&UcPo5h)@vtDyf*v!!fu;q?0WazmS^f`&#u4f zrkUjYgz48zteHL?WLuH=v!nYyEHv1Sa;1nY4FO$9feo-A0~HH3zrus|FV7sVd&k=WPX{dT$w-zx38@u4T4ns>`a0BCK6 z8US3xdO{A%l$UvsauU!2DXk}I&uH&4cHUanw!g_A<}NVapCxD4XwkxcC{CW-YI#2uJy+HVg)-!%5$ z4ShmAlhGi12o$hRL&|~jhBh6Fb5;qAC9!eiSSdn16 zM2v&5^5(%~ubepSYLrciB0YMx^{(st<`Hoc^YFn{%W8Z5v1(G|gt6L=H!kd6e0*rP zE2>2OuL@(4=&ilYS3m&D;PO!AWqo^TjJ5Nl2Ki{wWTT>_9iiq?rxw$zBa){K33QiTTd9u_7RLu%dO=VoE z4Lluy67a}tmwZJBup1Ad`E+qf&~B7BZFl^aUsT+f)_M7a)%V!bzuYo&)mX{{mQJ}h zt&B?%N5oEXRNP~=;b1Hp`+WbrvLjQ(Oi`uV@Fd=f%W-3~XxB5;qb3(SySk-aHz_SiufOp>9cuvLS+o#HdaTxMCnckdcXs43|-J*4A~q-EPyIk_({+ zjlSsjSmZ5wsJCMy{tXN8rACo5H6-#wQ$-3%!HUPUCM)~4IoaCOWIJ%c)9rP z0C1K2BGmoC0O}sIG5TWsf^0{`4$~W!FBhLjBE=gvY`|PhnmMoWdU5KlS&J4g zI>5sR_D|iEpY_tsZysxMGp9S;@{X7^b;|S`UaOllwGJ+Eq;oPy+C$G!Nqa=i zi70~V>jWuj07PRjb}0BUUgY!IeO`lPc>O9&h#Z4$DsCic)0hkgu7sQ z^|nKAyfSXB-+f|&_-c!IU#!5H=-+}V)pYN!f+F$x>A-1mv8Z5|INNYD-i? z{!}xUm9IM+JueM2&iFiHTk~nVLo1SD?wt5^>Z#j{=F(Uo^7QCdzi0^>{~ zLP81F0R_d;s(}!w1jAVd8@H=A;ZQlDY)4HyJ!($qv0(%IKWWy7LXeG-$A2?=-8pf+ zoEi|i$@uxo&>;GS`XnJmDQ8i}0f8x&O*tjJ5jdlqH2|eCzQ7nO7=<{5tjsi*!=7S|qsn6nJ+g_! zh$)W7ZrSb5fL)hB?Lm*zqI-;u3dk#?jL7@uZ4Xkjk&M~65xpBXN|#BNE&p%e5DvS= zBz(**Z<{|;OdHoJ7b=%3T%`bHy~LFO?L%2|nAf<83kR56WsY$=(GBx4qot(AFv21B zvBt*%f?Mv(Y90nB$Z5-NBgl}(BSAIJCCYIw4UiHU6$~jg(k8YanW1#`6h+ebFV9tn z5{wH+j@`#)Ta-9{KlrEsVyicbFW!>#EGL+CZEfWZ8*w|A@LJw}`=`^#wVSS4ID>X^ zht0j<_eqRd&?{7$oX}3`7Z!vRWSEhj>a-zPD5L*rH&X5PSkxLG37~O{W?4sXG=NFf zor^JT9O?g{xF6TAk8GbPK7I7gh?&u9Q|9A6iQ#aj$cgMsZ)!@!$Hfd8*|D5jZ1kAo zP+_}xMi~KuMwZ9Y8z7p|%!CC1R

    !pma|lSQ?8FBe{DRl|@FzhU7Cb>&@ataZS{g zrCQo@Lushkm71KyL3$%QD?KeSADp!x{f z=8g-xggQyFIyp&U(+DO4!2ygCg$vPu1pqEc^*Qa)IPstXB!i@fDLx<-```)5UsmT$Yl%a2onC zAFmUqUVZ7M=U;sB~=#ZhJ zS2lbGii8+5IZ#kxQUi*iaRi9X

    #R4x5a04mzf2YJ>#U<-Nc81}`=EFMdUjIP>d| zG57p9Z{fmuKgy=mdh|^p{vLBqr2nl~Uvs`o<0(=ordV4cwH1`u!(29-sxfB~U?9oiFvwlQ~h zMtU+gNR!Fi+Hj^AdqXJb=<~`-ovK?Q2R#<$zZh3ihYT*KQsJfd{QuUW`+mlZ**7k| zA+K}pJ!@y)aP?O)&%QQq-p#X*@bHBL*FI8|_w@2%-GiKm@1IvS?Pe;LTypfH#`^kM zfQIh3iSL1qiZgXY5~NUv20JiatF#2(>ujRX)dcQ^917nje3D-C>7-LO9D|Qr2Cw)-tt>dQFl~e294@LmTlbdf46VkAe*1~ zyZBW!i|uL`RpE%lty{b14)U|xxc)&pr`-?go~fAw@C`=J=7)S%*=`Utg#?VnlSZ}r z*t%tNu^Wee=n9lZJBvr%l9KC?*fbsKwZt(`REc-Qn7PEk!O?Qh=n6XB5e}y`r?9V6 zOq5wG?G^EldQlJQp(*LT-5;TjsQ~KE`=E|BEJ*H*53j#E&bRx)^OO>avtf&Eg4ANuBcw@ z0pJUa{U`|K8;6dzlw{-ZfDuShv4GK(Gu)6WW0Tp{xD-WVZ`))kl{cRGxK!Lek93=J z?))rLI8WS;w=a$Co_X=JS&_p{X~ae1)5GB;u>q$hg~REQpCT8HK6|}bmuU9cTmU}G z;3|couJ$<2jD68ltnyStBS8M`21&8SW^9l>5XBUYOJkFU&pEH1pH2J=#7xDH#Qgch z&p{VCcv=sK9TA5(WN@$nF@mKjCL>Hq<7GEjOcIRMC* zOBs>`N$QZ;rZU8dve#2j5M_SY*_=ozHm99VZQe=ti9*+zR32X6yellOzn7JU@s8+O zmI#wR+J>k~LcusO1|-Bd+fXVA{-%=1Gc9$>Li(&;Ek@zfeD^PVk6S)7J}0qtIbC(> z>;eImn3EVi=dgRwlZYKiojA+Gzr3wppCH2c_e*vGC;?gx1d)scLaF6bq-$Pz#k3z{uaZ7|A27A7BNZ*ymotzA>JCQk;Q9_goe1Sd(4ICW{YBDkH2xns_xE~ z4Y;k_^09}Hi&M4Nmru^jykg)D6S4JMaeCD7Tlp~AHkOj63W=fyF$^xxuldM-}a(rbpFuYmT?3 zVjN5{Bsg=*SHhFgq2HT_xs-F<1N{G}-O0?Ki#tmf;nc z`?V7RdkyZ7x46T)ek@X);8bBuIXuA+=GW6JGMHqtI16sIyCo%y$S73Fs)+f+(VH%Iq?yw z6vJ7LjLB>$P*JI&2EJUvH5W3TqEtO3ln;>B&3rLZ#}vcLVnEZ0%psLUITDxM+-o`d zIau_7An@DSf#-KSUwP(W&5A;&5Z`?^=B0{~L8~gJPwT$y^8IHpx9$&RwOLuU{ijbM z4z=}_!*6T;_`TLH{zGo>vYCVXhS2xbnnw$};d_N8G6WDh0I;8A%x0@uk`jUj<&fVo zvgyf52(lzlr79#V)X^|tX1jO#oL&ca#H65T!7}@*nf>Zcx7Bh?3*0&J&pTH|-da>W zp>Iw5MUl58>02nX{odhK=ROm|jqdv5qH8+mM_w?-Y4iJ0N^E(X^^@^iFpe`H86CUy-&mlopl&gn;DN6iEUyOiyz<^pqqEW$com z0RSeJwxWg}m6-HEsX=RKlxlgSgMA0w-hcBI#Ia;I>eq?HK2I!^zF*B7!!2{ako+oJ zs+Ch+O-=1E@n?gGF9ZM+8=0YeZ=Uw3(rJ2LVKDt|kE%<0%+L;*E9#R~l~JD*?N5g3nx_fn$&)F%K9{y=mzAiX!ErdvVeRU=W% zbIU%gBBpV~Mt0XFb=!$Cjl*u7KswEX=b-0z-#_bz)~~)stN7;2Z^Sp4+c@)vYo^_J zV$1W3_@7q>VB=Wj;e`1Z< z^Ca$#61Nk)Dl#?Xkex*sw<2Z9Nm9HcRHBYi9ivCn%hfTja;k1rZG-u=DcUC5e^;!_ zP9L0bTcInW@}v9GF$Ydzu58kiva@SslQxTV#gJvcey^}%NuR#5X|H{}TUR{Sy?Fcm zWTJTHf4Iu7KWt zZs_vVRgpbrm_2y;WD6HR(xR@BZ&iGm`UO47M*NTZh3kLPFO0(JZ=+wR<9-w4Gke;* zYf48>9Jln~uI57v->jr>9fwQpOM473|vZ&RXhO)SWp9P8bdWAaMM|Ym>Ww?4bo-M z1U*hd))gl(H_~PDbHy8U{Bqm2;?C|aS?Ps!J??pQ(EyN0!P&AGY}KbtIeihqb5lkqdU!Y;BJ8t{TtFsv=N()rdfpz7J$Kxxkf zC85CY^$XR*8Z~69LNpjP-Wt;}?7E>C+0@CFg?f!&Q$Gk8zw%leIOF{H2epIWU6_Fv zZ)(t9+w+DtbtYbwnTOT%~RG z3%^C)beM{H;=`%<$&|+)o{Imd7Z=gjAoMAlw~m)mRtjE);Pq_Y*c7crqks+$8a2D@K!n|AB+eil6#t25q^0_DO;Jf}Lo_6~sLJC~j$xao ziW|E<@Sv}M=bU7B!q5t*M{Jo&Y&YL&)wa$fHoC&vd|}pui3#r#HaIwj&QFnoh8SIe z*y=DfrwIV`eTgYa@xLUbT>B4N25@d|D`m(XGr!5{GY=!t_`Z-6cVXAW|L5#G;G#OZ zx97~vy?2+sEK3(ymSquah=5{S6?;Xn02b`nHI~?GOt+<|38a`_Owczm%^1^TG^W3) z8jWeDnqHJ0zB%{aEf`|n@2@{#(Jg2CnRA}!IVT%eQzrfhKgA}Wk^s9sbk9~;c(49> z9M?h}(0O#-5{e9K4dZe2(Aok=reS<|MZa+qCXDM>(fRp%MpsvlzUTQPx|O%LZM%CWT6s(H z*$0nKoBQg#DMugtvDmPADy-nour4GC@c`<)x-xZ^_4eL0hO{W2`K6F+2&cXRZ@(Kpg-*>HU-Mc)w5Y zww~I7cy4YGSHr!$1Ti`-;)oEi><>Me&|bJ#d<<^=>uUDo?*Q125l!KoDcK|oqbZ4w zSZ3Vl!!48&7J98$fJ`S$&j?qmfcxceynbSoi_pG6(q$qb&W_nOk_B18yS+gkpt9#< z4R%5E8~y-!!?!YaG=H+>*o z2;t*p?j#Elf=QrRpf}Mhibbm-zzKmGD<}vU1cg@;i$yIbsv6gaL9;ku7RhpDX4>T; zgDLe03BE~?^TJq51#a)qUYwQtYwth4-dkn6N;G;snHCi@Dy(;sje^&u8A}pj9T?T}=Yra`$;pn7%j7ZUY30#824Jt)Q*8Pg z#R<4PbtV9W;52#1p-)bo_Qc@zo3F?(=v=sM^Oiw-C)NCC@b)b?6m%|Vf6bQ0WoI9J z;OyB49y~j{d+%P|FWYw2kVhs@dvf^o+qM^X>)!c>?K_7(K6TQ*!JD_!l}|K@Cm$Dr zfR69Me5i{I^A2`q$V4bn!>pJ-;2b^jFe|Z!ky;B7O-H|0sX!HAvcZ+%V6{f{F_2uY z%=EOBWSemu*G>`#Q{OiZX8h^omKFOa-CtJURySB z#arBpao`ASW@c93Wh}HBf}KI96GidEna!b2A_&s$uf9Jcjwlq8x6he<>GJ!3`=(+<-@cIY z{jU(KQ1RvRIhQP)zxR)CdOfg@9--hsW59?d7nOI z9lZzkjJ>*T$IX-Xy!FXpb?l-seFhKh;5`r%79Sozs6*MvPGNEQpnnyiWE=mSZ8gUu z-fIt%yzq<&f)Mop^H%Yj9PbGH&Od+cwbuw?7&BJhQ(nZgo)nTX-ij!o3=lXRK)Qs1 z33c%=F;0leNj1?l9k!}4tU@;Q6oN*>F>$3Lfv`GyA78<^xkbZsuMQv0tIBIOi9f|omWyHN zTAJx3#N&46W#-A(XJpH$xm9+33sG~2$CDihnumtW2U)_HbN;YPH{5^6Rb9LE9&k6P z4_tfsEfG=Tih?<|D+iUA(B~cPliPP+F>C&^f*!p(wU1OMZ`g9Vc)4I~?uKrz8Qykz zZ4npC$znE{U5~g}zL;dgk`GRR21{T{(+TuuWEfQ8pnKR1^lSvv?-~>^A7PKR$LauE zAeX>Wwx79)Lb@A#iFnEP>y`Z{51X`se%O3oLAML$-9w^*jl6PA$(Z!Cxq~{l?&XMal;sxnFTK25=K&=I z$aP8to2%^m2Y(N{ajwrzKUN~{HumFL>@Iq_@)Gu8Fs>5d#$g~15Wa52RZIdLqGm%* zyIHp{n0Mj9G>|{C;Ep>MTyo2;3k&=AD=Hc^$oMS0Aibxg$K^&#>O?kuzdFtUUv> z&wchePNs>pj((`TLXt6-5Cv2xuyUOhz!9XxaRQABa9f}hX*V%S9QKhrK)jLORDkrC z;G`e=w#(3kt9^cP=U1t^?}lx+%w9E@LKUs6YwXykwWqLKdv(^14NFI^o-tlEeA$t7 zEB#jH;d04H@9=P-5CEY1uYUXLTffp8Qx848+DP#!0cwO&@?X)xOXWcHw(ZlJzALHoqsT}{k zTol3wL?zJ}*8rdlK`D$N`WQ($rdMLZQQ~azXrnp&9>WtUhN0o!(U0G!6?F3sOIN#u zj~p?3-R{=8t)0piV}4GjH^Jj_{VOEV+=b2bI+X+K-+qxJ2amtK7Ru;Nbm?sggp#@R z5teCU&Qc_D67Od)Ca`@lGZ=eOqVU|OZ@u;4)3@Aquwm?|kz*^%E5`7pHyk){!;%}H zeDcOoGiHohQeInIZg4%kWGB0yJw{wS1s{`aSo#+M4VJ=ILNE>^*M-Wcekv)9GrfoL zLGWa3Fltiu!08N9C>6ffPm3EC zcJolk79Y|2%^Sy+Trsk$cJwIq(9zQ`to~qd+liy#*7L@>Mznk6o{~gbAzvjGfF?*J z4pB5Eh%{HjEQ+cF&ckqmgb|ApW@!#Q*G5`F(;~G}ls>SVCtz%NB8KX847yz!YR9W{ zC`ZH7`$~Eb`|D)5*bxK2hUj`-yK=hvxXWO;?}LphT=CMXSY{Clx=Gg5(SH;Ziz zsXOnEYM}jSE+99Xyup6qn@PCj5m}K(WCth)NC40PxQ23x^wY&+7 zpeuB}OB{isyQHV`(p^v_ju`xp0ZqG=1pyYZHFK6AZnJz>Y42^bR$MMlbZDoFwAM3wJ^7R`J=3Ma zhn{hrp4e}IU)#31^Xyc}*{1_J-L8fEr-@geq6?nx0gW%YvLuvq>iu1TwMjs5r-?Zr zgr7qUmoaxO8lxPDJr+})6laEtH%?`o9*f|#+L#BpVlhUW7T*dzPR?_(`m^kDWn~=i$S1A3pX&gCp(a*Z4LklxsQP#3&Q_e|7xFZN3VJIE%D3#k%P*{ zA^W-M-Rbvr?SAK!cYpRRrF_+@ajS+6?eSh?UCA2vBa`Qrv&@MTGzzmuIm(8VR_?#$ zwmn^kR}bzSt8RO`xKpQM@#pDV)dib!T0hZY;OpIU_m1t^wJ&{i+l_zL-DTg=5cl%v&a2`t}F$E<}7a_{xqvZdl=0!=7;sbdMWVU;5_IvzhD1F5h(3 zB}=YS=o0bL!66c?`4A+%c^YhOJbdtm!gUL0+S4OHa`ekzywF#W)9Kdr^KX9tUV~G1 z7w^U&(_<9jnByRZ6I6-Yzk=*Zg>z-7c?>8#RDGe)={7l(yxaLk&c$~0u<(m^|Lmc! z&;RhXv2P7&TEqwPL&m;^SOu#}RU$Vps!&s?f~$(k6{LT$**f5bn0S|9A~4bJF;FgM zhZPtXL}K8^FeHS#k_+_tV(dzGqZ5b7#B|$=)_v}nJZ-MbP8ZhX4Z35_gvs=yIJ+v& z#kNgeI8zLYj8c3tnKmx;*w1La(m zDQrqIMGfDgCSv{?;}Qvk;`z+s!W6b*A|L!fsEektZU3Ie&V)nlH;np+EVjf@`1X1G zUYN#KQyY&oOJfsCvI$Wn#-GUD3eVR}Cj#RUF|8pxN#}x4J^qQrP*xdXrO2`mS19^^ z7L|rz6bNaZkE?;ygn~DCk>)JH@clq77Wt~6QO|Ro6En!chTgIOB(=k4X%uZ;4mn5tvgd5~QTkNqCtQ@7_ZHhzyi$v8Yszba9 z^6pqDUKm-`@p=-_R_xFMtUs+#w~bz3fBZNSgw8)%clFh>!xfSyc6nd$iq?`SB)fTs z=LHWujevF_NrceZh;>38fM`<$#;_vF(42uNQwbnR&$<;GY@p!B!H3sfhaiTvYvrlR zuF8KQUugPh-jw@7E_Ir?`BE`hjl)Sa&8K43UWeZmGfWu^E1&$@7E&s0Bl;>eB|{ zv~l#f^?k(8q5+0ORNpPb*?k80v@xt?QRl9Uy4-NjeI^YxEF6Y_6*|bTArV%cj_R9j zwX$C<;RpAN*B^TZ&zo`5ulY@mM<(e&x}esW3L0?$qp9(fBNow0D6`VaFbmZ*KR`zX z#(22`I@P#ieoa~Vk1|dkKZ_3Z@$Q==5v$83 zVs+l6b%RIresANFgME4*SlkRO#qN_ZRthu5=%>)oe*K4p!dQn47%(IhLIwRySSye= zgwQi;TVltxX#tSoR3HX9%#3D8r_+RzWwBVCYR&MgII-GXt0PwvQ(LF@A5!PrFa5|b z5ViWgqLvM8Ou>Dq>3cDlX=+;o^^^>%Vg*VpfJ7?4npwP_rsQRa`i&06fs+6MDhQ6z ztE?G?vs@l(A3c0VbZ7E%Xj$ys>GlYKlH4P_PS&K5y>L;AK_FlOCDx!&tXv;fuvk}22K=gPap zg*XpqMq)y&P1XEjW4*8$3ZdgvjxoMaIU9$&&?S#7>AQh$B_`KH`BeqYxL3+hb=~lT zls$1$JRKlgx$Ej~MhX@@%WnpH<6X)ZKUjRf2l-Z^Of0lj*Q%S(cabd8)!RAKnGx@> zg2tK5=Q6-vMW=j%YJiB)DXX9c=TFb7yIgKpyhCT#_`4O?ho@y=6)#tNaC95EO`mB2&aro{^YiI#xMT|8C=X8u!me(1IAifYcEaxYSZ2>h5=U#oZwYg5b zGESRw=ym_fxIym^D)+?ixQ&nSWAx*I5j>ahGl;^eQfUJ7agGJx`VROfj6N73P!BQ; zoW=TzMIklRqwzQ~VHW)jB#J*iIq?&{95MQn*A0?8-mN3XM=()0nvW3ge3kQyI0~T2 z-EG!2IjtWp9N4&e<#Z)^!aRNas$}l8n~Zf>kmI8=yp`xsD@tbL3Z|v8u{MmK%ILf% zBd7(e+lO^GzYEFt^YaPUU6=f$9{ZK=(L?z)_#UPn3(Q7$EsFTxqqxAKxS+5Ap1My1CT3Zv47!xzYuV#tJVZ`y#fKr_D6nk~75zq!RPAs7qN-QQ+o8<_YjVcFj zy-nr=UM?Njzf{?)uHC(Rty(f{e-|*F-9T4ROJ!X%`6oCHU|2#W1U|qq9@0)UeAM+kNge<0cml%1_CQ zu-t4AZ!Hu;VDxiN%o1nat7f@K`fHq z&y|-}_uJPgE5Gj2s?LWxxeAomYOWa5t8MOtYsPrn6ROXthvl47NEqhh1DRD41#&9H z8u|DHaCw+Bfw}12ph-;3NAw2Z^v4s*52|QXKv9Gqmux3$;O3@#aSU*FnQS(MxR^cT zAB2Q|7=(7Sh7GB$9Wrdz!kqNf?CjL^9MONmg#P`;jqlsa<7t)Q@#tePj6VcVD_>%- z58i8)(Y^2x*~t_m^&4|x&LgGSvnneeF%fC30G{4=@GJS2UGRD1`G4Su@?CSC!YDBL zdj-t(`ImJ7Wd$hk0i z4P-6pLwajZ zzBHyM@E@;YX-;>>rrK)n0BbKJMkvSop5S;l6WNWm#Tp{JNY|4+BRP9R=r`gs3JG-3 zw?9&dO@9>#A*?bK?eW*Ds1p1mJ zN#;5R@o#TaCyN`f)#Ng1^WEwJBOZeqizqHcxKh~+JrWGUNWV#uU-(As?jL^e9sA*j zrI57udL2Xp;o?A3lRrO(V~3BKN|qARVmoX!)zsgTJ;=8sKNY}qwc}* zx_Rrh1@Yp3-gYmk=rTfbwyT3`E?c)DWgEvQf)M2Z;$s3#RE(0=62Gv{Nd2xJgvx-N zLg1q1kVm_pD}&4FUE)zy*?VrpDYlg03Xx}it@4t5WFzUA*9*k3O!8q|XhaDwU4cNB zauMwYwpv^UtKi#Lj8d_BeYM?(z%>3nodLxY_?+&I#Xn0tm2r~Co=SsE%SD&E=gDxo zBjV)w8+o5o5xxh)Pkk=gF;~NaZ4y1G;8;s-Ki}>T19M8XDSHi7Nn~SjDz7Qn5kM@c ztfWt==Dpwu&Z$5!!9TN*pfG0Y zO`8&d$b6Fhu=W06DXw1E^CnhA_})b`K}m?5B4vdyEaiVTWsHtELF$$yGt(CH71 zbkzL@1>)Fm*n8rat;-c<`Bv6-^D;%bbPF)`+|f7X_ugsu-=Ef76HRX%ZB)Cb-FIIa zwai)$6yBzE-Msm0DHZJ~tT42MRLs%kVEovfQh$^XGgAhSuuV1a14=eTZtJeJbM=G|ZjA^aD)Pq+|^9YrWLVuR)u z!2v;1iYee0pKVLXMzlmxta+2dZ2?Vnnn|$?f`M8M^PKNbU%Y+8x~)rQ2@8--cf%mC z$iGAH-Uy1&e#?Z&U)sO=_B)5$|0aHQ{`Oflw0p2EB43=s+1>IFAQm(|Ajyks(wXnz@Zbm-wG$RBT zn?&I3PK9PXhC5wZYB*0y?~+};E~CR$;|_oF$&rfn?c>FiiL<+nN`=&ll39~^7hlHU zZj`Ac@8zv`cgnZequ{zoTBG?TP>p+sNs@|p7~q`LWJhj%uG}iZNgTvkLuFXv{2-Q2S@r;8#}*$1 z=g?7a1FIRe>WZ^(47{$pI1aYwCyX6GYWP&ooIcZ*Etp=Ty!8H~lXmClJ+<+TFWlLp zSh@YVKYTl8P8mM$(wfHeY3a)+%vt-o$-S7!2k^cmg{<*LB_&1@4pgC(1T>3NjH3}L zO@uNs-Gtx}V$*%Kh<|uWg8wNOeS^x%$KvALS$2$2Xdqk60@p4aP(SB}52zOJi{sx! zWC?}1<2}^{x#`i<_?r_>+Vu(xSJ#nDw zB2LnttoMei03=&GMfp-jf@w5km93J_MXHOp$tmq%?xjIh40#J2M%nDoA<2a$*YZX zYmDp@4<9OLak+OyD^OI#se}Vi_BZuC3!wa7wO`Fyjx2yl5Bo=;Ss=}4ASbf^0pMIv z;Gn%~X4He$I(J^mah3Kd^XA6{8GZp;BV;_X{Jd{D7^$DW3r zRqLEt%LnYZ`;qNxk#|tX;+_ZfHVqc5*kROJ#yc&T92!Fz9m=TTWf}s?8R<+K=>P*+ zEuqz#DVkm+F#u?1Y_Yua7qY`**W7WEIZ%HkY#)0=yLM2}W6=J)F1zL!^))JccZ*T$ z53Q*j6`$B{@`4dBbe)?&HpA5+#@>G4j+-FyfuEIAdS5pQF^QL>P6J}v!`b~r&a zn}4UnP^}q2w=uLajikBka>v8-*$k6=nsI%d{^0pN68e^yl$O1sd(EY`qFX23^|&}b zazwAm87#bQmZH8Kms(a{R`uprQktxA@s##?jo<5M_a$;a9<1ZB;!VHO02QF`^mmyn zm)#T?Y_7o$PIHvOY3@>daC04YF&9)-V;6r^pb|T@9fq>(`ZziyN3kIo zJ5m`+O9S!8jtGGYBamanj-WXm5o1UHZPUJ0W@cLZisD=Dm020+)vKcx{&rmSgALSU zdlT<(iBp>FAFj zLL>vjuZ-H*K^%wh&%Zb? zj!&LBaXdiExYF5^ph@PjyO;O``}AAL6t9S;n-4v44KxuWlP$YMa>In4^BLp~XMoqL zcn5`^#(@;8s~b%ulo~=Q33v+G2vC?E`fruq2q1E2<|Gp`Il37^;*?1%G4?)_i6WhE zT6*|rPQ@Q%%jZwJbklv8DVxXcYTvd~r>{Q&l@B|4NF6orv1`*dT%|*jQb9^ z{1QX^(PwFo#sEk_x4$#Bi)T)rGHc;XHn#WvDUK83qzvjizJb=h5A@Lf6%RakxiR0| zlB0bOFEL_qctH%)#XF5mj$&kT079ho?Zl4LO|{Fj!Fl0iD`xkIqoK3x1K=emeP0IW zSNuuib7N)utNBnvK`;dvKySaI69<4G?9jNe>j@9D)!EeJl>R^r#m{WE@jKv?(k@e% zv%+=|U*J7|{?$)iS@xvFwCt7LFIf${7fw6AbuG}1wZeCkI4_?1=o6S{?*EII5=tsX zrt&fGPm+n-&<&xKq$EZWj(61Hq#^p8aXFKd>^6BsDp^FHh1drgUR^M~mOizH<6uZy&n+YW1-Vz#v)tB90?$M!kA9T{tDv5#fTtKh!~3*{;3^CTxE$ zh0-po9ukK>JSkqx5FO|tn)v93s~cRcLiouE$R2T^Oi_fvO^)~*H#sgoF33$*AcUC= zCNDWld^rs=A!cWgm;9MHeyhPtu7N7sUJ#cZ)_KVfin^EvxZ+);CF?pJB2z7r>#sG& z^jyG5W}-vm3T7AraUHy1b6uiCj-@9d(P4~b0^aD29`fJ>*SpM?Eu%5b$Ml<(b5z+! zTQ<@z`4HV@fZV#?6UTkxJ6Be0Or|lobnq=w0*;k6-ba&Oyne~g8>jrf{TJ~_n&<%U zWf*}1XzCT%)EQr=ifc&>tiEJdC3ML z*4=vSsvRf{z2mB5x2^-WUZ0}Bi05a|m@ylALc~pYHz0@+m$9yJ8amf+SU^x)#e2f?0jZ7GtF3+_EKhV+tEn_W8Bf^XEm= z`+FWdES?d6iQmP3=v4PZ)iAZSC$HeYd+VdVduYa!Cq6vr^U3A(A`JLTB7S}Sb{(QW zm0x0^FuYgqK3*gK#eY@~6Bp|4C8-pMEy}=vDL_Fn!~_gw?Km@8EiXKAhBH+Zaud+6 zKk&Ef4=e1xb3;Z)M-p>IxI4#+({fdvv3Jgj$YKd`9i1C@bh|NDwev@AT4f3U1bC-B zUyX3VcXWGRCk~&4TbAw;Ypbe^d(ap;FHXpL5l`BnN>mttiVR(%5>2gD7$wAnMPsG4 zz!hB1ia47i%dH>6uK4)o)1atWp1^>!9QO!g=Qh1U^~Acw)K^X~eki}-p-Vn{B_-)n z^>T3?Yn9Y^C^?n(88&PZbeuSRXkWH);4PzYLd=^rYaULB(YFkE)z>)m))B*R9d_Gm zSM4}9MvfGSJ(PgF@4i5*b%oB}XyDEm=$fPBZ>pR|wjF?1LR<0UO zMV5^6IkIBoTsA%2gh5^GZZ6lT+vCC69x9mQLiWBACd0j-^o$uMY7s5~Sk9p5+VslLF*T>~C&giiXGZw6g2p5Blz@1(U1c*)Ext@`H7#GGa zT>x4}(AUY*4i&Y~r2Qt|z;rtkoH}-+McMdgblLd( zUyWhZ<~z%xU%9ts`S_QmeEcW-)zj(|r&^GYr=%SrmY%xiWftaIlM*R#y^aaN95m78 z{gg6`F6^l4xmGjWnVL#SYF4V0=$EfecDig?uCO>=npEB(@Z3i)P8#bfl=j>7IJ+OG ztUOb9?i}QZ56<~ci#0L~_HACyj=A-G`neUGHmyM9oOnz8@#fp&PyU7&B&x-=r~ZK6 z@T$y3XNsunW=%KsczQ|OuQT;>hj<6tVhovV3nLlnSf51Y8t&*;uqezjjsQ_KP)w<8 z+5cS59D@k$a^<;lGwl*(U0{z3OYp~VgF@!PMwubqAn3YRoeupgLzUIKx^w4cyWT(N z>tE+SnrlmE|XGPZ4)2Cd8H3tY$A4EJE zjs^#=%UmZQy3vXV3oW=7P;01K1prGKu~-7)-_o;20VedV1B)PoJIsh>r@yTM{*w#{ z|K4?32MC93j99MQ++k&B1e84`=l?;R{|Bw%-~9YL8vgs{dEr)(8Jzt_tX%BE^=n`c z5kndo0mQ0F(?AdAHRD|9A&^Lpxr`Kh za%PrOT4rgc`)@+-gCp-X^F7X$(e{KH*f4?Q%4%5LUVr9U0-?E7dt zR6UvBFE`#9m*=W3oO9WL(>o=Cq`rRVCw(uQ+o>vBVvOSQ`n7)wM(*^zQ}O|>zG-_T!?tZXOUG>4? zsrR(cduY|GpTBoZ9kqB#^`hAy>-kecR0qb?&|^S`l+FxC@9cmal= z%R=iRYy{hw%jXx2b?0X!#I+k%{5f2IlkZTu`0>H!brG&wF+olnO_9^)u#6W>p%1(onpq?UHb(m2v#_oQ+GFZ4F=_>1=voC8 zeL0v``Do#5cZ|Y7iIXTqnSHV6RC*Izw{~W<&TrkJO|HkC zk>kw4gFh=XLT1h+X(t0|F3tk~)OHv*W2cUVx0?Yso6~;?P+Pqw(eDqy7_ezZWfKNA z7%T1-KQcKwH{QH=@68&xl$-YMy=h3=An^S9C*%)HyW@_uVdAYne--Z!O1pz^di?&o z9;W~C4cm9;{rk-`b+_7E*=y*zVTKYR4Hm+z785^DV32X5@(`UJyS>n_k!~ws{`0h; z*@606wD|6PrmpQDa-jNr&%?wEk5dDytDHg~3vrZbt>i8d9TjG^Qr)%9NC!O6nT{~y zl#F8l;Fp(i6iP%gO1$Yhff-qa{?u;}jp(1*Y*SsIyS!4J^J9HGU$>>ly}gZkAbPeO zrpDOL{PG7{_Q#evwitNRKgS6%_cBWg)(64)ot)$+gD@I_ctD&(Kznma{mFV|q>-SO zHEX2L=f|=KJL$p-Hhqy-C+(@Pr&V>Z%dB7XXKrYBHT#K{p+ z5fP>$2f~i099rvlpv?LR0Y8epP+#wNq59Q}_2DPkX5EiMlf@(GLg8Mv6&Coo=}B?i zW3?HGPtk8KIhgJ8WaB0)u?mDHCE|in0WBw1X);mAg=x&fO>_cOaN|gGyYU|5&T;1$ zcbSxAxz?jeRF*T`mIomwV+TVBv?FLe(*J)9*e|=Bd+Y0s+Cz<>>+P6Ouk7`GVYCCg z{#;WC0L1Sd(ndof0BcK{K5R5eB&o>MN=c54;DjPZfnkd@;EeEb7DZ)xgBXJf!&6L3 ziisg4DK#lICNU<#ZZq0qZJ{lp_EQ#WKV-FNwU0M96z}cd@i&y0jC(Hul|+JWpQb9R zrqmM3QF97~S2$I8rJpMo7aA_sZ@qS-mbi3(%pzpLdLM|<4WM>4SNlqcc>uByzal<) zK>S$wVOsXqv3EWbO$7rVjeh37>W#T+AM!7!tq`-tv-#pVF=yFy$apTVd zbj*7W{cwBJH`)f}MdX!WrYHqFR3(xM#N?eZ*+G#~4}@#Lt8}{qx5hvcL;AInM`AY> zdvSip8UPDDG!^h%p&35QE+@t{4wwoK$N>YD|ChKUYr9^kYOf!oWmLo(AltbV(OL02 zUaJhE#ASwmBre2jG19x~52Z-o13e@US1$#s@&o`Sw{{o~l`(Ti7zWLa`gq(V zXRj=}ZFa#JS9V;hxV-+JOLp#n)bsF{&%SEefB(CDCA6(q{L|khUR<;~IpMgb_DilynxP>UQ&>-YQJ#5KZTgq z3hM{9%1z6SwOLdm5h0q11UFL>v833K?^pF2wFyBXDHLvB@HtTN)7c-zPXKWzCyhVN z_*17AY_jv>>x&lF)-IA<1SZ5+CmmmkQ;_}THF8Whw23L~rar&MWxsl*lHcRg`jyd_ z-?Mv@V`aUuuB!>*PxF%hpPGo#|4U88P`wxb@^+ZZ+fz_vR`@ya!B5l1uUz}Zd%h~& z3IA&7p#CGi+BbB-$nxF1WY>-%OVblKRo+x@kg|MNebJ?J=WfqHr-H7<1V}eR*pIx# zz*y?igouS}31b-@#8@IK5Iw|TWt{ix*rSg}h}SXDz~;zH^2R#f5}edH&iUeL?$i4x z8FP1GR)up-ufRir;@#MMf7PNe0KcdOA;~gAf;k?{DrQ?HO}QlGr?2N^}EDQj<`gcld(t2hq{L$N8ug>zD60nB%5iVl4Ww^Dro@g7~!5$j0E zTd}UVhYlx`{bFyPX-@DnP78{gU05_bdP}-1O1G?D`=*$4!xw83d#5>KQ}R6{3Z`D! zb(_p9q^()9t>nsS9Y^Hkr^d#m_fA;z*^Oe(8ynX6PB)|-7PA}TVeO0VHi~v6F?HgE zA795tzy9NdiK&T4G^LIEMX^4<0d733b2)oAeaAlV<4G>2TlBZvZ-RdNtepvT|1n6K|MRIge;PZiYNGJH^`!Vi#k^}p*@zM4_4M){T{;c$7OC4G zxohXZ<*tmi<90o~=MJ^=pkZD5^?Hf`8HF{TYJHRy3kBHFd8xLrsO0JGF`6Ymc6Iyt2#=>dh8z&F=eUU+`u zexPV_PG#b*mKn(zQ5bQ60;uVjptN|qjSk#yZFsx2A<6vWGX;&Trlp&v-qHQ?sU1gn z^3$T@()uJ`d;WSc|GNd7d{5W2tAi83!rQrC6W1gf69A1heC&aQa9sb1m~)M@M@MnO z5b|c^841x>qq$p|zLi6Q=wpowfD@AoE>AJ;t-%W|y|G?2zVv-No-wssJ^RY^_USd> z%Jc4>FX67!O*k{L?})#@)SGyTQM*Bk&_}9@N&st9>fG9(a`M$Stubc@L^vl2~J`TQ>bUBDo_?t19#=Ppx zOaAxTnb9rR&U84N$5iCM83vA`-Pgd>)vJwL(F?0Z{Tkm3#?YNPgZ*S!_x}6ls0HWx zzhjuA{|5~7DCyXKA$#V9HR2}u$@m$;Po>x3E^gN9viH&2mwqWCB%5x32Bla!qwa zQP2efI0dD57${1VS*eKY3^EI78fG#UWK??W#4(M3luwFI zi;ij)T{Buu6v>g%etCqbi1)GA-;0R!qgkUOAYK-vY`p1N&U~{OyJySRyD6hP8GEKq zuc3WNN!N3O$wqz$dqVDagjuwZ8>p%&9M^AXFd-p9c29CbvV5%rZ=p7Mw8B5$K@0h+ zJLToGu}F6=Hf@6G^6S|;Ueb8C?-cY3%$LRVTK&8+YV|9z(zXn&E0)$u--s!y$;SgD z81vxBoj2)9;}lK-LZ(dyNtf%|jp4G;paM?Y;LsRR2Re84gaz(_*=uNzdMwE29-4Vp z_+&Y;?d>aYIvZ<9>x<_FVw;jMwV`la*SM;KafRsSaRrxj zM4ZNoHo6G^HVfp9!Z-(bAPYQt$fN$R7+lNPKdh?p9_%ADC%BeJibL86)ON@HB`LAR zc#xqp-#mo7T^a7^QYIZgHLnng+7&Opdw??^{mtejH zv$v{3IY_sb={Ikxa!gfW%z&!ZNYymT=bMCEbNeF%Q!epw@;zd+WY?kIDQ2d0nIpeb z8w?jftkhYbUw`!YU*|sdEs?j?qg7+aRI{;LV8PzBtuNhm-)p!ZiT{ZImh~>JAcW(& zp@r#tQWD7|*`&-n%$=1aH$s(A9LIl%6ht?R6u4Z;$%MGFUD?T*$<9_O7|UrEDF}7H zm4I4D^LuuR-)b2pe8tf5Xnan$zkR*j-|jT6$BquDdx%?&me3BkYW(+z1H9yaj1NTr z&+&oK7W|{#kkz8SzBfay1k&5AVU~$XEjEN-f(B_oG-e7k(OQD>BSXPKqkyDdv!({R zl4^yK7YBkV6NeqcNt$Y)-K>ZPxgap0r;muKv7>vAAf``NnfM-JsddK8HpAnb-?5$_ zul)m}#JQgjHc5l5r}hPX3rZk~MWtHw@7k9IDHl(7VweiDN@)>~?ebE9<*)hrY^ zpd5KC1%-&1VpJ2eBDsiwMyiO#(p-e7%jupqGxI#YM{$9&=dS6S zR^O!W(K1e<=yQIocNEs`O97Q<0B8yj;3)L3DqO9_77;+9R}JL51q=CEnicYsy^$JH z%n#@NMT`0Q;UaX5jWz()aqE;_R@V z-3Aas_4B$LFZn-ULZbg26Vlv44>rK`(gUrKzH*4S{@TZX$yQu%cCpmGVg(`OJwz@uY>p5HZbjy|OfQ**-hOVg7 zX<$Jx6QijXCeK-gbK`UZGCQF63nLBk8s=i$Cd2hY6i9I6sS+ndIwC=2$4+0fM!yP) zbObjvb1#FJMj&gXeh}6o&p>@cO|SZ`sdz6m05hb*sP1Vvlo^Ou$Z{cA(kx4;`;bAv zWblDz5kGxA|B)_T_s{!C`~vP7)itvrd-nJVwaVT`+v#6N3>!A$w@=yGM%$_t%U1_~ zqD^b%xl+SR{%%wBzu6Sv#$~r;lUno3;VHLtD%w5e6)Bf8WWdO=;2u6=&`@6DTYmhz ze)7+E$LSTmo}5)PkHL zeXjYPxP*j+^n|obxeI4GCHyYIK=lXKM{wQ+)_GD>lU&{BKa{Jx9;H%_R*oH8$-jS8 zeE31bK3N5h&3SZ!6l+cr4^13AU=lI#08QU%`4VSWAoWBi6y&-YSo5+dfk;r8D~)m` zGd-TET=^UFE)1v!h`p;K0M_zA5e&850=k!x-@a{|)-s-Ng76{|FABo*$M%EZuvi_G zr{(LQya!*6V(^WyQ_HIl2SIsf#Fm&XegH&${q`i71F->!-ltEf2b7n8^+WWQLTk}G zts#Inm${Sa*Ri#HVPA zs%kYH$<&HQbomsJodBSR=qwdsV6iwXrS03wk`^vkte%m{)H|V|=z=3g4l8y>@D%d6 zLLnb?n**$$2Tq(>wHp27PpV9s?mv=F3K6#z-ClGg>9V&#F;Ab%XDN>=^DZ~eCFR{e zg0U4!I+@&iJ4rAQ6+t$-334SW2MQ{!(&1bRqb3<&ueF7DpCC6~4xaqW49#3urv-2b z+Q8Ebgjzki>bK#e#|f1dL*i9xLp1;Sd{lHpgAFv%1zr(<*&66JxXsiKe@OSg9hjYu zM6N5^!c{J9q2R~cV(&tk-{A{0)49qp?v1BFk2zAT=cd_YKc|r_%$B4i({R03XpEwUwoEgia#lU$ z7pB)s3N+0n5m#xc*eaY1`kZ#!^)|7<7!)^ak3JN#IqOfEgD}?|m3;ARVB+pH#^oD2 z0>>jbJ{vCa&s*PKV~k2<8)hndQC}Ccq>fl>VYxspo@^W^j9UVUKzE#*LQ15XSS&^j zoIaXjP93}3AwzSHOlzW{KCcGwoLO$O0DpXZ2*y+2pup~=Z>*WWei4@bga|C0^RTkA zK{Wo-xLKa+i`R`>9V+c;fZD^-IB zz-5MB86j#PO>^wy-`Hfh^CTSZlUP)!gQi(rqAeK z7C`cHTj!wWZc1`Yw8Du8JSNi=TjNN`E-Gev6PcuQ78hb0v4v)1iwo>7x_jzaF?pq` zy!`BK`}iGuZ-4w*uC5f5Dkn$C7OvT@uJXZ_nw5_4zs9C_a(cQbS~~BcTRm2?O6LN2r&M~(JO2|&Qhz~9Mc>fUBD)a#=noI zf3)Kh#AR~nsjYEv{f9^?ywKvfhK_!}#T~>ofuSapBoJcRCC9px?2Xah@(3$q;e-N& zqqb#oCD^)ITxZ^9)Ep?XpmRi?iG1k zT>)G_dxV!~Yb&v?40;`1iVB!TGjO1)oZvF}%X<=w#xx77wPJvPXtWYHA&evia$!2? zB$x}s34v1gc{#28XwZ((G2EXI^O$JRqGo7NgX4+$9h8v@&Y3x9z>NDCtSNK^;Be z)n}I8+cx*XRYyK6XxnX2@`G82HgCIr_UL&FzGgT06~++1Jd7Pue0qOO@zz|lKbm)g zKR=|Ep}Kw4|Di_;i<;Fo`S%`qa$IHggbvk7Nw4=ly84r*38UvO@x7bhr~BZ-ksI%U zhi>jXeaWSl%&PPE2c0{lXHjoRv0eXX|DiYESh3peTvoB`e!V+x$vv=oUIO5G*X0m=GOh=+)U{qvE6EB?`ut z2hG%dK#z0D_S(_=!f=mx{kD$H+8n*wQdmjm>^can}P|y)!B))IIeO^(X z|CZaoKBOqme52bx4Ef@Gm|;J9S7!aeO247aHTGJzxp}2!@SDo~#t&;&ZCOm7VWDt4 zwa%PaGs8;7Bx<>^@G?|iF-KA6T;ZFrah{^g+erKvb!D9L0&$>riya8l#qEgh)YO`p zN9<#NVA*L3P$1t5*wqe^Hg+s5z2iBm3Hg^C2FrrpXn|dR!Nqi?y1e)zec=V#;h%-2 z_!M~gui@|}Z2DJuDc$}}+{+*J+mCn;g41k*UPJ|3p{m#lMWGYXY(t>}n^Ga0;!rMZ z0Fz(tixm8(TX*hMckJ*z^$&%=&%=}F-h!8p|8xCsMi$Du*SA_eX-B-IAF1_D>(_@8 zYwuq12!ajQm12m>6|O3^B!YxRMTAvHLu5EuNu;%!L`6mQLC}=}`IgQdC`y2g*S>w< z()Ql=J$oS6qM&03bQWSASu!LdJ=WqP?r0v5=#-U^fTdOc76OoswL9cf^aInSlO=fp zc#1u8OXTXi5Gj8CaL&Gxu6t);)FR@)YISWbMlRxKO{%J`t(upxUcA&gzi89)1^uAY zeBml9fyb2lC~y2q@H35r2MrqBcm^83YUK384I2dCxPJL+IS00{UcP=KG;Y`c-(0?| zpdi*gcg`|VUOeae^?JXJl6&OO%6n$N&GpVI?IruIPw&uvlc3_TC~K|1|3dmLEG(j& zL`1}ebY8-Ro!77LMLJKqR-u6t*(un0L9mz8|DeUrQ#9;7o~6FpboROG>+?HaSM~f4 z;)pt;YEbXK!@)7EU$4Q{!;dAb6EC*zP^@=c;e7Gjts}d_W4t$G-+y%M;~xg}??2#& zkB@!yJ!2m(UVLoH;zdjDzkkW1#Y>JYUJSnl`s|y>isx=J{4PCirD;SXIT*)uf`x+~ zJDU}bXnmNahx=qsg!!3ymI|wJkGEI|vE*2C5Gp1kg>kwTk$fk)S|VffK;GP)vUQ3r z&L2k#KH#z+^l-VHV^n2JLG0L1FRz_2ezy2%c1`sR@i9bxG;e>`E|1Lr7^2mk3l^+h z`&FZD_3{;~*xBIEPk+Pk*RQ8_ZV@%&UCO?TwMC(`jJ8DoU)lmKwFSr2ANq%{SZ7r< zJW5lC4jM6Bd^~pKfFa`ePp?ebUDWB8DTjYidSOe%Ultu=R_+vVk zd9^nzLPx$K!_CmUY(*$TT3&5)&1SPyu&IEV9C(shd(Qt{R+@70cR;lvi8wXO)7i3V zA?meckbv$3HoAaSh!gMp37rH`Naa*~zI@|`Yp>n#w73o?JE8Oka6&kY6u$4(%~!sC zpEz;V))OGR@txib_i`d0yyzf%Iv0pi5|hEK)G{@^|$J{btEC--~~?jhn7 zJhx}v1L8T@^5D7$q4vQI4?;gN`@yvj=%0Tzzk`ied_+SHb6p8^qA4O{()J<{nP^T` z9X;kLc6t&V&~+csOxUMntbrfqz;S%94N3xAr88!hYn^@7=W!x(!^o`pS|HdE(%PMCF!cbLP5Z3ksH9jyf|+5=>BM z5{1~jSdD>jT|a4(4Pqorp+HN}W;_EWWy8IwFtZVkJ1VR1f> zjOyQu+G3U78I0=UhP>7-Z+4oZs1{YR)Mlq7Wri!(u!t~gL~WK`KGilm4tP9Z7OgrF zEXg28JghnuBGN$e)}pHAz{{Bs8z|l!(Z`eC$`!3d%0HzG-b+G$fpOdK(Be0Am6QRUv@-r++B4;;|9PluizdUo%MCrd{A zjP`BwTFcESCOt-S1!M%kZb~WV$PxBrH!Dhe;qUf~|Np=L{x9#xVyLoUQq4k6Yx6sH zY!6>jWnoRtC7jOeP|&em8UCjKr~IwGc)QuJzA?>S>Ki5hH~6dgve}cnH+x_Hi#mM9 zjNxitNlBjgRvlI=AGhfuA6tWueSd}g`{(M2>C;EZ$6eYe$^O^=^Ye*5x29<0^m>z4 z5=HDJiKG!1$svQh{WG2MjL{rdT2g}C1dzQL775Ig-71w6(7CG2B-?Qa7HwCMNM=>! zc0oAdv1&}7k|4JR=8?Ms6e|w5+!|1I#GRljpeY_V^RQw&B-k~^)5BNP^ec&w-)LGS z3>EjBUmPhOfhtx{p;xI}G&TN0#j|}OJI{7Qua{Yk?Z)0Ob&C=Y!6bf16pWPMP+P>R z$^Ea2@DtI`|u|DVVS`}EV-%tX#pPs!BWYnaGUO81M6W+LbL z{W6nr{$wDL!}PuTG-~N$MKsK9+G8mvNH^lRkAp%1k*@%~2t#l=pzfl%6Poxhg{P$^ zN-A2ol{=I;H5w_3#SA(6+!d0Y0e?_{zc)<`npSxZoZ{rU-(_e_=fC~FV)g13ztfm; zaB4gp5>0QuCB#909NO1w(~Ox{Nqd`q6*as@o{bFIB6!D7(YRhWg;B2XI8;c`GqMV! zp|w+5E}P^)V1+ameV2bPoy2?36zgXAI>CKW9WJl@hZ0Ju-h-tYwf$yu<2%AuNky6yG3>%pxYR-rvyS(Mai}>3RHVEiVvJM+FF7H4As97)v!;IT0_Km9Nvx?0AR$zs0LfFR zby6t(UYzsIG_CK1c<3w6$t`-v47h#QouL}nVu^T1e0EM+Mqht@*Aq{Pc}6b3i+GJM z#{3K7GiCM#6pVRutV|LSj&Z(#76tOWFv_QwIE^+5pl*Af(@5!KSD^?+Jk?BD^w6v! z`~I;AajX;w6wzlNTQPO|_VuO4V6sGduv-CQf z0a=2=0!K^I1mKd6m)^sA(Z$W1%kOm_>wD$APE}d?4rg4OzWEP8uQjq`f9i5?`DWiE zLFRa4V?c7CV1W3`Eh(rO5#xq z&uRU#SRW^O1G{z|PpiYWxCqgb<@k88Ey&;JkO(TxcI*6oO;Hit%PH5V5JE&NtrqH+ zn85&vU)9<}JO-KHX$wm*z@Y{I!sf@FG*U82huv^-N`D=H9f6}4qx8$M1ASk^yw@N( zHg5Dhg6+X%P_lCI)HwFvl;pwZ>d&j|Dau3?J^}GmF$_pMPhRrJHCI2Uzqc38!w9l~F735h2 zZJveTEtY>W;_e{T0@K*ML}y!|!M??zbPH)N?(O?5g{WhNdwZ6_D0O8TWi~Aej|3gg zuJh4(wXQr$#}HLDw{~-aNGnmqDo87BX4N8b6fo*ZqFiG@u9#x#J6bH&%{Py#p%B1`dmLxw71F1!Nxv4aO<-^gDMSIeeb7Nx*i!`Yn`*$3^4g zoByn_T50&QSG?U1E`0aFx?AcAB@>#?aMTXXF{;H$;;2Hqw5F=!PM}=H7W3Xjp(<5$ zR18)5bY{qQhqSn;uwC06c{P@8)?x#tZ3#9|wprENtfPMQ7-+1+-(uNR&6SP+Z8V*W zsSge8cI5z{c;Wj4YL{MDFHrJ39=!Rsm;3c{4a*g;7PeM%JGF1Cwk=3ZPEJgMu0M`H z1`(Wos%@Atar0g)E4qFBjgNjW&b_>K>fTaKgT>pcF6Deq`Az+o<++yjCqPogZmk>E zB0vO)QHOv@hgqdGs0*i1WsnBwGlp5IA&Eh$I+ExoAt6}%TSo$6z92l&BpEqvp$t(! z-)Q`)@MTgH1LYn}os2ibPbzY~Z)uFx)e?eA86fvtV~^JHx$uqfinI=?Hs}9acUkyJq(yD@UCF8>v}TdXw7F(0EF?6b z-N8sUS?W#Abf%`*W96m^4C_&n?-EE?9#Sj=IR2gT8X&PRvY;qPpivg{@*wZS#QJnKE z?vk9OY?+mkmpsf${VOx6@!osa{sTGbEY+a&-}p&|(x|VMy?zcA1+EvqA-bTG&Zq*a zbEqf~T8-jE{(>bYhsxphbEqz&6o-~>tLuHY7e(Um6kop?hXkV;1lX`(*L{`Jqs z$ZVc#j}jg5A$RCCJoB2qmieC-5l@aaJe}cG1rAJ=vyi12JC;fqS6QV6f@R)uM)~V4 z;HH(H2E-+$Q`55(ar>RB!%$v6;={G$^Xwxq;Z@x>fd*MbFLl~&QfT>BqS+db2P*5qQ_HRu~Xx1v! zDadrTa;Lcy(4&dabgcqxmBd!+jcTcmC-c|qq+fh4zSfqkd|VzV=bx4}J6B)@yGiSY z4Da*bTV#1o2GhqlAnj!K3){MmO+D~wXoXI;!K}z>)Un;COjY(8y6H|Ua*=_AgQCL; zxvInh(;Gz;)u>d@I2&W&ob+XnchW>#fRBvMxsGd!^~-PKgz_K$rL?+kIxK>9_lh@9 zHtCKF?5Mqcr#O#O%J5lM)3UwEqC7)YiHT#iZ4<$&KiH1Po9eSRC_sF4#Vd(NzR z%m6!;WA#)=99~mF8VhV3TI*k~_VzY=oDvsA^%vL;?9qiTi>JO`s)CLE0VZGn*d342 zLyxVPG97Ndb{^iJP0ac0P`@`(eBJI;!)4;nt_Rz!o3MHXlIKkwr@^{5q8rZ!Ta2Mg zWWkw?%ppb27d0`oYpa5N+Q(lCDoMu@U~yM1K2)oy#cIKUibJ@IVnu5CQ&uK5xh>vi z1&Gl5zMxz%-6R(HZLbi6l_Fm9|7UyjzuWHjz6<_td*J>ltL03&7WUQEnYQOkUXHj$ zI<{u7o22K)IRR1*y!O6%4)ksMM#)lM!k8)YSM>Ukk(A=8r?9^lk&OGFibxvDE^gdw zoW(_dFCsY|M#3C%i=51Fhy(0V#$NpIrB4sdFFyI>)_bhfcgb$$C9z$+b%!`Et^$}1 zJ$9&1eeFs4G`0AA!fU&g^qZg5`@Fl{R|e_vp~2n(fITb?SHeLLi7K3NrG_xza$OUk z9O!g}v6rDfo18R3XpvEBLf-vl>J6y_3DB~t0_fGTcpiuw4jvO9e*cDe50wO$Oqwrq zhF8D{Cbpvg^SGFi{>>p>LvZw>CDmIuJO(q=fc|G!v4ju6IFZg%0YcL(R0SI-M$vIq zCp&SXa%n6Ew5Ah6%m9o@e!}P|nrgG*24WcH{3n~~TU1;tIiOF1*mdA&(CDQ#X!Mn= zT#@e26QVZb+h<<+;{Bs)#iAvZTQ{Ptnq<^mDyH&pP?L+NTuMkb*;6UM!`nZ`g9guo zr|G}Q-9qp%!N4WA@D$MG#8EYzx}vX5#fw~~J=2|~ zyLFDR%sf5zJy~qzy>uNF8*$=i1+`QxJQ*~Wbg|EhZuzsYBBV3cDL)IZxUm>jXm%mT zs&UM5K~eFX5GfY;!`Fu%Z1ebCvVw7xqho4LTQ*lJtX@`U+;X99o$q|ZeUDkJCsnma zw*~_Puf)oJw2@@e)7vd7QZ@i6mSy3zw|~dg6E7Sx{eTv zv1j#OWv`#RX4(b42?qtR^aBuufnu0uQ7O`GG(T|-OyXKXye(R{uN5A}LHo@8wm_Z_ zMY{gGrgB%%)@r}4Xl&o=DI7AShvLklT%0)~cK+S;CYU*4@IAw)?C5{&ct{%tG<~fu z(Rp7*sN{s0O2rAl3$iIu)N=@dc+X61wY_PIIoZL-{`0v19X^ zIhPDv=1hEV=-C_o0?W0VwyuG;o~o&e#X1wJGBW|Y?%pRYux85g&uZsRn8~s;c7r;8 z>c?*_z9-*x&#KZ6J>Y>|on{wB?Y;bwD(pIT=RP7!iZq>8Ch=jU6VB=s;H+a71!@Hq zbV9X;;(QvW1*a2AK&4O}^(%VHM#g42-EM3pPG1#G*IhG@+1{WQPfN{JQZnUq z{&DXU`@^~viY9u~gzD11y~m2*sPfYzZ+%`-Ryz*TvwIKZTv-5>^z`)W`oI4byJW-Q zj-3w=@7HYr^y{MTeBptcDpq-tE*+KMHXp8Bk=Dx@jXWR83*N=IDUT5k$;Z<#3V#;B}yp3T2*-&F~fcN9psy2x55Fn$jV$78B6J2JDhTgV;_K7 zFs0CWYX9cd%?5Jg>Pu&4=7_g*5|+-Rzb-1@=E((>Rv1H=o~#3?KYnY$-5pv#w03#7 z%hTgCZs@yY&I;fA(?1_Ltly2}KEid)#WG&W2a;^k0i&6CDUAo*WUzgCxq38``&TuSz*=>d%k9@#W<4S1z^1iFabH6DsKq zX6xXJ!y~-ihW6{M?s%c$<}vHCleWzt>Kq>%Wvgj7cu1MJEZ8&xq+Qb)Z4uU0L|+_b zGB{zaz$lo29v7`OMS{%m3{{GE(mP;#ay>eb1 zuc@oU&1p8bSa5|Io717U!}x2Wlek1|`OF6ya9Z~juhTcBg4xAUd(CZ$J!5DUoKcta zk`UV!w4I>sj4*`+Y>)m&+g&=pxmZ^?YT0(xuw8xal`{i*>#Cu3>|yDg59~RloNL-_ zsKkXO8V|kvQ5YNbm3Usf`7`W2D$sCYk%r%RS-YD2sJ$!qjQ(BxW!POR`}{N6=k0J$ zXcrxYnOtJwBpFnYe8wEv44vi=gaaOYDg)JjzhF$Mz=*R0BhIoN^8SMn=a8GlKlguO z;2wu@GCIs)G#W>Q#Tp!iXWkC;{U>;%!^=i+7;uXV8HTR|S34jTay)*Q$Ds{9x^i3&rJ%9OP5ooXbX@U>C1m9?;%;Ju^j^xY`|$IFpi@_8RZ6OJXn>@ zze&60A0!FY%S6(V3l+cs}FU8n96f8KivR?N*Fo*TIrF@VzEMAR=p9)RE8 zjC9(P!3m)TJAwu~)Sxy&z+i`AFfI({i;Uu5K6zf8fW)82Z*AAE{j$E7EMC33Z!sGV zV`);i+IA1_gObm3PeX|-{dj8Hfz`R|o`SC36TT72n9Eu3jf~RS*$al(wXJoS-uND zh2?@bH{KVV$SV$xGbb_|yV-v)Dv+^yoX6?tA?NndUx5KV zd-dZm8y3Xals_Jo8_8NTJS?gQa?M$pB+8co)gkD5jCL-Fh6NdZ{kzO-% z*tVS3zw`f0Io@BMyyKt4yEl6q)~kucia1StT~8e-Q;M$)ocIN>e%26qsQ;!^PCe0Y zc;6c*9MbU<;vifN>A}zJ>Hkc3@Re8Tul&8wloS2%v_B!+StdVor<#cUqd)EsGvRGQ zo&_&4(4_$@k|4$Gn`!JP>Qg)8Yw$72hFXPy2DLC63$%Hq45Pw1@d8K_l9B>_I*`3h zKQlfHAIsOxjKbH=gvq$~cw>#%^#m|J(0C=qw$YFEw%PIBqar!OXX)>rNNpi4RQ}dh z7;!SJc1D&q&z5Pc&6dapD}|?8EFAkM3PuOZ~gkYbJw%%sq5BGtD3fI)iiyq5G=O|2#ys#CfOv$g0$Zl_!R5v zdhHAV%q9fpzVSL`^o6e@9^-ZRdiEd8;&~GkM)3_erRF=}A2lP7sHBV3h8U*f@;#)G z^dV(r6vpK%Ae@hauy7SLQVyy%(oU(W^d@0MAz?~206~!bu#FzMXq@_IB;o6JGH@IzQUmt;E}_YquUhZlSMifoW?W z>YSh7`JuH#*X=2mbxQZFW9!yF{Ls3gYxnF~+jy)?+2Ec-yOaP&K)An^br~9XM87N< zKD=aTNm*IR`Tq{)mkcJR=KoG;VviA(q^!K5lzY&GNI}w7|R6CO!7C69VXFwrQoH=mzEPG6> zu!|KS3zgzC9b_(>rQ{+w?h#5LC$Cuc+@09=xu)@yU%+3A(q-3+eE3WLx=isM`2Y$s zvK9_nd*y$FvMc7NNq^)5hjz``%|x?1w>w@P)FCZOC|URV%QD$^WYcG^B{o(1ybY}P0Y%I$0ANPri689ckk`|wwu^bN8bOQQ}Nlj ze*H&qm?m~}sDUGNz@oO>#I{qBS4thNrF&+-{o~2U)tc(1vuDfS^cZO`$rpOsj4gIV zo*gzJ+=D?%=6sKa((sh|Np*^j_1ytbLN~g zXU?4X_b#gv?@(G)vNP!F zxPYLmE9xr*Tns@|A84jrZv)_1kP5l!>Pmg(T6RB`R@O}}b=;K-U|6+4gb|lj{M0tt zW%Qk0i8E%7=}@My3LO{GfG>YR-oW>5*l^O_V`%$PGhrvrfSpjUJ8iz1*#i?(Fn*}J zV%Z9#SKwr7Qg3RpdXCA8Wi;QD>SsQm;NwIv3T46>qbU9GjzMibT=iMblgNU#IyX-% zr9lGpBUP%lhUh z(?0&U`Kr+QoTpG)wS98RPT+T7JQ>{sZZ0MWd3qBGBiHzZj6w8fDj-i0 zobP+8X4$tlzRevG)yCP)Q;6?kjM!IKSzX;@dU{ZG>D94SUJLQb0qw?*WtBbwVLdbO zT?pOSgVK<&hVD1~cZBQBXjMN-L%FMirICObN<*^mYi~gcExNH{xrvtbj-`GUx>Hq0 zQG#m5SF2Zl{ne^9U)S{Np5Ci>w~U^5R^nT#{emW345(H# zyB8LAAK9&_s2fBQceXQo6&l|+*KzRymKXbqB(saTG_jwB4WXU2xh|z0NY$5VdOM1-Kr(0RMuV@G>Y-J%If<74{a3Ym-L$ zP$Y7xG@F2oZ*MJ*?XmwhEkN z?9dGIeKeR;n*}bgv}gCOC>uQhqz@d}8zWvF-C%KzlkR^;jb*{I&C8b-_mz87ymFcvba?(didPQB>ojAEmWX(r zk?;~rm^nnbwgq7Q3=slNL$pW*(l_!21AQORHPBHb7^Ph#UJnFA;EV(=i`Z}a21fcV zd!^A5n#N*wFEPf%t;$GOME(DG94j);|2ZgxT&)oF&Fa-7y=!t}LR^RTEjzmD3gC^r zP3+a@mOMhoAijpuF^|&mETfC&Q#vA6q9f-*z8gik$N`wvfIt8NnFG(Y^CV? zLBM_d}`+KQ4<9r1P9`cBbyj}4D2pG08?L2 ze#=?!4+qaJ@8=z|YRZrmQm-Ceevf!&753>PAoEh)DK_X5k~^9pmW&}* z1@_Q#O-l8GWs>P_rB$t6O?jQS_`$0c_Y#xuRlUOX;P7I_z2wCEU%mh*d~;OUq)o;~ zTP6${{eH)YVdYzm%{ERf8;Rf~&B!4@5 zFQ-n)p5LBV$L?^~57rsz|H2E(E+54Q@Gn15`Lq}EWy?`)-7yV z5@)7Euf{(6pNW3u(Fxf-60>5X%Ki2I%cEkm5_@DPM3?(ne)d1?ptw|U^>TMfiN6+~ z;^OY*3aN3wed2G@x{qi2vmdz4j0K^Exh`FI1qpbQkxviWi8@3^K5f{9Sf{!> zWoXtZ=sW~>M(qa=W$)IXu74J{nSA{*j_8oCOlx*8S3z}1*VarSu?no2yHQN&m!Jc) zo9?L25Na6c>2Roxx2UTJk>{^+bLsPg=<}`&&oq4Qc|zCLghy$=Ii4{g|1r{KMLCuM z&}w+Jke>zR*~MT3L-9iV6fZ<|2P_23eFw=GzE+0*Ist!VrO;T;)`xuItQePB>?z&?$0(s=5v zV|8_C5|NT^**-+<D0H)C zf$*xw*0P;yjhjj>Ycd;|pL)L^@w9YrYc*@jd#+i9owm1t}@zQT{9YInG zav*0>A{zv2g&CO1h{wV$h-M{%LR}Rr2!e>*s6Si&Y@yEMmGkN&AEljJi=Wx7$prERp^{KYx`S9vapmyYcl<@lB^ynfQj{a>tL)9nyd7 z*#5t!CHp2AnM8ObC8=)w8lRdPKO`C;IZhd541ZwzP*U@yE53b*%|nmVa!$`nD-E;1-E z#W5taSD)>wWKO@5)9981TqWZoo-^HD4MpAZ&yD^+_xW?M8NW0Y<(202!QXjW z_ekGYt=hF@$r5P2y?a*QZri!aZ#Lx4^LANK6lqptQ52}`nsH5(gEu7GxuI)(bdTox zhnydQ!Gy6SFpvV|0uuGHdPeFW3z7l3B-UYPZP_9wbzL(@;UHI?1`EL$-hN*|hb^c^ zU$Lq}$VV2^e)l)dfjvy5sN;Fje@8IV+VY4LsQUC?FgUW0yp^?|bC8>VU}U?wN%=$V z&A-$&^mg#;q+o!VimPA?v%aMrVJ3qu_$IW*RmecZp^*ELwH59Y9OMKCxgE=(W;a7f zi27t~K_?d_QSxcgzp-W@99_Nom<~GFMVXc~CnvuBTn{JrvDuxxmwGvP@U=qTq)B-~ zKv=jxm?oCS1-6!T85A4Pn#@_monc*c^vIm9WKKI%eQ{1L`StK{BXaH$L;^@dq(#+1 zxhbjXNr#ZmSL@au6#%E0b_(bDgczO8wHXn>*ZPNr`3nQe%LfQfgGhj~;9y(WH6gIQ z5X>;*oTpd^VOaB8eG*R(TKbH&1mW1~HOF+|V3IN|c}N}FzMNn)prT@c5D*y|@B@FC z_6^{kv);lfT6KYp=V-12bUYoo2l;SQgnWu%-sq>*Ykoptw|ZZ4N5849Y#s9xJGxc2 zvUcHK3xv^c{SwhP+RMo?A~&k7Gozm4rSJop!;Z{ht|!4P=gF=~K0H1mkQK7ug?~ps zty%pO>S+`a#BmX!I+e@11o77%3iIX(<43Tzc7n4*S@By^UG5nL^Hh;Q9xnC z%3<&Zu#tvg{qCVVTL?gO5Lm+Fza%{8uySGl|6pxMTPzvti1_c3aAB`ieF-v4FTLu2S8@Q2&tJ$;#4zVO!{^Pc;$oXjV{FzrHTYoiK%TAxsl9 zD&HtObyB|Bm7byOzKg5eu%_FTR<;ZKZ`h*NO8xQgkA;G1DRIvA{T^&55Lsor(je6+Xn&RM~0cbiiZp>%266ZrEu~hSU=zN5*#l6 zDCB0ZnZ0?&s-BWhiQ>7O;Aza*68f2%(6gGRf20OI-b~|OLyPW#JHCET^`(f^mi&r} ze6A9|y?pr!{!2pTj>><*kSk>iq`Y-bLB_V!=s|@v9*x+QF7D{*DwYhzX52NvN8t<( zf=&i^q=E3t=}pWL{B$~Ar5>v@8Ow~GAvo}CSrCA_aHR}&j=y7wH^E)(O0>0lm<3

    b(7>T&1{%W+D%3_gwxcBR7_myM-&X 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 DnuPx& literal 0 HcmV?d00001 diff --git a/docs/saml2/_static/css/fonts/Roboto-Slab-Regular.woff b/docs/saml2/_static/css/fonts/Roboto-Slab-Regular.woff new file mode 100644 index 0000000000000000000000000000000000000000..f815f63f99da80ad2be69e4021023ec2981eaea0 GIT binary patch literal 86288 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 z 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

    msZ%O|+*Z+LS;;x4v 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%0AU9xwl*jMUcB+ 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 NDMp1hFB${m{{V&DCTjoy literal 0 HcmV?d00001 diff --git a/docs/saml2/_static/css/fonts/lato-bold-italic.woff2 b/docs/saml2/_static/css/fonts/lato-bold-italic.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..c4e3d804b57b625b16a36d767bfca6bbf63d414e GIT binary patch literal 193308 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

    l?s7}*4vp!2W|88=L^r| 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

    nR1?qM=|aYy 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

    F}woT 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"bt0}@AlIHaMn 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 literal 0 HcmV?d00001 diff --git a/docs/saml2/_static/css/fonts/lato-bold.woff2 b/docs/saml2/_static/css/fonts/lato-bold.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..bb195043cfc07fa52741c6144d7378b5ba8be4c5 GIT binary patch literal 184912 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

    x{o9f|7gEmo2*wv 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 z zszDMPcOXcsJmc55^ndeO7g@Vzt3hi7h|_rvbdL8^vuFU8Q+Al0X-FsB>Z;N#pKLqs z@%UfjhIRxPbum)AiaGr@&QI4Li$pV=h7tied(NMcO)QqGx?SHe}w=kD_n%u+*SbOltvK*Nz+~|-#X%o$X@(@2e z;Tasa;7fOK*{45|IJ5p6SifpUvsiR=vQHoDd!oYKTF^>W?k!zB!Cpie-kJ*Aa*5r_%ZvAYe(olaOm9swIDmx?EE zZUijbQ>Kwr!laZBXmk|XAwOgV#){)YmIh*Pwj#E`k(J1&JQUg> zCwa9)pYz2Kj~d^l@TjQ~2@PUC=S2#|UXUC?%z{0pFnKh8jo;|P3T#b0V+hqIo24?T z++{%~$(fJ|wP?ipbGgN7_p?pHL!?uCS+43%L!}Q+pmQN)syTNQmsP6X!aMZ zq0lUAsF4BsQ26!;$!|mulT&Xr1NsBX)t;rG_R;-%ZuNpv zNIRcK8jRX;6~JPk6p)_cq3D(LVzXT^nkby*L~@%lt8hVKoHqNRAq;dck;bN|vOlcm z<6rx>`04TwxI;iMBjTuDRorI_SE$F#P0yNsL*Es$XZ0RP9!*_oV(uDn+lf*Bs_7AE zhd&{ekkOaXo(!+rqMtSrbfp=!m_=uM|uI|ZFp&UG;L>`z3JDr09_=XEY)!UqX zoT!zq1g#W-QJ+Jmm>NO(e=kw_xK8vjCC=cE(1As*eSQ_8yW*W-U#;$pp>x-2L;IfE zE`U&;mCjASTG^qyu%D2@Lc1nq5Mtlz&{433$>{ZaddSbzapOu4L^*uP&-$NoIe6R& zT?B9fn!{sOWSJTro0s?EXmPEciG7kn*$GUy!UEY9he3`do1AL`Cq>kqXeouF!0e(@ z1|b%6&HWqdYpiMYCvd7)mh)HCkuL=>T?$YL$~5z3Xm$C58!6!Mxva~VmH$Sym)3Ab za&NY0kdm2y1WdbHn?B_wg_7QIA#a?r%50`fTujWy4E0nnncc}@L)Ln88cuDkT5@Wd zo(WtNQBD>RFgW-+WHS32gHi1Co8HqC=k_81>Sh?l?UGkhZEKMJYbph-T&92oCQQ2Mlz0Qkn2g<7q@zczmfvX zbW=Xt0`Btr5dHpPK0HrQgkP_ z;MgTcJXVeO;^VY@QT2CuF`zpM*&`9?Mb4Iymr#>1oLk%lgwzo=U>xy>2*|K(c>Nsju+)fxXaeZcW|52D(oJJbx}f^lb_kD+D%NG0_ZM2yf> z6~Y^#;UDcJ@RJN(*V$3Uv!fz;PTJlM4bE*Gy-t3lRkm9k({#>SGO9dOQDeSRlX?iB z#A!^f6_&_mpxZEPy!QtJ4@e{`LTL@e#yHE=@s@cvZe@r{U#YR=b7WzX#0BfHoTXe# zqwtj;A~iz~U9!i^6dZ~`X5)e}wT-N8R7ePH9F#u0U<+OfLVqTBf93iB ziBHPwEYF-Vnr8S5>1ZRaee*>eNg0NiDF%I0n{^A0PAW={38K=(?1ev=*^{AUFrT8y zTt0~MFILw!&o{@ED)~&G+{e*p^3-T}1J8s1VX)E-FHe&cZz9K;STSDQoEO;Q73vgo za*dx+usnAQQSXNJI0|5Ef;mkTr@)qpiNT@BVT?9)$6#GB84+0%?EI+bbIUw&-WZA3 zL{>B{$v22I8V9IiE2WeJ`PcIV_>WcAxBz`H!DZ6F;WAX4G1SDf30!;`G&X#-)UbMI zf7UTMG3fDYKM#ymC=otJ;O!?ao+b4!<0K4l>C*Q(RB#`IlnmJ@hHciWPCj7vy6jR^ zf6dY`Nn2>hG+u8^Jx^_x)n$ZH7RC>*n&+~&cX0a;nPMv`h5}rx&PM98xT=Z3haRL1 zGbFA4`DzWszjoMcJpi;+(as#}G2W}$3G(G(Kna3*>rGW=$ za;FO^n*wWR=F{mt=voD@Xf|;MVk<(o-&EI5y4VIwg?$=_rcr(jS5Nn25G$I&GyGeh zu#&t|0yjUR(w5*A@)DdD*eSyRAf9 zh%hnd=ph%UH8|SxnI6^0hOl9s+ZQ~2AtaY^scE}0n0@r*o)Vd1YxnH>AVS@|;D~v%Gj_ zkfH#y>&anz0|eY1y0|>xzhE3;4WrW81_i&Jr(Tn?_!pGwa!%#k zFVUekt?LQhsb>~j7Dio7EY>8 z)TODeb~T=0B!{+%X5(|O06gglP;YRd`${y|fUGxq*u$CJQ`DmF)6`5qXNW~c)^klN zG@Nh&2D}Ge89LwS8+qWclbKeh?uCtG1^lB)m2oN&%i4vM^*MAPa}m#AB^HX@ORDIcbg6W z3-@jNNscyZ8%1a**U5`3wiTIGH~tA;o_FBr6$svoI2~FP-}8-9W^*yRVp}nWTvue@ zMoBX-{xQb_y0^C8tkHKBsFphwU6<(tgrIbaf&O$RgCdot!s)oBlRB$WZCH}Mo~I7N z3}e7&-o#Idp~1H}xOtG~RT+N`x0tsZTq@jeUuV4f>v|jm z2}T1mt|Qccnmf!gs1{)PvRv!3Jm*%ysG*9Y45QYg z$`$mdD+Deo6ahoY$O6r%s9a`F2N)-AwVL-Tt8Q87T{NO{qt+a#>3Y`H`b+2%ARQ|c z+Lkx(jZ)Pybcb@4Juq5yM3-8Nv8MaEq`nKTu7R^$$T2!Ek4g#pu`a%j7+IGnEki%a%w<4-Dba0UV?@#e#K<+5WVq( zsQB-ix@iAc9tXLzKX&NuuZiNks#i3nzO0wIUlAWZT-!lO#o*h@;G+A6e& zT99ioE)PLI0InE$iCq{Z5?}^-(t7m9CDCyp+jy6v==-kvL#SkUHazwJpLyRL5jq+y` z%I*-)T|QS4#Lzd7AHQw&l-~>Nq?b!oe2Og>x*ABb3z>-xsK(T#Fl#P}>+)!4TEl2_ z{IUrl7xM8CSDZA1;fC>Ts?F;1CrfwXqv`1Ej<7d96b#DHa_8MA%_+v+&_wAa_?IdB* zHbctzF*Y=!95G{C>UKl&*ka*%BxZf^={Xf9bGYTlstFP$#)M>gGn**EUU(@p6ke=z zw-Q-AO4`FXJ@+ti=#Iex(%21ROAV=7_xB)vSz$1Z#IMKs+W5 zwrBM>Rph8DXj-8BRv#hKwKRu%QG z{&S)IRc!dkQuJpJZS(UP`s5cI-N2(kl8@Xw|0#(^jU#jP`{!WFcT!WvTY&(qi*mR% zahw?2^yrEWbcx!5&2o=pDB_S02JU!@$VUy zDl~rZzY^OvJ<+?}N1WpV`9+3bC3ElU25UxwL*h|fH`w9q*JBuB;HzGmt`p6aTR*Pui#8~tz(kH!E?d{yhJCfrUrT?It!Rau4r z)`GX>xIE?K9*{bJw)o&qh=Qil7S z5Hd&h^utA8fW$AOqIp?!D9xlJ4ja-5jC5&j6l9V_JiJGTbU0GErQ?txZMMltDQ>CDBsHBEOIHyEGzw&C)9kiG^L4j=&&kZ*EjFMpAFVvCvNSmOTjsFi+!2KUl+h6 zxdPcI$g=$1Gy>o3DZv|m;qTGNfZ%u-c0wERj$1w@Hu*W5y2DV$*pt%QExV+#a*heU8Lkr z%Qa6}q<)1YAN`m@#^mS=sz|tp&%2Iy$aNeAP~)23LN1-s;0h0^XqTEVj34*las8AErYFT~ z6HYDGNCE^7zmNis+0F{g3>rb}a=L1$?yJl|E=g4$$-st&We}ReWBWZgc*^n~>}CR2 zvszc#UDw1|UpV+{)%Kr(6T`MZ!+PeF zPh>TF6kgA?dGh+nx4c24-T!3#1#Dbe30`YxJk7{$zs;y`q>Pk}rmGvDdCtAbXlNv# z_>^#|GQFb`tM9aqgVM@fZ?7T-sVa%Y)E}_G1{J7bC0j3$D>+kGc;?%l0>n&WtyfRvc*9wQsBe7zU}(k8ETlJHT90pzE`U z43t==4l9wm8AfW`2DT_!wr5CG8tvIESvY0OPvi1CZs9gbTe2+eSscExwABVw;36Wz zaxP_3d{R=80UKgogGJEd|uwh zUf=zj<8i<{;OGDzvG2*vsedBN;#B&vKI&*d9<_^h@*AHw$Yy z0xtJ#Vrfm(CNp?{Mio|K`^@?jPx^IT?6_y($$%Ly3H!~~?0X3j(~epH8^iXs6Pu(f zEwCjK*p3Ja2}h6JPx1DqB1AGRM_N&^)lO7gyCS}TBh^Zb`9gw~b8Z^m)eQp;VVbD) z^y}jfP%Cv%`@VnjyvK42;F?nN!C)((f3pE%0~F}~@?St5N80ovv7(MYV+5`rIQcO* zD{7Ct40wrHSxY#b|F>%Co^oK0UIhQZc|Mm#<#hVH#05{2`&>kRYQEM&D~ScRIwy8H zf06^ia4SK`GscIzW1xN}_b$g5{_ncoX4`#l-*Wj;ktD*z&&6U$<9)FGWx&{Wt7!3o zTPS12D@Pr*U1T65l_m}Rxa-1GkPj`F1#~MmRKPnJjBHT}_=80WtySS4`2taYM#k;{ zNvV-V>IXpq|3^V$MJo`{vQK`S=88}KuNjM?X0TadywW)Kv@L7uYZFi6_R9DfsZRkT zZ(fc9hRdS1^+`w*eG@JU49BSA;0UuYI~X3~M8n(RoA3^>0X-xR5fD7ojRuzCPv7`V zuo)Dxu)NC+wIFE8tV{uY^3v=PZbC6JE+<+SlNtarf3;gUunAU|}m)*0xX6KV9l*y_Q>v{m$P?0Lh+xCX^r z%2?mOb-NUqbC7r_Z(&%m_F0ke~P(B*HyzrONc5wu5 zLcF$oT_H&mWPl5}QMxf%)Fyjs(M38X6ezobRZzJdb>NOH73F}bm zilK#?SOVzJ_{|Y0EUm#*$EFRVbp!k&(mvG#oR$Y7wbVV|Dq`;X33(!3GSN!&&PJjQ z@k_}_8jUSwRf}c$#R?rKx5Bf-DXwh0Q-Viv(JAta#WHublx->gyD7+sjP_>%^-6rN z>5be1fcwlE(f%^=I#rxN2b>ig4LMEO4P6EC$MI|8Bb)@tgvHVv_PXloosz1+_us#t zB39X2zh$q>uHA)N$>r%$iHo%C=G326`jr(sf=aFxW;n$&poAf7X0qyY#Q}}!B zZhpj?>`jhSPHN2j`*SX17W#MgYAk)F9M#65c9%U4Tm+CqMetFcP!gu8xB^<+bF(Lj zGJ79M)BJkktfUcOl^*I<(!-MN*=npjUP9%k#E8qeD%E44ic{I8iH;=9q^w$Kohp(S zVSIKhS^KR)^V86lm})4@tB~iYWa3Dbsm`D>srUO5>g~ zd=92}~$u&AMga6A|xT_0G$rV2d_LkX-qQx;R^j&jtFV^gKn_ zT%u?Gy?exB-2dj{DWrtOUsRI;U086R(mh?1z#(rU8Cm2o+toxPmVN%&bCVWSA>ZyW2Y z++{EWxemz&L@d!J3X^A<9oIujZH<`aq#yfqd~Uu+(TKLD{B-OIup_vf;}`c5q*JWs zv;DJn>q6&LpJ|H6qXG{<`J7siuQkJcDs4=i3S7Jh5Y9X}A9uufzAp)gOf<4+v+m60 zgF*7fl!7+0G0vwHq!Jt5U;E+!(QO8Zq}7j}_O)yjHmPY%k$oF?KY6^G9O%e)E2s6I ztHsrB>q@eba&}CW`_^!5)6CMZ3$z>(Kvv#Zlk;W>2AfT#PIm3aVOX?~zRmCvGykVF zUaZoXS!JjaZd0|3rQwAeL0hPus%hc)Agl;#Zit6p*@tGA;*OMKs+mVlyonQX?v>l7 z3kEzqv?^&7{2D0%miO}-E@Z+@!!@}NMqsv`w9876D0K^LLyJ|*frq8E^Q09Og7ei7@R~< z^gu(*)0HFpM-9S@ktC9$*`QEv$O1XN{Ds6Dw~9iIzt@M--Ug^J;p)!}3{TTj9R?aN zjgPF0Bn1$N7ugi}>|rQkH$ufuHph$N@vr4c$bYlv{}5#UF+MqqY0pOg2P83V<%S9~ zyv&Dxo)^V02KogS`|Of563?EE`A_Ir{fvQ@E^Am09-l50jWUxy5(yXC)bHEK2&;jA z{9=th%)uqv9}fzM8A)57!uF=N((zdZhn@oE!)iaka+uT-9Y?B3$&q%Bfc@++HqJ>* z2yH>221Vgc3-7|q_ovpdjsX8-F$D&p@(y9?;5KPFP=XSWpPdXbfjhPV2s|7bx48ld9DK&q z713ThkBFJfXD0#?`UILC_I5tG+=yp7Mw#-+3KOnlDkNNa<%lPChDNyi{DAsxI)O2E z@*!OwfBPc@dwQq?U8+f8WM08l%Z>#2(n~D5e5~B+;<`yHE=`jB7)?+X%E*Y6D=Y!q zz4P)E5I{GqvaK_9x;rzz>U%(gdvGSYA>tMlXQO`7=o97q1b)S=?Wl<@#@SSWGeHwA z;mn`w{o>KgXd6ow^XVnuiibD6RKl|{$lETSO{H>?Lw0t4u0v6bkz0#+YCFmwZj7xn zC#l-MYY6ob;sx-N58Wflw+&7-Je6LdF#W;3gCA(ZG6?By}U2NNDUjYG@g zxdZX`jPA8H&Yhp^U=nNyQqNL-Te@mz*d%l~l0PdUIist$5q#s~9mD_V<_&Anu8}Xb zcm`vik(F(iM!9l*M5uEUiY+2eDQd{fg6nfn_JQo>oUKP%;tp@Zo|9(MSuk)3v92w{ zgJ>NrvwD*PcSW`CK39alo$M70K575+dQE?_RL)4T!mK7655@Os;|WcF+VvLuz)vl zJ<%CM`Mq;2WE)Kp%+Q}_qiFO_7HXKx>J?t);k^cBA5)&kwgtv~cv&H%Vsfk`2VIo| zW1VB8X|$OD<1Du&3;z+(EM2p*yK{5AW>n3^-6#|_0&~Ht!G)Mue|wSt<3-Biz|`r= zoD$G|walUQZ+L`Q;5--yMuRAkUMOh>Z97P*M+7aPmHc7Ej?*AIvH^4C((Ow} z@?Nm#-X3uM&d40*D@?D#=Bh#~F{`x^JI|^?9v+#R2U(wv8~r`B){ULr;+lcOl6=t0t zl&F0hYTsV)eO$aUkj zVYa;LrPE*UGq&p15g$dMk0Y(D;!3lefs+3yTx}e4>vv98<+Q0`Mf*?|+~}`r*natEHoIBKP+8 zRK|PQSFYR+pF#3E%mR4ktspWT-HqG23mdE~3s*25O#wcl*r_YRE5_(?s1irxhW+k` z`5PkCnZ%a(3lH?B*#*mIhO0aaxN18lY(|3c^#GDNN$1v7o7b41ZEIA_B*ZFicvqId zFAURRSIt@lNMiNRY*V4NlG^@2G7$6FFO-tJ^He+*FRWd#!=amq8rJd;nRk&$_$bF~ zye4S|n?ZWoD2|7nS_!_j!Tc^hi-b5D-fvjw5iGW}(!1tH^Vz;xUxpKbgHu;}pDSch zjEDG*x=Ut%ZYfUuZ7u}A5udi)3o{EYe61mTxKPkiAo`;3k)_eHuWy$x@JDYt5=#}3 z!}QqUn%4;Y>;woBAXgUlYV-npLXF|!#y<^(hL1MhF#xMP4Q8huQaOgUp-E*V78+(>hnmC_;Kv-zP z8G4=otFw^CB_G0~`Gt4kmm1$?aQg3S{ueg+I7X69jhum~KYey1a<1q)3vn_8uHyu9 z*al1{e)8zW=R)q$xo2ns?*4DD@nY~`h-}fBEU95JWPzy z=@9nw!3WyZbd)w(e{W!IAXuK~3aQ0bDT-4yjhJa4^^IK8N!TPH4WP?EO9QHe_#|Kq z)SsxnuIUnEPblIG9P4D7CylFUrqGMtQ-N`+v-e#_yFgF96;q{FYV$`E$SN3cO^@u(VZCR7+WsiQc&iZ#reAV(vi=+^%Td%R zZugz)KGlE92=Fa)eM`8SNUTpSip=*oHl!=li62qU99nlIKzi3*J4%`^RQE~`CI@^p4|IVJ`U)?3HC=rC>VP*8$n47x3T%G7Z-6}CutsBFB9=zG?97;l*!qm6LOe;NZC2esmN72_#Q}O$XqX>d zegJm@6`Vxg4+43rh(7w(my<6CoslJ~^K0_ldD5ep-pwkIId6Dd7j7|NjC#rRMDoz7 zHzQx^)HF~25fqR$R`J5YH!XK{MjY!!JmTMuuL2R6T4b5P5p#$W8Lu*!y-Pv8oGq$E zP!-lex+)c3laE%|#TaSMnSl|)AcKJXKjhbh(yQ`Xg`M#dFHTA3c}|7U$pC^EtXwc8 zBMHk{1}h=YiY0jE3J3e-hemvi8b0oC5Y@@2<-YD?odf~Wz4K(Cf#e_>M8V=4CyB9y z$L0qe-hH944^}-ustaMrcV48$S}bFm92w!oLo-? zz)hXZD_7C{rmS?JqdA?)tC8L(Op(-$^pY-GTOUr;Gg2oWGykXibX>E>z9`~6snW-% zgH%1CmzB#YGY;qKwzB;E#2 zcWbqdh%2_?k0%ubAB=5(!)a}O6EW5oxl)jMD?+4Gng|Dw6ozKvRitBZaEA)1wiuk7Qs^XaQhH9V1CdT<=Wt!6O$KR@# zwbkZ39+ZrzS@7I|f6MFdQMm&~Z8zz4je);^Iu^t2`kFKNJTQIhzyyRK3f&$=o`~wocX15O_dftRK*qn2NIPFiUC2!nFG%Z$XtBQa{xWS&RC^#R zh$LR=g7tw|Ss{22wc5pl2B%7;cHt}C6OY>FS1ONHfRfT*0x{K@4^g5%gqifAeXe!7 zb;0Bh8r9R)U^rE!@$C4DBbZ7;t@BG64-l16-{h*D&-mIWsHv%r$--OX%BDw z{67Y+-hBxY#O36c=z5mqDg%%!yTmm<;SF+;1p%Dijt3yX>XSyfC+MGfK11#APg$9M z(y#7Wt^xkqe`Ww10s$qs;$V6Hsh|>j0n#}ob4Yod zGY`?x<1+)@ZE%vrI1=hOW-2-L@BGH6!N&aOY?vG5M*SuzKGix12r!LB^W;3PH|caQ z@S2d|l2@QhDI;B8OO#Rjy_N1^8LTEFJY!M_RIBhsb@DTs>g0Cg!DEvmaTVaLQE6US8O7EPM-4kBlda z;Ka%s-tD!l`$OAkZ(Byrv-3Fd2~EfW{3W_Z3W3l}W)_jVlclt74z~kM)P?iK(TTw{ z%}c3ZCK~ACcLJPv#PMSr$aP7N_+d5=f^kE(Qz3?l=@DLgS zUC{4nP=T(XYJ?9eu(9jZ6HEdE1LP4fcfb~7;txu%wqeBT$b4SBo+<-&FV*9#-Bk4 zB@Uk)6+y@1&YbBKeeY8MxIvP}`J;AEy=wwAFbr`5=t}}_FQJkWisWWS2Q>6Kjz~Q< zW|6QLnCMOaFT6;EoUkb_S0C!W6izDWXozQAwGMZt{53otq7_XMOw-;;u77E2N%cXP zqPB(cM#xU{;f}T*kU3l&N*m|J4vU*Y$)*C%D`dHjHfQeU$GLlOQQ^|J5kn+YP5Zmq z{o2%-XA#FvWNlozWg~igW=Oy*wUg3pvgy2ACCKE12F~wSPNyO- z>wA0Q)#T(tndxsn7liW252bTAXk5!MKI=U*z+DO?@`G-P`Ln**UL5=ib#?>-dZCq| zX}Zv5`#~CS(H23-D*=G9?^Wmp?+A=mi!2rkkP)r225o>iKe^Z65?h4_;8AB>*Q)^B z^pBZ<23aT)AqwT{@Q-}#JrDFel0s2^Ci1ORPts*Xm>UG*Q8ue?L(tSIHoOrvrFyqA zr&DrJQVMC|W+DvpxDJI$7z=V!{UFSbQV_^qs0W-L#&da zlzWrMG70s9N~>$=!j1bILrNuMFF(vO94-sYdF&LZRe^su8Tjv3ZvyA$2=*mo?qQvj zSNB_?wz8lk1S=Dd;g}=TEwytmMi4qq4{r%06+nH_^e1CzN3Ng!Hv0FVZ;a&sMi-i7YRm;AKYME2I^P^i6(@ej10!`t`mVbNi^l$D z6&tk-0l|8~?&G_A=+=}X47Yv2U4*h3wgX!!aJX9TQ7=9xc)#d8M&B)T%)G`tC3ZYu zV2a2fP(g!}>3MDEf%F0FWEEG|W+7pirj0<7C8Yz9Osi&txtUnYddXy+)JW@lcU-PO zr>2@b9q)yPQgmxTbrU59ijtF>$`MKd3viYlXIT}Z1ZTWpSE)8|N>whi7qR_hZY@Ksa5VRFpGX^dVKP9@~I&K(az+>gMeE7eEC*x zD~CxAI`w%e@IGw0mO9FGZ(!wK71z2+zpq z@LWG=Ez3HYv`aPgM#nH1oiYednux!yS{+CiJX9?x97_bhUZ;~2!Y|%ZN zLk7|(wyDgy*yvfJkf>RHNYgfi8&Uq+$n^RNa)fSkHL(JddXONwTGc_h##~34vZx)q zpSOR;pb@`(kRKhFd9rbFKTm=;67Vr*lcBaVbFA@rNAI~ynjCRcZoUa_@ze3^oeHvJ zN656}$sf1Q7q9xT?|RpRcEG+tSY{>qx_TewqipY9(VO9v^ovDMI&ao2UU)mRqTYVL zHl+4fGwa~H0aaLJm&jtzr_d~jt%f1AJCC~Jf*@JU!00`Y%_^I=m>Ze4BgV9v{pyTC zfpOBiJ)OrhDKPxc5Wz~2qFR-R`AG`Q?F}K!)x%^TqsF?q?FKyiQxBZG5GA0#X!S!6 z{<4<#&p*V)eYOU^matkNR}8CiMN8U+SM(B^W&0&wBdE;lWLk@6AllxBxwto!V~i#W za)(*ZOFbSr&#|c%8N3k`uuFv5@uhukBgxYNg>%+AuKxyU{61b>=c}~YlXL^YUcAwm zcAhp-R-YBdn3*4%qn^ueg;Zw-Cr}kJipq=ZvYgs6(v_ARbKSHroCwsKtvS^~W|(__$9-N}^g`!aB^;-x`I^dq}E4YMy; zO2@Gab(x0i)fq?W8BHfmlQx4D5C;f5uZW~}V%kk+LaW|Zgy#vzIy%6{_4$~vd9>wPJuEmd{PGBKRe@1CqgZvWo(+_;Enb7lNA=T^fk z)BEPG3&o9jiIWOJ^g!wbO_#6WrMHzbKaC}~8~^Wh%@S0l^8}0(_vl0_+u>yrmJR{a z&>K$nsmXH)uKK0 zayXKq@E~R!(;m zp*|LWCl~F{L0`WYzs^vQxEw9YJ|mia)HUo|{f(V*aKrL&cq}ZO<;he^Y$R?&FFuSA zmQ0}rqO;iCoii|P&FWBV*RmuYFaUFiozbH(! zM6+&Zv0kZR$J>MVhAh?7l5+?dxHaJ|!Lo}Ht(pXI2pI(PGp#Q^g|3S_IFTC8p^-fl zb67eB$(;T2ME@p@U=Xr^$N|;XUR_^5NsG`{ z&`q^x{{RHA)|eJWcuDq!?I>ZYGtR*0rdn>Y2^rdxDU{`VQb^tGBC^6Wu4>2kG5MVJ zTv0PqB5$DT<0x%P;G5NFQSDxf4BTkR5ci^HIidIgVro%nYnXk{os4(n`X~j)eLNOk zZ(pN~W9WBX;Up%&83Pu30R2>RVgf1zyP`WklvCTS2H8qCKTkY#9%JlH;)lQ0Dp`@X zG8vATT;2g?IC{Ih2Plc*x&J;jlsE7j`Wl!~4M$0ZZcBHnZ3dtQzBw#(10$}VJCi8D z@1z#ido?+i-V;aI{`5AbIi@5+R@rq~Ur!di)|!2Y^0MYdjKbNI8!~%VC^rSMA%WB@ z4Up?G30iYU!glC%>MH{7oj_dRytcBf8F6`Y4ql@k;TGP9+0RN2t)>zlso+Mzm4Ii9D92hTnZ#OE7m4)1gb;UErr zQ*+97$;A|x8*@uYa>^wW6f&j(wBFRH>nqRGuYXcP;oXn(w6C8gV+jTA`ts^yo>%kN z-Hur#}^|JuZWy5e4Lz(!JHpI}ZZBJT<*XmBbs24U=~f5uKP zg3N)rLjoBeOy;^evHJ?;%j^NM%K1ewvUjm^BV3m%X}3mUk4IwUOJEV2We6>Tu#EYu zqccHEdG<>LJ736^t(OW*Z_l6Yr_|Mx{#^+{7D@A}?OBmLJB4GGu(5ulq86jY*bq`6&ST;h`1PoQSVL?am>ulo%inb z=zwT_1b48v_?#i5@vNb=N1-DPyBdggZ7%0(vf128b~`PMeqtVsp3CN2bz3jAD&5~M zftlIzUdXMGW2coXP`6Xmz`Jd;z+SGp{dHiE45X#KE~Z^|s>5?#?4nE#%AUkXEM@c? z+@yF}(DX>w-ck_P@)}@eM^GQ*>n2?oC`V#`BIJOHURE^C6|iv&|5J(uz>eBzbh57p zRum2(hcL^(2l~PGwc9tbfqy6`dQx{;JHyIxxgt!$FF3n7Siw;)q8OeTDEm8 zWT<6VtQ4qxsg?}0Xe`94u91B=WW*w!zZVf46*JUKos!mDUbI`7Q8~{}igJeN0`>@7 zSg&s3)-ydKwv``g56%fU#6sVqr}agDWlB92#%c3<8>Ox6My6NFDG;gsHZT6G^)3c! zlcN<%P=XPVwLT1={MiFa^@q910Mezs{xsI5BbZXL#sj%O2(INB#SCA8w7S}w+#*JK z21_SPDTbI$fpF{5YM z@_S;{uiEVnZ61t2#xFxL7YF?OPz+1Cyy6bQi|Nluj;O9fmp@b$T~`0pAP#GD}>Ag++_@WK5~~gw|8zYB{1VR)EwKad@x8&`ot(Ruf$& zQkhsr2x?S*S_tDuXwb)vT|6?k+@C9l47l;GgD=!Ha=P7)+X z(Ltn+c)`*|#bk5j3{2`~C8#nE(6kIbZvKo|C%e^8dfe^l!x9X^%%mveDjieKJ~%<6 z4yt=HO2W<>@^L8>_5iNE5*|(BhPA5Iuv&>FgqO%8M5z&0!V6-)l4=syX)96E)KXuL zh89_dkrtD5W#j{j1$x_|Xi;F7KCfSUpdwNC;!wlkkc|Gz5qS&zSMvzg_B9_LjcOwc zmvQoMFK<-?k#a}kx@T=HQ`}UNP9sdu*>Fs+i!tVC%1>w-&@^=Te>Qe-p!9Uc1ONQa zA!2b%&wS>h|HFm`$>(EF4wNotI;D4f?tgkLI2}5#p@#cv`L^kny^@O_DK)Qp6sZ!) zH21=Ynq&Z8NO%qG?JS53VjdVGWKX@EG4ZyS;-*vzSwikWMc+37Z?Ejv1S;%40J z8wBF9PA|poQ|HHYH4p2?xs|(|Z;#AZ|CH5i`z5!&ZPOUNoX>t$?INp{Pv5^|)4Fc^ zRbzYh96|HJXvU%Lr`0^K-Lv);f9v76cgiiQ`OA=v#iElZ(x30Ee9fJ?<#n9z>;KIG zdlW6HK-z61toWFEHi4h_ssL6Jv%M;5j7wuWR%s~j{g5Gdj(gyr^387}1@EphqTiPI z^LGEb!`nY`5sdQxziW2g2is?i?+UXQmVi7z#UK3E`ghGv+x^IBLLSJNFHU`no1bZ! zXa78P?)#$m*JLT-hxJ! zKK_rCArkj@yv4oMp8WL%i&aT(u{?dubrnT5>NC0$w#wwU zZxgS-`^KC-tvQF+b?k9iJbBcaTfMuj&tIIvdF z)Icb=mv%5do_a8{xZfU!HmxYCLIPdyW17ys8n5Yfk7VXtPp#*iKxIk1~T> z45_?$XKbnNP-E9+q?95{4;(Q6X<+a2-XzAP3O&T) zoLa$_);Q~$TyIhm^Cf<2BY2YKm*9DG1q)rT2TUIkiUAe*v9>+GN_NTfnhm?+qgNl# z34R9-mqG--9X;uLCBphcrb26Nr8y(*_uUv6JJ{QjUNd~6w%3j$7u%p@Xxxr?Cm3*w z&TsO#Ug%o`40%-4a7gJSGG%zcj#T`aD0+pH)Bc&??K*aQ0 zpRDHps7*h1oR+3GpX>&w-OO>DKhC_A%7W#mgJXAHMM{{3kfs$=9yhqgh+{SuJ7=ho z1jF(igb#|jAFkbadbi$8Asy?zJyB3?>O!z%bR|XGk?&J6ixo_`TPBGjWl+U35K*4> zQW0HUtgDrz!BQ_p;`U?MZ~!;{-tQ&@-v{vp9q$(2{?C!Y|5m8q&S^-?i+>a7N>9+? zrdZxP8^!+|k5b`mM6gIdWWJ9dGppjgjT*S@Af1aY8lj z61Q=N5%29}A9R$R)~h9U{mY|T{M(L@wOYtFJ%Ow7YN3V`K`x_)+uz&Cw)7U`5r3aW zKlEuolUl?r;8eMx2hj=A*J{mN^WT3#Q3&6ey{(DD9Pd+TAc+i-`;c)E%CI-CP6|2cOh9QJ}52Pr4`ey$qM>d_jFk! z-I>ozySz2VnBOok-9m&HPucX@+EVtfAUpCdRT`509I&xHh>mC&&#{%AU9H_!E!LkH zsyIf@Y(B3q8^j%HlsMjH)q3yh;1J}GNMS$t2(cP1Hp-Wvi`1$@q)-^aDmCaRPXsTL zsUsLbIU|nD|B)5WXdCFrYPQI87%ksN;dL-{x=w~Kgh-Avy|c`9Dkzj?%`^*mnFce{1e7&d zh8JqDS)k7RE+>{;^Enz$5s)!fMw&Oo7+FZjlt(gBGK?YKOe3)Mrgk9pm}<<%2!%ct zsh#rWMYg1^6%K11nE>8qn<2cdtrTGOTvpIoVO%wY%XG5X#qErtLT<1y)nsTSYwd)_ zP^h_5&ae_yY>Jk105PfN+-~1r^*{Yb503Ocw+U#xV5ioe)e*f9tO5Wu5&454LG~I| z(IxYUq$5UQYSHKHKc;QT#RMaJlS%geknXI5M0aMD&bX*?W{rkfmvz`89v@q*)fEy1 z{3t=A!xr;-*dmQCisu&UyII=o88sTu5Dav)_4?dkp!tyucC&z!D8s7#@FY>nS>&*s z0DDoZ38#m16|LD#c2;&6JJz;|Dt7W(G_t%BRbfk2KnuS#q5tFks1&=6>SVy~3`91h ze4t4K1S(Ry9J7B-T8DT}bWAxdXm zjBuDaCdi9E*!$uew{z21ZjruI@(P&yh1Kg~qOt)xRGY_Fa@JCObe3HbTk0laYBihx zk||SfQMxiT{*9}|c{?e3`?dVANPz_hRiww1v8V1MmmRi9H*Qo^S}d1}HVieW*4raw zV`#`aB%m%JLRt@jW65iP?D)SucQXz@)Q1c&eD)k?!v6yH>eVoC13Y%s;pF{k`|I{A zl*cxCr-u2z*MIt~s%9=vXzO#0DIHLbN!?nXbjq_}TZ;P5*jl%>zk8$hE%dp`!2f9A zq;6i#?XmhbQXT!*H>%>?dRju4paxFLvq$K64xexBZcgUKl%W4mc!H#n1L z#tTEl2nGIgn(C$BVFYxEV>NFYP91$nTR9`VtW|VrY_INrmXzawOldpzH~T);FvpF1 zYVeTXX#!36>iNn z^42`oTZ!=1&IG>w{sK(>sC?x)r;Ok1y6@|>GPWNFO8xvbR=&kf%2ttVn>KaiRH;+8 zmiyXetmgWpR-q;`Qq<588w~2UK_zxMuDoNbV+-la!G7Fjyw9|L8bFxqXh-KqhjlA; zkFOt}s+_Vmc@@}t7F|IwmphZ%QrJQc$?Bo}B|^#QP#yQ8vY?d+iU$-tRue0XQF6*) zu*Izjj;#e-NWhcvZkFWYkP7b>2*vokC?t_vwj(z;eaF4giMZ?^clcCQ5S(?p@hNse z=Q7(k;M<*_?Qi~eZ#vtkG={}aW+oeSJJtNuRU7t)=!j{U1+*tX{DD<2WjFBmVbcZK zZ<=+gF`t!6O~4xeX+q1Z#T6II9)I{!1+>4z@6#x1_Gcd=|!MO zz7K?hO+DTm&)XE9OI0z7z+r?Bj4gmouXq7SK45<`rMqPQddjVR!FOw)Ds{`6Lu=ym zdu^Z}?EvvEIl#j8bIFH3eADj!re+Z=Bfgp6DLJy{rGcPo_lK`5OTL%f0^5jh=Xc-y zQSvhW@1(WdtK&j@hwptKm}H|$f2dwaI#dbH5kY0r#g9Pp+?M%nuZm5F5`12b>?^D?&Q(?dwCpmf#|+7jAOvqNIy)ss&6Bx?qNtlb(znC# zs|}e$j7bo+vjTl7-$ajkEb6CX)(EL7j}E^c7oA9;{Tu-s272T3XBtVf&t^|G4$cE5 z!*7>OrX%p}(nzeBcfI^6@^W}}xz;abcy?EJf+EkDOkw7G zl*%IPuQ;_aXh3{vZXr2|X?>)##lPwox70&cI8X$xWfPG$m9!WmlX-|WM$-^SrnkRR zRWn=!mbT~aGAVZ&qC?#oV~%pBbexGQ-ej=hD4~8H7ZFxy3yHsO#vq;g=c>l6M~Ax; z@2aX@#?w>sk5D`l(ys#Eb9@{P?vcqOSSeYP-n{-NtbCuvmZapiC%X8(wsCK!{pRoW z;%FTFTMuH+b&tlQ;r{O!r)q#x#zodCRueNe?rE&I*0Z*A;k|(@-CHH8A7d^MJO||= z9}Ax8v;zli_MPf!afhY0f1#X6(`Vja1PGWh#`s`3MVnqSTt_*{4hRm9yGfT_*&NDo z9#tO>e~OLNTD2@KAU;OllGzoU+)^TCU>bHymCL487ooo1*yBcLfACvdT&7~F6~G%~ z53__uO(908ZxH@$!HvACs=}yC%qs!6{US`7(7qA`2`0ZTP~EECp;J^0r8dHnR;*)@ z)Ono(ouUM*)e=j#t7(#)6^TJM;JT=Y@UkHE`?ojZmHGzxZ|zl>uby-h`TZ9!1Yv~R zVAg#h@wOoB`=2l4725jW?{U+rF`r{HOq8#GycUEKUj`!9rL^$=Il({Z4}zc2|D>m9 zwqkrt<>yE7JgH(FIho$8b{*5d_O3Sk#Or(Q&57)lPgLt6E+bPC?IoLiumP5BG5Le> zu;LHUU&j?khQhr14+`!De75)zb(fWq+0F^5(yIAV`9xwWFGvR}6VH#<&eP+|#vWEP zN}n$eLiWn*0)aYtFH&88-da0Cn*+IcCgNuY8#8lN_T3Y-_QIgH&Bcrng2BX`(oSik zTDIFpA1`cc4=Ua~Mu+1km1YMTGjsfnw6ze|zPaeD!pCQoc1RnBws+w8y*m}v>Wv9W>s!# z$1R?s*epF9l(X+$TT({V#QS>eP31wYlZ$LkhnD8LAC6tOsm`3#yO&+Hxed3--v)5G z`ySem(yPbcJGh^>F&&P}b3GirVN-P0tC2pc+J`l2G2f_jY;dpH0p8a(-F9~U>@*-9 zb~e4L$pTKvH58n9a-yJq_2DOn3jnF=^J!pCdQoUS zcK33uADvFkbMAPh234K~uAnx}7rNw)`PfXLJU;B2B537_OeN%8S3jiZd8+z~09zQZ zvSGNWqMC3NzC<7ScoNJOZJ`GkRGYCRYCa!hEE~NYalo`NVUXYg%ZC)}fdLj1<($o6 zHYcp+A27=8!P=I!>?@?IOF{=O%aNj^x@d}sIPt%<1)iuWni7$X6Qbm9`L+IX_R>;a zIME#0lqjW3aqaK27@gKS6d05d$!skw1i5w~2#l`FZUqpDpFjFYHOK;M4(5-KQ%z3!2%{0O6#)?h_{>+u!#@+|gVjwebUqBGd*Yze!T zOJdQa559(h!)?g+=}WpEuj^c2H#)IZf=(KTLV_~riDXLFdh7Jsm5C04npWqrtR|Su zT?DU1k*d*&3QBlQz2B^leaxY6z$+AWc)Q-7CWITn<#mgz{LW*cy6E9Akk8ZWJ5j_q z8l-94G0*if!!4$$hNz(pCD*r+omO&{gpCOZ9eNXH-Cz?<6#)>_a@}NH%h8MmmW8gC zXfR`H6lUfCPteDvF};~4%rAS$3D@6YbTau5`Ob2gkj+*qx!)|3V`O{>N{`1=A{%^$ z2Hu}#)R6RgRFGb%tyboma}6bKIibFwpp$CXRU&Lkeo3aHmNLKe&&Z@Z(UjbV7v9qi z@RDOEgTUq~Bz~qwo_>)u@2~w*ApGK+Po(l>e8GgB?PRpfeN@v%w+q3IYHGDfP3iQ6 zx;Zvu8{O_Db{g0z0f?92-qH6sJO)X83p}mqM9L{#Z}=;ZXcc5x-oA9L1RvDo^llBj z;sUbe(QplJR(IEj0p|lI4RV~ySk|J02sZX?YoNU3H7<7nFyaK-~3|1OhB)fvn>kxb6a zcG6{;mz`W8?3WUYqI5`M;yY6-GlgwHpLrw6JlNF7%k9^EoT7B@RC zKQ!^dl12cQ6sUFLnFwUR5Dl2~$s?K=V(s9owjxl3O-X;Al}nb+jtpu-wVTe=h=giG zM5y;%xC{OEpKL(zENjLcGSgyp0jq46MFYXKXcU2RL@ChcF@a!sVf}~iHcB7>Vr4c? znW=>7(E(Ufrg2slo9Tv|&{nu7lRI15**0zCnR0m=uU(f%E{vgn7G3qcVSnY1N3p-v zkSQbnp0L06*MsPS4HX{7T&`IqS3dvVML=^>7h8f>DBvO9QCA_4SHwE%X%tc@iqT6s z1V^b7h!To{(n2oId~8{vK=>b8W&>0;kO>`9)QOH?P7p5A@dZrg|F?#!(X!@Ixfrj^ zi}-;Bw2u5~_Jd0we{YZn-NU3Vinvm5ynXDPfKs1wy=7_2;mxbw&ET@`u@zUAZ~muI zaaNHMEy%kGv4d1}(f((HwCG9gOG;;?UVr;om&>vGiW4 zhAhY>rTmJ>=jJYl5u*qfF$CfgI+VInm6!KOdf#$~t%^J%JPC=F<_Y}HZR4DHn&Wb; zyiA2YpYG+P0Z*1F+fKnx64+$z!ac{nDMip|X^>-i{y;J@ddUH-WebQ!8V;o0{8xOC z3#xf&0J#;{T`|AxS|e=Nfuxo&>tCkXIvV1|Bd?$grXjb7eQOya;5TV3C^EUEM<-Fh zh6gcp6|kdvyc-zOyRCbue@DWWs**@qS)^w9y@G%fUg<@wkuZ2j0Rq@uU)nKw-lAQ~ zX6&civYJiX8n!PB1;B&nPo9kTdOQ?h1+AF^=t`H%WCS1>-)4@|<1rbo-?S1R=W>+; z?aPLeG)vH`Z%fZyT$wG6v&Qzh*~3K)h;rLWkg=C1242lzBF$S5Aw91fI5}ezuV#Ha zNoDNS$zx81{U_>PAynyr)F^WQlu`bie4)Pn1Q@0_q78?NuEcN$zi@P<8{ ze;xY{sBiD-Y{>fSWhesS@`dS{tWee^D;m@d(1S8T21K2&D<19Q^Z}~!OsBG%)Vc%8 z5PTRN1ysjAaYT6Mc=zXzkZLns$K_3?S4HX%QP;bP0Lzu0`l~(Z#Q`D5&tyI}UJy9x!68d? z-dh!PAA#ZOpm@E|&94~vO*Su# zSa!b0RVbG+P!A)tvr~UM9#?hb7mB$zlWFN=^CHXvY9OY@V6G+Vulk$M5Iee;W4HA3e1)_gt(Deb z)cO(loDU*4q)%Lb;L42Hd`NpkR;=8+7&&pkdEkTYz79*Ls#pe<%MdD{uOtRgX;#s%%G z_*1PXmtnCbS=NpDGBwrwU)yp)V~2e7mr-eKXLCUd>npe~D|bu>w5+GIvc=0gx}*zQ zI=_MPlgV|eo4TMQ`re4NtnAFTYgwPmQuN%y|w?cAX&4-kthGoKxi@{FQuW}eP9{`;8g0xIYJ_M z4A3C3sVCv^d`TxJQynx-y_;AIXFGc!jle-pEguT^p&tw&L)|%sQ0-_70@gaFMZ((V zhCz;PCx$C1Og(taHPLrDQgR5Qi8s0=;q=k_i&MmT1YAh*3Sa z%!{`qOZqYy{JdAGZb<+ppv|~wUd-DgfTnL6ek#S0UlBov`mdWgNMjH+6RpJH!Ky+F zn9eH#|8DA!0FTcnv#^z#m$gq-PI?zs52(J=29LO5oU>5|G_VsumwPNo7J$6hC`*t= zpC`fZzWzfDE1}#s>k~o74zx>6DUn?f`N^y}Tk^-q<9Pd0m!bnco=s(z%5vPLN%Ne3 zZ{J})=$-^HXq;O2X$86OpDd!7Uv7NWoU-XBj|#L zp2nf)Q{>7v4KOmf^0}Yj4(JCo-VQ76j&7p0~fB|_J z4rr`5H&X0 z{pK~bUms9{&Y>r^{}=%l-0O!u_PllfvuER50(Z*);b_8@=QmL83p4(BKE6WKJ;X=4 z-YZ-cjilT?TQt_l0kV}9y~zUSTEKiFz1R;0*!ZmF3z&pb`Y0nmdVXiYl3%)}C--}4 zXZ#0p;=3N_=#&PGQHc(5>2R}w+XnIMZL;>UqM&;y5P&ZdW&KwG;@lnfkB2-Uv9)SV z`NwBBfTQEvFc#Nw;**6Ru&A+_G>ab-k}5KP!+ixKR^iy(xy%lif%+frst#e?A-l6u z2*Zx~Nw;Or3jRlVy6*Jsb0;buk><{~)aW$pjQX!Df6Zsdoc0|?-d|{*Y4RiauhAtD z*rF4A?x@OTwSp1foxmRT*mDc-abg_@vRzH}{WPtj?4+xIg;RR&j>0Cbl6eVFThHUw zIU5zSO-Vz3FJ&i+~=l`CQkDF$tx$}Yerov3^_#5y*d zb6i}xmKBjg5S-ks4RavUo4*`MjH#q>p^8@^HF2?hv z&b|rmBkEYfMQ^i^BKd1#FYg@sjjhl_D;l@To@x40!xE^cC0K5&$aaQLsO88;QY@6n zb2mM^%oY*=*|FvKWe8_{jHD3dQ$7p(O++d*o649+@_Zb$^}zlSY?|Zaru39VeYlP4 zTrVvV^Y%?u>P!RTx;PLRbq~yNfkR;(bR!YqGmk7p>adSZBP8sUgh`JqocE|hi zh3}`$OStGrBK6k;%*P99K8zi^$qm|O_>L9Ql|=X@*9fawvD~^j`O+O)gwBF?QW?_z;SoRsMWmsQw{K!BSwT#V0AhU6VA`Y{bJ$eo2c|ga{lK z3uw|v+-p?azKop%JlKcp`&~CybYoQae5i1dTx^%|oKQyG3VYeJ{U*X$g^m9^9$uvze;Pf0XO^7`j zwadd8osz1?g;JOHg6g`R;K$R%9Hhmdt*HJ5aJ3N02)GEpeQtTbSA?}b5}}lpTs7={CW1*k=xx-R?n1FC=2^aF>f2WV zPeT&*pm%2{)XiOOJQ|+E=w5wdNdI5=sK@)|XNz-HX-a zA_4-)hE-Q40i#l}g}F(G*8e82VAXYR^KeV_D9+O5cGfc~I19>m(XjOxm9q3WDFYD4 zv*Gj4B*TmJ9C9gEkg$I#9;Z#SO?zdP9AAn0BVU93XQ#hb+S?ArbLc!Z%1 z-D&W^qL?w@T%Fhbwz6{J<$Jmc2X7==A(XJz(>X{dhG^D`#?L>ii@`9p(%%rw;$t1AC1`U z{H;I2;w^zgdG3{SG(!D>-+BCS&-3umngHe=+Ni#`a!M9+HaZ7X{VRE6NXJ9&rP3}$ zbtihg=Zzq4oqNyQ-befjwBWgh*EG*C(9RD+biD>q6(@Q~A{H+BFX}n0LRW)X>kf zX_{tM;*++8(y7aBnWA+w_uA}>>P6>58+Y{3hh2J{b!wubCJ5OS-QO>on^x3FeD1-Z z8#m0(pM#&6Z7?M3K5~7_OBX47t|f2y<6cJMo@QT*#`3Bay2zW)xA=qd-Ia)7AN3bNHDFD!|2g@2d#0>nNGiY zHB3(8*0kDk+;0k(TEXAFtHyDuC8FIqn%&f)wn^TVEH26AR0KDw;rd&1;?an>w*QH< zzwt><)j{mbpI3k2BKBeTfAoUVxu(mXk5^`J6)k&qL^6XDX9+{Z)%6`#o>zFMFgQrq zW#l)IwmBO{{rE@A8<(|H{5cymLsJzHw|^nsb~72>_GJ+nu85Wu-SEbQw@G@u{r~p@ zi`vjk%a{N≶M9Zv|5`G?V+t`dH%WEMXqpk;_3Hk#c)5bow}^>GHdOg=@5pYx=49b13Ly zrJynzDRebu8rnRSaDCBo@|9g53n?-Toj%;)7@=UY|Noy}Ma{<^CTZW`I9PGat{6t2 zaDw4J3oUfqrNwG{*q4kXjQO#)d%E?I`6unq0@g?R`EPa45Y|xpiRX?D^@^QqfPjrp z`1!tA9;wNGs_VH%HtUBo8*1_;v~g)avtfdTTKYQ+w|_T@R1|@!z4MWbhw@XQ#{r8n zFBZwGL~*S&Epds65DN=S9LDmC%OD``N?Ja3mq@-KYHmwkg<5GJXlu`C1U~hF;}GUt zEV_svDDRB0pxY;h0iVB6!+t9MoPjoUN2{tnNvmp71!_;3|F6OefCDX3EgVYSHa&Y~ zU}M_~aPglu-?Am;VW9ar%IVbGkq@`{Tq5wAt!;iU^4x0?c1udN{X&%ux4V&|KJ4W8 z^-G%6%-Mk6r|NGgPt?6Gz6|&!zRxhaV4(F`GZ8rK4j__!PX?Q7^;*CQ1mh5%DvR6C z?e!IxGMT8~xw^^9NiPO6=pQ@SRODn~Fsq8jD|VmScW#=CFmzog>dWa9ISWyD>bX6* zxnZ#2TwN0;af`pj^X|5h-UR+^#wtJ!B@tKiK!pf5VjK5!fZLt zzX!?qwZ#;g@k9fI%Zc#zkaZg@VAO57d!5O3Ue&0UhOx8iALeb!OI zRvYFgz{G^aMMxIXA9Q8%Ks(ns{Ng8LwsDMRXV9_!HbeTZg#-D*^A&&kWc>8s7s|rI z=ynSoO>Z_is~m>%blt}gX#It_>e2=$ntV|mjceXzzfgYM^uWLH4|qn@T1Vx$y+(mB zr_oTl_0pJ8ZR}4L)pN;gX0*r7gHMr7oprWF%6?*dA?da-61JtJNHWwP^#36TIN#Kl zHX65H`VqI9GB?_ee-?H!Yb?FpyYadu!+UaImNKBnpF_sZf6ay)B7}^R1hodXNP?eK z4UhCNG=>^Ra(w)t-DekO(;#W^pCZ0-I00f84I3?O?7O zB%x)J$UIIl{lRxlFHDbkes}4Xh27aeZ@lufb-e26$_f|NxU&YtVUJG#2x>BxrM_VU z1LCx!(BL?8gDF{Vq$J)xNc>yL)$=)7I*g|%)(&0-;j8CsUSvGa_^S6DVMpH zb8v0T>G{!ohHICj*>1fftNM+zZtws0KCZLv(IK>C_QT1EH@fD(8$2iPv~!>Y4cV!P zSZxtr!>L43HPE+4<>ftO;-5eywKK$aj&E__@NZF$Ub2O545&GURXRcHZf+@4Q*sxY zWp376swn?@b?YH5czxG41HZOjt)n5ymnfWVn}~l0v!W9f09Qb$zl@4>j!2BD$PIWy zG%tHq1co&qIS~ddbB3;u@9Dn~xdsYnr5s8^`W*i3m@VDEnOB&F?o#)Rs5v1eUah$w>#9 z(>0sd(bH1S1!0KMO_Fm9-(30B*tAvEi!I8bj@kN6Z10~`=5`eh*Oh@TSgc~GARo=W z<9@k$y19A1xpq3jbDRG4LD38!4XW&4?>FThiiaoIiVSTRo?|YzPPb-muv!>myEIOW zt#-bt4&U((ZdDWewL`sy(~|m2`BN@QF+Q{|G9ZCrd6ta$KC*E*TQ_G)_3NXw*H*CY zbEL^@$wN>XGaLNe4bu&0*GJMUg5zS=CN)LQyqHShcMq=mcw_4h9+%ozHC=V~`$%?N z`DWW#&TppMgR}4%!g!nK9J8-_y1Mk=F&oUrC@E68`W@5@I(HpHbiw(p1ld9~Keub^ z`qq-^lE(GMES@?`16xSt_yAA?b$A0;omip zd;&xiYAZP+=TTDO@|)ZHN*1!_{_wQ@i@zy5-UG{`HCbB7dqN5AgmRL@R~5 z-KAmZe7J3gow^U;9@e_R+QBj&j{H=={FCM3!&hwmwI9#NDh>OjBmRdo%0A!wRKaXY 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