diff --git a/.flake8 b/.flake8 new file mode 100644 index 00000000..3b2e86e6 --- /dev/null +++ b/.flake8 @@ -0,0 +1,4 @@ +[flake8] +ignore = E731,W503,W504,E501 +max-complexity = 50 +max-line-length = 200 diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 109cfae2..1bdc929c 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -13,13 +13,11 @@ on: jobs: test_py3: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 strategy: fail-fast: false matrix: python-version: - - "3.5" - - "3.6" - "3.7" - "3.8" - "3.9" @@ -27,80 +25,75 @@ jobs: - "3.11" - "3.12" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - uses: actions/cache@v2 + - uses: actions/cache@v4 with: path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/setup.py') }} + key: ${{ runner.os }}-pip-${{ hashFiles('**/pyproject.toml') }} restore-keys: | ${{ 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 + sudo apt-get install -qq swig libxml2-dev libxmlsec1-dev make install-req 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 + lint: + runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 - - uses: actions/cache@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - uses: actions/cache@v4 with: path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/setup.py') }} + key: ${{ runner.os }}-pip-${{ hashFiles('**/pyproject.toml') }} 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 + sudo apt-get update -qq + sudo apt-get install -qq swig libxml2-dev libxmlsec1-dev make install-req - make install-test - - name: Test - run: make pytest - lint: - runs-on: ubuntu-20.04 + make install-lint + - name: Run linters + run: | + make flake8 + make black + coveralls: + runs-on: ubuntu-22.04 environment: CI steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: python-version: "3.12" - - uses: actions/cache@v2 + - uses: actions/cache@v4 with: path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/setup.py') }} + key: ${{ runner.os }}-pip-${{ hashFiles('**/pyproject.toml') }} restore-keys: | ${{ 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 - pip install --force-reinstall --no-binary lxml lxml + sudo apt-get install -qq swig libxml2-dev libxmlsec1-dev make install-req make install-test - - name: Run linters - 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 + make coverage + make coveralls diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 6313f31b..00000000 --- a/.travis.yml +++ /dev/null @@ -1,28 +0,0 @@ -language: python -python: - - '2.7' - - '3.5' - - '3.6' - - '3.7' - - '3.8' - - '3.9' - - '3.10' - -matrix: - include: - - python: '3.7' - dist: xenial # required for Python >= 3.7 (travis-ci/travis-ci#9069) - -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 .' - - 'travis_retry pip install -e ".[test]"' - -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 --ignore E226,E302,E41,E731,E501,C901,W504' diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 9b0be815..00000000 --- a/MANIFEST.in +++ /dev/null @@ -1,6 +0,0 @@ -include README.md -include LICENSE -recursive-include src *.py -recursive-include src *.xsd -recursive-exclude * __pycache__ -recursive-exclude * *.py[co] diff --git a/Makefile b/Makefile index 95848867..31efe284 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,10 @@ PIP=pip +BLACK=black FLAKE8=flake8 PYTEST=pytest -PYCODESTYLE=pycodestyle COVERAGE=coverage COVERAGE_CONFIG=tests/coverage.rc -PEP8_CONFIG=tests/pep8.rc +COVERALLS=coveralls MAIN_SOURCE=src/onelogin/saml2 DEMOS=demo-django demo-flask demo-tornado demo_pyramid TESTS=tests/src/OneLogin/saml2_tests @@ -14,19 +14,28 @@ install-req: $(PIP) install . install-test: - $(PIP) install -e ".[test]" + $(PIP) install -e ".[test]" + +install-lint: + $(PIP) install -e ".[lint]" pytest: - $(COVERAGE) run --source $(MAIN_SOURCE) --rcfile=$(COVERAGE_CONFIG) -m pytest - $(COVERAGE) report -m --rcfile=$(COVERAGE_CONFIG) + $(PYTEST) + +coverage: + $(COVERAGE) run -m $(PYTEST) + $(COVERAGE) report -m + +coveralls: + $(COVERALLS) -pycodestyle: - $(PYCODESTYLE) --ignore=E501,E731,W504 $(SOURCES) --config=$(PEP8_CONFIG) +black: + $(BLACK) $(SOURCES) flake8: $(FLAKE8) $(SOURCES) -clean: +clean: rm -rf .pytest_cache/ rm -rf .eggs/ find . -type d -name "__pycache__" -exec rm -r {} + diff --git a/README.md b/README.md index d8fd5b26..5eaac841 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ![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) +![Python versions](https://img.shields.io/python/required-version-toml?tomlFilePath=https%3A%2F%2Fraw.githubusercontent.com%2FSAML-Toolkits%2Fpython3-saml%2Fmaster%2Fpyproject.toml) Add SAML support to your Python software using this library. Forget those complicated libraries and use the open source library provided by the SAML tool community. @@ -13,7 +13,7 @@ This version supports Python3. Python 2 support was deprecated on Jan 1st, 2020: #### Warning #### -Version 1.16.X is the latest version supporting Python2, consider its use deprecated. 1.17 won't be Python2 compatible. +Version 1.16.X is the latest version supporting Python2, consider its use deprecated. 1.17 won't be Python2 and old Python3 compatible. Version 1.13.0 sets sha256 and rsa-sha256 as default algorithms @@ -90,13 +90,13 @@ Installation ### Dependencies ### - * python 2.7 (deprecated) // python 3.6 + * python => 3.7 * [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 -Review the ``setup.py`` file to know the version of the library that ``python3-saml`` is using +Review the ``pyproject.toml`` file to know the version of the library that ``python3-saml`` is using ### Code ### @@ -107,7 +107,7 @@ The toolkit is hosted on GitHub. You can download it from: * 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). +Find the core of the library at ``src/onelogin/saml2`` folder. #### Option 2. Download from pypi #### @@ -217,12 +217,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) +It requires python3.8 (it's using tornado 6.4.1) -#### setup.py #### - -Setup script is the centre of all activity in building, distributing, and installing modules. -Read more at https://pythonhosted.org/an_example_pypi_project/setuptools.html #### tests #### @@ -230,19 +226,23 @@ 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 properly: ``` -pip install -e ".[test]" +make install-test ``` and execute: ``` -python setup.py test +make pytest ``` The previous line will run the tests for the whole toolkit. You can also run the tests for a specific module. To do so for the auth module you would have to execute this: ``` -python setup.py test --test-suite tests.src.OneLogin.saml2_tests.auth_test.OneLogin_Saml2_Auth_Test +pytest tests/src/OneLogin/saml2_tests/auth_test.py::OneLogin_Saml2_Auth_Test +``` + +Or for an specific method: +``` +pytest tests/src/OneLogin/saml2_tests/auth_test.py::OneLogin_Saml2_Auth_Test::testBuildRequestSignature ``` -With the ``--test-suite`` parameter you can specify the module to test. You'll find all the module available and their class names at ``tests/src/OneLogin/saml2_tests/``. ### How It Works ### @@ -650,7 +650,7 @@ def prepare_from_django_request(request): def prepare_from_flask_request(request): url_data = urlparse(request.url) return { - 'http_host': request.host, + 'http_host': request.netloc, 'script_name': request.path, 'get_data': request.args.copy(), 'post_data': request.form.copy() @@ -772,7 +772,7 @@ Notice that we saved the user data in the session before the redirection to have In order to retrieve attributes we use: ```python -attributes = auth.get_attributes(); +attributes = auth.get_attributes() ``` With this method we get a dict with all the user data provided by the IdP in the assertion of the SAML response. @@ -793,7 +793,7 @@ Each attribute name can be used as a key to obtain the value. Every attribute is The following code is equivalent: ```python -attributes = auth.get_attributes(); +attributes = auth.get_attributes() print(attributes['cn']) print(auth.get_attribute('cn')) @@ -1212,17 +1212,9 @@ can find more details and an installation guide in the [official documentation](http://virtualenv.readthedocs.org/en/latest/). Once you have your virtualenv ready and loaded, then you can install the -toolkit on it in development mode executing this: -``` - python setup.py develop -``` - -Using this method of deployment the toolkit files will be linked instead of -copied, so if you make changes on them you won't need to reinstall the toolkit. - -If you want install it in a normal mode, execute: +toolkit executing this: ``` - python setup.py install + make install-req ``` ### Demo Flask ### diff --git a/demo-django/demo/urls.py b/demo-django/demo/urls.py index 5b64e7ce..92bc0a2b 100644 --- a/demo-django/demo/urls.py +++ b/demo-django/demo/urls.py @@ -1,10 +1,11 @@ from django.urls import re_path from django.contrib import admin from .views import attrs, index, metadata + admin.autodiscover() urlpatterns = [ - re_path(r'^$', index, name='index'), - re_path(r'^attrs/$', attrs, name='attrs'), - re_path(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/demo/views.py b/demo-django/demo/views.py index b9acdd76..82ae55eb 100644 --- a/demo-django/demo/views.py +++ b/demo-django/demo/views.py @@ -1,7 +1,6 @@ from django.conf import settings from django.urls import reverse -from django.http import (HttpResponse, HttpResponseRedirect, - HttpResponseServerError) +from django.http import HttpResponse, HttpResponseRedirect, HttpResponseServerError from django.shortcuts import render from onelogin.saml2.auth import OneLogin_Saml2_Auth @@ -17,13 +16,13 @@ def init_saml_auth(req): def prepare_django_request(request): # If server is behind proxys or balancers use the HTTP_X_FORWARDED fields result = { - 'https': 'on' if request.is_secure() else 'off', - 'http_host': request.META['HTTP_HOST'], - 'script_name': request.META['PATH_INFO'], - 'get_data': request.GET.copy(), + "https": "on" if request.is_secure() else "off", + "http_host": request.META["HTTP_HOST"], + "script_name": request.META["PATH_INFO"], + "get_data": request.GET.copy(), # Uncomment if using ADFS as IdP, https://github.com/onelogin/python-saml/pull/144 # 'lowercase_urlencoding': True, - 'post_data': request.POST.copy() + "post_data": request.POST.copy(), } return result @@ -38,61 +37,61 @@ def index(request): attributes = False paint_logout = False - if 'sso' in req['get_data']: + if "sso" in req["get_data"]: return HttpResponseRedirect(auth.login()) # If AuthNRequest ID need to be stored in order to later validate it, do instead # sso_built_url = auth.login() # request.session['AuthNRequestID'] = auth.get_last_request_id() # return HttpResponseRedirect(sso_built_url) - elif 'sso2' in req['get_data']: - return_to = OneLogin_Saml2_Utils.get_self_url(req) + reverse('attrs') + elif "sso2" in req["get_data"]: + return_to = OneLogin_Saml2_Utils.get_self_url(req) + reverse("attrs") return HttpResponseRedirect(auth.login(return_to)) - elif 'slo' in req['get_data']: + elif "slo" in req["get_data"]: name_id = session_index = name_id_format = name_id_nq = name_id_spnq = None - if 'samlNameId' in request.session: - name_id = request.session['samlNameId'] - if 'samlSessionIndex' in request.session: - session_index = request.session['samlSessionIndex'] - if 'samlNameIdFormat' in request.session: - name_id_format = request.session['samlNameIdFormat'] - if 'samlNameIdNameQualifier' in request.session: - name_id_nq = request.session['samlNameIdNameQualifier'] - if 'samlNameIdSPNameQualifier' in request.session: - name_id_spnq = request.session['samlNameIdSPNameQualifier'] + if "samlNameId" in request.session: + name_id = request.session["samlNameId"] + if "samlSessionIndex" in request.session: + session_index = request.session["samlSessionIndex"] + if "samlNameIdFormat" in request.session: + name_id_format = request.session["samlNameIdFormat"] + if "samlNameIdNameQualifier" in request.session: + name_id_nq = request.session["samlNameIdNameQualifier"] + if "samlNameIdSPNameQualifier" in request.session: + name_id_spnq = request.session["samlNameIdSPNameQualifier"] return HttpResponseRedirect(auth.logout(name_id=name_id, session_index=session_index, nq=name_id_nq, name_id_format=name_id_format, spnq=name_id_spnq)) # If LogoutRequest ID need to be stored in order to later validate it, do instead # slo_built_url = auth.logout(name_id=name_id, session_index=session_index) # request.session['LogoutRequestID'] = auth.get_last_request_id() # return HttpResponseRedirect(slo_built_url) - elif 'acs' in req['get_data']: + elif "acs" in req["get_data"]: request_id = None - if 'AuthNRequestID' in request.session: - request_id = request.session['AuthNRequestID'] + if "AuthNRequestID" in request.session: + request_id = request.session["AuthNRequestID"] auth.process_response(request_id=request_id) errors = auth.get_errors() not_auth_warn = not auth.is_authenticated() if not errors: - if 'AuthNRequestID' in request.session: - del request.session['AuthNRequestID'] - request.session['samlUserdata'] = auth.get_attributes() - request.session['samlNameId'] = auth.get_nameid() - request.session['samlNameIdFormat'] = auth.get_nameid_format() - request.session['samlNameIdNameQualifier'] = auth.get_nameid_nq() - 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']: + if "AuthNRequestID" in request.session: + del request.session["AuthNRequestID"] + request.session["samlUserdata"] = auth.get_attributes() + request.session["samlNameId"] = auth.get_nameid() + request.session["samlNameIdFormat"] = auth.get_nameid_format() + request.session["samlNameIdNameQualifier"] = auth.get_nameid_nq() + 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'])) + return HttpResponseRedirect(auth.redirect_to(req["post_data"]["RelayState"])) elif auth.get_settings().is_debug_active(): error_reason = auth.get_last_error_reason() - elif 'sls' in req['get_data']: + elif "sls" in req["get_data"]: request_id = None - if 'LogoutRequestID' in request.session: - request_id = request.session['LogoutRequestID'] + if "LogoutRequestID" in request.session: + request_id = request.session["LogoutRequestID"] dscb = lambda: request.session.flush() url = auth.process_slo(request_id=request_id, delete_session_cb=dscb) errors = auth.get_errors() @@ -106,26 +105,25 @@ def index(request): elif auth.get_settings().is_debug_active(): error_reason = auth.get_last_error_reason() - if 'samlUserdata' in request.session: + if "samlUserdata" in request.session: paint_logout = True - if len(request.session['samlUserdata']) > 0: - attributes = request.session['samlUserdata'].items() + if len(request.session["samlUserdata"]) > 0: + attributes = request.session["samlUserdata"].items() - return render(request, 'index.html', {'errors': errors, 'error_reason': error_reason, 'not_auth_warn': not_auth_warn, 'success_slo': success_slo, - 'attributes': attributes, 'paint_logout': paint_logout}) + return render( + request, "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 attrs(request): paint_logout = False attributes = False - if 'samlUserdata' in request.session: + if "samlUserdata" in request.session: paint_logout = True - if len(request.session['samlUserdata']) > 0: - attributes = request.session['samlUserdata'].items() - return render(request, 'attrs.html', - {'paint_logout': paint_logout, - 'attributes': attributes}) + if len(request.session["samlUserdata"]) > 0: + attributes = request.session["samlUserdata"].items() + return render(request, "attrs.html", {"paint_logout": paint_logout, "attributes": attributes}) def metadata(request): @@ -137,7 +135,7 @@ def metadata(request): errors = saml_settings.validate_metadata(metadata) if len(errors) == 0: - resp = HttpResponse(content=metadata, content_type='text/xml') + resp = HttpResponse(content=metadata, content_type="text/xml") else: - resp = HttpResponseServerError(content=', '.join(errors)) + resp = HttpResponseServerError(content=", ".join(errors)) return resp diff --git a/demo-django/demo/wsgi.py b/demo-django/demo/wsgi.py index b1c7db13..49617a60 100644 --- a/demo-django/demo/wsgi.py +++ b/demo-django/demo/wsgi.py @@ -8,7 +8,9 @@ """ import os + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "demo.settings") from django.core.wsgi import get_wsgi_application # noqa: E402 + application = get_wsgi_application() diff --git a/demo-django/requirements.txt b/demo-django/requirements.txt index 3cf7bed6..c4d84d80 100644 --- a/demo-django/requirements.txt +++ b/demo-django/requirements.txt @@ -1,2 +1,2 @@ -Django==4.1.10 +Django==4.1.13 python3-saml diff --git a/demo-flask/index.py b/demo-flask/index.py index 3ad2a4e6..a03eeccd 100644 --- a/demo-flask/index.py +++ b/demo-flask/index.py @@ -1,36 +1,35 @@ import os -from flask import (Flask, request, render_template, redirect, session, - make_response) +from flask import Flask, request, render_template, redirect, session, make_response from onelogin.saml2.auth import OneLogin_Saml2_Auth from onelogin.saml2.utils import OneLogin_Saml2_Utils app = Flask(__name__) -app.config['SECRET_KEY'] = 'onelogindemopytoolkit' -app.config['SAML_PATH'] = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'saml') +app.config["SECRET_KEY"] = "onelogindemopytoolkit" +app.config["SAML_PATH"] = os.path.join(os.path.dirname(os.path.abspath(__file__)), "saml") def init_saml_auth(req): - auth = OneLogin_Saml2_Auth(req, custom_base_path=app.config['SAML_PATH']) + auth = OneLogin_Saml2_Auth(req, custom_base_path=app.config["SAML_PATH"]) return auth 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.host, - 'script_name': request.path, - 'get_data': request.args.copy(), + "https": "on" if request.scheme == "https" else "off", + "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 # 'lowercase_urlencoding': True, - 'post_data': request.form.copy() + "post_data": request.form.copy(), } -@app.route('/', methods=['GET', 'POST']) +@app.route("/", methods=["GET", "POST"]) def index(): req = prepare_flask_request(request) auth = init_saml_auth(req) @@ -41,57 +40,57 @@ def index(): attributes = False paint_logout = False - if 'sso' in request.args: + if "sso" in request.args: return redirect(auth.login()) # If AuthNRequest ID need to be stored in order to later validate it, do instead # sso_built_url = auth.login() # request.session['AuthNRequestID'] = auth.get_last_request_id() # return redirect(sso_built_url) - elif 'sso2' in request.args: - return_to = '%sattrs/' % request.host_url + elif "sso2" in request.args: + return_to = "%sattrs/" % request.host_url return redirect(auth.login(return_to)) - elif 'slo' in request.args: + elif "slo" in request.args: name_id = session_index = name_id_format = name_id_nq = name_id_spnq = None - if 'samlNameId' in session: - name_id = session['samlNameId'] - if 'samlSessionIndex' in session: - session_index = session['samlSessionIndex'] - if 'samlNameIdFormat' in session: - name_id_format = session['samlNameIdFormat'] - if 'samlNameIdNameQualifier' in session: - name_id_nq = session['samlNameIdNameQualifier'] - if 'samlNameIdSPNameQualifier' in session: - name_id_spnq = session['samlNameIdSPNameQualifier'] + if "samlNameId" in session: + name_id = session["samlNameId"] + if "samlSessionIndex" in session: + session_index = session["samlSessionIndex"] + if "samlNameIdFormat" in session: + name_id_format = session["samlNameIdFormat"] + if "samlNameIdNameQualifier" in session: + name_id_nq = session["samlNameIdNameQualifier"] + if "samlNameIdSPNameQualifier" in session: + name_id_spnq = session["samlNameIdSPNameQualifier"] return redirect(auth.logout(name_id=name_id, session_index=session_index, nq=name_id_nq, name_id_format=name_id_format, spnq=name_id_spnq)) - elif 'acs' in request.args: + elif "acs" in request.args: request_id = None - if 'AuthNRequestID' in session: - request_id = session['AuthNRequestID'] + if "AuthNRequestID" in session: + request_id = session["AuthNRequestID"] auth.process_response(request_id=request_id) errors = auth.get_errors() not_auth_warn = not auth.is_authenticated() 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() - session['samlNameIdSPNameQualifier'] = auth.get_nameid_spnq() - session['samlSessionIndex'] = auth.get_session_index() + 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() + session["samlNameIdSPNameQualifier"] = auth.get_nameid_spnq() + 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']: + 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'])) + 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: + elif "sls" in request.args: request_id = None - if 'LogoutRequestID' in session: - request_id = session['LogoutRequestID'] + if "LogoutRequestID" in session: + request_id = session["LogoutRequestID"] dscb = lambda: session.clear() url = auth.process_slo(request_id=request_id, delete_session_cb=dscb) errors = auth.get_errors() @@ -105,37 +104,28 @@ def index(): elif auth.get_settings().is_debug_active(): error_reason = auth.get_last_error_reason() - if 'samlUserdata' in session: + if "samlUserdata" in session: paint_logout = True - if len(session['samlUserdata']) > 0: - attributes = session['samlUserdata'].items() + if len(session["samlUserdata"]) > 0: + attributes = session["samlUserdata"].items() - return render_template( - 'index.html', - errors=errors, - error_reason=error_reason, - not_auth_warn=not_auth_warn, - success_slo=success_slo, - attributes=attributes, - paint_logout=paint_logout - ) + return render_template("index.html", errors=errors, error_reason=error_reason, not_auth_warn=not_auth_warn, success_slo=success_slo, attributes=attributes, paint_logout=paint_logout) -@app.route('/attrs/') +@app.route("/attrs/") def attrs(): paint_logout = False attributes = False - if 'samlUserdata' in session: + if "samlUserdata" in session: paint_logout = True - if len(session['samlUserdata']) > 0: - attributes = session['samlUserdata'].items() + if len(session["samlUserdata"]) > 0: + attributes = session["samlUserdata"].items() - return render_template('attrs.html', paint_logout=paint_logout, - attributes=attributes) + return render_template("attrs.html", paint_logout=paint_logout, attributes=attributes) -@app.route('/metadata/') +@app.route("/metadata/") def metadata(): req = prepare_flask_request(request) auth = init_saml_auth(req) @@ -145,11 +135,11 @@ def metadata(): if len(errors) == 0: resp = make_response(metadata, 200) - resp.headers['Content-Type'] = 'text/xml' + resp.headers["Content-Type"] = "text/xml" else: - resp = make_response(', '.join(errors), 500) + resp = make_response(", ".join(errors), 500) return resp if __name__ == "__main__": - app.run(host='0.0.0.0', port=8000, debug=True) + app.run(host="0.0.0.0", port=8000, debug=True) diff --git a/demo-tornado/Settings.py b/demo-tornado/Settings.py index 4b80459a..46fb48ca 100644 --- a/demo-tornado/Settings.py +++ b/demo-tornado/Settings.py @@ -2,5 +2,5 @@ BASE_DIR = os.path.dirname(__file__) -SAML_PATH = os.path.join(BASE_DIR, 'saml') -TEMPLATE_PATH = os.path.join(BASE_DIR, 'templates') +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 index 9b1bd132..0f21d3ed 100644 --- a/demo-tornado/requirements.txt +++ b/demo-tornado/requirements.txt @@ -1 +1 @@ -tornado==6.3.3 +tornado==6.4.1 diff --git a/demo-tornado/views.py b/demo-tornado/views.py index 70ef1565..677cf3a8 100644 --- a/demo-tornado/views.py +++ b/demo-tornado/views.py @@ -18,12 +18,7 @@ def __init__(self): (r"/attrs", AttrsHandler), (r"/metadata", MetadataHandler), ] - settings = { - "template_path": Settings.TEMPLATE_PATH, - "saml_path": Settings.SAML_PATH, - "autoreload": True, - "debug": True - } + settings = {"template_path": Settings.TEMPLATE_PATH, "saml_path": Settings.SAML_PATH, "autoreload": True, "debug": True} tornado.web.Application.__init__(self, handlers, **settings) @@ -41,23 +36,23 @@ def post(self): 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() + 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'): + 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')) + 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: + if "samlUserdata" in session: paint_logout = True - if len(session['samlUserdata']) > 0: - attributes = session['samlUserdata'].items() + 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) @@ -69,38 +64,38 @@ def get(self): attributes = False paint_logout = False - if 'sso' in req['get_data']: - print('-sso-') + 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 + 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-') + 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'] + 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-') + 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() + 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'))) + 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-') + 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() @@ -113,13 +108,13 @@ def get(self): success_slo = True elif auth.get_settings().is_debug_active(): error_reason = auth.get_last_error_reason() - if 'samlUserdata' in session: - print('-samlUserdata-') + if "samlUserdata" in session: + print("-samlUserdata-") paint_logout = True - if len(session['samlUserdata']) > 0: - attributes = session['samlUserdata'].items() + 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): @@ -127,12 +122,12 @@ def get(self): paint_logout = False attributes = False - if 'samlUserdata' in session: + if "samlUserdata" in session: paint_logout = True - if len(session['samlUserdata']) > 0: - attributes = session['samlUserdata'].items() + 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): @@ -145,11 +140,11 @@ def get(self): if len(errors) == 0: # resp = HttpResponse(content=metadata, content_type='text/xml') - self.set_header('Content-Type', 'text/xml') + self.set_header("Content-Type", "text/xml") self.write(metadata) else: # resp = HttpResponseServerError(content=', '.join(errors)) - self.write(', '.join(errors)) + self.write(", ".join(errors)) # return resp @@ -157,16 +152,9 @@ 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': request.host, - 'script_name': request.path, - 'get_data': dataDict, - 'post_data': dataDict, - 'query_string': request.query - } + dataDict[key] = request.arguments[key][0].decode("utf-8") + + result = {"https": "on" if request == "https" else "off", "http_host": request.host, "script_name": request.path, "get_data": dataDict, "post_data": dataDict, "query_string": request.query} return result diff --git a/demo_pyramid/demo_pyramid/__init__.py b/demo_pyramid/demo_pyramid/__init__.py index 805e51d1..02abda7b 100644 --- a/demo_pyramid/demo_pyramid/__init__.py +++ b/demo_pyramid/demo_pyramid/__init__.py @@ -2,18 +2,17 @@ from pyramid.session import SignedCookieSessionFactory -session_factory = SignedCookieSessionFactory('onelogindemopytoolkit') +session_factory = SignedCookieSessionFactory("onelogindemopytoolkit") def main(global_config, **settings): - """ This function returns a Pyramid WSGI application. - """ + """This function returns a Pyramid WSGI application.""" config = Configurator(settings=settings) config.set_session_factory(session_factory) - config.include('pyramid_jinja2') - config.add_static_view('static', 'static', cache_max_age=3600) - config.add_route('index', '/') - config.add_route('attrs', '/attrs/') - config.add_route('metadata', '/metadata/') + config.include("pyramid_jinja2") + config.add_static_view("static", "static", cache_max_age=3600) + config.add_route("index", "/") + config.add_route("attrs", "/attrs/") + config.add_route("metadata", "/metadata/") config.scan() return config.make_wsgi_app() diff --git a/demo_pyramid/demo_pyramid/tests.py b/demo_pyramid/demo_pyramid/tests.py index 58882283..f2a171cf 100644 --- a/demo_pyramid/demo_pyramid/tests.py +++ b/demo_pyramid/demo_pyramid/tests.py @@ -12,18 +12,21 @@ def tearDown(self): def test_my_view(self): from .views import my_view + request = testing.DummyRequest() info = my_view(request) - self.assertEqual(info['project'], 'demo_pyramid') + self.assertEqual(info["project"], "demo_pyramid") class FunctionalTests(unittest.TestCase): def setUp(self): from demo_pyramid import main + app = main({}) from webtest import TestApp + self.testapp = TestApp(app) def test_root(self): - res = self.testapp.get('/', status=200) - self.assertTrue(b'Pyramid' in res.body) + res = self.testapp.get("/", status=200) + self.assertTrue(b"Pyramid" in res.body) diff --git a/demo_pyramid/demo_pyramid/views.py b/demo_pyramid/demo_pyramid/views.py index 5b6be9dd..98852671 100644 --- a/demo_pyramid/demo_pyramid/views.py +++ b/demo_pyramid/demo_pyramid/views.py @@ -1,12 +1,16 @@ import os -from pyramid.httpexceptions import (HTTPFound, HTTPInternalServerError, HTTPOk,) +from pyramid.httpexceptions import ( + HTTPFound, + HTTPInternalServerError, + HTTPOk, +) from pyramid.view import view_config from onelogin.saml2.auth import OneLogin_Saml2_Auth from onelogin.saml2.utils import OneLogin_Saml2_Utils -SAML_PATH = os.path.join(os.path.dirname(__file__), 'saml') +SAML_PATH = os.path.join(os.path.dirname(__file__), "saml") def init_saml_auth(req): @@ -23,17 +27,17 @@ def prepare_pyramid_request(request): # request.scheme = request.headers['X-Forwarded-Proto'] return { - 'https': 'on' if request.scheme == 'https' else 'off', - 'http_host': request.host, - 'script_name': request.path, - 'get_data': request.GET.copy(), + "https": "on" if request.scheme == "https" else "off", + "http_host": request.host, + "script_name": request.path, + "get_data": request.GET.copy(), # Uncomment if using ADFS as IdP, https://github.com/onelogin/python-saml/pull/144 # 'lowercase_urlencoding': True, - 'post_data': request.POST.copy(), + "post_data": request.POST.copy(), } -@view_config(route_name='index', renderer='templates/index.jinja2') +@view_config(route_name="index", renderer="templates/index.jinja2") def index(request): req = prepare_pyramid_request(request) auth = init_saml_auth(req) @@ -46,36 +50,36 @@ def index(request): session = request.session - if 'sso' in request.GET: + if "sso" in request.GET: return HTTPFound(auth.login()) - elif 'sso2' in request.GET: - return_to = '%s/attrs/' % request.host_url + elif "sso2" in request.GET: + return_to = "%s/attrs/" % request.host_url return HTTPFound(auth.login(return_to)) - elif 'slo' in request.GET: + elif "slo" in request.GET: name_id = None session_index = None - if 'samlNameId' in session: - name_id = session['samlNameId'] - if 'samlSessionIndex' in session: - session_index = session['samlSessionIndex'] + if "samlNameId" in session: + name_id = session["samlNameId"] + if "samlSessionIndex" in session: + session_index = session["samlSessionIndex"] return HTTPFound(auth.logout(name_id=name_id, session_index=session_index)) - elif 'acs' in request.GET: + elif "acs" in request.GET: 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() + 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 request.POST and self_url != request.POST['RelayState']: + 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'])) + return HTTPFound(auth.redirect_to(request.POST["RelayState"])) else: error_reason = auth.get_last_error_reason() - elif 'sls' in request.GET: + elif "sls" in request.GET: dscb = lambda: session.clear() url = auth.process_slo(delete_session_cb=dscb) errors = auth.get_errors() @@ -87,40 +91,40 @@ def index(request): else: success_slo = True - if 'samlUserdata' in session: + if "samlUserdata" in session: paint_logout = True - if len(session['samlUserdata']) > 0: - attributes = session['samlUserdata'].items() + if len(session["samlUserdata"]) > 0: + attributes = session["samlUserdata"].items() return { - 'errors': errors, - 'error_reason': error_reason, - 'not_auth_warn': not_auth_warn, - 'success_slo': success_slo, - 'attributes': attributes, - 'paint_logout': paint_logout, + "errors": errors, + "error_reason": error_reason, + "not_auth_warn": not_auth_warn, + "success_slo": success_slo, + "attributes": attributes, + "paint_logout": paint_logout, } -@view_config(route_name='attrs', renderer='templates/attrs.jinja2') +@view_config(route_name="attrs", renderer="templates/attrs.jinja2") def attrs(request): paint_logout = False attributes = False session = request.session - if 'samlUserdata' in session: + if "samlUserdata" in session: paint_logout = True - if len(session['samlUserdata']) > 0: - attributes = session['samlUserdata'].items() + if len(session["samlUserdata"]) > 0: + attributes = session["samlUserdata"].items() return { - 'paint_logout': paint_logout, - 'attributes': attributes, + "paint_logout": paint_logout, + "attributes": attributes, } -@view_config(route_name='metadata', renderer='html') +@view_config(route_name="metadata", renderer="html") def metadata(request): req = prepare_pyramid_request(request) auth = init_saml_auth(req) @@ -129,7 +133,7 @@ def metadata(request): errors = settings.validate_metadata(metadata) if len(errors) == 0: - resp = HTTPOk(body=metadata, headers={'Content-Type': 'text/xml'}) + resp = HTTPOk(body=metadata, headers={"Content-Type": "text/xml"}) else: - resp = HTTPInternalServerError(body=', '.join(errors)) + resp = HTTPInternalServerError(body=", ".join(errors)) return resp diff --git a/demo_pyramid/setup.py b/demo_pyramid/setup.py index 50291385..1c61bcbc 100644 --- a/demo_pyramid/setup.py +++ b/demo_pyramid/setup.py @@ -3,52 +3,52 @@ from setuptools import setup, find_packages here = os.path.abspath(os.path.dirname(__file__)) -with open(os.path.join(here, 'README.txt')) as f: +with open(os.path.join(here, "README.txt")) as f: README = f.read() -with open(os.path.join(here, 'CHANGES.txt')) as f: +with open(os.path.join(here, "CHANGES.txt")) as f: CHANGES = f.read() requires = [ - 'pyramid', - 'pyramid_jinja2', - 'pyramid_debugtoolbar', - 'waitress', - 'xmlsec', - 'isodate', - 'python3-saml', + "pyramid", + "pyramid_jinja2", + "pyramid_debugtoolbar", + "waitress", + "xmlsec", + "isodate", + "python3-saml", ] tests_require = [ - 'WebTest >= 1.3.1', # py3 compat - 'pytest', - 'pytest-cov', + "WebTest >= 1.3.1", # py3 compat + "pytest", + "pytest-cov", ] setup( - name='demo_pyramid', - version='0.0', - description='demo_pyramid', - long_description=README + '\n\n' + CHANGES, + name="demo_pyramid", + version="0.0", + description="demo_pyramid", + long_description=README + "\n\n" + CHANGES, classifiers=[ - 'Programming Language :: Python', - 'Framework :: Pyramid', - 'Topic :: Internet :: WWW/HTTP', - 'Topic :: Internet :: WWW/HTTP :: WSGI :: Application', + "Programming Language :: Python", + "Framework :: Pyramid", + "Topic :: Internet :: WWW/HTTP", + "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", ], - author='', - author_email='', - url='', - keywords='web pyramid pylons', + author="", + author_email="", + url="", + keywords="web pyramid pylons", packages=find_packages(), include_package_data=True, zip_safe=False, extras_require={ - 'testing': tests_require, + "testing": tests_require, }, install_requires=requires, entry_points={ - 'paste.app_factory': [ - 'main = demo_pyramid:main', + "paste.app_factory": [ + "main = demo_pyramid:main", ], }, ) diff --git a/pyproject.toml b/pyproject.toml index 5d1bcd6b..eee84f5b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,13 +1,19 @@ -[tool.poetry] +[build-system] +requires = ["setuptools>=61.0.0"] +build-backend = "setuptools.build_meta" + +[project] name = "python3-saml" version = "1.16.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 "] +license = {file = "LICENSE"} +authors = [ + {name = "SAML-Toolkits", email = "contact@iamdigitalservices.com"} +] +maintainers = [ + {name = "Sixto Martin", email = "sixto.martin.garcia@gmail.com"} +] readme = "README.md" -homepage = "https://saml.info" -repository = "https://github.com/SAML-Toolkits/python3-saml" keywords = [ "saml", "saml2", @@ -17,72 +23,42 @@ keywords = [ "identity", ] classifiers = [ - "Topic :: Software Development :: Build Tools", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "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" } +dependencies = [ + "lxml>=4.6.5,!=4.7.0", + "xmlsec>=1.3.9", + "isodate>=0.6.1", ] +requires-python = ">=3.7" -[tool.poetry.urls] +[project.urls] +Homepage = "https://saml.info" +Source = "https://github.com/SAML-Toolkits/python3-saml" "Bug Tracker" = "https://github.com/SAML-Toolkits/python3-saml/issues" +Changelog = "https://github.com/SAML-Toolkits/python3-saml/blob/master/changelog.md" -[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 = "^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} -coverage = { version = ">=4.5.2", optional = true} -#pylint = ">=1.9.4" - -[tool.poetry.extras] -test = ["flake8", "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 +[project.optional-dependencies] +test = [ + "coverage[toml]>=4.5.2", + "pytest>=4.6", +] +lint = [ + "black==24.4.2", + "flake8>=3.6.0, <=5.0.0", +] -#[tool.poetry.group.docs.dependencies] -#sphinx = "*" +[tool.setuptools] +package-dir = {"" = "src"} -[build-system] -requires = [ - "poetry>=1.1.15", - "setuptools >= 40.1.0", - "wheel" -] -build-backend = "poetry.core.masonry.api" +[tool.setuptools.package-data] +"onelogin.saml2.schemas" = ["*.xsd"] [tool.pytest.ini_options] minversion = "4.6.11" @@ -94,75 +70,11 @@ pythonpath = [ "tests", ] -[tool.coverage.run] -branch = true -source = ["src/onelogin/saml2"] - -[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:", -] -ignore_errors = true - -[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' +profile = "black" # The 'black' profile means: # multi_line_output = 3 # include_trailing_comma = true @@ -180,7 +92,31 @@ honor_noqa = true atomic = true ignore_comments = true skip_gitignore = true -src_paths = [ - 'src', - 'tests', +src_paths = ['src'] + +[tool.coverage.run] +branch = true + +[tool.coverage.paths] +source = [ + "src/onelogin/saml2" ] + +[tool.coverage.report] +# Regexes for lines to exclude from consideration +exclude_lines = [ + "pragma: no cover", + "def __repr__", + "if self.debug", + "if debug", + "raise AssertionError", + "raise NotImplementedError", + "if 0:", + "if __name__ == .__main__.:" +] +show_missing = true +ignore_errors = true + + +[tool.coverage.html] +directory = "coverage_html_report" \ No newline at end of file diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 36fa9534..00000000 --- a/setup.cfg +++ /dev/null @@ -1,7 +0,0 @@ -[flake8] -ignore = E731,W504,E501 -max-complexity = 48 -max-line-length = 1900 - -[wheel] -python-tag = py27 diff --git a/setup.py b/setup.py deleted file mode 100644 index 352ea892..00000000 --- a/setup.py +++ /dev/null @@ -1,53 +0,0 @@ -#! /usr/bin/env python -# -*- coding: utf-8 -*- - -from setuptools import setup - - -setup( - name='python3-saml', - version='1.16.0', - description='Saml Python Toolkit. Add SAML support to your Python software using this library', - classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: Developers', - 'Intended Audience :: System Administrators', - 'Operating System :: OS Independent', - 'Programming Language :: Python :: 2.7', - '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.10', - 'Programming Language :: Python :: 3.11', - 'Programming Language :: Python :: 3.12', - ], - license='MIT', - url='https://github.com/SAML-Toolkits/python3-saml', - packages=['onelogin', 'onelogin/saml2'], - include_package_data=True, - package_data={ - 'onelogin/saml2/schemas': ['*.xsd'], - }, - package_dir={ - '': 'src', - }, - test_suite='tests', - install_requires=[ - 'lxml>=4.6.5, !=4.7.0', - 'isodate>=0.6.1', - 'xmlsec>=1.3.9' - ], - dependency_links=['http://github.com/mehcode/python-xmlsec/tarball/master'], - extras_require={ - 'test': ( - 'coverage>=4.5.2', - 'freezegun>=0.3.11, <=1.1.0', - # 'pylint>=1.9.4', - 'flake8>=3.6.0, <=5.0.0', - 'pytest>=4.6', - ), - }, - keywords='saml saml2 sso xmlsec federation identity', -) diff --git a/src/onelogin/saml2/auth.py b/src/onelogin/saml2/auth.py index ac85ebc1..a55f7f8e 100644 --- a/src/onelogin/saml2/auth.py +++ b/src/onelogin/saml2/auth.py @@ -123,23 +123,20 @@ def process_response(self, request_id=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']) + 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._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 - ) + 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) def process_slo(self, keep_local_session=False, request_id=None, delete_session_cb=None): """ @@ -156,30 +153,30 @@ def process_slo(self, keep_local_session=False, request_id=None, delete_session_ 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']) + 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') + 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._errors.append("invalid_logout_response") 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 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']) + 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') + 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._errors.append("invalid_logout_request") else: if not keep_local_session: OneLogin_Saml2_Utils.delete_local_session(delete_session_cb) @@ -191,21 +188,18 @@ def process_slo(self, keep_local_session=False, request_id=None, delete_session_ 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'] + 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']) + 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 - ) + 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) def redirect_to(self, url=None, parameters={}): """ @@ -218,8 +212,8 @@ 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'] + 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): @@ -421,16 +415,16 @@ def login(self, return_to=None, force_authn=False, is_passive=False, set_nameid_ self._last_request_id = authn_request.get_id() saml_request = authn_request.get_request() - parameters = {'SAMLRequest': saml_request} + parameters = {"SAMLRequest": saml_request} if return_to is not None: - parameters['RelayState'] = return_to + 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() - if security.get('authnRequestsSigned', False): - self.add_request_signature(parameters, security['signatureAlgorithm']) + if security.get("authnRequestsSigned", False): + self.add_request_signature(parameters, security["signatureAlgorithm"]) return self.redirect_to(self.get_sso_url(), parameters) def logout(self, return_to=None, name_id=None, session_index=None, nq=None, name_id_format=None, spnq=None): @@ -459,10 +453,7 @@ def logout(self, return_to=None, name_id=None, session_index=None, nq=None, name """ 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 - ) + 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 @@ -470,26 +461,19 @@ 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 = self.logout_request_class( - self._settings, - name_id=name_id, - session_index=session_index, - nq=nq, - name_id_format=name_id_format, - spnq=spnq - ) + 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()} + parameters = {"SAMLRequest": logout_request.get_request()} if return_to is not None: - parameters['RelayState'] = return_to + 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() - if security.get('logoutRequestSigned', False): - self.add_request_signature(parameters, security['signatureAlgorithm']) + if security.get("logoutRequestSigned", False): + self.add_request_signature(parameters, security["signatureAlgorithm"]) return self.redirect_to(slo_url, parameters) def get_sso_url(self): @@ -529,7 +513,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_SHA256): """ @@ -540,7 +524,7 @@ 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): @@ -553,10 +537,10 @@ def _build_sign_query_from_qs(query_string, saml_type): :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('&') + 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)) + 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): @@ -578,11 +562,11 @@ def _build_sign_query(saml_data, relay_state, algorithm, saml_type, lowercase_ur :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))] + 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) + 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): """ @@ -596,32 +580,26 @@ def _build_signature(self, data, saml_type, sign_algorithm=OneLogin_Saml2_Consta :param sign_algorithm: Signature algorithm method :type sign_algorithm: string """ - assert saml_type in ('SAMLRequest', 'SAMLResponse') + 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 - ) + 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) + 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 + 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 + data["Signature"] = OneLogin_Saml2_Utils.b64encode(signature) + data["SigAlg"] = sign_algorithm def validate_request_signature(self, request_data): """ @@ -632,7 +610,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): """ @@ -643,7 +621,7 @@ 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): """ @@ -662,77 +640,49 @@ def _validate_signature(self, data, saml_type, raise_exceptions=False): :type raise_exceptions: Boolean """ try: - signature = data.get('Signature', None) + 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 - ) + 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'] + 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 + 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 - ) + raise OneLogin_Saml2_Error(error_msg, OneLogin_Saml2_Error.CERT_NOT_FOUND) - sign_alg = data.get('SigAlg', OneLogin_Saml2_Constants.RSA_SHA1) + sign_alg = data.get("SigAlg", OneLogin_Saml2_Constants.RSA_SHA1) if isinstance(sign_alg, bytes): - sign_alg = sign_alg.decode('utf8') + sign_alg = sign_alg.decode("utf8") security = self._settings.get_security_data() - reject_deprecated_alg = security.get('rejectDeprecatedAlgorithm', False) + 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 - ) + 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'): + 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']: - if OneLogin_Saml2_Utils.validate_binary_sign(signed_query, - OneLogin_Saml2_Utils.b64decode(signature), - cert, - sign_alg): + 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 - ) + 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 - ) + 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) @@ -752,7 +702,7 @@ def get_last_response_xml(self, pretty_print_if_possible=False): 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): diff --git a/src/onelogin/saml2/authn_request.py b/src/onelogin/saml2/authn_request.py index 0a9f4438..42585e8b 100644 --- a/src/onelogin/saml2/authn_request.py +++ b/src/onelogin/saml2/authn_request.py @@ -48,82 +48,90 @@ def __init__(self, settings, force_authn=False, is_passive=False, set_nameid_pol self._id = self._generate_request_id() issue_instant = OneLogin_Saml2_Utils.parse_time_to_SAML(OneLogin_Saml2_Utils.now()) - destination = idp_data['singleSignOnService']['url'] + destination = idp_data["singleSignOnService"]["url"] - provider_name_str = '' + 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' + 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'] + 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'] + provider_name_str = "\n" + ' ProviderName="%s"' % organization_data[lang]["displayname"] - force_authn_str = '' + force_authn_str = "" if force_authn is True: force_authn_str = "\n" + ' ForceAuthn="true"' - is_passive_str = '' + is_passive_str = "" if is_passive is True: is_passive_str = "\n" + ' IsPassive="true"' - subject_str = '' + subject_str = "" if name_id_value_req: subject_str = """ %s - """ % (sp_data['NameIDFormat'], name_id_value_req) + """ % ( + sp_data["NameIDFormat"], + name_id_value_req, + ) - nameid_policy_str = '' + nameid_policy_str = "" if set_nameid_policy: - name_id_policy_format = sp_data['NameIDFormat'] - if security['wantNameIdEncrypted']: + name_id_policy_format = sp_data["NameIDFormat"] + if security["wantNameIdEncrypted"]: name_id_policy_format = OneLogin_Saml2_Constants.NAMEID_ENCRYPTED - nameid_policy_str = """ + nameid_policy_str = ( + """ """ % name_id_policy_format + AllowCreate="true" />""" + % name_id_policy_format + ) - requested_authn_context_str = '' - if security['requestedAuthnContext'] is not False: - authn_comparison = security['requestedAuthnContextComparison'] + requested_authn_context_str = "" + if security["requestedAuthnContext"] is not False: + authn_comparison = security["requestedAuthnContextComparison"] - if security['requestedAuthnContext'] is True: - requested_authn_context_str = """ + if security["requestedAuthnContext"] is True: + requested_authn_context_str = ( + """ urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport - """ % authn_comparison + """ + % authn_comparison + ) else: requested_authn_context_str = ' ' % authn_comparison - for authn_context in security['requestedAuthnContext']: - requested_authn_context_str += '%s' % authn_context - requested_authn_context_str += ' ' - - 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') - } + for authn_context in security["requestedAuthnContext"]: + requested_authn_context_str += "%s" % authn_context + requested_authn_context_str += " " + + 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 diff --git a/src/onelogin/saml2/compat.py b/src/onelogin/saml2/compat.py index a70bf97e..138e09f8 100644 --- a/src/onelogin/saml2/compat.py +++ b/src/onelogin/saml2/compat.py @@ -18,25 +18,25 @@ unicode = str -if isinstance(b'', type('')): # py 2.x +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 """ + """return utf8-encoded string""" if isinstance(data, basestring): return data.decode("utf8") return unicode(data) def to_string(data): - """ return string """ + """return string""" if isinstance(data, unicode): return data.encode("utf8") return str(data) def to_bytes(data): - """ return bytes """ + """return bytes""" if isinstance(data, unicode): return data.encode("utf8") return str(data) @@ -47,7 +47,7 @@ def to_bytes(data): str_type = str def utf8(data): - """ return utf8-encoded string """ + """return utf8-encoded string""" if isinstance(data, bytes): return data.decode("utf8") return str(data) diff --git a/src/onelogin/saml2/constants.py b/src/onelogin/saml2/constants.py index 44220c44..51392393 100644 --- a/src/onelogin/saml2/constants.py +++ b/src/onelogin/saml2/constants.py @@ -20,98 +20,92 @@ class OneLogin_Saml2_Constants(object): 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' + 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' + 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#' + 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' + 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 - } + 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' + 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' + 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' + 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' + 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' + 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' + 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' + 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] diff --git a/src/onelogin/saml2/idp_metadata_parser.py b/src/onelogin/saml2/idp_metadata_parser.py index 29ba08be..6710ff1f 100644 --- a/src/onelogin/saml2/idp_metadata_parser.py +++ b/src/onelogin/saml2/idp_metadata_parser.py @@ -60,14 +60,14 @@ def get_metadata(cls, url, validate_cert=True, timeout=None, headers=None): if xml: try: dom = OneLogin_Saml2_XML.to_etree(xml) - idp_descriptor_nodes = OneLogin_Saml2_XML.query(dom, '//md:IDPSSODescriptor') + 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)) + raise Exception("Not valid IdP XML found from URL: %s" % (url)) return xml @@ -91,16 +91,11 @@ 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, headers=kwargs.pop('headers', None)) + 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 - 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): + 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. @@ -139,40 +134,34 @@ def parse( 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' + 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') + 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) + idp_entity_id = entity_descriptor_node.get("entityID", None) - want_authn_requests_signed = idp_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') + 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 - ) + 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) + 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 - ) + 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) + 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") @@ -180,50 +169,47 @@ def parse( if len(signing_nodes) > 0 or len(encryption_nodes) > 0: certs = {} if len(signing_nodes) > 0: - certs['signing'] = [] + certs["signing"] = [] for cert_node in signing_nodes: - certs['signing'].append(''.join(OneLogin_Saml2_XML.element_text(cert_node).split())) + certs["signing"].append("".join(OneLogin_Saml2_XML.element_text(cert_node).split())) if len(encryption_nodes) > 0: - certs['encryption'] = [] + certs["encryption"] = [] for cert_node in encryption_nodes: - certs['encryption'].append(''.join(OneLogin_Saml2_XML.element_text(cert_node).split())) + certs["encryption"].append("".join(OneLogin_Saml2_XML.element_text(cert_node).split())) - data['idp'] = {} + data["idp"] = {} if idp_entity_id is not None: - data['idp']['entityId'] = idp_entity_id + 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 + 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 + 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" + data["security"] = {} + data["security"]["authnRequestsSigned"] = want_authn_requests_signed == "true" if idp_name_id_format: - data['sp'] = {} - data['sp']['NameIDFormat'] = 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] + 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] + data["idp"]["x509cert"] = certs["encryption"][0] else: - data['idp']['x509certMulti'] = certs + data["idp"]["x509certMulti"] = certs return data @staticmethod @@ -239,18 +225,18 @@ def merge_settings(settings, new_metadata_settings): """ for d in (settings, new_metadata_settings): if not isinstance(d, dict): - raise TypeError('Both arguments must be dictionaries.') + 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'] + 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) diff --git a/src/onelogin/saml2/logout_request.py b/src/onelogin/saml2/logout_request.py index 23fc6216..dea7f806 100644 --- a/src/onelogin/saml2/logout_request.py +++ b/src/onelogin/saml2/logout_request.py @@ -62,20 +62,18 @@ def __init__(self, settings, request=None, name_id=None, session_index=None, nq= 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 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] + 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'] + 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 = 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: @@ -89,29 +87,21 @@ def __init__(self, settings, request=None, name_id=None, session_index=None, nq= 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 - ) + name_id_obj = OneLogin_Saml2_Utils.generate_name_id(name_id, spnq, name_id_format, cert, False, nq) if session_index: - session_index_str = '%s' % session_index + session_index_str = "%s" % 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, - } + 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) @@ -152,7 +142,7 @@ def get_id(cls, request): """ elem = OneLogin_Saml2_XML.to_etree(request) - return elem.get('ID', None) + return elem.get("ID", None) @classmethod def get_nameid_data(cls, request, key=None): @@ -167,34 +157,26 @@ def get_nameid_data(cls, request, key=None): """ elem = OneLogin_Saml2_XML.to_etree(request) name_id = None - encrypted_entries = OneLogin_Saml2_XML.query(elem, '/samlp:LogoutRequest/saml:EncryptedID') + 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 - ) + 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') + 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') + 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']: + 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] @@ -212,7 +194,7 @@ def get_nameid(cls, request, key=None): :rtype: string """ name_id = cls.get_nameid_data(request, key) - return name_id['Value'] + return name_id["Value"] @classmethod def get_nameid_format(cls, request, key=None): @@ -227,8 +209,8 @@ def get_nameid_format(cls, request, key=None): """ 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'] + if name_id_data and "Format" in name_id_data.keys(): + name_id_format = name_id_data["Format"] return name_id_format @classmethod @@ -243,7 +225,7 @@ def get_issuer(cls, request): elem = OneLogin_Saml2_XML.to_etree(request) issuer = None - issuer_nodes = OneLogin_Saml2_XML.query(elem, '/samlp:LogoutRequest/saml:Issuer') + 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 @@ -260,7 +242,7 @@ def get_session_indexes(cls, request): elem = OneLogin_Saml2_XML.to_etree(request) session_indexes = [] - session_index_nodes = OneLogin_Saml2_XML.query(elem, '/samlp:LogoutRequest/samlp:SessionIndex') + 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 @@ -282,63 +264,50 @@ def is_valid(self, request_data, raise_exceptions=False): root = OneLogin_Saml2_XML.to_etree(self._logout_request) idp_data = self._settings.get_idp_data() - idp_entity_id = idp_data['entityId'] + idp_entity_id = idp_data["entityId"] - get_data = ('get_data' in request_data and request_data['get_data']) or dict() + 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()) + 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 - ) + 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 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 - ) + raise OneLogin_Saml2_ValidationError("Could not validate timestamp: expired. Check system clock.)", OneLogin_Saml2_ValidationError.RESPONSE_EXPIRED) # Check destination - destination = root.get('Destination', None) + 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, + "The LogoutRequest was received at " + "%(currentURL)s instead of %(destination)s" + % { + "currentURL": current_url, + "destination": destination, }, - OneLogin_Saml2_ValidationError.WRONG_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 + "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 - ) + 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: diff --git a/src/onelogin/saml2/logout_response.py b/src/onelogin/saml2/logout_response.py index 40e9978a..573a4d5d 100644 --- a/src/onelogin/saml2/logout_response.py +++ b/src/onelogin/saml2/logout_response.py @@ -39,7 +39,7 @@ def __init__(self, settings, response=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) + self.id = self.document.get("ID", None) def get_issuer(self): """ @@ -48,7 +48,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 @@ -59,10 +59,10 @@ 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'] + status = entries[0].attrib["Value"] return status def is_valid(self, request_data, request_id=None, raise_exceptions=False): @@ -80,16 +80,13 @@ def is_valid(self, request_data, request_id=None, raise_exceptions=False): self._error = None try: idp_data = self._settings.get_idp_data() - idp_entity_id = idp_data['entityId'] - get_data = request_data['get_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()) + 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 - ) + 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() @@ -97,39 +94,29 @@ def is_valid(self, request_data, request_id=None, raise_exceptions=False): 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 + "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 + "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) + 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 - ) + 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: @@ -182,7 +169,7 @@ def get_in_response_to(self): :returns: ID of LogoutRequest this LogoutResponse is in response to or None if it is not present :rtype: str """ - return self.document.get('InResponseTo') + return self.document.get("InResponseTo") def get_response(self, deflate=True): """ diff --git a/src/onelogin/saml2/metadata.py b/src/onelogin/saml2/metadata.py index d07685f8..d6f1d6ae 100644 --- a/src/onelogin/saml2/metadata.py +++ b/src/onelogin/saml2/metadata.py @@ -29,7 +29,7 @@ class OneLogin_Saml2_Metadata(object): """ - TIME_VALID = 172800 # 2 days + TIME_VALID = 172800 # 2 days TIME_CACHED = 604800 # 1 week @classmethod @@ -65,14 +65,14 @@ def builder(cls, sp, authnsign=False, wsign=False, valid_until=None, cache_durat 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) + 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 + cache_duration_str = "PT%sS" % cache_duration # Period of Time x Seconds else: cache_duration_str = cache_duration @@ -81,82 +81,84 @@ def builder(cls, sp, authnsign=False, wsign=False, valid_until=None, cache_durat 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'], - } + 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_authnsign = "true" if authnsign else "false" + str_wsign = "true" if wsign else "false" - str_organization = '' + str_organization = "" if len(organization) > 0: organization_names = [] organization_displaynames = [] organization_urls = [] - for (lang, info) in organization.items(): - organization_names.append(""" %s""" % (lang, info['name'])) - organization_displaynames.append(""" %s""" % (lang, info['displayname'])) - organization_urls.append(""" %s""" % (lang, info['url'])) - org_data = '\n'.join(organization_names) + '\n' + '\n'.join(organization_displaynames) + '\n' + '\n'.join(organization_urls) - str_organization = """ \n%(org)s\n """ % {'org': org_data} - - str_contacts = '' + for lang, info in organization.items(): + organization_names.append(""" %s""" % (lang, info["name"])) + organization_displaynames.append(""" %s""" % (lang, info["displayname"])) + organization_urls.append(""" %s""" % (lang, info["url"])) + org_data = "\n".join(organization_names) + "\n" + "\n".join(organization_displaynames) + "\n" + "\n".join(organization_urls) + str_organization = """ \n%(org)s\n """ % {"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'], - } + 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 = """ %s -""" % sp['attributeConsumingService']['serviceDescription'] + 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 = ( + """ %s +""" + % 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']] + 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']: + for attrValue in req_attribs["attributeValue"]: req_attr_aux_str += """ - %(attributeValue)s""" % \ - { - 'attributeValue': attrValue - } + %(attributeValue)s""" % { + "attributeValue": attrValue + } req_attr_aux_str += """ """ - requested_attribute = """ %(service_name)s %(attr_cs_desc)s%(requested_attribute_str)s -""" % \ - { - '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 +""" % { + "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 @staticmethod @@ -217,15 +217,15 @@ def sign_metadata(metadata, key, cert, sign_algorithm=OneLogin_Saml2_Constants.R @staticmethod def _add_x509_key_descriptors(root, cert, signing): - key_descriptor = OneLogin_Saml2_XML.make_child(root, '{%s}KeyDescriptor' % OneLogin_Saml2_Constants.NS_MD) + 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) + 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 = 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]) + key_descriptor.set("use", ("encryption", "signing")[signing]) @classmethod def add_x509_key_descriptors(cls, metadata, cert=None, add_encryption=True): @@ -245,18 +245,18 @@ def add_x509_key_descriptors(cls, metadata, cert=None, add_encryption=True): :returns: Metadata with KeyDescriptors :rtype: string """ - if cert is None or cert == '': + 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)) + raise Exception("Error parsing metadata. " + str(e)) - assert root.tag == '{%s}EntityDescriptor' % OneLogin_Saml2_Constants.NS_MD + assert root.tag == "{%s}EntityDescriptor" % OneLogin_Saml2_Constants.NS_MD try: - sp_sso_descriptor = next(root.iterfind('.//md:SPSSODescriptor', namespaces=OneLogin_Saml2_Constants.NSMAP)) + sp_sso_descriptor = next(root.iterfind(".//md:SPSSODescriptor", namespaces=OneLogin_Saml2_Constants.NSMAP)) except StopIteration: - raise Exception('Malformed metadata.') + raise Exception("Malformed metadata.") if add_encryption: cls._add_x509_key_descriptors(sp_sso_descriptor, cert, False) diff --git a/src/onelogin/saml2/response.py b/src/onelogin/saml2/response.py index 595b8449..ae3cef9c 100644 --- a/src/onelogin/saml2/response.py +++ b/src/onelogin/saml2/response.py @@ -40,7 +40,7 @@ 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 @@ -65,56 +65,41 @@ def is_valid(self, request_data, request_id=None, raise_exceptions=False): 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 - ) + 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 - ) + 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 - ) + 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'] + idp_entity_id = idp_data["entityId"] sp_data = self._settings.get_sp_data() - sp_entity_id = sp_data['entityId'] + 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 + 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()) + 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 - ) + 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()) + 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 - ) + 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) @@ -124,138 +109,106 @@ def is_valid(self, request_data, request_id=None, raise_exceptions=False): 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 + "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 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 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 - ) + 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 - ) + 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 - ) + 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: + 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 + '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 - ) + 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', - OneLogin_Saml2_ValidationError.ENCRYPTED_ATTRIBUTES - ) + 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) + 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 - ) + 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 - ) + 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 + "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 + "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') + subject_confirmation_nodes = self._query_assertion("/saml:Subject/saml:SubjectConfirmation") for scn in subject_confirmation_nodes: - method = scn.get('Method', None) + 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) + sc_data = scn.find("saml:SubjectConfirmationData", namespaces=OneLogin_Saml2_Constants.NSMAP) if sc_data is None: continue else: - irt = sc_data.get('InResponseTo', None) + irt = sc_data.get("InResponseTo", None) if in_response_to and irt and irt != in_response_to: continue - recipient = sc_data.get('Recipient', None) + recipient = sc_data.get("Recipient", None) if recipient and current_url not in recipient: continue - nooa = sc_data.get('NotOnOrAfter', None) + 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) + 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(): @@ -268,52 +221,38 @@ def is_valid(self, request_data, request_id=None, raise_exceptions=False): break if not any_subject_confirmation: - raise OneLogin_Saml2_ValidationError( - 'A valid SubjectConfirmation was not found on this Response', - OneLogin_Saml2_ValidationError.WRONG_SUBJECTCONFIRMATION - ) + 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["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 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 - ) + 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) + fingerprint = idp_data.get("certFingerprint", None) if fingerprint: fingerprint = OneLogin_Saml2_Utils.format_finger_print(fingerprint) - fingerprintalg = idp_data.get('certFingerprintAlgorithm', None) + 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 "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 - ) + 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 - ) + 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: @@ -332,24 +271,21 @@ def check_status(self): :raises: Exception. If the status is not success """ status = OneLogin_Saml2_Utils.get_status(self.document) - code = status.get('code', None) + code = status.get("code", None) if code and code != OneLogin_Saml2_Constants.STATUS_SUCCESS: - splited_code = code.split(':') + 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) + 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 - ) + status_exception_msg += " -> " + status_msg + raise OneLogin_Saml2_ValidationError(status_exception_msg, OneLogin_Saml2_ValidationError.STATUS_CODE_IS_NOT_SUCCESS) 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: @@ -359,7 +295,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: @@ -372,7 +308,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): @@ -382,7 +318,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): @@ -391,7 +327,7 @@ def get_in_response_to(self): :returns: ID of AuthNRequest this Response is in response to or None if it is not present :rtype: str """ - return self.document.get('InResponseTo') + return self.document.get("InResponseTo") def get_issuers(self): """ @@ -402,28 +338,22 @@ def get_issuers(self): """ issuers = set() - message_issuer_nodes = OneLogin_Saml2_XML.query(self.document, '/samlp:Response/saml:Issuer') + 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 - ) + 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') + 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 - ) + raise OneLogin_Saml2_ValidationError("Issuer of the Assertion not found or multiple.", OneLogin_Saml2_ValidationError.ISSUER_NOT_FOUND_IN_ASSERTION) return list(set(issuers)) @@ -437,43 +367,34 @@ 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() 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) + 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 - ) + 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 - ) + 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']: + 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': + if is_strict and attr == "SPNameQualifier": sp_data = self._settings.get_sp_data() - sp_entity_id = sp_data.get('entityId', '') + 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 - ) + 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 @@ -487,8 +408,8 @@ def get_nameid(self): """ nameid_value = None nameid_data = self.get_nameid_data() - if nameid_data and 'Value' in nameid_data.keys(): - nameid_value = nameid_data['Value'] + if nameid_data and "Value" in nameid_data.keys(): + nameid_value = nameid_data["Value"] return nameid_value def get_nameid_format(self): @@ -500,8 +421,8 @@ def get_nameid_format(self): """ nameid_format = None nameid_data = self.get_nameid_data() - if nameid_data and 'Format' in nameid_data.keys(): - nameid_format = nameid_data['Format'] + if nameid_data and "Format" in nameid_data.keys(): + nameid_format = nameid_data["Format"] return nameid_format def get_nameid_nq(self): @@ -513,8 +434,8 @@ def get_nameid_nq(self): """ nameid_nq = None nameid_data = self.get_nameid_data() - if nameid_data and 'NameQualifier' in nameid_data.keys(): - nameid_nq = nameid_data['NameQualifier'] + if nameid_data and "NameQualifier" in nameid_data.keys(): + nameid_nq = nameid_data["NameQualifier"] return nameid_nq def get_nameid_spnq(self): @@ -526,8 +447,8 @@ def get_nameid_spnq(self): """ nameid_spnq = None nameid_data = self.get_nameid_data() - if nameid_data and 'SPNameQualifier' in nameid_data.keys(): - nameid_spnq = nameid_data['SPNameQualifier'] + if nameid_data and "SPNameQualifier" in nameid_data.keys(): + nameid_spnq = nameid_data["SPNameQualifier"] return nameid_spnq def get_session_not_on_or_after(self): @@ -539,9 +460,9 @@ 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')) + not_on_or_after = OneLogin_Saml2_Utils.parse_SAML_to_time(authn_statement_nodes[0].get("SessionNotOnOrAfter")) return not_on_or_after def get_assertion_not_on_or_after(self): @@ -561,9 +482,9 @@ 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') + session_index = authn_statement_nodes[0].get("SessionIndex") return session_index def get_attributes(self): @@ -571,30 +492,27 @@ def get_attributes(self): Gets the Attributes from the AttributeStatement element. EncryptedAttributes are not supported """ - return self._get_attributes('Name') + 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') + 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: 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 - ) + 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']): + 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() @@ -602,14 +520,8 @@ def _get_attributes(self, attr_name): 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 - } - }) + 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: @@ -623,13 +535,13 @@ def validate_num_assertions(self): :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') + 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') + if self.encrypted: + assertion_nodes = OneLogin_Saml2_XML.query(self.decrypted_document, "//saml:Assertion") valid = valid and len(assertion_nodes) == 1 return valid @@ -643,87 +555,63 @@ 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 = [] verified_ids = [] - response_tag = '{%s}Response' % OneLogin_Saml2_Constants.NS_SAMLP - assertion_tag = '{%s}Assertion' % OneLogin_Saml2_Constants.NS_SAML + 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) + 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') + 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 - ) + 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') + ref = OneLogin_Saml2_XML.query(sign_node, ".//ds:Reference") if ref: ref = ref[0] - if ref.get('URI'): - sei = ref.get('URI')[1:] + 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 - ) + 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 - ) + 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') + 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 - ) + 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') + 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 - ) + 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 - ) + raise OneLogin_Saml2_ValidationError("Found an unexpected Signature Element. SAML Response rejected", OneLogin_Saml2_ValidationError.UNEXPECTED_SIGNED_ELEMENTS) return signed_elements @return_false_on_exception @@ -739,12 +627,14 @@ def validate_signed_elements(self, signed_elements): 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 + 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): + 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 @@ -752,18 +642,12 @@ def validate_signed_elements(self, signed_elements): 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 - ) + 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 - ) + raise OneLogin_Saml2_ValidationError("Unexpected number of Assertion signatures found. SAML Response rejected.", OneLogin_Saml2_ValidationError.WRONG_NUMBER_OF_SIGNATURES_IN_ASSERTION) return True @@ -775,21 +659,15 @@ 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') - nooa_attr = conditions_node.get('NotOnOrAfter') + 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 - ) + 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 - ) + 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): @@ -803,26 +681,26 @@ def _query_assertion(self, xpath_expr): :rtype: list """ - assertion_expr = '/saml:Assertion' - signature_expr = '/ds:Signature/ds:SignedInfo/ds:Reference' - signed_assertion_query = '/samlp:Response' + assertion_expr + signature_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) tagid = None if not assertion_reference_nodes: # Check if the message is signed - signed_message_query = '/samlp:Response' + signature_expr + 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') + 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]" + 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) @@ -860,40 +738,28 @@ def _decrypt_assertion(self, xml): 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 - ) + 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') + 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') + 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') + 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 - ) + 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 - ) + 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('#'): + 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) + 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]) @@ -925,7 +791,7 @@ def get_id(self): :returns: the ID of the response :rtype: string """ - return self.document.get('ID', None) + return self.document.get("ID", None) def get_assertion_id(self): """ @@ -933,11 +799,8 @@ def get_assertion_id(self): :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) + 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) def get_assertion_issue_instant(self): """ @@ -945,9 +808,6 @@ def get_assertion_issue_instant(self): :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) + 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/src/onelogin/saml2/utils.py b/src/onelogin/saml2/utils.py index 6050ea8d..6a6339a3 100644 --- a/src/onelogin/saml2/utils.py +++ b/src/onelogin/saml2/utils.py @@ -41,15 +41,17 @@ def return_false_on_exception(func): 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): + if not kwargs.pop("raise_exceptions", False): try: return func(*args, **kwargs) except Exception: return False else: return func(*args, **kwargs) + return exceptfalse @@ -61,12 +63,12 @@ class OneLogin_Saml2_Utils(object): """ - RESPONSE_SIGNATURE_XPATH = '/samlp:Response/ds:Signature' - ASSERTION_SIGNATURE_XPATH = '/samlp:Response/saml:Assertion/ds:Signature' + 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?$') + 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): @@ -140,13 +142,13 @@ def format_cert(cert, heads=True): :returns: Formatted cert :rtype: string """ - x509_cert = cert.replace('\x0D', '') - x509_cert = x509_cert.replace('\r', '') - x509_cert = x509_cert.replace('\n', '') + 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(' ', '') + 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" @@ -167,20 +169,20 @@ def format_private_key(key, heads=True): :returns: Formated private key :rtype: string """ - private_key = key.replace('\x0D', '') - private_key = private_key.replace('\r', '') - private_key = private_key.replace('\n', '') + 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 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(' ', '') + 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 @@ -205,38 +207,35 @@ def redirect(url, parameters={}, request_data={}): 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) + 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 - ) + 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 = '?' + if url.find("?") < 0: + param_prefix = "?" else: - param_prefix = '&' + param_prefix = "&" for name, value in parameters.items(): if value is None: param = OneLogin_Saml2_Utils.escape_url(name) elif isinstance(value, list): - param = '' + param = "" for val in value: - param += OneLogin_Saml2_Utils.escape_url(name) + '[]=' + OneLogin_Saml2_Utils.escape_url(val) + '&' + 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) + param = OneLogin_Saml2_Utils.escape_url(name) + "=" + OneLogin_Saml2_Utils.escape_url(value) if param: url += param_prefix + param - param_prefix = '&' + param_prefix = "&" return url @@ -253,20 +252,19 @@ def get_self_url_host(request_data): :rtype: string """ current_host = OneLogin_Saml2_Utils.get_self_host(request_data) - protocol = 'https' if OneLogin_Saml2_Utils.is_https(request_data) else 'http' + protocol = "https" if OneLogin_Saml2_Utils.is_https(request_data) else "http" - if request_data.get('server_port') is not None: + 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.', + "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'] + 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')): + 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) + return "%s://%s" % (protocol, current_host) @staticmethod def get_self_host(request_data): @@ -279,12 +277,12 @@ def get_self_host(request_data): :return: The current host :rtype: string """ - if 'http_host' in request_data: - return request_data['http_host'] - elif 'server_name' in request_data: + 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') + return request_data["server_name"] + raise Exception("No hostname defined") @staticmethod def is_https(request_data): @@ -297,9 +295,9 @@ def is_https(request_data): :return: False if https is not active :rtype: boolean """ - is_https = 'https' in request_data and request_data['https'] != 'off' + 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') + is_https = is_https or ("server_port" in request_data and str(request_data["server_port"]) == "443") return is_https @staticmethod @@ -314,15 +312,15 @@ def get_self_url_no_query(request_data): :rtype: string """ self_url_host = OneLogin_Saml2_Utils.get_self_url_host(request_data) - script_name = request_data['script_name'] + script_name = request_data["script_name"] if script_name: - if script_name[0] != '/': - script_name = '/' + script_name + if script_name[0] != "/": + script_name = "/" + script_name else: - script_name = '' + 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'] + if "path_info" in request_data: + self_url_no_query += request_data["path_info"] return self_url_no_query @@ -338,11 +336,11 @@ def get_self_routed_url_no_query(request_data): :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'], '') + 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 @@ -359,11 +357,11 @@ def get_self_url(request_data): """ 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) + 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] @@ -377,7 +375,7 @@ def generate_unique_id(): :return: A unique string :rtype: string """ - return 'ONELOGIN_%s' % sha1(compat.to_bytes(uuid4().hex)).hexdigest() + return "ONELOGIN_%s" % sha1(compat.to_bytes(uuid4().hex)).hexdigest() @staticmethod def parse_time_to_SAML(time): @@ -414,7 +412,7 @@ def parse_SAML_to_time(timestr): 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')) + 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()) @@ -480,7 +478,7 @@ def get_expire_time(cache_duration=None, valid_until=None): expire_time = valid_until_time if expire_time is not None: - return '%d' % expire_time + return "%d" % expire_time return None @staticmethod @@ -493,7 +491,7 @@ def delete_local_session(callback=None): callback() @staticmethod - def calculate_x509_fingerprint(x509_cert, alg='sha1'): + def calculate_x509_fingerprint(x509_cert, alg="sha1"): """ Calculates the fingerprint of a formatted x509cert. @@ -508,21 +506,21 @@ def calculate_x509_fingerprint(x509_cert, alg='sha1'): """ assert isinstance(x509_cert, compat.str_type) - lines = x509_cert.split('\n') - data = '' + 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-----': + if line == "-----BEGIN CERTIFICATE-----": inData = True - elif line == '-----BEGIN PUBLIC KEY-----' or line == '-----BEGIN RSA PRIVATE KEY-----': + elif line == "-----BEGIN PUBLIC KEY-----" or line == "-----BEGIN RSA PRIVATE KEY-----": # This isn't an X509 certificate. return None else: - if line == '-----END CERTIFICATE-----': + if line == "-----END CERTIFICATE-----": break # Append the current line to the certificate data. @@ -533,11 +531,11 @@ def calculate_x509_fingerprint(x509_cert, alg='sha1'): decoded_data = base64.b64decode(compat.to_bytes(data)) - if alg == 'sha512': + if alg == "sha512": fingerprint = sha512(decoded_data) - elif alg == 'sha384': + elif alg == "sha384": fingerprint = sha384(decoded_data) - elif alg == 'sha256': + elif alg == "sha256": fingerprint = sha256(decoded_data) else: fingerprint = sha1(decoded_data) @@ -555,7 +553,7 @@ def format_finger_print(fingerprint): :returns: Formatted fingerprint :rtype: string """ - formatted_fingerprint = fingerprint.replace(':', '') + formatted_fingerprint = fingerprint.replace(":", "") return formatted_fingerprint.lower() @staticmethod @@ -586,13 +584,13 @@ def generate_name_id(value, sp_nq, sp_format=None, cert=None, debug=False, nq=No """ 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) + 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) + name_id.set("SPNameQualifier", sp_nq) if sp_format is not None: - name_id.set('Format', sp_format) + name_id.set("Format", sp_format) if nq is not None: - name_id.set('NameQualifier', nq) + name_id.set("NameQualifier", nq) name_id.text = value if cert is not None: @@ -603,8 +601,7 @@ def generate_name_id(value, sp_nq, sp_format=None, cert=None, debug=False, nq=No 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") + 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") @@ -615,7 +612,7 @@ def generate_name_id(value, sp_nq, sp_format=None, cert=None, debug=False, nq=No 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 '' + compat.to_string(OneLogin_Saml2_XML.to_string(enc_data)) + '' + return "" + compat.to_string(OneLogin_Saml2_XML.to_string(enc_data)) + "" else: return OneLogin_Saml2_XML.extract_tag_text(root, "saml:NameID") @@ -632,30 +629,24 @@ def get_status(dom): """ status = {} - status_entry = OneLogin_Saml2_XML.query(dom, '/samlp:Response/samlp: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 - ) + 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]) + 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 - ) + 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["code"] = code - status['msg'] = '' - message_entry = OneLogin_Saml2_XML.query(dom, '/samlp:Response/samlp:Status/samlp:StatusMessage', status_entry[0]) + 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]) + 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] + status["msg"] = subcode_entry[0].values()[0] elif len(message_entry) == 1: - status['msg'] = OneLogin_Saml2_XML.element_text(message_entry[0]) + status["msg"] = OneLogin_Saml2_XML.element_text(message_entry[0]) return status @@ -721,8 +712,8 @@ def add_sign(xml, key, cert, debug=False, sign_algorithm=OneLogin_Saml2_Constant :returns: Signed XML :rtype: string """ - if xml is None or xml == '': - raise Exception('Empty string supplied as input') + if xml is None or xml == "": + raise Exception("Empty string supplied as input") elem = OneLogin_Saml2_XML.to_etree(xml) @@ -731,33 +722,33 @@ def add_sign(xml, key, cert, debug=False, sign_algorithm=OneLogin_Saml2_Constant 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 + 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') + signature = xmlsec.template.create(elem, xmlsec.Transform.EXCL_C14N, sign_algorithm_transform, ns="ds") - issuer = OneLogin_Saml2_XML.query(elem, '//saml:Issuer') + 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') + 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) + elem_id = elem_to_sign.get("ID", None) if elem_id is not None: if elem_id: - elem_id = '#' + 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 + elem_id = "#" + generated_id + elem_to_sign.attrib["ID"] = generated_id xmlsec.enable_debug_trace(debug) xmlsec.tree.add_ids(elem_to_sign, ["ID"]) @@ -766,7 +757,7 @@ def add_sign(xml, key, cert, debug=False, sign_algorithm=OneLogin_Saml2_Constant 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 + OneLogin_Saml2_Constants.SHA512: xmlsec.Transform.SHA512, } digest_algorithm_transform = digest_algorithm_transform_map.get(digest_algorithm, xmlsec.Transform.SHA256) @@ -787,7 +778,7 @@ def add_sign(xml, key, cert, debug=False, sign_algorithm=OneLogin_Saml2_Constant @staticmethod @return_false_on_exception - def validate_sign(xml, cert=None, fingerprint=None, fingerprintalg='sha1', validatecert=False, debug=False, xpath=None, multicerts=None): + def validate_sign(xml, cert=None, fingerprint=None, fingerprintalg="sha1", validatecert=False, debug=False, xpath=None, multicerts=None): """ Validates a signature (Message or Assertion). @@ -818,8 +809,8 @@ def validate_sign(xml, cert=None, fingerprint=None, fingerprintalg='sha1', valid :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') + 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) @@ -846,19 +837,13 @@ def validate_sign(xml, cert=None, fingerprint=None, fingerprintalg='sha1', valid 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 - ) + 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 - ) + raise OneLogin_Saml2_ValidationError("Expected exactly one signature node; got {}.".format(len(signature_nodes)), OneLogin_Saml2_ValidationError.WRONG_NUMBER_OF_SIGNATURES) @staticmethod @return_false_on_exception - def validate_metadata_sign(xml, cert=None, fingerprint=None, fingerprintalg='sha1', validatecert=False, debug=False): + def validate_metadata_sign(xml, cert=None, fingerprint=None, fingerprintalg="sha1", validatecert=False, debug=False): """ Validates a signature of a EntityDescriptor. @@ -883,21 +868,21 @@ def validate_metadata_sign(xml, cert=None, fingerprint=None, fingerprintalg='sha :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') + 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') + 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') + 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') + 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: @@ -905,11 +890,11 @@ def validate_metadata_sign(xml, cert=None, fingerprint=None, fingerprintalg='sha 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.') + raise Exception("Could not validate metadata signature: No signature nodes found.") @staticmethod @return_false_on_exception - def validate_node_sign(signature_node, elem, cert=None, fingerprint=None, fingerprintalg='sha1', validatecert=False, debug=False): + def validate_node_sign(signature_node, elem, cert=None, fingerprint=None, fingerprintalg="sha1", validatecert=False, debug=False): """ Validates a signature node. @@ -937,8 +922,8 @@ def validate_node_sign(signature_node, elem, cert=None, fingerprint=None, finger :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 (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) @@ -947,11 +932,8 @@ def validate_node_sign(signature_node, elem, cert=None, fingerprint=None, finger 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 - ) + 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') @@ -972,11 +954,7 @@ def validate_node_sign(signature_node, elem, cert=None, fingerprint=None, finger 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) - ) + raise OneLogin_Saml2_ValidationError("Signature validation failed. SAML Response rejected. %s", OneLogin_Saml2_ValidationError.INVALID_SIGNATURE, str(err)) return True @@ -999,7 +977,7 @@ def sign_binary(msg, key, algorithm=xmlsec.Transform.RSA_SHA256, debug=False): """ if isinstance(msg, str): - msg = msg.encode('utf8') + msg = msg.encode("utf8") xmlsec.enable_debug_trace(debug) dsig_ctx = xmlsec.SignatureContext() @@ -1037,13 +1015,11 @@ def validate_binary_sign(signed_query, signature, cert=None, algorithm=OneLogin_ 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 + 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)) + 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: diff --git a/src/onelogin/saml2/xml_utils.py b/src/onelogin/saml2/xml_utils.py index 5cda946c..a3c5dba9 100644 --- a/src/onelogin/saml2/xml_utils.py +++ b/src/onelogin/saml2/xml_utils.py @@ -19,7 +19,7 @@ class OneLogin_Saml2_XML(object): - _element_class = type(etree.Element('root')) + _element_class = type(etree.Element("root")) _parse_etree = staticmethod(fromstring) _schema_class = etree.XMLSchema _text_class = compat.text_types @@ -65,7 +65,7 @@ def to_etree(xml): 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)) + raise ValueError("unsupported type %r" % type(xml)) @staticmethod def validate_xml(xml, schema, debug=False): @@ -87,18 +87,18 @@ def validate_xml(xml, schema, debug=False): except Exception as e: if debug: print(e) - return 'unloaded_xml' + return "unloaded_xml" - schema_file = join(dirname(__file__), 'schemas', schema) - with open(schema_file, 'r') as f_schema: + 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: ') + print("Errors validating the metadata: ") for error in xmlschema.error_log: print(error.message) - return 'invalid_xml' + return "invalid_xml" return xml @staticmethod @@ -144,11 +144,7 @@ def cleanup_namespaces(tree_or_element, top_nsmap=None, keep_ns_prefixes=None): :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 - ] + 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))) diff --git a/src/onelogin/saml2/xmlparser.py b/src/onelogin/saml2/xmlparser.py index 4bf8df3d..9210480e 100644 --- a/src/onelogin/saml2/xmlparser.py +++ b/src/onelogin/saml2/xmlparser.py @@ -22,8 +22,7 @@ class DTDForbidden(ValueError): - """Document type definition is forbidden - """ + """Document type definition is forbidden""" def __init__(self, name, sysid, pubid): super(DTDForbidden, self).__init__() @@ -37,8 +36,7 @@ def __str__(self): class EntitiesForbidden(ValueError): - """Entity definition is forbidden - """ + """Entity definition is forbidden""" def __init__(self, name, value, base, sysid, pubid, notation_name): super(EntitiesForbidden, self).__init__() @@ -55,13 +53,11 @@ def __str__(self): class NotSupportedError(ValueError): - """The operation is not supported - """ + """The operation is not supported""" class RestrictedElement(_etree.ElementBase): - """A restricted Element class that filters out instances of some classes - """ + """A restricted Element class that filters out instances of some classes""" __slots__ = () blacklist = (_etree._Entity, _etree._ProcessingInstruction, _etree._Comment) @@ -103,16 +99,9 @@ def getiterator(self, tag=None): class GlobalParserTLS(threading.local): - """Thread local context for custom parser instances - """ + """Thread local context for custom parser instances""" - parser_config = { - "resolve_entities": False, - 'remove_comments': True, - 'no_network': True, - 'remove_pis': True, - 'huge_tree': False - } + parser_config = {"resolve_entities": False, "remove_comments": True, "no_network": True, "remove_pis": True, "huge_tree": False} element_class = RestrictedElement diff --git a/tests/coverage.rc b/tests/coverage.rc deleted file mode 100644 index 855ec7f9..00000000 --- a/tests/coverage.rc +++ /dev/null @@ -1,30 +0,0 @@ -[run] -branch = True - -omit = - -[paths] -source = src/onelogin/saml2 - -[report] -# Regexes for lines to exclude from consideration -exclude_lines = - - - # Have to re-enable the standard pragma - pragma: no cover - - # Don't complain about missing debug-only code: - def __repr__ - if self\.debug - if debug - - # Don't complain if tests don't hit defensive assertion code: - raise AssertionError - raise NotImplementedError - - # Don't complain if non-runnable code isn't run: - if 0: - if __name__ == .__main__.: - -ignore_errors = True diff --git a/tests/data/responses/invalids/not_before_failed.xml.base64 b/tests/data/responses/invalids/not_before_failed.xml.base64 index de62f5e4..8a2ffd3d 100644 --- a/tests/data/responses/invalids/not_before_failed.xml.base64 +++ b/tests/data/responses/invalids/not_before_failed.xml.base64 @@ -1 +1 @@ -PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9InBmeGU0MjcxNTkzLTJiNzctOTE4ZS1hNTMxLTdmODljMjVjYTJlZiIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTQtMDItMTlUMDE6MDU6NDlaIiBEZXN0aW5hdGlvbj0iaHR0cHM6Ly9leGFtcGxlLmNvbS9uZXdvbmVsb2dpbi9kZW1vMS9pbmRleC5waHA/YWNzIiBJblJlc3BvbnNlVG89Ik9ORUxPR0lOX2FmM2Q0YTcxMGZjOGIzMDU4ODRiOTZkMDA5NGFiNjI4ODAyZjU2OTIiPjxzYW1sOklzc3Vlcj5odHRwczovL2V4YW1wbGUuY29tL3NpbXBsZXNhbWwvc2FtbDIvaWRwL21ldGFkYXRhLnBocDwvc2FtbDpJc3N1ZXI+PGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+DQogIDxkczpTaWduZWRJbmZvPjxkczpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+DQogICAgPGRzOlNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNyc2Etc2hhMSIvPg0KICA8ZHM6UmVmZXJlbmNlIFVSST0iI3BmeGU0MjcxNTkzLTJiNzctOTE4ZS1hNTMxLTdmODljMjVjYTJlZiI+PGRzOlRyYW5zZm9ybXM+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNlbnZlbG9wZWQtc2lnbmF0dXJlIi8+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjwvZHM6VHJhbnNmb3Jtcz48ZHM6RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3NoYTEiLz48ZHM6RGlnZXN0VmFsdWU+eDJWa3BENml0ek5WRDh6cHhwZXZhRTU4cW4wPTwvZHM6RGlnZXN0VmFsdWU+PC9kczpSZWZlcmVuY2U+PC9kczpTaWduZWRJbmZvPjxkczpTaWduYXR1cmVWYWx1ZT5kc3R6cEdYcFFNL0hzdHU4TmFzWFJad2wxcjh6TC81TXlhbFhYS2xZUWZ3R2s3ZEE4S0Z5T1E1ajRneERFMFRjQmdwUVo0U0REOUl6SFNiOUNZTFFzR2lyVVVBTERNVUNjTHlXcndzQ01zUEpheVltRTJBUkdMc2IvTWIxK0FkaVZSTUhNL05NUVpoaTBFYnNqN2Nod1FRTWNuUXhiaG92a0g5cWVBdHp0UGc9PC9kczpTaWduYXR1cmVWYWx1ZT4NCjxkczpLZXlJbmZvPjxkczpYNTA5RGF0YT48ZHM6WDUwOUNlcnRpZmljYXRlPk1JSUNnVENDQWVvQ0NRQ2JPbHJXRGRYN0ZUQU5CZ2txaGtpRzl3MEJBUVVGQURDQmhERUxNQWtHQTFVRUJoTUNUazh4R0RBV0JnTlZCQWdURDBGdVpISmxZWE1nVTI5c1ltVnlaekVNTUFvR0ExVUVCeE1EUm05dk1SQXdEZ1lEVlFRS0V3ZFZUa2xPUlZSVU1SZ3dGZ1lEVlFRREV3OW1aV2xrWlM1bGNteGhibWN1Ym04eElUQWZCZ2txaGtpRzl3MEJDUUVXRW1GdVpISmxZWE5BZFc1cGJtVjBkQzV1YnpBZUZ3MHdOekEyTVRVeE1qQXhNelZhRncwd056QTRNVFF4TWpBeE16VmFNSUdFTVFzd0NRWURWUVFHRXdKT1R6RVlNQllHQTFVRUNCTVBRVzVrY21WaGN5QlRiMnhpWlhKbk1Rd3dDZ1lEVlFRSEV3TkdiMjh4RURBT0JnTlZCQW9UQjFWT1NVNUZWRlF4R0RBV0JnTlZCQU1URDJabGFXUmxMbVZ5YkdGdVp5NXViekVoTUI4R0NTcUdTSWIzRFFFSkFSWVNZVzVrY21WaGMwQjFibWx1WlhSMExtNXZNSUdmTUEwR0NTcUdTSWIzRFFFQkFRVUFBNEdOQURDQmlRS0JnUURpdmJoUjdQNTE2eC9TM0JxS3h1cFFlMExPTm9saXVwaUJPZXNDTzNTSGJEcmwzK3E5SWJmbmZtRTA0ck51TWNQc0l4QjE2MVRkRHBJZXNMQ243YzhhUEhJU0tPdFBsQWVUWlNuYjhRQXU3YVJqWnEzK1BiclA1dVczVGNmQ0dQdEtUeXRIT2dlL09sSmJvMDc4ZFZoWFExNGQxRUR3WEpXMXJSWHVVdDRDOFFJREFRQUJNQTBHQ1NxR1NJYjNEUUVCQlFVQUE0R0JBQ0RWZnA4NkhPYnFZK2U4QlVvV1E5K1ZNUXgxQVNEb2hCandPc2cyV3lrVXFSWEYrZExmY1VIOWRXUjYzQ3RaSUtGRGJTdE5vbVBuUXo3bmJLK29ueWd3QnNwVkVibkh1VWloWnEzWlVkbXVtUXFDdzRVdnMvMVV2cTNvck9vL1dKVmhUeXZMZ0ZWSzJRYXJRNC82N09aZkhkN1IrUE9CWGhvcGhTTXYxWk9vPC9kczpYNTA5Q2VydGlmaWNhdGU+PC9kczpYNTA5RGF0YT48L2RzOktleUluZm8+PC9kczpTaWduYXR1cmU+PHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9InBmeDdlNDhmZDc1LWNlMmItNjBlZC0yOWVlLWU3OTg0NmM5NTljNiIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTQtMDItMTlUMDE6MDU6NDlaIj48c2FtbDpJc3N1ZXI+aHR0cHM6Ly9leGFtcGxlLmNvbS9zaW1wbGVzYW1sL3NhbWwyL2lkcC9tZXRhZGF0YS5waHA8L3NhbWw6SXNzdWVyPg0KPHNhbWw6U3ViamVjdD48c2FtbDpOYW1lSUQgU1BOYW1lUXVhbGlmaWVyPSJodHRwczovL2V4YW1wbGUuY29tL25ld29uZWxvZ2luL2RlbW8xL21ldGFkYXRhLnBocCIgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjE6bmFtZWlkLWZvcm1hdDplbWFpbEFkZHJlc3MiPjQ5Mjg4MjYxNWFjZjMxYzgwOTZiNjI3MjQ1ZDc2YWU1MzAzNmMwOTA8L3NhbWw6TmFtZUlEPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uRGF0YSBOb3RPbk9yQWZ0ZXI9IjI5OTMtMDItMTlUMDE6MTA6NDlaIiBSZWNpcGllbnQ9Imh0dHBzOi8vZXhhbXBsZS5jb20vbmV3b25lbG9naW4vZGVtbzEvaW5kZXgucGhwP2FjcyIgSW5SZXNwb25zZVRvPSJPTkVMT0dJTl9hZjNkNGE3MTBmYzhiMzA1ODg0Yjk2ZDAwOTRhYjYyODgwMmY1NjkyIi8+PC9zYW1sOlN1YmplY3RDb25maXJtYXRpb24+PC9zYW1sOlN1YmplY3Q+PHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMjQtMDItMTlUMDE6MDU6MTlaIiBOb3RPbk9yQWZ0ZXI9IjI5OTMtMDItMTlUMDE6MTA6NDlaIj48c2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPjxzYW1sOkF1ZGllbmNlPmh0dHBzOi8vZXhhbXBsZS5jb20vbmV3b25lbG9naW4vZGVtbzEvbWV0YWRhdGEucGhwPC9zYW1sOkF1ZGllbmNlPjwvc2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPjwvc2FtbDpDb25kaXRpb25zPjxzYW1sOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAxNC0wMi0xOFQxOTo0MjoyMFoiIFNlc3Npb25Ob3RPbk9yQWZ0ZXI9IjI5OTMtMDItMTlUMDk6MDU6NDlaIiBTZXNzaW9uSW5kZXg9Il8wZjRmMTg4ZGMxYmZkM2JmZWEzNmExNjM0YTc0NDE1ODNhZmMzYjM3ODEiPjxzYW1sOkF1dGhuQ29udGV4dD48c2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZDwvc2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj48L3NhbWw6QXV0aG5Db250ZXh0Pjwvc2FtbDpBdXRoblN0YXRlbWVudD48c2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+PHNhbWw6QXR0cmlidXRlIE5hbWU9InVpZCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+dXNlcjwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJtYWlsIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj51c2VyQGV4YW1wbGUuY29tPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9ImNuIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj50ZXN0PC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9InNuIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj51c2VyPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9ImVkdVBlcnNvbkFmZmlsaWF0aW9uIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj51c2VyPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPmFkbWluPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD48L3NhbWw6QXNzZXJ0aW9uPjwvc2FtbHA6UmVzcG9uc2U+ +PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9InBmeDMxNGFjZTYwLTRiOTUtN2NiOS01YmJlLTkwYjc0NWRlYTY4NiIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTQtMDItMTlUMDE6MDU6NDlaIiBEZXN0aW5hdGlvbj0iaHR0cHM6Ly9leGFtcGxlLmNvbS9uZXdvbmVsb2dpbi9kZW1vMS9pbmRleC5waHA/YWNzIiBJblJlc3BvbnNlVG89Ik9ORUxPR0lOX2FmM2Q0YTcxMGZjOGIzMDU4ODRiOTZkMDA5NGFiNjI4ODAyZjU2OTIiPjxzYW1sOklzc3Vlcj5odHRwczovL2V4YW1wbGUuY29tL3NpbXBsZXNhbWwvc2FtbDIvaWRwL21ldGFkYXRhLnBocDwvc2FtbDpJc3N1ZXI+PGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+DQogIDxkczpTaWduZWRJbmZvPjxkczpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+DQogICAgPGRzOlNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNyc2Etc2hhMSIvPg0KICA8ZHM6UmVmZXJlbmNlIFVSST0iI3BmeDMxNGFjZTYwLTRiOTUtN2NiOS01YmJlLTkwYjc0NWRlYTY4NiI+PGRzOlRyYW5zZm9ybXM+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNlbnZlbG9wZWQtc2lnbmF0dXJlIi8+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjwvZHM6VHJhbnNmb3Jtcz48ZHM6RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3NoYTEiLz48ZHM6RGlnZXN0VmFsdWU+Ymp2ZEVIN3U3MzlsNStaMG1WcitGNE8rd0RJPTwvZHM6RGlnZXN0VmFsdWU+PC9kczpSZWZlcmVuY2U+PC9kczpTaWduZWRJbmZvPjxkczpTaWduYXR1cmVWYWx1ZT5QM243SE5PR3FpSGFybGJ1aUV0bjNEaEFxSkIzU1RDOXUwcE1YTktSdU9PTlVOa2JDcTZGVHYzVlFZOFRaUnZ0NFJkRHpoZDlmZ1ZBNjIwREFjNVJqVE9iVThRekNnVHlPTTBkVlhxQ3FwdElFMVZuQ2xmd210MlhaNU1SemlyQ3QzS3dVS1RmOENyeU5FMWREWFpRL0QyTllSUmRWMkU4VGZCakxkS3hieXc9PC9kczpTaWduYXR1cmVWYWx1ZT4NCjxkczpLZXlJbmZvPjxkczpYNTA5RGF0YT48ZHM6WDUwOUNlcnRpZmljYXRlPk1JSUNnVENDQWVvQ0NRQ2JPbHJXRGRYN0ZUQU5CZ2txaGtpRzl3MEJBUVVGQURDQmhERUxNQWtHQTFVRUJoTUNUazh4R0RBV0JnTlZCQWdURDBGdVpISmxZWE1nVTI5c1ltVnlaekVNTUFvR0ExVUVCeE1EUm05dk1SQXdEZ1lEVlFRS0V3ZFZUa2xPUlZSVU1SZ3dGZ1lEVlFRREV3OW1aV2xrWlM1bGNteGhibWN1Ym04eElUQWZCZ2txaGtpRzl3MEJDUUVXRW1GdVpISmxZWE5BZFc1cGJtVjBkQzV1YnpBZUZ3MHdOekEyTVRVeE1qQXhNelZhRncwd056QTRNVFF4TWpBeE16VmFNSUdFTVFzd0NRWURWUVFHRXdKT1R6RVlNQllHQTFVRUNCTVBRVzVrY21WaGN5QlRiMnhpWlhKbk1Rd3dDZ1lEVlFRSEV3TkdiMjh4RURBT0JnTlZCQW9UQjFWT1NVNUZWRlF4R0RBV0JnTlZCQU1URDJabGFXUmxMbVZ5YkdGdVp5NXViekVoTUI4R0NTcUdTSWIzRFFFSkFSWVNZVzVrY21WaGMwQjFibWx1WlhSMExtNXZNSUdmTUEwR0NTcUdTSWIzRFFFQkFRVUFBNEdOQURDQmlRS0JnUURpdmJoUjdQNTE2eC9TM0JxS3h1cFFlMExPTm9saXVwaUJPZXNDTzNTSGJEcmwzK3E5SWJmbmZtRTA0ck51TWNQc0l4QjE2MVRkRHBJZXNMQ243YzhhUEhJU0tPdFBsQWVUWlNuYjhRQXU3YVJqWnEzK1BiclA1dVczVGNmQ0dQdEtUeXRIT2dlL09sSmJvMDc4ZFZoWFExNGQxRUR3WEpXMXJSWHVVdDRDOFFJREFRQUJNQTBHQ1NxR1NJYjNEUUVCQlFVQUE0R0JBQ0RWZnA4NkhPYnFZK2U4QlVvV1E5K1ZNUXgxQVNEb2hCandPc2cyV3lrVXFSWEYrZExmY1VIOWRXUjYzQ3RaSUtGRGJTdE5vbVBuUXo3bmJLK29ueWd3QnNwVkVibkh1VWloWnEzWlVkbXVtUXFDdzRVdnMvMVV2cTNvck9vL1dKVmhUeXZMZ0ZWSzJRYXJRNC82N09aZkhkN1IrUE9CWGhvcGhTTXYxWk9vPC9kczpYNTA5Q2VydGlmaWNhdGU+PC9kczpYNTA5RGF0YT48L2RzOktleUluZm8+PC9kczpTaWduYXR1cmU+PHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9InBmeDdlNDhmZDc1LWNlMmItNjBlZC0yOWVlLWU3OTg0NmM5NTljNiIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTQtMDItMTlUMDE6MDU6NDlaIj48c2FtbDpJc3N1ZXI+aHR0cHM6Ly9leGFtcGxlLmNvbS9zaW1wbGVzYW1sL3NhbWwyL2lkcC9tZXRhZGF0YS5waHA8L3NhbWw6SXNzdWVyPg0KPHNhbWw6U3ViamVjdD48c2FtbDpOYW1lSUQgU1BOYW1lUXVhbGlmaWVyPSJodHRwczovL2V4YW1wbGUuY29tL25ld29uZWxvZ2luL2RlbW8xL21ldGFkYXRhLnBocCIgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjE6bmFtZWlkLWZvcm1hdDplbWFpbEFkZHJlc3MiPjQ5Mjg4MjYxNWFjZjMxYzgwOTZiNjI3MjQ1ZDc2YWU1MzAzNmMwOTA8L3NhbWw6TmFtZUlEPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uRGF0YSBOb3RPbk9yQWZ0ZXI9IjI5OTMtMDItMTlUMDE6MTA6NDlaIiBSZWNpcGllbnQ9Imh0dHBzOi8vZXhhbXBsZS5jb20vbmV3b25lbG9naW4vZGVtbzEvaW5kZXgucGhwP2FjcyIgSW5SZXNwb25zZVRvPSJPTkVMT0dJTl9hZjNkNGE3MTBmYzhiMzA1ODg0Yjk2ZDAwOTRhYjYyODgwMmY1NjkyIi8+PC9zYW1sOlN1YmplY3RDb25maXJtYXRpb24+PC9zYW1sOlN1YmplY3Q+PHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjI5OTMtMDItMTlUMDE6MDU6MTlaIiBOb3RPbk9yQWZ0ZXI9IjI5OTMtMDItMTlUMDE6MTA6NDlaIj48c2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPjxzYW1sOkF1ZGllbmNlPmh0dHBzOi8vZXhhbXBsZS5jb20vbmV3b25lbG9naW4vZGVtbzEvbWV0YWRhdGEucGhwPC9zYW1sOkF1ZGllbmNlPjwvc2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPjwvc2FtbDpDb25kaXRpb25zPjxzYW1sOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAxNC0wMi0xOFQxOTo0MjoyMFoiIFNlc3Npb25Ob3RPbk9yQWZ0ZXI9IjI5OTMtMDItMTlUMDk6MDU6NDlaIiBTZXNzaW9uSW5kZXg9Il8wZjRmMTg4ZGMxYmZkM2JmZWEzNmExNjM0YTc0NDE1ODNhZmMzYjM3ODEiPjxzYW1sOkF1dGhuQ29udGV4dD48c2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZDwvc2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj48L3NhbWw6QXV0aG5Db250ZXh0Pjwvc2FtbDpBdXRoblN0YXRlbWVudD48c2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+PHNhbWw6QXR0cmlidXRlIE5hbWU9InVpZCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+dXNlcjwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJtYWlsIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj51c2VyQGV4YW1wbGUuY29tPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9ImNuIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj50ZXN0PC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9InNuIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj51c2VyPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9ImVkdVBlcnNvbkFmZmlsaWF0aW9uIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj51c2VyPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPmFkbWluPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD48L3NhbWw6QXNzZXJ0aW9uPjwvc2FtbHA6UmVzcG9uc2U+ \ No newline at end of file diff --git a/tests/data/responses/valid_encrypted_assertion_encrypted_nameid.xml.base64 b/tests/data/responses/valid_encrypted_assertion_encrypted_nameid.xml.base64 index 83431af3..33eaf12d 100644 --- a/tests/data/responses/valid_encrypted_assertion_encrypted_nameid.xml.base64 +++ b/tests/data/responses/valid_encrypted_assertion_encrypted_nameid.xml.base64 @@ -1 +1 @@ -PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJfYzIzZjAwODk2NmZmMjcxMGI5ZDQ3NjA4NDIyNDRkZGM4MTE2NzY2NjkyIiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAxNC0wOS0yMlQxNjozMzoxNVoiIERlc3RpbmF0aW9uPSJodHRwOi8vcHl0b29sa2l0LmNvbTo4MDAwLz9hY3MiIEluUmVzcG9uc2VUbz0iT05FTE9HSU5fOTlmYjdiOWQ4OTYzNDdmY2UxMjEyZDE5MjRkNDQ3M2I1NDcyZjJhOSI+PHNhbWw6SXNzdWVyPmh0dHBzOi8vcGl0YnVsay5uby1pcC5vcmcvc2ltcGxlc2FtbC9zYW1sMi9pZHAvbWV0YWRhdGEucGhwPC9zYW1sOklzc3Vlcj48c2FtbHA6U3RhdHVzPjxzYW1scDpTdGF0dXNDb2RlIFZhbHVlPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6c3RhdHVzOlN1Y2Nlc3MiLz48L3NhbWxwOlN0YXR1cz48c2FtbDpFbmNyeXB0ZWRBc3NlcnRpb24+PHhlbmM6RW5jcnlwdGVkRGF0YSB4bWxuczp4ZW5jPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyMiIHhtbG5zOmRzaWc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiIFR5cGU9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jI0VsZW1lbnQiPjx4ZW5jOkVuY3J5cHRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNhZXMxMjgtY2JjIi8+PGRzaWc6S2V5SW5mbyB4bWxuczpkc2lnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj48eGVuYzpFbmNyeXB0ZWRLZXk+PHhlbmM6RW5jcnlwdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jI3JzYS1vYWVwLW1nZjFwIi8+PHhlbmM6Q2lwaGVyRGF0YT48eGVuYzpDaXBoZXJWYWx1ZT4yd09rZERoNXVaL25BNTRZeHdEdVljbTFFTXdlNXRvZ2NqOGxpeVdXWmNaUGROS3FYdzJuWmUxVVpBTlAxZ2V6WFFMa0xidTMzbUE3Z0JQQ0NRQ3J4Q1gramtycEpPbnFpNFoyaklVc2FteFQycGVmaW1NRUM0ZE54bWdub2hxWFFHTEhvN0VrMnprTGU0ZEtDdTBmVEl6WkRNRERjSXdyclQ4MmcwYjdQL1U9PC94ZW5jOkNpcGhlclZhbHVlPjwveGVuYzpDaXBoZXJEYXRhPjwveGVuYzpFbmNyeXB0ZWRLZXk+PC9kc2lnOktleUluZm8+CiAgIDx4ZW5jOkNpcGhlckRhdGE+CiAgICAgIDx4ZW5jOkNpcGhlclZhbHVlPk5jYVlxSVhzUFZlU1dQUUpGZXAwVzJVNHZ2Ym9xVitQWlgrdlNMa0pldXR5VW16Qzk3aGpsZUNyQ3VXN3BPYy9xWXZia1lPUlE0NmhsR1VqT2FTWmJ6d2R0MlBBWmZRbUQ2M05uSzFZS084ek42TEhhUVRkUFI0c1Q5UUFwUkFDNndhbDV6cDAyU1dQVlpzNzlIaGk2Z0pXdTZyL1R6U3c2czN2YkNXTThBeWs5MU8wWURwaXpvSHlFYTR3U29SQ0NEemFFTHFaNyszUEFNaWVyRklYWHBWUDh0aE9RNXEwNmt5Yi92YTdRK1ZLMW0vL1ZxZnpYdnhMNmlsKzFWSzVFbTF1T2xlaU14bG9tRnZzdnRBNmpocU1WQ0dNQkZvdXBzWWwybTVPS3VNNWxuRTl4NEk1dnpFS0xaa3lRY0pETVo2aVE4T2FQMWRFY0dITU1vRmNuTmpheG9hTFhrUDBaMEptM3RxTE9Ka2ZjNW9RS2puWjNQYTJkelVDS1YvWllLYTlmR1R6b2ZuSVFzRURKbk9pd21UeXExcHVLWW5MaWdycEpxYWJKQjFUVXIzTTFkVWtMVTBIaWk5T2Nod1NrZVU1bm0wTmVXRG02KzA5dkFlWmZzKzB1WmdBUHpsNHU3TlM3bTJpRkxhbFdxYmhYWnpkZXlHWVQ0K24xNEdGZFNYcmhTcW9tNDVBTVNwREY4MFl4dXVESGg4ZzIydGcvSVBvT2JvVG4xelFKaFBjbi8rYnFMYkE0bjd2K0VmU1JRdjh4TFQ4SGZza25DSWpzNzhsU3NqSVpJRVU2V0ZKUUhkNWlGS0MvRXNUT3N4UTNsR1hleVBaVlNBYU1jcUQ4YllheXQ5eVJ5NXNVTUtkWmJMMXJVWUp0enZJb1pwY3dDM0xtc3VEM3FKdXZZbyt6K1MzVG9vUXNOWnlTeU9pYThRL2k2MFhmQ0lZOW1LSnAzbTZwYnBjNk5ETmJuRWJyNmJ0U2pvalFZUVlDWStKUEZtdWd0c2tNeVdkbEY4cWhYN2wvbGVHTzE1UUZDRkY1Qld0c1lmMmI2SWFKbGRlRDAxcTZsRFpYTjI1RkJQRDNVanZNS0RFZVkyblA1TkJqRUdKb09MMzlQWU5tNVJIMVBxaVhYVUVQNWVvcVN3UExSM2hQN1NtRUNkbVhHMDhUVlErQUMvbEQrNHB6ZEhEbjVQWUhsZWhqQ2dZb1lteWFxaVVONTUreDdUR0hWZXF5SXBHSVBQNWV2aURDZC95RHBEMXJwUXhVdVVESU1hTms4SHBOR1ZnUzR0UFdKZHBtdUtxb3hEbGY4SmZDaDRCc2pxVnRoeVVEMUY4ci9MdmlQU2UyS1RwbVJrSGhvOFVMZXVlZFZOS0p0S0hScGIyTUxtblY3T3M0Sk0vaStoT3pXWnVsU2JjMjhoTk1ua09tVEZabEV5Q1dpdWRoUEp6Tkd4S0F3M0E2amZYRHp0Wkl2RDBPM1ZHaGkxR1hOQmc3NUJrSEpIV3Vka09NR0JtMmR0MWpUT3FJajA2b1c2czhnVjB5M2ErV1RMaUhhV1ZucGV0a0t4elJhRlFGNmZNMnhTSmFNNmp2alJWTVk5ZGtrckZqZVFYTHp5dVNoczZuSzI2elFKdVJhY1VybEJrK0hHVS9DM1JiVVhDaElza1N0ajZ4YXF4MHQrbENzK3dQWUVubEZwckluSWR4SmhrcEJ2b0xmRGI1U1FEZnhmdTdoSXBaY2xDUGs0QWM4MWUySElPNHFHSGtwNWxaYUxkSjU3WldFRmpGc2FIRkV6RitCcFVoZExoUzdVMksxaWlhd2x0QlZQcnpEZUU5Y09VRFpuQ3pQNEthbFFnZlRENDNSUzJNS0Z4MWFMWElKdnUrUlBYOHZzd1NHdjN0Q29LNHI0K3ptVWhmM3JZaFFwWFhCd0EyUVJGS1F0ODhRb290bVJIcitRU0ZneHBnUm1iTjhnQnF6SHRYOFIyVUNWOExvSy8ySXhrRDVQWUlsVWN3Qm40SzUxNFZMMWRGZmtWMENOUnNPTG1NNDJxSm9EK09tQUJ0VEJkVHBkcWVLUVdlZjNjcjlSY1pBSUlzMHBOS2NvMUpVVTIyN2poYVJVTWkrbnZzYjJqemloZGUzdnYyY09BTHN5NWRIWDlOREZqOWs4elVhejJ2UGZXYURoMlFqTWJleFNuWFZGWk1BeFAvZGNET09PaDBzMHRGL20wMmJ4RE1IV1cvdkFwWnMxT0VXQ2xyZGFRMWF6RFdXQ2FTeHNnQy9MV2Y0SmVwR1Jhc3JhZDNVc0ZDWE9kYmJTWG52UnlSbU5IbjUyZUFjOWFHdlNhQWtLaDl4YnQwdTRjU3FEUThrOENkclpDb21VUnJFb1Z2TW9Gb0ZtVy8xWENWRFZvQ29OZmtGZnhJMXVRS3ZJTHBBbzNwZC9Wa0xqQW8zNU5mMlI4WW9QVXJPZFVVQ2JBb3NLTkQ0c1Z5QTgrMElIOE50OWhZYUZSWnNyNkRPdzNkQjFwWVh6TktyUDdNakVOMVhoVlNJZW9VNmE3VnRDbnNtLzE5dVpIa3dpb09rMm9UVXJyY1pzMW9oZlM2dDdIbUI0SEdSeE9QQzNjSzBMWVVreVF2bXlINEk0V0RIdmdOVE5BS2E1M0k4Ym94ZDNnSG5Dd3pUUXFXQjVWM1BXQUd3NFJUOUM2QXo5TTdlcHZGemw4VFpQZEJjbGR1Qi9JNzlIZE9LTVM3OFNIOFFKL1FSSUZXV1MvSG1GU3lqVFZoVG9Na2llT1p6aXk1SW5XTlJFaXlIS0oyazZNaGJETUZKOW4wRTJZVStETWhWZkFadFFzYXFFd2xaSURseHl1UmRqYVBGQjljalo5UGJlZWtoVi9BeEtuMklIenUyRW51ZHZuUUhvUHBmUVFTSk4wZEk4ekMxRXVlQTJCZjdDazRBc3c1RFVlUnQ5N2lBMkFEeitJbU95dHZDa1J0b2k4YUgxTG5mQzRKRGMzdGwxU0d4YXJEOGdVR0drSUd2ZkhHdFlncE5PamZhUURPOTZzVHBCVU8xcWI3SlRFU0VzbkxkaVpxRzlCTm8rWGlNQmxuYWRKdE1DMWttMHY0MWVGY2s1dWVveDZJQlhCVngxVkMvN01KdnhaV2Q1ZDBhaFptWEg0c1p6OHdFdVR3djdzanBBUk5VM1IvSkJsY3k3VXVjZnBCQmZycVkvWU9zMUQ4SVRjSlNZMjh4dlVrblFGVjJLSWM3NkFwMzNCR3NJclFSTTlxaWZNMU9TNWdKeVBCMWZsa3RHcmlSc1lmeGdlanVaZEM5N0FqNTdzQkgrS2VTdUxINmwzRVJOODJ0SXB5MDBRVFk1NlJQMzdCclI0RkFhSXVHVTgzWWJxK3V3ZUo3aFFXeUt3VGlkYmJjS1hSNWlsOXFwNW1JazJlbDBkSXRiUzhPenBTek5XYzJaTU9zSVhlUVdMWTd1QVNyVGR5Q1dyQmRrZXhuZnB0QUgzM1JuWkI4Q0lKVVlmQWxCYk81MWxpd2tmeEhaR2ZVeEtOSFlvTjF2b2dZOGRKUk13YUxMQ3B5dlgyT29FVXBEOEZZNWRPbVZGQUxWMnZMeUluVWRSaVZKQ3Yxb1A1RU9lbHZ4MjlsUlFCN0NJVDlJOElRdUhCa3ZYeUw1K1FKZERITXF6ZjI0OHZjU3diUXE0ZVpWNktlemxNVVFWYmJXWHVVOEFTd0VneThjLzREZ2FBZjdSZE1ZcUhYWWZENTR6TjJtNzZ2WkZ4SDRBZUswM3JveHBBeVc3ZVMwL0NJcThKSWJ0QmNQVk1NVHAvR1d2Z1RwUUlZT0FtOFhNRGhFSGtsaVl5ZjlMUHd0UEw5eFZPdHBhSzIrOW4vdFpUQm9qdFd0TE9JMm9HYUZvS29Wb0dYMVlMZVdBZGZXTUxUU1pEQy9XRVhMcWtSZjhKY3VOa3VGSm1NTGpuNUNBaUljTExueW43YlZKZUY4L3FxR3NNNzNja2ViWDY0cEkrRENuMmpBZGcxSENXSE80TEc3N3FqS3d5WmpqWndjWHpOb1lGYnh0aGNQZUY3ajU4eXpTUEJ1UFFEMkFaT3BRc3ZMVXplb2QxY0E0eDlEdnB3OVBxeWEzSkIxYUdOdEVUR0FqTVRVOHg1ejJjRHRBWHFhWXpVK1d4S1NEUW5qc3FLK0xhdm9iTkx2YXZqK2xVRkl3Yk1kMU9Yc1FOU2RPY2dhNHhYeHpxWUFpWXUvZkRQN2lDQlMvN1RuMWFBT0lvb2JPQmovcHVFMUREMEFkRVpBN3hpU1BJbjhBbmRBbVhtdDlTSElpUTBpcnlPWjhQQ1pzd09odDNIYitOaGV1SlhCYWlBRSs5UGtxYmowV3dZQkZIWXhlSFFFWmhMTXdiUEFZVHVELzBQbUhyTFl6V3BPemI5bHZGY3B3VE1QTFdsVlNjbkozbVd0MmRyYllHV0NGTlYrQXYyU01OVzF0TnpML2xXWVh3ZGl2bWRQYWp3czl6K2E5Y2FNSlFSWWlDemFaVHJpbTRpQmovZmREY1UzUHZHRUpyZGZUbTJRbWJ2S1lqdDRtM3lBbTBXbWpZK2RJZkJQNUpEZFIrZmFPME1KYlRVM3BNU1RKdmJYOWY2M1Z4T0JaNjNyZ0dKVkhlTit2ZW9WdmJ5LzJYeUtWUWJYSEtmQ2I5cjh0Wkk0Mk1sQ0hBdHJzbEdUaVVBN0xMUWFVVlpUVENxcGY0VzhMdk0yaEoraC9CTjFJZGlNc2h5MmhMb2JDdjk0TmwrVDFQc1l4alU5aUtTcmt0OG53eGo2V3NFejlJOHVJMlFkSGNwZStSODBoY1Q2NHRpSGZCa2FiMlp4N3NaQ1A2TlZqRC9MYk5EUDdwalk5aHZWQWpxNWJucjdQQ1RueEZtYW1GTjUrcklVWTBieHRpRU9HajFDeGV6N3J5Ym52VlJ5QjVDOFRPSmF2UlQ0TEhyek5aVzRPOFNhUVYzVmpXMGY2dzU1NFlZRHZuS0UxSzYxZ2x6eDVrWU9Mc2J0eEt3NVhmd2VocEEySVNnbjc3OWhHV0drak81QkV0N1QzWWNsSEpsZzFIcHU1YTNRdmEwVHEzWXBOSEljWTFQZ2VCMlRVQnpubVRDWjVPbDVKSndQNllUY3YvZWRRVUtVbDBIYVJnb29TNDBTWkVsN2hhZmZzYWtqS29pZmV1S3doWE9mREN4Sm9NdXVJanBwSzNSNmJCN3liY1FDTjJHMFczOUR1UkJvSitzZ1Y5Q0NwN3NmUnY1OE5tZnVXNHdMRnIydnRITlZsdnRkWEtNcG5qTEJvWno1dGEyTFRXbFlsaktuRi9DZUtsakVYTGErRG45WkVKR2x1a3NZZFFEREdFZlBvcWUwZXhUR08vZXhmR3JNMERZMW9iWXJ0OFZ5Q2lIK3JPT1NMSVcxV1pIb1NXdmxZaFNYbitSd0Q4a1ZqUE5CaCszZWpOMU1INEIwWi9nVmRmcXFXYWVmTW4vUEZzNENvL0dNUDFHeEhLTmNsZVIzZllpalhVdWhzcU5ibXZocXRUcG5Ya2QxeklrTnFBZ3dLTGdpSlNlRkVSNklLSGpEUE9iRjJSNGFKRTB3U3lla0hDNjNWQ3ErdjIxc096eXRXSEo2eTdadHh1UlZOSHZjYmsxVDBVSjVxZXQ2US9qcjNZMm13ejBSQk1pVXEvMjZ0RDh1K0c4TjJzOGR6c0gyLzlFcHhhNWRPYWRwRUNXQ3hoRDdtNGVmbmVVK3RGbVVhUFRURmpFWU05K3l3WHRCbUEzbytkeHVmMkJKMXJCNmtZZXBRTmRNNG5VVUZRYkgzL3hKTlE1TFdubVYrS1lnSC9mS1hnOUIyaHM5SFpOMXM4WCtjckJQby8rSUZUOVEwbGgxRkN0VzFDV29CclMxeUViakhjVEFxR21PVDhySmNndGhGRVc3ZHRZSFg1VUJZRXBSQzFOT2pPTjhoeXhtdU16OGhFNU9tSUo3b1RkMUxoeUt4TmNBVlRaWHhNOUJIN3ZFaXk3UFM0c2xpN3RFVXduTlhBNUpwWnFEVEx5anBNd3N5djk5YUlib1haTDlwaFZ0QzRrZGt0aTlid0xtNEJZeU9wcFpWakYwRzl4MEpxbEpjT01WVi9YMnR0ZWUwc2tralVqZmE0VkI5MG5FKy8wYUl2SS92d0ZZUHNGVDVOY0RWSDdQZDB6bERNZnZUcTgvVzJEbHhkRTZrUnpGUldYcjhNdDhWTFBldVp1UDFoZ1JBS0tsb2RZc2NFVjh6UVY1bGNjdnFGMmdHY01YbCtnUk52aXMvS3VJclc2QjlUSkMwanJGWDFXQUZKZUdURXBTTGhSTmlTdWVDb0VCNUFKcWN0Q0VmMkdCVUlkWXVjM2JwRjRiNWVqcz08L3hlbmM6Q2lwaGVyVmFsdWU+CiAgIDwveGVuYzpDaXBoZXJEYXRhPgo8L3hlbmM6RW5jcnlwdGVkRGF0YT48L3NhbWw6RW5jcnlwdGVkQXNzZXJ0aW9uPjwvc2FtbHA6UmVzcG9uc2U+ \ No newline at end of file +PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9InBmeDFjYWMxMjlhLWFhZjItYzBiYi01YmQyLTdjNjY5Yjg1MDlhMSIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTQtMDktMjJUMTY6MzM6MTVaIiBEZXN0aW5hdGlvbj0iaHR0cDovL3B5dG9vbGtpdC5jb206ODAwMC8/YWNzIiBJblJlc3BvbnNlVG89Ik9ORUxPR0lOXzk5ZmI3YjlkODk2MzQ3ZmNlMTIxMmQxOTI0ZDQ0NzNiNTQ3MmYyYTkiPjxzYW1sOklzc3Vlcj5odHRwczovL3BpdGJ1bGsubm8taXAub3JnL3NpbXBsZXNhbWwvc2FtbDIvaWRwL21ldGFkYXRhLnBocDwvc2FtbDpJc3N1ZXI+PGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+DQogIDxkczpTaWduZWRJbmZvPjxkczpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+DQogICAgPGRzOlNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNyc2Etc2hhMSIvPg0KICA8ZHM6UmVmZXJlbmNlIFVSST0iI3BmeDFjYWMxMjlhLWFhZjItYzBiYi01YmQyLTdjNjY5Yjg1MDlhMSI+PGRzOlRyYW5zZm9ybXM+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNlbnZlbG9wZWQtc2lnbmF0dXJlIi8+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjwvZHM6VHJhbnNmb3Jtcz48ZHM6RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3NoYTEiLz48ZHM6RGlnZXN0VmFsdWU+TUg2NUJ4SSt4QkFXNUpmUS9KdVdMVmpEL3lzPTwvZHM6RGlnZXN0VmFsdWU+PC9kczpSZWZlcmVuY2U+PC9kczpTaWduZWRJbmZvPjxkczpTaWduYXR1cmVWYWx1ZT53bG1MdXZ0dUc4bjEyRm9MOWlJRWFiekxxTUxJNEtSYU04VlM1bzU5NmtHdjhoclpvc25BQVNUYS9vak0xdkhlMnNMcmF0Q2NNYkhwVFJFaXNZcnpmUkhaVkk5bVNBaTU3SHVLZitsSnJla24rL1VNdndJWFVDUGtXcUIzT3orWWdxM3ZKOHNsaGM3THlVTG5haXZORmVkVXBKMFBPVkt4dU41TEdvRmN2SU09PC9kczpTaWduYXR1cmVWYWx1ZT4NCjxkczpLZXlJbmZvPjxkczpYNTA5RGF0YT48ZHM6WDUwOUNlcnRpZmljYXRlPk1JSUNnVENDQWVvQ0NRQ2JPbHJXRGRYN0ZUQU5CZ2txaGtpRzl3MEJBUVVGQURDQmhERUxNQWtHQTFVRUJoTUNUazh4R0RBV0JnTlZCQWdURDBGdVpISmxZWE1nVTI5c1ltVnlaekVNTUFvR0ExVUVCeE1EUm05dk1SQXdEZ1lEVlFRS0V3ZFZUa2xPUlZSVU1SZ3dGZ1lEVlFRREV3OW1aV2xrWlM1bGNteGhibWN1Ym04eElUQWZCZ2txaGtpRzl3MEJDUUVXRW1GdVpISmxZWE5BZFc1cGJtVjBkQzV1YnpBZUZ3MHdOekEyTVRVeE1qQXhNelZhRncwd056QTRNVFF4TWpBeE16VmFNSUdFTVFzd0NRWURWUVFHRXdKT1R6RVlNQllHQTFVRUNCTVBRVzVrY21WaGN5QlRiMnhpWlhKbk1Rd3dDZ1lEVlFRSEV3TkdiMjh4RURBT0JnTlZCQW9UQjFWT1NVNUZWRlF4R0RBV0JnTlZCQU1URDJabGFXUmxMbVZ5YkdGdVp5NXViekVoTUI4R0NTcUdTSWIzRFFFSkFSWVNZVzVrY21WaGMwQjFibWx1WlhSMExtNXZNSUdmTUEwR0NTcUdTSWIzRFFFQkFRVUFBNEdOQURDQmlRS0JnUURpdmJoUjdQNTE2eC9TM0JxS3h1cFFlMExPTm9saXVwaUJPZXNDTzNTSGJEcmwzK3E5SWJmbmZtRTA0ck51TWNQc0l4QjE2MVRkRHBJZXNMQ243YzhhUEhJU0tPdFBsQWVUWlNuYjhRQXU3YVJqWnEzK1BiclA1dVczVGNmQ0dQdEtUeXRIT2dlL09sSmJvMDc4ZFZoWFExNGQxRUR3WEpXMXJSWHVVdDRDOFFJREFRQUJNQTBHQ1NxR1NJYjNEUUVCQlFVQUE0R0JBQ0RWZnA4NkhPYnFZK2U4QlVvV1E5K1ZNUXgxQVNEb2hCandPc2cyV3lrVXFSWEYrZExmY1VIOWRXUjYzQ3RaSUtGRGJTdE5vbVBuUXo3bmJLK29ueWd3QnNwVkVibkh1VWloWnEzWlVkbXVtUXFDdzRVdnMvMVV2cTNvck9vL1dKVmhUeXZMZ0ZWSzJRYXJRNC82N09aZkhkN1IrUE9CWGhvcGhTTXYxWk9vPC9kczpYNTA5Q2VydGlmaWNhdGU+PC9kczpYNTA5RGF0YT48L2RzOktleUluZm8+PC9kczpTaWduYXR1cmU+PHNhbWxwOlN0YXR1cz48c2FtbHA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+PC9zYW1scDpTdGF0dXM+PHNhbWw6RW5jcnlwdGVkQXNzZXJ0aW9uPjx4ZW5jOkVuY3J5cHRlZERhdGEgeG1sbnM6eGVuYz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjIiB4bWxuczpkc2lnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIiBUeXBlPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNFbGVtZW50Ij48eGVuYzpFbmNyeXB0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjYWVzMTI4LWNiYyIvPjxkc2lnOktleUluZm8geG1sbnM6ZHNpZz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+PHhlbmM6RW5jcnlwdGVkS2V5Pjx4ZW5jOkVuY3J5cHRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNyc2EtMV81Ii8+PHhlbmM6Q2lwaGVyRGF0YT48eGVuYzpDaXBoZXJWYWx1ZT5VeWcxMW42d0E3MGFoMGxFb2xJL0VWelpjVzFuM3RpUjluRFNnR1FXY3RwK2l2ektBRTg4ekl0UHFWdnlaMjJFbmFjU2tIbjUwRlNWUk1ZYm91bzhYTFZtSzVndzdQdW9sZGVVRmsxTmNuNnpiaUowV1hBSXNUNG9vOTFDcExXR29hN3FaRjNSL20xVjZmcjBKYTBZemhVV2lvZUhtMUQ0TXRncUJSbG4zTlk9PC94ZW5jOkNpcGhlclZhbHVlPjwveGVuYzpDaXBoZXJEYXRhPjwveGVuYzpFbmNyeXB0ZWRLZXk+PC9kc2lnOktleUluZm8+DQogICA8eGVuYzpDaXBoZXJEYXRhPg0KICAgICAgPHhlbmM6Q2lwaGVyVmFsdWU+clhyUGQ3WUt5dk9VZThpZ2NPeE1tdHl5aFUyYndNbU5GTEJSOUtvdm9yT1BEb1pBWGxtcGVyNUFRT2FjM0NkTzlBbDNKcWl0Uk1kYW44cU51M1RibCtsQ1YrY1ljTG5sdUlpYngra20yT0NyMTBsbWZpRWlhUGdWbzJ5djh6ZFZuK1RxZkJINUVCTG1BS09vUXIycmdCeTFvMVQwYXRrRW5MUzZnMGFLRUtJOXFzSGNaRVVkSkNYTU03NUlYQzY5dm5BMis5MEljQ01GY0JRT3B3aCtBRWhrWVY2ZmhpMHZKRDN0YzIxOGJiQ2xla25NeEZjYUdvUWRJZHd0U2tpUW1NTHRLY2RKSncrakVreDJOQ1d3N1BxM3Q3WkJBL0NKOHNVRERZNHFnWjJXRVh1TFRXZDVNb0xoZ1hwczUvR1lKZUtnVG9VNURVK2M2R3I0bllhVzZ0bkdsNHVQeHpFeDQ1czYxL1pjUkU5T1UwSHVseU9uL1ZyRmw5b1dxMXpERjFzQkMxdFI2dnhSS3lqYWkvazRsMnRCZit2ZGJmc21hYml3YS9MdzdUMUcxbVFVdWtZVk42SkU3YmNxM0N0Vm02VmxUYmxNTTNqSFNQUWVTTy9KbUZEOU9QRUNhZ3N1akZQRmNDLzVKa2VyK0YvRHI0M09IYy9odFBNTzlLMjRTUThzMi9ycWJGWXlIcFVrME0zN0RXWW9YRDVjMDBEY05qUjRQM1NDdlhibC9XSVpWYnd5MksxMnN0ZmpvcHdCT2taNWJLQVNXNkFYeWI4OHhHc3JreWFBOTI3ekQ0Nnp6bUNRWDMrZWNmQzFQU3FmVjRPRXQwSStiMzY5OS9FZm9ETXFzd08wWkpGb1JJSmR2U0RicnBwVmZ4WGFqZWhVSllDZUhNdVFZeFZmK202WW5VUHJaKzBLRzVzRmtZR2V5SEdYc25sUTVFcS9GQzFNRXRNS2IzemJHUllGTTZ1RDhMZ0JJZk1jNjlsL3VMKzhLcjZ0OFVvUmJwTm4raDJDOURzQ3A2WE5LdE5JYWhydE0xYWlkK1dtK2d4TW0vK1dNdGlzajVBRXA2TjlQUjE5WVNMdndjMjZhdnFWTjRRLzBMN1QrSEtqWlNLSG4vMCtmQWJMYUo3bGZLZVJYazJVTzhMUDFJei8xQVp5ekovRU5GMDdUK2xCci8xQlp5OHpLVFdlM1dkenF4MXhlN29JbU53RjJZbHpwc29WL29QU211M1VoRTNrVFJuV20rUlVpM0R3UVZqVDNSbWRoR1Vmb01FMDVrQWhlQUJhbVNWVVJVd2xLSC80b04vbDBNZEVWOElXdE9lOGQyVTdCQ3N1WWlTQ3UxQ1kwcmZwWEQ2U2RyNURseFNtWk9zTUM1NUVubE44MlowU25mUitUNXQrRmRpa2VmM3IwYzlQSG51dlpLeVd6OXZlV01Pa012SVFlQ1duN2ZTYXFpdmQydUxDaVUyYnpCWG1vdVBaNTdQMko5TFF6RjJpQ3hvY0lLOFhiRTJ5QmsyczRuUitjckt4L2lLTzJzR1FBSmtSMkNJRVhDSGdwUWVwSmdzbml0Qk9FVUhnSGlEVUE4enhuTGEzQkNtSVdyNjA4cnpZOEt5SG1ZZFFuVjhiajYyVnhFRmZvN0hMMVpRQ2xPNFpWdHdZVGhlUE1DQmtWcU5iTDhnVm8wUmM4cHp4b2ZQUG9hVXJhbmMwUGtabm1hbG5RR2lvQnErbEhRTktxR3BxRXZuNCtCTEZUQ3U1MkcrUnJTZXkvK25CdG9SVXdCMjJnOGZDRjl2ZWs1N3MzZ0gvQnQxUkJVb1VXUXpPeDdiK2pGSmYvTHMyYkVacVFHT09hWVp5ZVFaN1pxREFRV1VEdXV0NHpqYklrejFnWlFUSnlFMllCdE0vL2hYYi83MmZyWjkycnV3N1Z0dXhOY2NIekZDVzBmbzZWV0U2Q0NEdFlQaThnTlFnbHBlM3FJM0dyaG5zRytiRzd6dmRWUXJteXdTblh6aHNMOGFteDRkZytETGZQS3FXZVplSm5ocnY5VXl5TWVnYkM2K1cwWGROSm15dGRJcEhYTWs0ajd5QTBmOWtLbGRjeEpmOHh6b3I4MkF1UTlsWjQvdXB2T1BRdUk1L2IvTjRzdFFDK0kvZTJvUnlIWENlNUE0ZlpENjJ3UFphNTRsQmNCRWxUU2NFYWpoTEVaZ1dEY25rYmRaNFk4L3ZVQ29UUWp5QUdJS1QrdlI4cGtUbXVxd2g5WXZyZDltdGwxS2FTQ3ZNRUc1R0tlZXVMdWNocURUNlJteUs2YW93TFAyWFNFSk1WczY5WFBWQTBiNWxNWlIzWmQ4YlhvT1NkWElsbHhSbnNPNHNtSlJiOCtyTFhqaXpaSmd5TWYzVVNidkJQalFqTjVDS3ptNi9BSDRObHMrelRYWmVpekNYc3NCOE5kZXBTZmxvT1hxdTl1UU54cTI3RlpNaFZ1UG9CVTM1MG9lZzBTUHhVL2RGWUpEcEFGclZhVmtKRkE0d3VUYTJTYW5zaFc0MG4xM1p1ZGRsdkZMTkZaNGxYd1pFY3NhMm9DRWJ1aERscWFzYThLVnUrVVM4Z0pibWhERisyQlR1RjNOcTJrWlQrWTZxanFNTWRsVytpbHlsUXFVdG5vWWNMdklwUTBkTzdtM0NwLzB4eGdXeGJGVm9BMjB6MGVKZEk2T2N2a2VIYkxGeW5ydWV0ak5IdUROODNxMm9mcUVvNjZKV2tPRXdGYlBsL1BhZzFxc0VURGJVaCt4RmQ5cUdoUERqd0hCcCs3TS84a0hlbDhKTUZRM2t3b0ZZdFVDMjBGcmJ5REc1SkRiV1FhTzBPM1M0UGRBRkRrRDk2VDVYN3MrbThEN01GYkl0NWI3di9iMVdWZG9JVkJYSjRtZmsvelJ4NjBWUHFpSFVoUkM1Q3BoWXFGTVN3aWZXMFkyN1RuM25ucWxNdGFFcUV5RFlMRlBkdm1sWFJqc1MwQW5zSm03ejliK1VTTUM4ZXZMdnVMMVc4V0ZkdHpielZJUGRwYmJrQm9BMFlSZldOeTNVMUhqd3ZmcXFxQytDOFpXcUY4RVJSNFBpM3VVT2NsOUl5dFVNcDdqTVNDd2wxQXB2aElWRTdSQm1kbVdMSTlMbkhocjFxcGJOWE5remtiNS9aM2YyQnZkbEZ6eU1rZkg3UVdVMmRuaHN4QjRURThIb2xYSHoxQ2xUSXRlU3BqRG5ITHlTbTVjM0h6ODAvNkIvSWxvYTE5c0hJN0F1V2VHdVFFYUVkSWhOMUt5YlVoUWs3Tk50UHJXQVBkbng3NC83QjFIR3Q2N2dPb05Zb0VWWEZvUTJaVTUvQ1N1ZUFRbTdDV01td0tORHFXSGd1cHNwZWgvTkdHekYrTUtPOXVSWmJWbktLL3l1NXJCVDdYNGE4QnRCbGRuQTJ2UGt2Y2pROEpUWkpES00xNmxxR09ITGJXVUpiNDZJTVg2Y0RnQTd6blF2Z0hyeU5CRnl4U2orQXBoSitQRHEzblhKeFFNc1d0dlVWaFBiZEswZkpSbUNTd01UWHloYXEwMHpaMGtCL2o0NmFJN2I2RXFvR01Ka2JrdzJaUlpkelE2Ty9SZWUyWDRtSWkyNllVQUk3bnNNbk1HNnpwNG1UMFdCNFhGcFNUanZtYm9SRithaEhHR1VLc2JEQjEweUdCSVdCV2xLV1RzMyt3Sm5ZT0VadXpaMU1OTzVOWW5abDllczc3eXp6QWFIUy9xeVNDNW1oQmg4WjYzYklPUmFIdmUyaVJiY2Qvcm9laUJPajhXTnNkdnBWOXFGbU9WcEdkUWdlaGtsd2s3alRIZHBxRC94NjI1OFA4KzhxQnEzK0g1OHpnV0dLcm5OaERhbkRhNWFyblBqR1RNQ296TE5ZU2M2eHJZTWhlcGdUaUhPaTlFYnBwUS9UYnNSOWppU2VYZWtRTytjV2pDVFFvcEFoaU9qc2x5Z0R3V0sxajUvclVUM1BaVVlrUm5rNGYwODB1b3poc2JybkxydCtUME9WZDYwcnFGYW93SFBXLzZqcGg2OE5Jait2Z21Ba0pvNDNqbTJwR2JwcGNERWpIa05FYjdiNmZoL2RZSEZjdmVBM3lsTFc3T0VKSVl6Tm1GWEtrdklDVmNMZmw2WDZjRWtGTHUyVmFIekRCVllDMTdFWFJyTXYyaEN2VUhmaDFLMXBNMXVOWmtNVmpyblU1ZU9jNDRJZEtEOEg5N1FjZm5rRGhadGxpbHZGR2JVTGNPWTRqcHkya3dkSSt6TTFxMktHazN0S0Z5aURHdndBQXhXVC9zZHl1YTFkaHAwV1lybGpwSmpEVzVMSmRYYWN2bk1heWRlallGd3E5a083OTB1eit0YjlNMTQxQktPYnhRSE1VZENyWktNazYzbWtGeUlTMzhHRVhjSGlKWHkyR0RpZjFQa0dYVzVyUHpiRHc2YlBDbS9oUGJaWmI0d3hiT3ZiWmUyeHVxaDAyNmY5RW13TzZyaGFhOGVITytJRVJ5WmZwNGd0S2cycElqS3JTQjlvSU52UU4xMVpKQ1ZsdnpURGd1Z25RL1lGVVlNeWZpTmR6QURVRVg4d0VhT0VYS1loSHFaTzdXSEV0ak10N3lXdERqaVBhbGQ1WGw4Tnl4RVBldzJHdEZrNkI5VzhuMmhwUGlaWTBpK1BGaEpEallBQ2hPYVlSM1ZPVHpSYTB4ZFNtMHBDcTVVellRQzhoakdRR3I4SER2UEt4TW5Tb2tvamlsYitxZlZ1cW94WENhWVFLOVZuUktTUERsYzVPRmJhbkNKZWQ4V2ZaRXhpWXBqZUxEMXV2NXBpZVU0QndZaWZENGw1OWdWQ2hCczh0UUJEayswZmw2TEY4RlBCcEtuNStySndYRENvdjMxZ1U3eit4VjNZVkppclh6VFZrTzk0S2lnWFpCYlZ2Q0w3Q280M2toUjVrL1I4eWl2Ym9EcHY4TThSQlNGSUg3ZFA0RkFBQjZrdHRuVWl4Zjcvc28zMGNuMVJ5a3djVFdMTzRnRlZuTWRtTDM1R1M1ZXFMYjNvMmNtdU5tSXV3ZGM4Ymx4REx0TGhjQ0FMYmkvZktXYjhtSmdwdEczcThHNm13bkwydXJVcVJQM2ZNVFdJNW1DZUR5dkZEMENFbHBSeHRuN3ZFd0FZTlI1cGVlSkY5UzE4WWJuL3p0a3lTL2xaNEU4OVUzTGNyQ0hROGJaOW9MTmQxUVc4MVVSMm9aVE52NkZHZlZxenYrbyticFd4UytpT1RST0huNEdEdmtsVlNBNytubUxhSHFBWnREeFZ1ZEh1eVdyYmtuNUU4d2dSckRKTy9qYkxvRGYxRy9RcXVlOXhNRFdIV0J0QjVFZzFRRUVsejhFRVZ2K1A5aFhVWXF2SnRBcHVxK1oxT0NCWDBPeFlybCtBcENObVUybXRqc3hwNGprYWtpSzVVb3FWaDIzb0tkeFczNkI4dEVFY05TdDV5UnVBc3hsZmxjVUNGK0U1QU5vdVV6UnRtYkpDZS94ZUxPVEdXUHk3bVpRVG5PcjN4K1RWN0RkNU5vVUU0TlN5d1JTS1hieTN5TTVlV0l0blRxQTdiNEVjNjBxeVhmeE4xT1cvaDdUL3RLR3JrRGJoRllDQktrQzdwcGlpc3ArSkhxWExzTXk2bnNHY1U3aDlzQWhaQWFIWllla3lSLzBQYU5MTjVJTTk1bWVVZkpRNDh2OTlaRWpYMTN6enJtY2pNZG1WOXZCWk5yd1l1NXRHU2NkMWdtbUlOR1pIcGlnTUJ4YnRMUy9Mb2MzTlVzemozYlo0QmpqY0NPQmsvdlEvTE9CMGFmOWY1bzV4SSt1Nk10RklmWDE3MS9Zbjc3clY5Y0dZek1xZGdHTzVoZGV3dG5FTG9uSldGTmQwNlRlZlRMRlV6WVNwY3NQRHpoWWxJNTNDQ1E3cFF5Q2JiTDVLQ1ZKQWZEVHRyTmlieXFPZFlDQ1dzYXloczVqY01jS05PQk1ITmdzalFqQzVGZUtGc0JJdk9DbEM4MUZ2VU5TdXI4aUh0bUVzYnVoRW1XeTFTaXlDMWkyRkRudThWbHlkVmJlV2ZHRFdYK284c1lGanZpaEFSeWtqR3JSUlAvc1ZsWDBSK0pnb0VTWmpyRjk2QzVhNkxxWmVITTVkUDdqWmtWeDRaZnVmbis0Qnp4dlVHczJuZkk1aXdESGtyZz08L3hlbmM6Q2lwaGVyVmFsdWU+DQogICA8L3hlbmM6Q2lwaGVyRGF0YT4NCjwveGVuYzpFbmNyeXB0ZWREYXRhPjwvc2FtbDpFbmNyeXB0ZWRBc3NlcnRpb24+PC9zYW1scDpSZXNwb25zZT4= \ No newline at end of file diff --git a/tests/data/responses/valid_response_without_inresponseto.xml.base64 b/tests/data/responses/valid_response_without_inresponseto.xml.base64 index 120a6c3c..64fcaba0 100644 --- a/tests/data/responses/valid_response_without_inresponseto.xml.base64 +++ b/tests/data/responses/valid_response_without_inresponseto.xml.base64 @@ -1 +1 @@ -PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1scDpSZXNwb25zZSB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiB4bWxuczpzYW1sPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiBJRD0icGZ4MDVmM2NlMTAtMTYxNS1mM2VhLWE5ODgtNjBlMzgwYjMyOTlmIiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAxNC0wMi0xOVQwMTozNzowMVoiIERlc3RpbmF0aW9uPSJodHRwczovL3BpdGJ1bGsubm8taXAub3JnL25ld29uZWxvZ2luL2RlbW8xL2luZGV4LnBocD9hY3MiPgogIDxzYW1sOklzc3Vlcj5odHRwczovL3BpdGJ1bGsubm8taXAub3JnL3NpbXBsZXNhbWwvc2FtbDIvaWRwL21ldGFkYXRhLnBocDwvc2FtbDpJc3N1ZXI+CiAgPGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+CiAgICA8ZHM6U2lnbmVkSW5mbz4KICAgICAgPGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz4KICAgICAgPGRzOlNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNyc2Etc2hhMSIvPgogICAgICA8ZHM6UmVmZXJlbmNlIFVSST0iI3BmeDA1ZjNjZTEwLTE2MTUtZjNlYS1hOTg4LTYwZTM4MGIzMjk5ZiI+CiAgICAgICAgPGRzOlRyYW5zZm9ybXM+CiAgICAgICAgICA8ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz4KICAgICAgICAgIDxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz4KICAgICAgICA8L2RzOlRyYW5zZm9ybXM+CiAgICAgICAgPGRzOkRpZ2VzdE1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNzaGExIi8+CiAgICAgICAgPGRzOkRpZ2VzdFZhbHVlPkRjUWNDL1BoS05qRTlLa29YRXZZRlhXMHZGdz08L2RzOkRpZ2VzdFZhbHVlPgogICAgICA8L2RzOlJlZmVyZW5jZT4KICAgIDwvZHM6U2lnbmVkSW5mbz4KICAgIDxkczpTaWduYXR1cmVWYWx1ZT5xVjcvc2YvVEt1S0x5allaMGNDSlhCWnZSYmF1RXNoMXQvaEtJeStpVHJRSjYxWG0rMXZDcEtvVXdleGNuL1ZpCitsemZlaHZjL2tDMjE5TjZVTUUxZnRLTDY2OSsxYkpFb1NLejQrN2VhWi9XTFdYL0hRYndMVmh6dlh3bWdMQVAKUEhLNmZJZHpocGRkLzRydjlXVnpjaGoveGcxWVNkaXFrcnU3YUhhS2FEOD08L2RzOlNpZ25hdHVyZVZhbHVlPgogIDwvZHM6U2lnbmF0dXJlPgogIDxzYW1scDpTdGF0dXM+CiAgICA8c2FtbHA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+CiAgPC9zYW1scDpTdGF0dXM+CiAgPHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9InBmeGI0ZWM5YzhhLTQ4ZWItZmRhMi03Zjc0LWZhMWExMDVhOTlmZSIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTQtMDItMTlUMDE6Mzc6MDFaIj4KICAgIDxzYW1sOklzc3Vlcj5odHRwczovL3BpdGJ1bGsubm8taXAub3JnL3NpbXBsZXNhbWwvc2FtbDIvaWRwL21ldGFkYXRhLnBocDwvc2FtbDpJc3N1ZXI+CiAgICA8c2FtbDpTdWJqZWN0PgogICAgICA8c2FtbDpOYW1lSUQgU1BOYW1lUXVhbGlmaWVyPSJodHRwczovL3BpdGJ1bGsubm8taXAub3JnL25ld29uZWxvZ2luL2RlbW8xL21ldGFkYXRhLnBocCIgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjE6bmFtZWlkLWZvcm1hdDplbWFpbEFkZHJlc3MiPjQ5Mjg4MjYxNWFjZjMxYzgwOTZiNjI3MjQ1ZDc2YWU1MzAzNmMwOTA8L3NhbWw6TmFtZUlEPgogICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+CiAgICAgICAgPHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgTm90T25PckFmdGVyPSIyMDIzLTA4LTIzVDA2OjU3OjAxWiIgUmVjaXBpZW50PSJodHRwczovL3BpdGJ1bGsubm8taXAub3JnL25ld29uZWxvZ2luL2RlbW8xL2luZGV4LnBocD9hY3MiIEluUmVzcG9uc2VUbz0iT05FTE9HSU5fNWZlOWQ2ZTQ5OWIyZjA5MTMyMDZhYWIzZjcxOTE3MjkwNDliYjgwNyIvPgogICAgICA8L3NhbWw6U3ViamVjdENvbmZpcm1hdGlvbj4KICAgIDwvc2FtbDpTdWJqZWN0PgogICAgPHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTQtMDItMTlUMDE6MzY6MzFaIiBOb3RPbk9yQWZ0ZXI9IjIwMjMtMDgtMjNUMDY6NTc6MDFaIj4KICAgICAgPHNhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj4KICAgICAgICA8c2FtbDpBdWRpZW5jZT5odHRwczovL3BpdGJ1bGsubm8taXAub3JnL25ld29uZWxvZ2luL2RlbW8xL21ldGFkYXRhLnBocDwvc2FtbDpBdWRpZW5jZT4KICAgICAgPC9zYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+CiAgICA8L3NhbWw6Q29uZGl0aW9ucz4KICAgIDxzYW1sOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAxNC0wMi0xOVQwMTozNzowMVoiIFNlc3Npb25Ob3RPbk9yQWZ0ZXI9IjIwMTQtMDItMTlUMDk6Mzc6MDFaIiBTZXNzaW9uSW5kZXg9Il82MjczZDc3YjhjZGUwYzMzM2VjNzlkMjJhOWZhMDAwM2I5ZmUyZDc1Y2IiPgogICAgICA8c2FtbDpBdXRobkNvbnRleHQ+CiAgICAgICAgPHNhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6UGFzc3dvcmQ8L3NhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+CiAgICAgIDwvc2FtbDpBdXRobkNvbnRleHQ+CiAgICA8L3NhbWw6QXV0aG5TdGF0ZW1lbnQ+CiAgICA8c2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+CiAgICAgIDxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJ1aWQiIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiPgogICAgICAgIDxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPnNtYXJ0aW48L3NhbWw6QXR0cmlidXRlVmFsdWU+CiAgICAgIDwvc2FtbDpBdHRyaWJ1dGU+CiAgICAgIDxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJtYWlsIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj4KICAgICAgICA8c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj5zbWFydGluQHlhY28uZXM8L3NhbWw6QXR0cmlidXRlVmFsdWU+CiAgICAgIDwvc2FtbDpBdHRyaWJ1dGU+CiAgICAgIDxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJjbiIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+CiAgICAgICAgPHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+U2l4dG8zPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPgogICAgICA8L3NhbWw6QXR0cmlidXRlPgogICAgICA8c2FtbDpBdHRyaWJ1dGUgTmFtZT0ic24iIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiPgogICAgICAgIDxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPk1hcnRpbjI8L3NhbWw6QXR0cmlidXRlVmFsdWU+CiAgICAgIDwvc2FtbDpBdHRyaWJ1dGU+CiAgICAgIDxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJlZHVQZXJzb25BZmZpbGlhdGlvbiIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+CiAgICAgICAgPHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+dXNlcjwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT4KICAgICAgICA8c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj5hZG1pbjwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT4KICAgICAgPC9zYW1sOkF0dHJpYnV0ZT4KICAgIDwvc2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+CiAgPC9zYW1sOkFzc2VydGlvbj4KPC9zYW1scDpSZXNwb25zZT4K +PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9InBmeGRkMzRiOWJhLWUwNmQtZmQ2Mi01NmI5LTM3ZmExNDA4ZDdjYyIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTQtMDItMTlUMDE6Mzc6MDFaIiBEZXN0aW5hdGlvbj0iaHR0cHM6Ly9waXRidWxrLm5vLWlwLm9yZy9uZXdvbmVsb2dpbi9kZW1vMS9pbmRleC5waHA/YWNzIj4NCiAgPHNhbWw6SXNzdWVyPmh0dHBzOi8vcGl0YnVsay5uby1pcC5vcmcvc2ltcGxlc2FtbC9zYW1sMi9pZHAvbWV0YWRhdGEucGhwPC9zYW1sOklzc3Vlcj48ZHM6U2lnbmF0dXJlIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj4NCiAgPGRzOlNpZ25lZEluZm8+PGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz4NCiAgICA8ZHM6U2lnbmF0dXJlTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3JzYS1zaGExIi8+DQogIDxkczpSZWZlcmVuY2UgVVJJPSIjcGZ4ZGQzNGI5YmEtZTA2ZC1mZDYyLTU2YjktMzdmYTE0MDhkN2NjIj48ZHM6VHJhbnNmb3Jtcz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PC9kczpUcmFuc2Zvcm1zPjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjc2hhMSIvPjxkczpEaWdlc3RWYWx1ZT5oc3ZTSUJwdDcvMjZIeGt3OGNEcXBZYUJhSTA9PC9kczpEaWdlc3RWYWx1ZT48L2RzOlJlZmVyZW5jZT48L2RzOlNpZ25lZEluZm8+PGRzOlNpZ25hdHVyZVZhbHVlPmxWbEZhdGZUWTN2Y1BNRlZ6VjNpWXp4aXliL0FLaTZLOFl2VTJmZEo2L3N1RXdRcHdZZlRyYXRLYitmN3kzVFdySGN3SW1vanRJbE9xaXdNdENxOHZhTUI5WTVQbndNQ0dYalRLcUxDZ3N4aktJNFBtbHpMdzNFMDB5aERsTEVKSjgwdFBBem9WMmxlQmFqOWMxRzhJUVZEMFA3eWxrZGxtbFB5ZmF3ZjdCVT08L2RzOlNpZ25hdHVyZVZhbHVlPg0KPGRzOktleUluZm8+PGRzOlg1MDlEYXRhPjxkczpYNTA5Q2VydGlmaWNhdGU+TUlJQ2dUQ0NBZW9DQ1FDYk9scldEZFg3RlRBTkJna3Foa2lHOXcwQkFRVUZBRENCaERFTE1Ba0dBMVVFQmhNQ1RrOHhHREFXQmdOVkJBZ1REMEZ1WkhKbFlYTWdVMjlzWW1WeVp6RU1NQW9HQTFVRUJ4TURSbTl2TVJBd0RnWURWUVFLRXdkVlRrbE9SVlJVTVJnd0ZnWURWUVFERXc5bVpXbGtaUzVsY214aGJtY3VibTh4SVRBZkJna3Foa2lHOXcwQkNRRVdFbUZ1WkhKbFlYTkFkVzVwYm1WMGRDNXViekFlRncwd056QTJNVFV4TWpBeE16VmFGdzB3TnpBNE1UUXhNakF4TXpWYU1JR0VNUXN3Q1FZRFZRUUdFd0pPVHpFWU1CWUdBMVVFQ0JNUFFXNWtjbVZoY3lCVGIyeGlaWEpuTVF3d0NnWURWUVFIRXdOR2IyOHhFREFPQmdOVkJBb1RCMVZPU1U1RlZGUXhHREFXQmdOVkJBTVREMlpsYVdSbExtVnliR0Z1Wnk1dWJ6RWhNQjhHQ1NxR1NJYjNEUUVKQVJZU1lXNWtjbVZoYzBCMWJtbHVaWFIwTG01dk1JR2ZNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0R05BRENCaVFLQmdRRGl2YmhSN1A1MTZ4L1MzQnFLeHVwUWUwTE9Ob2xpdXBpQk9lc0NPM1NIYkRybDMrcTlJYmZuZm1FMDRyTnVNY1BzSXhCMTYxVGREcEllc0xDbjdjOGFQSElTS090UGxBZVRaU25iOFFBdTdhUmpacTMrUGJyUDV1VzNUY2ZDR1B0S1R5dEhPZ2UvT2xKYm8wNzhkVmhYUTE0ZDFFRHdYSlcxclJYdVV0NEM4UUlEQVFBQk1BMEdDU3FHU0liM0RRRUJCUVVBQTRHQkFDRFZmcDg2SE9icVkrZThCVW9XUTkrVk1ReDFBU0RvaEJqd09zZzJXeWtVcVJYRitkTGZjVUg5ZFdSNjNDdFpJS0ZEYlN0Tm9tUG5RejduYksrb255Z3dCc3BWRWJuSHVVaWhacTNaVWRtdW1RcUN3NFV2cy8xVXZxM29yT28vV0pWaFR5dkxnRlZLMlFhclE0LzY3T1pmSGQ3UitQT0JYaG9waFNNdjFaT288L2RzOlg1MDlDZXJ0aWZpY2F0ZT48L2RzOlg1MDlEYXRhPjwvZHM6S2V5SW5mbz48L2RzOlNpZ25hdHVyZT4NCiAgPHNhbWxwOlN0YXR1cz4NCiAgICA8c2FtbHA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+DQogIDwvc2FtbHA6U3RhdHVzPg0KICA8c2FtbDpBc3NlcnRpb24geG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiBJRD0icGZ4YjRlYzljOGEtNDhlYi1mZGEyLTdmNzQtZmExYTEwNWE5OWZlIiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAxNC0wMi0xOVQwMTozNzowMVoiPg0KICAgIDxzYW1sOklzc3Vlcj5odHRwczovL3BpdGJ1bGsubm8taXAub3JnL3NpbXBsZXNhbWwvc2FtbDIvaWRwL21ldGFkYXRhLnBocDwvc2FtbDpJc3N1ZXI+DQogICAgPHNhbWw6U3ViamVjdD4NCiAgICAgIDxzYW1sOk5hbWVJRCBTUE5hbWVRdWFsaWZpZXI9Imh0dHBzOi8vcGl0YnVsay5uby1pcC5vcmcvbmV3b25lbG9naW4vZGVtbzEvbWV0YWRhdGEucGhwIiBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+NDkyODgyNjE1YWNmMzFjODA5NmI2MjcyNDVkNzZhZTUzMDM2YzA5MDwvc2FtbDpOYW1lSUQ+DQogICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+DQogICAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjk5OS0wOC0yM1QwNjo1NzowMVoiIFJlY2lwaWVudD0iaHR0cHM6Ly9waXRidWxrLm5vLWlwLm9yZy9uZXdvbmVsb2dpbi9kZW1vMS9pbmRleC5waHA/YWNzIiBJblJlc3BvbnNlVG89Ik9ORUxPR0lOXzVmZTlkNmU0OTliMmYwOTEzMjA2YWFiM2Y3MTkxNzI5MDQ5YmI4MDciLz4NCiAgICAgIDwvc2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uPg0KICAgIDwvc2FtbDpTdWJqZWN0Pg0KICAgIDxzYW1sOkNvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDE0LTAyLTE5VDAxOjM2OjMxWiIgTm90T25PckFmdGVyPSIyOTk5LTA4LTIzVDA2OjU3OjAxWiI+DQogICAgICA8c2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPg0KICAgICAgICA8c2FtbDpBdWRpZW5jZT5odHRwczovL3BpdGJ1bGsubm8taXAub3JnL25ld29uZWxvZ2luL2RlbW8xL21ldGFkYXRhLnBocDwvc2FtbDpBdWRpZW5jZT4NCiAgICAgIDwvc2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPg0KICAgIDwvc2FtbDpDb25kaXRpb25zPg0KICAgIDxzYW1sOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAxNC0wMi0xOVQwMTozNzowMVoiIFNlc3Npb25Ob3RPbk9yQWZ0ZXI9IjI5OTktMDItMTlUMDk6Mzc6MDFaIiBTZXNzaW9uSW5kZXg9Il82MjczZDc3YjhjZGUwYzMzM2VjNzlkMjJhOWZhMDAwM2I5ZmUyZDc1Y2IiPg0KICAgICAgPHNhbWw6QXV0aG5Db250ZXh0Pg0KICAgICAgICA8c2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZDwvc2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj4NCiAgICAgIDwvc2FtbDpBdXRobkNvbnRleHQ+DQogICAgPC9zYW1sOkF1dGhuU3RhdGVtZW50Pg0KICAgIDxzYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4NCiAgICAgIDxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJ1aWQiIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiPg0KICAgICAgICA8c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj5zbWFydGluPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPg0KICAgICAgPC9zYW1sOkF0dHJpYnV0ZT4NCiAgICAgIDxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJtYWlsIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj4NCiAgICAgICAgPHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+c21hcnRpbkB5YWNvLmVzPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPg0KICAgICAgPC9zYW1sOkF0dHJpYnV0ZT4NCiAgICAgIDxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJjbiIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+DQogICAgICAgIDxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPlNpeHRvMzwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT4NCiAgICAgIDwvc2FtbDpBdHRyaWJ1dGU+DQogICAgICA8c2FtbDpBdHRyaWJ1dGUgTmFtZT0ic24iIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiPg0KICAgICAgICA8c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj5NYXJ0aW4yPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPg0KICAgICAgPC9zYW1sOkF0dHJpYnV0ZT4NCiAgICAgIDxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJlZHVQZXJzb25BZmZpbGlhdGlvbiIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+DQogICAgICAgIDxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPnVzZXI8L3NhbWw6QXR0cmlidXRlVmFsdWU+DQogICAgICAgIDxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPmFkbWluPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPg0KICAgICAgPC9zYW1sOkF0dHJpYnV0ZT4NCiAgICA8L3NhbWw6QXR0cmlidXRlU3RhdGVtZW50Pg0KICA8L3NhbWw6QXNzZXJ0aW9uPg0KPC9zYW1scDpSZXNwb25zZT4= \ No newline at end of file diff --git a/tests/pylint.rc b/tests/pylint.rc deleted file mode 100644 index e5dd9bb9..00000000 --- a/tests/pylint.rc +++ /dev/null @@ -1,75 +0,0 @@ -[MASTER] -profile=no -persistent=yes -ignore= -cache-size=500 - -[REPORTS] -output-format=text -files-output=no -reports=yes - -[BASIC] -no-docstring-rgx=__.*__|_.* -class-rgx=[A-Z_][a-zA-Z0-9_]+$ -function-rgx=[a-zA_][a-zA-Z0-9_]{2,70}$ -method-rgx=[a-z_][a-zA-Z0-9_]{2,70}$ -const-rgx=(([A-Z_][A-Z0-9_]*)|([a-z_][a-z0-9_]*)|(__.*__)|register|urlpatterns)$ -good-names=_,i,j,k,e,qs,pk,setUp,tearDown,el,ns,fd,js,nb,na,sp,SAML_SINGLE_LOGOUT_NOT_SUPPORTED,SAML_SINGLE_LOGOUT_NOT_SUPPORTED,NAMEID_WINDOWS_DOMAIN_QUALIFIED_NAME -docstring-min-length=1 - -disable=E0611,W0703,W0511,W1401,F0401,W0102,E1103,W0212,I0011 - -[TYPECHECK] - -# Tells whether missing members accessed in mixin class should be ignored. A -# mixin class is detected if its name ends with "mixin" (case insensitive). -ignore-mixin-members=yes - -# List of module names for which member attributes should not be checked -# (useful for modules/projects where namespaces are manipulated during runtime -# and thus extisting member attributes cannot be deduced by static analysis -ignored-modules= - -# List of classes names for which member attributes should not be checked -# (useful for classes with attributes dynamically set). -ignored-classes=SQLObject,WSGIRequest - -# When zope mode is activated, add a predefined set of Zope acquired attributes -# to generated-members. -zope=no - -# List of members which are set dynamically and missed by pylint inference -# system, and so shouldn't trigger E0201 when accessed. -generated-members=objects,DoesNotExist,id,pk,_meta,base_fields,context,views,save - -# List of method names used to declare (i.e. assign) instance attributes -defining-attr-methods=__init__,__new__,setUp - -[VARIABLES] -init-import=no -dummy-variables-rgx=_|dummy - -[SIMILARITIES] -min-similarity-lines=6 -ignore-comments=yes -ignore-docstrings=yes -[MISCELLANEOUS] -notes=FIXME,XXX,TODO - -[FORMAT] -max-line-length=200 -max-module-lines=1200 -indent-string=' ' -indent-after-paren=4 - -[DESIGN] -max-args=10 -max-locals=40 -max-returns=6 -max-branches=50 -max-statements=120 -max-parents=10 -max-attributes=10 -min-public-methods=0 -max-public-methods=100 diff --git a/tests/src/OneLogin/saml2_tests/auth_test.py b/tests/src/OneLogin/saml2_tests/auth_test.py index b088874f..455b2d28 100644 --- a/tests/src/OneLogin/saml2_tests/auth_test.py +++ b/tests/src/OneLogin/saml2_tests/auth_test.py @@ -20,36 +20,32 @@ class OneLogin_Saml2_Auth_Test(unittest.TestCase): - data_path = join(dirname(dirname(dirname(dirname(__file__)))), 'data') - settings_path = join(dirname(dirname(dirname(dirname(__file__)))), 'settings') + data_path = join(dirname(dirname(dirname(dirname(__file__)))), "data") + settings_path = join(dirname(dirname(dirname(dirname(__file__)))), "settings") # assertRaisesRegexp deprecated on python3 def assertRaisesRegex(self, exception, regexp, msg=None): - if hasattr(unittest.TestCase, 'assertRaisesRegex'): + if hasattr(unittest.TestCase, "assertRaisesRegex"): return super(OneLogin_Saml2_Auth_Test, self).assertRaisesRegex(exception, regexp, msg=msg) else: return self.assertRaisesRegexp(exception, regexp) - def loadSettingsJSON(self, name='settings1.json'): + def loadSettingsJSON(self, name="settings1.json"): filename = join(self.settings_path, name) if exists(filename): - stream = open(filename, 'r') + stream = open(filename, "r") settings = json.load(stream) stream.close() return settings def file_contents(self, filename): - f = open(filename, 'r') + f = open(filename, "r") content = f.read() f.close() return content def get_request(self): - return { - 'http_host': 'example.com', - 'script_name': '/index.html', - 'get_data': {} - } + return {"http_host": "example.com", "script_name": "/index.html", "get_data": {}} def testGetSettings(self): """ @@ -72,7 +68,7 @@ def testGetSSOurl(self): settings_info = self.loadSettingsJSON() auth = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings_info) - sso_url = settings_info['idp']['singleSignOnService']['url'] + sso_url = settings_info["idp"]["singleSignOnService"]["url"] self.assertEqual(auth.get_sso_url(), sso_url) def testGetSLOurl(self): @@ -82,7 +78,7 @@ def testGetSLOurl(self): settings_info = self.loadSettingsJSON() auth = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings_info) - slo_url = settings_info['idp']['singleLogoutService']['url'] + slo_url = settings_info["idp"]["singleLogoutService"]["url"] self.assertEqual(auth.get_slo_url(), slo_url) def testGetSLOresponseUrl(self): @@ -90,14 +86,14 @@ def testGetSLOresponseUrl(self): Tests the get_slo_response_url method of the OneLogin_Saml2_Auth class """ settings_info = self.loadSettingsJSON() - settings_info['idp']['singleLogoutService']['responseUrl'] = "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']['responseUrl'] + 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') + settings_info["idp"]["singleLogoutService"].pop("responseUrl") auth = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings_info) - slo_url = settings_info['idp']['singleLogoutService']['url'] + slo_url = settings_info["idp"]["singleLogoutService"]["url"] self.assertEqual(auth.get_slo_response_url(), slo_url) def testGetSessionIndex(self): @@ -109,16 +105,14 @@ def testGetSessionIndex(self): self.assertIsNone(auth.get_session_index()) request_data = self.get_request() - message = self.file_contents(join(self.data_path, 'responses', 'valid_response.xml.base64')) - del request_data['get_data'] - request_data['post_data'] = { - 'SAMLResponse': message - } + message = self.file_contents(join(self.data_path, "responses", "valid_response.xml.base64")) + del request_data["get_data"] + request_data["post_data"] = {"SAMLResponse": message} auth2 = OneLogin_Saml2_Auth(request_data, old_settings=self.loadSettingsJSON()) self.assertIsNone(auth2.get_session_index()) auth2.process_response() - self.assertEqual('_6273d77b8cde0c333ec79d22a9fa0003b9fe2d75cb', auth2.get_session_index()) + self.assertEqual("_6273d77b8cde0c333ec79d22a9fa0003b9fe2d75cb", auth2.get_session_index()) def testGetSessionExpiration(self): """ @@ -129,11 +123,9 @@ def testGetSessionExpiration(self): self.assertIsNone(auth.get_session_expiration()) request_data = self.get_request() - message = self.file_contents(join(self.data_path, 'responses', 'valid_response.xml.base64')) - del request_data['get_data'] - request_data['post_data'] = { - 'SAMLResponse': message - } + message = self.file_contents(join(self.data_path, "responses", "valid_response.xml.base64")) + del request_data["get_data"] + request_data["post_data"] = {"SAMLResponse": message} auth2 = OneLogin_Saml2_Auth(request_data, old_settings=self.loadSettingsJSON()) self.assertIsNone(auth2.get_session_expiration()) @@ -146,15 +138,13 @@ def testGetLastErrorReason(self): Case Invalid Response """ request_data = self.get_request() - message = self.file_contents(join(self.data_path, 'responses', 'response1.xml.base64')) - del request_data['get_data'] - request_data['post_data'] = { - 'SAMLResponse': message - } + message = self.file_contents(join(self.data_path, "responses", "response1.xml.base64")) + del request_data["get_data"] + request_data["post_data"] = {"SAMLResponse": message} auth = OneLogin_Saml2_Auth(request_data, old_settings=self.loadSettingsJSON()) auth.process_response() - self.assertEqual(auth.get_last_error_reason(), 'Signature validation failed. SAML Response rejected') + self.assertEqual(auth.get_last_error_reason(), "Signature validation failed. SAML Response rejected") def testProcessNoResponse(self): """ @@ -162,9 +152,9 @@ def testProcessNoResponse(self): Case No Response, An exception is throw """ auth = OneLogin_Saml2_Auth(self.get_request(), old_settings=self.loadSettingsJSON()) - with self.assertRaisesRegex(OneLogin_Saml2_Error, 'SAML Response not found'): + with self.assertRaisesRegex(OneLogin_Saml2_Error, "SAML Response not found"): auth.process_response() - self.assertEqual(auth.get_errors(), ['invalid_binding']) + self.assertEqual(auth.get_errors(), ["invalid_binding"]) def testProcessResponseInvalid(self): """ @@ -174,19 +164,17 @@ def testProcessResponseInvalid(self): the error array is not empty, contains 'invalid_response """ request_data = self.get_request() - message = self.file_contents(join(self.data_path, 'responses', 'response1.xml.base64')) - del request_data['get_data'] - request_data['post_data'] = { - 'SAMLResponse': message - } + message = self.file_contents(join(self.data_path, "responses", "response1.xml.base64")) + del request_data["get_data"] + request_data["post_data"] = {"SAMLResponse": message} auth = OneLogin_Saml2_Auth(request_data, old_settings=self.loadSettingsJSON()) auth.process_response() self.assertFalse(auth.is_authenticated()) self.assertEqual(len(auth.get_attributes()), 0) self.assertEqual(auth.get_nameid(), None) - self.assertEqual(auth.get_attribute('uid'), None) - self.assertEqual(auth.get_errors(), ['invalid_response']) + self.assertEqual(auth.get_attribute("uid"), None) + self.assertEqual(auth.get_errors(), ["invalid_response"]) def testProcessResponseInvalidRequestId(self): """ @@ -194,27 +182,25 @@ def testProcessResponseInvalidRequestId(self): Case Invalid Response, Invalid requestID """ request_data = self.get_request() - message = self.file_contents(join(self.data_path, 'responses', 'unsigned_response.xml.base64')) + message = self.file_contents(join(self.data_path, "responses", "unsigned_response.xml.base64")) plain_message = compat.to_string(b64decode(message)) current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data) - plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/acs.php', current_url) - del request_data['get_data'] - request_data['post_data'] = { - 'SAMLResponse': compat.to_string(b64encode(compat.to_bytes(plain_message))) - } + plain_message = plain_message.replace("http://stuff.com/endpoints/endpoints/acs.php", current_url) + del request_data["get_data"] + request_data["post_data"] = {"SAMLResponse": compat.to_string(b64encode(compat.to_bytes(plain_message)))} auth = OneLogin_Saml2_Auth(request_data, old_settings=self.loadSettingsJSON()) - request_id = 'invalid' + request_id = "invalid" auth.process_response(request_id) - self.assertEqual('No Signature found. SAML Response rejected', auth.get_last_error_reason()) + self.assertEqual("No Signature found. SAML Response rejected", auth.get_last_error_reason()) auth.set_strict(True) auth.process_response(request_id) - self.assertEqual(auth.get_errors(), ['invalid_response']) - self.assertEqual('The InResponseTo of the Response: _57bcbf70-7b1f-012e-c821-782bcb13bb38, does not match the ID of the AuthNRequest sent by the SP: invalid', auth.get_last_error_reason()) + self.assertEqual(auth.get_errors(), ["invalid_response"]) + self.assertEqual("The InResponseTo of the Response: _57bcbf70-7b1f-012e-c821-782bcb13bb38, does not match the ID of the AuthNRequest sent by the SP: invalid", auth.get_last_error_reason()) - valid_request_id = '_57bcbf70-7b1f-012e-c821-782bcb13bb38' + valid_request_id = "_57bcbf70-7b1f-012e-c821-782bcb13bb38" auth.process_response(valid_request_id) - self.assertEqual('No Signature found. SAML Response rejected', auth.get_last_error_reason()) + self.assertEqual("No Signature found. SAML Response rejected", auth.get_last_error_reason()) def testProcessResponseValid(self): """ @@ -224,24 +210,22 @@ def testProcessResponseValid(self): the error array is empty """ request_data = self.get_request() - message = self.file_contents(join(self.data_path, 'responses', 'valid_response.xml.base64')) - del request_data['get_data'] - request_data['post_data'] = { - 'SAMLResponse': message - } + message = self.file_contents(join(self.data_path, "responses", "valid_response.xml.base64")) + del request_data["get_data"] + request_data["post_data"] = {"SAMLResponse": message} auth = OneLogin_Saml2_Auth(request_data, old_settings=self.loadSettingsJSON()) auth.process_response() self.assertTrue(auth.is_authenticated()) self.assertEqual(len(auth.get_errors()), 0) - self.assertEqual('492882615acf31c8096b627245d76ae53036c090', auth.get_nameid()) + self.assertEqual("492882615acf31c8096b627245d76ae53036c090", auth.get_nameid()) attributes = auth.get_attributes() self.assertNotEqual(len(attributes), 0) - self.assertEqual(auth.get_attribute('mail'), attributes['mail']) + 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("_6273d77b8cde0c333ec79d22a9fa0003b9fe2d75cb", session_index) self.assertEqual("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", auth.get_nameid_format()) self.assertIsNone(auth.get_nameid_nq()) self.assertEqual("http://stuff.com/endpoints/metadata.php", auth.get_nameid_spnq()) @@ -254,8 +238,8 @@ def testRedirectTo(self): Case redirect without url parameter """ request_data = self.get_request() - relay_state = 'http://sp.example.com' - request_data['get_data']['RelayState'] = relay_state + relay_state = "http://sp.example.com" + request_data["get_data"]["RelayState"] = relay_state auth = OneLogin_Saml2_Auth(request_data, old_settings=self.loadSettingsJSON()) target_url = auth.redirect_to() self.assertEqual(target_url, relay_state) @@ -268,9 +252,9 @@ def testRedirectTowithUrl(self): Case redirect with url parameter """ request_data = self.get_request() - relay_state = 'http://sp.example.com' - url_2 = 'http://sp2.example.com' - request_data['get_data']['RelayState'] = relay_state + relay_state = "http://sp.example.com" + url_2 = "http://sp2.example.com" + request_data["get_data"]["RelayState"] = relay_state auth = OneLogin_Saml2_Auth(request_data, old_settings=self.loadSettingsJSON()) target_url = auth.redirect_to(url_2) self.assertEqual(target_url, url_2) @@ -281,9 +265,9 @@ def testProcessNoSLO(self): Case No Message, An exception is throw """ auth = OneLogin_Saml2_Auth(self.get_request(), old_settings=self.loadSettingsJSON()) - with self.assertRaisesRegex(OneLogin_Saml2_Error, 'SAML LogoutRequest/LogoutResponse not found'): + with self.assertRaisesRegex(OneLogin_Saml2_Error, "SAML LogoutRequest/LogoutResponse not found"): auth.process_slo(True) - self.assertEqual(auth.get_errors(), ['invalid_binding']) + self.assertEqual(auth.get_errors(), ["invalid_binding"]) def testProcessSLOResponseInvalid(self): """ @@ -291,8 +275,8 @@ def testProcessSLOResponseInvalid(self): Case Invalid Logout Response """ request_data = self.get_request() - message = self.file_contents(join(self.data_path, 'logout_responses', 'logout_response_deflated.xml.base64')) - request_data['get_data']['SAMLResponse'] = message + message = self.file_contents(join(self.data_path, "logout_responses", "logout_response_deflated.xml.base64")) + request_data["get_data"]["SAMLResponse"] = message auth = OneLogin_Saml2_Auth(request_data, old_settings=self.loadSettingsJSON()) auth.process_slo(True) @@ -301,7 +285,7 @@ def testProcessSLOResponseInvalid(self): auth.set_strict(True) auth.process_slo(True) # The Destination fails - self.assertEqual(auth.get_errors(), ['invalid_logout_response']) + self.assertEqual(auth.get_errors(), ["invalid_logout_response"]) auth.set_strict(False) auth.process_slo(True) @@ -313,18 +297,18 @@ def testProcessSLOResponseNoSucess(self): 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')) + message = self.file_contents(join(self.data_path, "logout_responses", "invalids", "status_code_responder.xml.base64")) # In order to avoid the destination problem 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) - plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url) + plain_message = plain_message.replace("http://stuff.com/endpoints/endpoints/sls.php", current_url) message = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message) - request_data['get_data']['SAMLResponse'] = message + request_data["get_data"]["SAMLResponse"] = message auth = OneLogin_Saml2_Auth(request_data, old_settings=self.loadSettingsJSON()) auth.set_strict(True) auth.process_slo(True) - self.assertEqual(auth.get_errors(), ['logout_not_success']) + self.assertEqual(auth.get_errors(), ["logout_not_success"]) def testProcessSLOResponseRequestId(self): """ @@ -332,21 +316,21 @@ def testProcessSLOResponseRequestId(self): Case Logout Response with valid and invalid Request ID """ request_data = self.get_request() - message = self.file_contents(join(self.data_path, 'logout_responses', 'logout_response_deflated.xml.base64')) + message = self.file_contents(join(self.data_path, "logout_responses", "logout_response_deflated.xml.base64")) # In order to avoid the destination problem 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) - plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url) + plain_message = plain_message.replace("http://stuff.com/endpoints/endpoints/sls.php", current_url) message = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message) - request_data['get_data']['SAMLResponse'] = message + request_data["get_data"]["SAMLResponse"] = message auth = OneLogin_Saml2_Auth(request_data, old_settings=self.loadSettingsJSON()) - request_id = 'wrongID' + request_id = "wrongID" auth.set_strict(True) auth.process_slo(True, request_id) - self.assertEqual(auth.get_errors(), ['invalid_logout_response']) + self.assertEqual(auth.get_errors(), ["invalid_logout_response"]) - request_id = 'ONELOGIN_21584ccdfaca36a145ae990442dcd96bfe60151e' + request_id = "ONELOGIN_21584ccdfaca36a145ae990442dcd96bfe60151e" auth.process_slo(True, request_id) self.assertEqual(len(auth.get_errors()), 0) @@ -356,13 +340,13 @@ def testProcessSLOResponseValid(self): Case Valid Logout Response """ request_data = self.get_request() - message = self.file_contents(join(self.data_path, 'logout_responses', 'logout_response_deflated.xml.base64')) + message = self.file_contents(join(self.data_path, "logout_responses", "logout_response_deflated.xml.base64")) # In order to avoid the destination problem 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) - plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url) + plain_message = plain_message.replace("http://stuff.com/endpoints/endpoints/sls.php", current_url) message = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message) - request_data['get_data']['SAMLResponse'] = message + request_data["get_data"]["SAMLResponse"] = message auth = OneLogin_Saml2_Auth(request_data, old_settings=self.loadSettingsJSON()) # FIXME @@ -386,7 +370,7 @@ def testProcessSLOResponseValidDeletingSession(self): Case Valid Logout Response, validating deleting the local session """ request_data = self.get_request() - message = self.file_contents(join(self.data_path, 'logout_responses', 'logout_response_deflated.xml.base64')) + message = self.file_contents(join(self.data_path, "logout_responses", "logout_response_deflated.xml.base64")) # FIXME # if (!isset($_SESSION)) { @@ -397,9 +381,9 @@ def testProcessSLOResponseValidDeletingSession(self): # In order to avoid the destination problem 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) - plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url) + plain_message = plain_message.replace("http://stuff.com/endpoints/endpoints/sls.php", current_url) message = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message) - request_data['get_data']['SAMLResponse'] = message + request_data["get_data"]["SAMLResponse"] = message auth = OneLogin_Saml2_Auth(request_data, old_settings=self.loadSettingsJSON()) auth.set_strict(True) @@ -417,29 +401,29 @@ def testProcessSLORequestInvalidValid(self): """ settings_info = self.loadSettingsJSON() request_data = self.get_request() - message = self.file_contents(join(self.data_path, 'logout_requests', 'logout_request_deflated.xml.base64')) - request_data['get_data']['SAMLRequest'] = message + message = self.file_contents(join(self.data_path, "logout_requests", "logout_request_deflated.xml.base64")) + request_data["get_data"]["SAMLRequest"] = message auth = OneLogin_Saml2_Auth(request_data, old_settings=settings_info) target_url = auth.process_slo(True) parsed_query = parse_qs(urlparse(target_url)[4]) self.assertEqual(len(auth.get_errors()), 0) - slo_url = settings_info['idp']['singleLogoutService']['url'] + slo_url = settings_info["idp"]["singleLogoutService"]["url"] self.assertIn(slo_url, target_url) - self.assertIn('SAMLResponse', parsed_query) + self.assertIn("SAMLResponse", parsed_query) # self.assertNotIn('RelayState', parsed_query) auth.set_strict(True) auth.process_slo(True) # Fail due destination missmatch - self.assertEqual(auth.get_errors(), ['invalid_logout_request']) + self.assertEqual(auth.get_errors(), ["invalid_logout_request"]) auth.set_strict(False) target_url_2 = auth.process_slo(True) parsed_query_2 = parse_qs(urlparse(target_url_2)[4]) self.assertEqual(len(auth.get_errors()), 0) - slo_url = settings_info['idp']['singleLogoutService']['url'] + slo_url = settings_info["idp"]["singleLogoutService"]["url"] self.assertIn(slo_url, target_url_2) - self.assertIn('SAMLResponse', parsed_query_2) + self.assertIn("SAMLResponse", parsed_query_2) # self.assertNotIn('RelayState', parsed_query_2) def testProcessSLORequestNotOnOrAfterFailed(self): @@ -448,18 +432,18 @@ def testProcessSLORequestNotOnOrAfterFailed(self): Case Logout Request NotOnOrAfter failed """ request_data = self.get_request() - message = self.file_contents(join(self.data_path, 'logout_requests', 'invalids', 'not_after_failed.xml.base64')) + message = self.file_contents(join(self.data_path, "logout_requests", "invalids", "not_after_failed.xml.base64")) # In order to avoid the destination problem 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) - plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url) + plain_message = plain_message.replace("http://stuff.com/endpoints/endpoints/sls.php", current_url) message = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message) - request_data['get_data']['SAMLRequest'] = message + request_data["get_data"]["SAMLRequest"] = message auth = OneLogin_Saml2_Auth(request_data, old_settings=self.loadSettingsJSON()) auth.set_strict(True) auth.process_slo(True) - self.assertEqual(auth.get_errors(), ['invalid_logout_request']) + self.assertEqual(auth.get_errors(), ["invalid_logout_request"]) def testProcessSLORequestDeletingSession(self): """ @@ -469,13 +453,13 @@ def testProcessSLORequestDeletingSession(self): """ settings_info = self.loadSettingsJSON() request_data = self.get_request() - message = self.file_contents(join(self.data_path, 'logout_requests', 'logout_request_deflated.xml.base64')) + message = self.file_contents(join(self.data_path, "logout_requests", "logout_request_deflated.xml.base64")) # In order to avoid the destination problem 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) - plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url) + plain_message = plain_message.replace("http://stuff.com/endpoints/endpoints/sls.php", current_url) message = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message) - request_data['get_data']['SAMLRequest'] = message + request_data["get_data"]["SAMLRequest"] = message # FIXME # if (!isset($_SESSION)) { # $_SESSION = array(); @@ -486,9 +470,9 @@ def testProcessSLORequestDeletingSession(self): auth.set_strict(True) target_url = auth.process_slo(True) parsed_query = parse_qs(urlparse(target_url)[4]) - slo_url = settings_info['idp']['singleLogoutService']['url'] + slo_url = settings_info["idp"]["singleLogoutService"]["url"] self.assertIn(slo_url, target_url) - self.assertIn('SAMLResponse', parsed_query) + self.assertIn("SAMLResponse", parsed_query) # self.assertNotIn('RelayState', parsed_query) # FIXME // Session is not alive @@ -500,9 +484,9 @@ def testProcessSLORequestDeletingSession(self): target_url_2 = auth.process_slo(True) target_url_2 = auth.process_slo(True) parsed_query_2 = parse_qs(urlparse(target_url_2)[4]) - slo_url = settings_info['idp']['singleLogoutService']['url'] + slo_url = settings_info["idp"]["singleLogoutService"]["url"] self.assertIn(slo_url, target_url_2) - self.assertIn('SAMLResponse', parsed_query_2) + self.assertIn("SAMLResponse", parsed_query_2) # self.assertNotIn('RelayState', parsed_query_2) # FIXME // Session is alive @@ -517,24 +501,24 @@ def testProcessSLORequestRelayState(self): """ settings_info = self.loadSettingsJSON() request_data = self.get_request() - message = self.file_contents(join(self.data_path, 'logout_requests', 'logout_request_deflated.xml.base64')) + message = self.file_contents(join(self.data_path, "logout_requests", "logout_request_deflated.xml.base64")) # In order to avoid the destination problem 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) - plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url) + plain_message = plain_message.replace("http://stuff.com/endpoints/endpoints/sls.php", current_url) message = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message) - request_data['get_data']['SAMLRequest'] = message - request_data['get_data']['RelayState'] = 'http://relaystate.com' + request_data["get_data"]["SAMLRequest"] = message + request_data["get_data"]["RelayState"] = "http://relaystate.com" auth = OneLogin_Saml2_Auth(request_data, old_settings=settings_info) auth.set_strict(True) target_url = auth.process_slo(False) parsed_query = parse_qs(urlparse(target_url)[4]) - slo_url = settings_info['idp']['singleLogoutService']['url'] + slo_url = settings_info["idp"]["singleLogoutService"]["url"] self.assertIn(slo_url, target_url) - self.assertIn('SAMLResponse', parsed_query) - self.assertIn('RelayState', parsed_query) - self.assertIn('http://relaystate.com', parsed_query['RelayState']) + self.assertIn("SAMLResponse", parsed_query) + self.assertIn("RelayState", parsed_query) + self.assertIn("http://relaystate.com", parsed_query["RelayState"]) def testProcessSLORequestSignedResponse(self): """ @@ -543,29 +527,29 @@ def testProcessSLORequestSignedResponse(self): a signed LogoutResponse is created and a redirection executed """ settings_info = self.loadSettingsJSON() - settings_info['security']['logoutResponseSigned'] = True + settings_info["security"]["logoutResponseSigned"] = True request_data = self.get_request() - message = self.file_contents(join(self.data_path, 'logout_requests', 'logout_request_deflated.xml.base64')) + message = self.file_contents(join(self.data_path, "logout_requests", "logout_request_deflated.xml.base64")) # In order to avoid the destination problem 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) - plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url) + plain_message = plain_message.replace("http://stuff.com/endpoints/endpoints/sls.php", current_url) message = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message) - request_data['get_data']['SAMLRequest'] = message - request_data['get_data']['RelayState'] = 'http://relaystate.com' + request_data["get_data"]["SAMLRequest"] = message + request_data["get_data"]["RelayState"] = "http://relaystate.com" auth = OneLogin_Saml2_Auth(request_data, old_settings=settings_info) auth.set_strict(True) target_url = auth.process_slo(False) parsed_query = parse_qs(urlparse(target_url)[4]) - slo_url = settings_info['idp']['singleLogoutService']['url'] + slo_url = settings_info["idp"]["singleLogoutService"]["url"] self.assertIn(slo_url, target_url) - self.assertIn('SAMLResponse', parsed_query) - self.assertIn('RelayState', parsed_query) - self.assertIn('SigAlg', parsed_query) - self.assertIn('Signature', parsed_query) - self.assertIn('http://relaystate.com', parsed_query['RelayState']) - self.assertIn(OneLogin_Saml2_Constants.RSA_SHA256, parsed_query['SigAlg']) + self.assertIn("SAMLResponse", parsed_query) + self.assertIn("RelayState", parsed_query) + self.assertIn("SigAlg", parsed_query) + self.assertIn("Signature", parsed_query) + self.assertIn("http://relaystate.com", parsed_query["RelayState"]) + self.assertIn(OneLogin_Saml2_Constants.RSA_SHA256, parsed_query["SigAlg"]) def testLogin(self): """ @@ -578,12 +562,12 @@ def testLogin(self): target_url = auth.login() parsed_query = parse_qs(urlparse(target_url)[4]) - sso_url = settings_info['idp']['singleSignOnService']['url'] + sso_url = settings_info["idp"]["singleSignOnService"]["url"] self.assertIn(sso_url, target_url) - self.assertIn('SAMLRequest', parsed_query) - self.assertIn('RelayState', parsed_query) + self.assertIn("SAMLRequest", parsed_query) + self.assertIn("RelayState", parsed_query) hostname = OneLogin_Saml2_Utils.get_self_host(request_data) - self.assertIn(u'http://%s/index.html' % hostname, parsed_query['RelayState']) + self.assertIn("http://%s/index.html" % hostname, parsed_query["RelayState"]) def testLoginWithRelayState(self): """ @@ -593,15 +577,15 @@ def testLoginWithRelayState(self): """ settings_info = self.loadSettingsJSON() auth = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings_info) - relay_state = 'http://sp.example.com' + relay_state = "http://sp.example.com" target_url = auth.login(relay_state) parsed_query = parse_qs(urlparse(target_url)[4]) - sso_url = settings_info['idp']['singleSignOnService']['url'] + sso_url = settings_info["idp"]["singleSignOnService"]["url"] self.assertIn(sso_url, target_url) - self.assertIn('SAMLRequest', parsed_query) - self.assertIn('RelayState', parsed_query) - self.assertIn(relay_state, parsed_query['RelayState']) + self.assertIn("SAMLRequest", parsed_query) + self.assertIn("RelayState", parsed_query) + self.assertIn(relay_state, parsed_query["RelayState"]) def testLoginSigned(self): """ @@ -609,20 +593,20 @@ def testLoginSigned(self): Case Login signed. An AuthnRequest signed is built an redirect executed """ settings_info = self.loadSettingsJSON() - settings_info['security']['authnRequestsSigned'] = True + settings_info["security"]["authnRequestsSigned"] = True auth = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings_info) - return_to = u'http://example.com/returnto' + return_to = "http://example.com/returnto" target_url = auth.login(return_to) parsed_query = parse_qs(urlparse(target_url)[4]) - sso_url = settings_info['idp']['singleSignOnService']['url'] + sso_url = settings_info["idp"]["singleSignOnService"]["url"] self.assertIn(sso_url, target_url) - self.assertIn('SAMLRequest', parsed_query) - self.assertIn('RelayState', parsed_query) - self.assertIn('SigAlg', parsed_query) - self.assertIn('Signature', parsed_query) - self.assertIn(return_to, parsed_query['RelayState']) - self.assertIn(OneLogin_Saml2_Constants.RSA_SHA256, parsed_query['SigAlg']) + self.assertIn("SAMLRequest", parsed_query) + self.assertIn("RelayState", parsed_query) + self.assertIn("SigAlg", parsed_query) + self.assertIn("Signature", parsed_query) + self.assertIn(return_to, parsed_query["RelayState"]) + self.assertIn(OneLogin_Saml2_Constants.RSA_SHA256, parsed_query["SigAlg"]) def testLoginForceAuthN(self): """ @@ -630,31 +614,31 @@ def testLoginForceAuthN(self): Case AuthN Request is built with ForceAuthn and redirect executed """ settings_info = self.loadSettingsJSON() - return_to = u'http://example.com/returnto' + return_to = "http://example.com/returnto" auth = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings_info) target_url = auth.login(return_to) parsed_query = parse_qs(urlparse(target_url)[4]) - sso_url = settings_info['idp']['singleSignOnService']['url'] + sso_url = settings_info["idp"]["singleSignOnService"]["url"] self.assertIn(sso_url, target_url) - self.assertIn('SAMLRequest', parsed_query) - request = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(parsed_query['SAMLRequest'][0])) + self.assertIn("SAMLRequest", parsed_query) + request = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(parsed_query["SAMLRequest"][0])) self.assertNotIn('ForceAuthn="true"', request) auth_2 = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings_info) target_url_2 = auth_2.login(return_to, False, False) parsed_query_2 = parse_qs(urlparse(target_url_2)[4]) self.assertIn(sso_url, target_url_2) - self.assertIn('SAMLRequest', parsed_query_2) - request_2 = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(parsed_query_2['SAMLRequest'][0])) + self.assertIn("SAMLRequest", parsed_query_2) + request_2 = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(parsed_query_2["SAMLRequest"][0])) self.assertNotIn('ForceAuthn="true"', request_2) auth_3 = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings_info) target_url_3 = auth_3.login(return_to, True, False) parsed_query_3 = parse_qs(urlparse(target_url_3)[4]) self.assertIn(sso_url, target_url_3) - self.assertIn('SAMLRequest', parsed_query_3) - request_3 = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(parsed_query_3['SAMLRequest'][0])) + self.assertIn("SAMLRequest", parsed_query_3) + request_3 = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(parsed_query_3["SAMLRequest"][0])) self.assertIn('ForceAuthn="true"', request_3) def testLoginIsPassive(self): @@ -663,32 +647,32 @@ def testLoginIsPassive(self): Case AuthN Request is built with IsPassive and redirect executed """ settings_info = self.loadSettingsJSON() - return_to = u'http://example.com/returnto' - settings_info['idp']['singleSignOnService']['url'] + return_to = "http://example.com/returnto" + settings_info["idp"]["singleSignOnService"]["url"] auth = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings_info) target_url = auth.login(return_to) parsed_query = parse_qs(urlparse(target_url)[4]) - sso_url = settings_info['idp']['singleSignOnService']['url'] + sso_url = settings_info["idp"]["singleSignOnService"]["url"] self.assertIn(sso_url, target_url) - self.assertIn('SAMLRequest', parsed_query) - request = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(parsed_query['SAMLRequest'][0])) + self.assertIn("SAMLRequest", parsed_query) + request = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(parsed_query["SAMLRequest"][0])) self.assertNotIn('IsPassive="true"', request) auth_2 = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings_info) target_url_2 = auth_2.login(return_to, False, False) parsed_query_2 = parse_qs(urlparse(target_url_2)[4]) self.assertIn(sso_url, target_url_2) - self.assertIn('SAMLRequest', parsed_query_2) - request_2 = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(parsed_query_2['SAMLRequest'][0])) + self.assertIn("SAMLRequest", parsed_query_2) + request_2 = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(parsed_query_2["SAMLRequest"][0])) self.assertNotIn('IsPassive="true"', request_2) auth_3 = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings_info) target_url_3 = auth_3.login(return_to, False, True) parsed_query_3 = parse_qs(urlparse(target_url_3)[4]) self.assertIn(sso_url, target_url_3) - self.assertIn('SAMLRequest', parsed_query_3) - request_3 = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(parsed_query_3['SAMLRequest'][0])) + self.assertIn("SAMLRequest", parsed_query_3) + request_3 = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(parsed_query_3["SAMLRequest"][0])) self.assertIn('IsPassive="true"', request_3) def testLoginSetNameIDPolicy(self): @@ -697,33 +681,33 @@ def testLoginSetNameIDPolicy(self): Case AuthN Request is built with and without NameIDPolicy """ settings_info = self.loadSettingsJSON() - return_to = u'http://example.com/returnto' - settings_info['idp']['singleSignOnService']['url'] + return_to = "http://example.com/returnto" + settings_info["idp"]["singleSignOnService"]["url"] auth = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings_info) target_url = auth.login(return_to) parsed_query = parse_qs(urlparse(target_url)[4]) - sso_url = settings_info['idp']['singleSignOnService']['url'] + sso_url = settings_info["idp"]["singleSignOnService"]["url"] self.assertIn(sso_url, target_url) - self.assertIn('SAMLRequest', parsed_query) - request = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(parsed_query['SAMLRequest'][0])) - self.assertIn('', request) - self.assertNotIn('", request) + self.assertNotIn("', request_2) + self.assertIn("SAMLRequest", parsed_query_2) + request_2 = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(parsed_query_2["SAMLRequest"][0])) + self.assertIn("", request_2) self.assertIn('Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">testuser@example.com', request_2) self.assertIn('', request_2) - settings_info['sp']['NameIDFormat'] = 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress' + settings_info["sp"]["NameIDFormat"] = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" auth_3 = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings_info) - target_url_3 = auth_3.login(return_to, name_id_value_req='testuser@example.com') + target_url_3 = auth_3.login(return_to, name_id_value_req="testuser@example.com") parsed_query_3 = parse_qs(urlparse(target_url_3)[4]) self.assertIn(sso_url, target_url_3) - self.assertIn('SAMLRequest', parsed_query_3) - request_3 = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(parsed_query_3['SAMLRequest'][0])) - self.assertIn('', request_3) + self.assertIn("SAMLRequest", parsed_query_3) + request_3 = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(parsed_query_3["SAMLRequest"][0])) + self.assertIn("", request_3) self.assertIn('Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">testuser@example.com', request_3) self.assertIn('', request_3) @@ -777,12 +761,12 @@ def testLogout(self): target_url = auth.logout() parsed_query = parse_qs(urlparse(target_url)[4]) - slo_url = settings_info['idp']['singleLogoutService']['url'] + slo_url = settings_info["idp"]["singleLogoutService"]["url"] self.assertIn(slo_url, target_url) - self.assertIn('SAMLRequest', parsed_query) - self.assertIn('RelayState', parsed_query) + self.assertIn("SAMLRequest", parsed_query) + self.assertIn("RelayState", parsed_query) hostname = OneLogin_Saml2_Utils.get_self_host(request_data) - self.assertIn(u'http://%s/index.html' % hostname, parsed_query['RelayState']) + self.assertIn("http://%s/index.html" % hostname, parsed_query["RelayState"]) def testLogoutWithRelayState(self): """ @@ -792,15 +776,15 @@ def testLogoutWithRelayState(self): """ settings_info = self.loadSettingsJSON() auth = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings_info) - relay_state = 'http://sp.example.com' + relay_state = "http://sp.example.com" target_url = auth.logout(relay_state) parsed_query = parse_qs(urlparse(target_url)[4]) - slo_url = settings_info['idp']['singleLogoutService']['url'] + slo_url = settings_info["idp"]["singleLogoutService"]["url"] self.assertIn(slo_url, target_url) - self.assertIn('SAMLRequest', parsed_query) - self.assertIn('RelayState', parsed_query) - self.assertIn(relay_state, parsed_query['RelayState']) + self.assertIn("SAMLRequest", parsed_query) + self.assertIn("RelayState", parsed_query) + self.assertIn(relay_state, parsed_query["RelayState"]) def testLogoutSigned(self): """ @@ -809,20 +793,20 @@ def testLogoutSigned(self): the assertion is built and redirect executed """ settings_info = self.loadSettingsJSON() - settings_info['security']['logoutRequestSigned'] = True + settings_info["security"]["logoutRequestSigned"] = True auth = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings_info) - return_to = u'http://example.com/returnto' + return_to = "http://example.com/returnto" target_url = auth.logout(return_to) parsed_query = parse_qs(urlparse(target_url)[4]) - slo_url = settings_info['idp']['singleLogoutService']['url'] + slo_url = settings_info["idp"]["singleLogoutService"]["url"] self.assertIn(slo_url, target_url) - self.assertIn('SAMLRequest', parsed_query) - self.assertIn('RelayState', parsed_query) - self.assertIn('SigAlg', parsed_query) - self.assertIn('Signature', parsed_query) - self.assertIn(return_to, parsed_query['RelayState']) - self.assertIn(OneLogin_Saml2_Constants.RSA_SHA256, parsed_query['SigAlg']) + self.assertIn("SAMLRequest", parsed_query) + self.assertIn("RelayState", parsed_query) + self.assertIn("SigAlg", parsed_query) + self.assertIn("Signature", parsed_query) + self.assertIn(return_to, parsed_query["RelayState"]) + self.assertIn(OneLogin_Saml2_Constants.RSA_SHA256, parsed_query["SigAlg"]) def testLogoutNoSLO(self): """ @@ -830,11 +814,11 @@ def testLogoutNoSLO(self): Case IdP no SLO endpoint. """ settings_info = self.loadSettingsJSON() - del settings_info['idp']['singleLogoutService'] + del settings_info["idp"]["singleLogoutService"] auth = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings_info) # The Header of the redirect produces an Exception - with self.assertRaisesRegex(OneLogin_Saml2_Error, 'The IdP does not support Single Log Out'): - auth.logout('http://example.com/returnto') + with self.assertRaisesRegex(OneLogin_Saml2_Error, "The IdP does not support Single Log Out"): + auth.logout("http://example.com/returnto") def testLogoutNameIDandSessionIndex(self): """ @@ -845,15 +829,15 @@ def testLogoutNameIDandSessionIndex(self): request_data = self.get_request() auth = OneLogin_Saml2_Auth(request_data, old_settings=settings_info) - name_id = 'name_id_example' - session_index = 'session_index_example' + name_id = "name_id_example" + session_index = "session_index_example" target_url = auth.logout(name_id=name_id, session_index=session_index) parsed_query = parse_qs(urlparse(target_url)[4]) - slo_url = settings_info['idp']['singleLogoutService']['url'] + slo_url = settings_info["idp"]["singleLogoutService"]["url"] self.assertIn(slo_url, target_url) - self.assertIn('SAMLRequest', parsed_query) + self.assertIn("SAMLRequest", parsed_query) - logout_request = OneLogin_Saml2_Utils.decode_base64_and_inflate(parsed_query['SAMLRequest'][0]) + logout_request = OneLogin_Saml2_Utils.decode_base64_and_inflate(parsed_query["SAMLRequest"][0]) name_id_from_request = OneLogin_Saml2_Logout_Request.get_nameid(logout_request) sessions_index_in_request = OneLogin_Saml2_Logout_Request.get_session_indexes(logout_request) self.assertIn(session_index, sessions_index_in_request) @@ -865,11 +849,9 @@ def testLogoutNameID(self): Case nameID loaded after process SAML Response """ request_data = self.get_request() - message = self.file_contents(join(self.data_path, 'responses', 'valid_response.xml.base64')) - del request_data['get_data'] - request_data['post_data'] = { - 'SAMLResponse': message - } + message = self.file_contents(join(self.data_path, "responses", "valid_response.xml.base64")) + del request_data["get_data"] + request_data["post_data"] = {"SAMLResponse": message} auth = OneLogin_Saml2_Auth(request_data, old_settings=self.loadSettingsJSON()) auth.process_response() @@ -878,8 +860,8 @@ def testLogoutNameID(self): target_url = auth.logout() parsed_query = parse_qs(urlparse(target_url)[4]) - self.assertIn('SAMLRequest', parsed_query) - logout_request = OneLogin_Saml2_Utils.decode_base64_and_inflate(parsed_query['SAMLRequest'][0]) + self.assertIn("SAMLRequest", parsed_query) + logout_request = OneLogin_Saml2_Utils.decode_base64_and_inflate(parsed_query["SAMLRequest"][0]) name_id_from_request = OneLogin_Saml2_Logout_Request.get_nameid(logout_request) name_id_format_from_request = OneLogin_Saml2_Logout_Request.get_nameid_format(logout_request) @@ -890,8 +872,8 @@ def testLogoutNameID(self): new_name_id_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" target_url_2 = auth.logout(name_id=new_name_id, name_id_format=new_name_id_format) parsed_query = parse_qs(urlparse(target_url_2)[4]) - self.assertIn('SAMLRequest', parsed_query) - logout_request = OneLogin_Saml2_Utils.decode_base64_and_inflate(parsed_query['SAMLRequest'][0]) + self.assertIn("SAMLRequest", parsed_query) + logout_request = OneLogin_Saml2_Utils.decode_base64_and_inflate(parsed_query["SAMLRequest"][0]) name_id_from_request = OneLogin_Saml2_Logout_Request.get_nameid(logout_request) name_id_format_from_request = OneLogin_Saml2_Logout_Request.get_nameid_format(logout_request) @@ -903,7 +885,7 @@ def testSetStrict(self): Tests the set_strict method of the OneLogin_Saml2_Auth """ settings_info = self.loadSettingsJSON() - settings_info['strict'] = False + settings_info["strict"] = False auth = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings_info) settings = auth.get_settings() @@ -917,26 +899,22 @@ def testSetStrict(self): settings = auth.get_settings() self.assertFalse(settings.is_strict()) - self.assertRaises(AssertionError, auth.set_strict, '42') + self.assertRaises(AssertionError, auth.set_strict, "42") def testIsAuthenticated(self): """ Tests the is_authenticated method of the OneLogin_Saml2_Auth """ request_data = self.get_request() - del request_data['get_data'] - message = self.file_contents(join(self.data_path, 'responses', 'response1.xml.base64')) - request_data['post_data'] = { - 'SAMLResponse': message - } + del request_data["get_data"] + message = self.file_contents(join(self.data_path, "responses", "response1.xml.base64")) + request_data["post_data"] = {"SAMLResponse": message} auth = OneLogin_Saml2_Auth(request_data, old_settings=self.loadSettingsJSON()) auth.process_response() self.assertFalse(auth.is_authenticated()) - message = self.file_contents(join(self.data_path, 'responses', 'valid_response.xml.base64')) - request_data['post_data'] = { - 'SAMLResponse': message - } + message = self.file_contents(join(self.data_path, "responses", "valid_response.xml.base64")) + request_data["post_data"] = {"SAMLResponse": message} auth = OneLogin_Saml2_Auth(request_data, old_settings=self.loadSettingsJSON()) auth.process_response() self.assertTrue(auth.is_authenticated()) @@ -947,30 +925,24 @@ def testGetNameId(self): """ settings = self.loadSettingsJSON() request_data = self.get_request() - del request_data['get_data'] - message = self.file_contents(join(self.data_path, 'responses', 'response1.xml.base64')) - request_data['post_data'] = { - 'SAMLResponse': message - } + del request_data["get_data"] + message = self.file_contents(join(self.data_path, "responses", "response1.xml.base64")) + request_data["post_data"] = {"SAMLResponse": message} auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_response() self.assertFalse(auth.is_authenticated()) self.assertEqual(auth.get_nameid(), None) - message = self.file_contents(join(self.data_path, 'responses', 'valid_response.xml.base64')) - request_data['post_data'] = { - 'SAMLResponse': message - } + message = self.file_contents(join(self.data_path, "responses", "valid_response.xml.base64")) + request_data["post_data"] = {"SAMLResponse": message} auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_response() self.assertTrue(auth.is_authenticated()) self.assertEqual("492882615acf31c8096b627245d76ae53036c090", auth.get_nameid()) - settings_2 = self.loadSettingsJSON('settings2.json') - message = self.file_contents(join(self.data_path, 'responses', 'signed_message_encrypted_assertion2.xml.base64')) - request_data['post_data'] = { - 'SAMLResponse': message - } + settings_2 = self.loadSettingsJSON("settings2.json") + message = self.file_contents(join(self.data_path, "responses", "signed_message_encrypted_assertion2.xml.base64")) + request_data["post_data"] = {"SAMLResponse": message} auth = OneLogin_Saml2_Auth(request_data, old_settings=settings_2) auth.process_response() self.assertTrue(auth.is_authenticated()) @@ -982,30 +954,24 @@ def testGetNameIdFormat(self): """ settings = self.loadSettingsJSON() request_data = self.get_request() - del request_data['get_data'] - message = self.file_contents(join(self.data_path, 'responses', 'response1.xml.base64')) - request_data['post_data'] = { - 'SAMLResponse': message - } + del request_data["get_data"] + message = self.file_contents(join(self.data_path, "responses", "response1.xml.base64")) + request_data["post_data"] = {"SAMLResponse": message} auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_response() self.assertFalse(auth.is_authenticated()) self.assertEqual(auth.get_nameid_format(), None) - message = self.file_contents(join(self.data_path, 'responses', 'valid_response.xml.base64')) - request_data['post_data'] = { - 'SAMLResponse': message - } + message = self.file_contents(join(self.data_path, "responses", "valid_response.xml.base64")) + request_data["post_data"] = {"SAMLResponse": message} auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_response() self.assertTrue(auth.is_authenticated()) self.assertEqual("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", auth.get_nameid_format()) - settings_2 = self.loadSettingsJSON('settings2.json') - message = self.file_contents(join(self.data_path, 'responses', 'signed_message_encrypted_assertion2.xml.base64')) - request_data['post_data'] = { - 'SAMLResponse': message - } + settings_2 = self.loadSettingsJSON("settings2.json") + message = self.file_contents(join(self.data_path, "responses", "signed_message_encrypted_assertion2.xml.base64")) + request_data["post_data"] = {"SAMLResponse": message} auth = OneLogin_Saml2_Auth(request_data, old_settings=settings_2) auth.process_response() self.assertTrue(auth.is_authenticated()) @@ -1016,11 +982,9 @@ def testGetNameIdNameQualifier(self): Tests the get_nameid_nq method of the OneLogin_Saml2_Auth """ settings = self.loadSettingsJSON() - message = self.file_contents(join(self.data_path, 'responses', 'valid_response_with_namequalifier.xml.base64')) + message = self.file_contents(join(self.data_path, "responses", "valid_response_with_namequalifier.xml.base64")) request_data = self.get_request() - request_data['post_data'] = { - 'SAMLResponse': message - } + request_data["post_data"] = {"SAMLResponse": message} auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) self.assertIsNone(auth.get_nameid_nq()) auth.process_response() @@ -1032,11 +996,9 @@ def testGetNameIdNameQualifier2(self): Tests the get_nameid_nq method of the OneLogin_Saml2_Auth """ settings = self.loadSettingsJSON() - message = self.file_contents(join(self.data_path, 'responses', 'valid_response.xml.base64')) + message = self.file_contents(join(self.data_path, "responses", "valid_response.xml.base64")) request_data = self.get_request() - request_data['post_data'] = { - 'SAMLResponse': message - } + request_data["post_data"] = {"SAMLResponse": message} auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) self.assertIsNone(auth.get_nameid_nq()) auth.process_response() @@ -1048,11 +1010,9 @@ def testGetNameIdSPNameQualifier(self): Tests the get_nameid_spnq method of the OneLogin_Saml2_Auth """ settings = self.loadSettingsJSON() - message = self.file_contents(join(self.data_path, 'responses', 'valid_response_with_namequalifier.xml.base64')) + message = self.file_contents(join(self.data_path, "responses", "valid_response_with_namequalifier.xml.base64")) request_data = self.get_request() - request_data['post_data'] = { - 'SAMLResponse': message - } + request_data["post_data"] = {"SAMLResponse": message} auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) self.assertIsNone(auth.get_nameid_spnq()) auth.process_response() @@ -1064,11 +1024,9 @@ def testGetNameIdSPNameQualifier2(self): Tests the get_nameid_spnq method of the OneLogin_Saml2_Auth """ settings = self.loadSettingsJSON() - message = self.file_contents(join(self.data_path, 'responses', 'valid_response.xml.base64')) + message = self.file_contents(join(self.data_path, "responses", "valid_response.xml.base64")) request_data = self.get_request() - request_data['post_data'] = { - 'SAMLResponse': message - } + request_data["post_data"] = {"SAMLResponse": message} auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) self.assertIsNone(auth.get_nameid_spnq()) auth.process_response() @@ -1080,17 +1038,17 @@ def testBuildRequestSignature(self): Tests the build_request_signature method of the OneLogin_Saml2_Auth """ settings = self.loadSettingsJSON() - message = self.file_contents(join(self.data_path, 'logout_requests', 'logout_request_deflated.xml.base64')) - relay_state = 'http://relaystate.com' + message = self.file_contents(join(self.data_path, "logout_requests", "logout_request_deflated.xml.base64")) + relay_state = "http://relaystate.com" parameters = {"SAMLRequest": message, "RelayState": relay_state} auth = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings) auth.add_request_signature(parameters) - valid_signature = 'CqdIlbO6GieeJFV+PYqyqz1QVJunQXdZZl+ZyIby9O3/eMJM0XHi+TWReRrpgNxKkbmmvx5fp/t7mphbLiVYNMgGINEaaa/OfoaGwU9GM5YCVULA2t7qZBel1yrIXGMxijJizB7UPR2ZMo4G+Wdhx1zbmbB0GYM0A27w6YCe/+k=' + valid_signature = "CqdIlbO6GieeJFV+PYqyqz1QVJunQXdZZl+ZyIby9O3/eMJM0XHi+TWReRrpgNxKkbmmvx5fp/t7mphbLiVYNMgGINEaaa/OfoaGwU9GM5YCVULA2t7qZBel1yrIXGMxijJizB7UPR2ZMo4G+Wdhx1zbmbB0GYM0A27w6YCe/+k=" self.assertEqual(valid_signature, parameters["Signature"]) - settings['sp']['privateKey'] = '' - settings['custom_base_path'] = u'invalid/path/' + settings["sp"]["privateKey"] = "" + settings["custom_base_path"] = "invalid/path/" auth2 = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings) with self.assertRaisesRegex(OneLogin_Saml2_Error, "Trying to sign the SAMLRequest but can't load the SP private key"): auth2.add_request_signature(parameters) @@ -1100,18 +1058,18 @@ def testBuildResponseSignature(self): Tests the build_response_signature method of the OneLogin_Saml2_Auth """ settings = self.loadSettingsJSON() - message = self.file_contents(join(self.data_path, 'logout_responses', 'logout_response_deflated.xml.base64')) - relay_state = 'http://relaystate.com' + message = self.file_contents(join(self.data_path, "logout_responses", "logout_response_deflated.xml.base64")) + relay_state = "http://relaystate.com" auth = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings) - parameters = {"SAMLResponse": message, 'RelayState': relay_state} + parameters = {"SAMLResponse": message, "RelayState": relay_state} auth.add_response_signature(parameters) - valid_signature = 'fFGaOuO/2+ch/xlwU5o7iS6R+v2quWchLAtiDyQTxStFQZKY1NsBs/eYIin2Meq7oTl1Ks6tpT6JshH5OwhPh/08K7M2oa6FIKb99cjg+jIJ/WwpuJ5h9SH0XXP8y3RLhCxLIomHDsBOGQK8WvOlXFUg+9nvOaEMNi6raUWrGhA=' - self.assertEqual(valid_signature, parameters['Signature']) + valid_signature = "fFGaOuO/2+ch/xlwU5o7iS6R+v2quWchLAtiDyQTxStFQZKY1NsBs/eYIin2Meq7oTl1Ks6tpT6JshH5OwhPh/08K7M2oa6FIKb99cjg+jIJ/WwpuJ5h9SH0XXP8y3RLhCxLIomHDsBOGQK8WvOlXFUg+9nvOaEMNi6raUWrGhA=" + self.assertEqual(valid_signature, parameters["Signature"]) - settings['sp']['privateKey'] = '' - settings['custom_base_path'] = u'invalid/path/' + settings["sp"]["privateKey"] = "" + settings["custom_base_path"] = "invalid/path/" auth2 = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings) with self.assertRaisesRegex(OneLogin_Saml2_Error, "Trying to sign the SAMLResponse but can't load the SP private key"): auth2.add_response_signature(parameters) @@ -1120,231 +1078,231 @@ def testIsInValidLogoutResponseSign(self): """ Tests the is_valid method of the OneLogin_Saml2_LogoutResponse """ - request_data = { - 'http_host': 'example.com', - 'script_name': 'index.html', - 'get_data': {} - } + request_data = {"http_host": "example.com", "script_name": "index.html", "get_data": {}} settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) settings.set_strict(False) - request_data['get_data'] = { - 'SAMLResponse': 'fZJva8IwEMa/Ssl7TZrW/gnqGHMMwSlM8cXeyLU9NaxNQi9lfvxVZczB5ptwSe733MPdjQma2qmFPdjOvyE5awiDU1MbUpevCetaoyyQJmWgQVK+VOvH14WSQ6Fca70tbc1ukPsEEGHrtTUsmM8mbDfKUhnFci8gliGINI/yXIAAiYnsw6JIRgWWAKlkwRZb6skJ64V6nKjDuSEPxvdPIowHIhpIsQkTFaYqSt9ZMEPy2oC/UEfvHSnOnfZFV38MjR1oN7TtgRv8tAZre9CGV9jYkGtT4Wnoju6Bauprme/ebOyErZbPi9XLfLnDoohwhHGc5WVSVhjCKM6rBMpYQpWJrIizfZ4IZNPxuTPqYrmd/m+EdONqPOfy8yG5rhxv0EMFHs52xvxWaHyd3tqD7+j37clWGGyh7vD+POiSrdZdWSIR49NrhR9R/teGTL8A', - 'RelayState': 'https://pitbulk.no-ip.org/newonelogin/demo1/index.php', - 'SigAlg': 'http://www.w3.org/2000/09/xmldsig#rsa-sha1', - 'Signature': 'vfWbbc47PkP3ejx4bjKsRX7lo9Ml1WRoE5J5owF/0mnyKHfSY6XbhO1wwjBV5vWdrUVX+xp6slHyAf4YoAsXFS0qhan6txDiZY4Oec6yE+l10iZbzvie06I4GPak4QrQ4gAyXOSzwCrRmJu4gnpeUxZ6IqKtdrKfAYRAcVfNKGA=' + request_data["get_data"] = { + "SAMLResponse": "fZJva8IwEMa/Ssl7TZrW/gnqGHMMwSlM8cXeyLU9NaxNQi9lfvxVZczB5ptwSe733MPdjQma2qmFPdjOvyE5awiDU1MbUpevCetaoyyQJmWgQVK+VOvH14WSQ6Fca70tbc1ukPsEEGHrtTUsmM8mbDfKUhnFci8gliGINI/yXIAAiYnsw6JIRgWWAKlkwRZb6skJ64V6nKjDuSEPxvdPIowHIhpIsQkTFaYqSt9ZMEPy2oC/UEfvHSnOnfZFV38MjR1oN7TtgRv8tAZre9CGV9jYkGtT4Wnoju6Bauprme/ebOyErZbPi9XLfLnDoohwhHGc5WVSVhjCKM6rBMpYQpWJrIizfZ4IZNPxuTPqYrmd/m+EdONqPOfy8yG5rhxv0EMFHs52xvxWaHyd3tqD7+j37clWGGyh7vD+POiSrdZdWSIR49NrhR9R/teGTL8A", + "RelayState": "https://pitbulk.no-ip.org/newonelogin/demo1/index.php", + "SigAlg": "http://www.w3.org/2000/09/xmldsig#rsa-sha1", + "Signature": "vfWbbc47PkP3ejx4bjKsRX7lo9Ml1WRoE5J5owF/0mnyKHfSY6XbhO1wwjBV5vWdrUVX+xp6slHyAf4YoAsXFS0qhan6txDiZY4Oec6yE+l10iZbzvie06I4GPak4QrQ4gAyXOSzwCrRmJu4gnpeUxZ6IqKtdrKfAYRAcVfNKGA=", } auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_slo() self.assertEqual([], auth.get_errors()) - relay_state = request_data['get_data']['RelayState'] - del request_data['get_data']['RelayState'] + relay_state = request_data["get_data"]["RelayState"] + del request_data["get_data"]["RelayState"] auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_slo() self.assertIn("invalid_logout_response_signature", auth.get_errors()) - request_data['get_data']['RelayState'] = relay_state + request_data["get_data"]["RelayState"] = relay_state settings.set_strict(True) auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_slo() - self.assertIn('invalid_logout_response', auth.get_errors()) + self.assertIn("invalid_logout_response", auth.get_errors()) settings.set_strict(False) - old_signature = request_data['get_data']['Signature'] - request_data['get_data']['Signature'] = 'vfWbbc47PkP3ejx4bjKsRX7lo9Ml1WRoE5J5owF/0mnyKHfSY6XbhO1wwjBV5vWdrUVX+xp6slHyAf4YoAsXFS0qhan6txDiZY4Oec6yE+l10iZbzvie06I4GPak4QrQ4gAyXOSzwCrRmJu4gnpeUxZ6IqKtdrKfAYRAcVf3333=' + old_signature = request_data["get_data"]["Signature"] + request_data["get_data"][ + "Signature" + ] = "vfWbbc47PkP3ejx4bjKsRX7lo9Ml1WRoE5J5owF/0mnyKHfSY6XbhO1wwjBV5vWdrUVX+xp6slHyAf4YoAsXFS0qhan6txDiZY4Oec6yE+l10iZbzvie06I4GPak4QrQ4gAyXOSzwCrRmJu4gnpeUxZ6IqKtdrKfAYRAcVf3333=" auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_slo() - self.assertIn('invalid_logout_response_signature', auth.get_errors()) + self.assertIn("invalid_logout_response_signature", auth.get_errors()) - request_data['get_data']['Signature'] = old_signature - old_signature_algorithm = request_data['get_data']['SigAlg'] - del request_data['get_data']['SigAlg'] + request_data["get_data"]["Signature"] = old_signature + old_signature_algorithm = request_data["get_data"]["SigAlg"] + del request_data["get_data"]["SigAlg"] auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_slo() self.assertEqual([], auth.get_errors()) - request_data['get_data']['RelayState'] = 'http://example.com/relaystate' + request_data["get_data"]["RelayState"] = "http://example.com/relaystate" auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_slo() - self.assertIn('invalid_logout_response_signature', auth.get_errors()) + self.assertIn("invalid_logout_response_signature", auth.get_errors()) settings.set_strict(True) current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data) - plain_message_6 = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(request_data['get_data']['SAMLResponse'])) - plain_message_6 = plain_message_6.replace('https://pitbulk.no-ip.org/newonelogin/demo1/index.php?sls', current_url) - plain_message_6 = plain_message_6.replace('https://pitbulk.no-ip.org/simplesaml/saml2/idp/metadata.php', 'http://idp.example.com/') - request_data['get_data']['SAMLResponse'] = compat.to_string(OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message_6)) + plain_message_6 = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(request_data["get_data"]["SAMLResponse"])) + plain_message_6 = plain_message_6.replace("https://pitbulk.no-ip.org/newonelogin/demo1/index.php?sls", current_url) + plain_message_6 = plain_message_6.replace("https://pitbulk.no-ip.org/simplesaml/saml2/idp/metadata.php", "http://idp.example.com/") + request_data["get_data"]["SAMLResponse"] = compat.to_string(OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message_6)) auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_slo() - self.assertIn('invalid_logout_response_signature', auth.get_errors()) + self.assertIn("invalid_logout_response_signature", auth.get_errors()) settings.set_strict(False) auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_slo() - self.assertIn('invalid_logout_response_signature', auth.get_errors()) + self.assertIn("invalid_logout_response_signature", auth.get_errors()) - request_data['get_data']['SigAlg'] = 'http://www.w3.org/2000/09/xmldsig#dsa-sha1' + request_data["get_data"]["SigAlg"] = "http://www.w3.org/2000/09/xmldsig#dsa-sha1" auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_slo() - self.assertIn('invalid_logout_response_signature', auth.get_errors()) + self.assertIn("invalid_logout_response_signature", auth.get_errors()) settings_info = self.loadSettingsJSON() - settings_info['strict'] = True - settings_info['security']['wantMessagesSigned'] = True + settings_info["strict"] = True + settings_info["security"]["wantMessagesSigned"] = True settings = OneLogin_Saml2_Settings(settings_info) - request_data['get_data']['SigAlg'] = old_signature_algorithm - old_signature = request_data['get_data']['Signature'] - del request_data['get_data']['Signature'] - request_data['get_data']['SAMLResponse'] = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message_6) + request_data["get_data"]["SigAlg"] = old_signature_algorithm + old_signature = request_data["get_data"]["Signature"] + del request_data["get_data"]["Signature"] + request_data["get_data"]["SAMLResponse"] = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message_6) auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_slo() - self.assertIn('Signature validation failed. Logout Response rejected', auth.get_errors()) + self.assertIn("Signature validation failed. Logout Response rejected", auth.get_errors()) - request_data['get_data']['Signature'] = old_signature - settings_info['idp']['certFingerprint'] = 'afe71c28ef740bc87425be13a2263d37971da1f9' - del settings_info['idp']['x509cert'] + request_data["get_data"]["Signature"] = old_signature + settings_info["idp"]["certFingerprint"] = "afe71c28ef740bc87425be13a2263d37971da1f9" + del settings_info["idp"]["x509cert"] settings_2 = OneLogin_Saml2_Settings(settings_info) auth = OneLogin_Saml2_Auth(request_data, old_settings=settings_2) auth.process_slo() - self.assertIn('Signature validation failed. Logout Response rejected', auth.get_errors()) + 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=' - } + "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_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()) + 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 process_slo method of the OneLogin_Saml2_Auth """ request_data = { - 'http_host': 'example.com', - 'script_name': 'index.html', - 'get_data': { - 'SAMLRequest': 'lVLBitswEP0Vo7tjeWzJtki8LIRCYLvbNksPewmyPc6K2pJqyXQ/v1LSQlroQi/DMJr33rwZbZ2cJysezNms/gt+X9H55G2etBOXlx1ZFy2MdMoJLWd0wvfieP/xQcCGCrsYb3ozkRvI+wjpHC5eGU2Sw35HTg3lA8hqZFwWFcMKsStpxbEsxoLXeQN9OdY1VAgk+YqLC8gdCUQB7tyKB+281D6UaF6mtEiBPudcABcMXkiyD26Ulv6CevXeOpFlVvlunb5ttEmV3ZjlnGn8YTRO5qx0NuBs8kzpAd829tXeucmR5NH4J/203I8el6gFRUqbFPJnyEV51Wq30by4TLW0/9ZyarYTxt4sBsjUYLMZvRykl1Fxm90SXVkfwx4P++T4KSafVzmpUcVJ/sfSrQZJPphllv79W8WKGtLx0ir8IrVTqD1pT2MH3QAMSs4KTvui71jeFFiwirOmprwPkYW063+5uRq4urHiiC4e8hCX3J5wqAEGaPpw9XB5JmkBdeDqSlkz6CmUXdl0Qae5kv2F/1384wu3PwE=', - 'RelayState': '_1037fbc88ec82ce8e770b2bed1119747bb812a07e6', - 'SigAlg': 'http://www.w3.org/2000/09/xmldsig#rsa-sha1', - 'Signature': 'XCwCyI5cs7WhiJlB5ktSlWxSBxv+6q2xT3c8L7dLV6NQG9LHWhN7gf8qNsahSXfCzA0Ey9dp5BQ0EdRvAk2DIzKmJY6e3hvAIEp1zglHNjzkgcQmZCcrkK9Czi2Y1WkjOwR/WgUTUWsGJAVqVvlRZuS3zk3nxMrLH6f7toyvuJc=' - } + "http_host": "example.com", + "script_name": "index.html", + "get_data": { + "SAMLRequest": "lVLBitswEP0Vo7tjeWzJtki8LIRCYLvbNksPewmyPc6K2pJqyXQ/v1LSQlroQi/DMJr33rwZbZ2cJysezNms/gt+X9H55G2etBOXlx1ZFy2MdMoJLWd0wvfieP/xQcCGCrsYb3ozkRvI+wjpHC5eGU2Sw35HTg3lA8hqZFwWFcMKsStpxbEsxoLXeQN9OdY1VAgk+YqLC8gdCUQB7tyKB+281D6UaF6mtEiBPudcABcMXkiyD26Ulv6CevXeOpFlVvlunb5ttEmV3ZjlnGn8YTRO5qx0NuBs8kzpAd829tXeucmR5NH4J/203I8el6gFRUqbFPJnyEV51Wq30by4TLW0/9ZyarYTxt4sBsjUYLMZvRykl1Fxm90SXVkfwx4P++T4KSafVzmpUcVJ/sfSrQZJPphllv79W8WKGtLx0ir8IrVTqD1pT2MH3QAMSs4KTvui71jeFFiwirOmprwPkYW063+5uRq4urHiiC4e8hCX3J5wqAEGaPpw9XB5JmkBdeDqSlkz6CmUXdl0Qae5kv2F/1384wu3PwE=", + "RelayState": "_1037fbc88ec82ce8e770b2bed1119747bb812a07e6", + "SigAlg": "http://www.w3.org/2000/09/xmldsig#rsa-sha1", + "Signature": "XCwCyI5cs7WhiJlB5ktSlWxSBxv+6q2xT3c8L7dLV6NQG9LHWhN7gf8qNsahSXfCzA0Ey9dp5BQ0EdRvAk2DIzKmJY6e3hvAIEp1zglHNjzkgcQmZCcrkK9Czi2Y1WkjOwR/WgUTUWsGJAVqVvlRZuS3zk3nxMrLH6f7toyvuJc=", + }, } settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data) - request = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(request_data['get_data']['SAMLRequest'])) + request = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(request_data["get_data"]["SAMLRequest"])) settings.set_strict(False) auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_slo() self.assertEqual([], auth.get_errors()) - relay_state = request_data['get_data']['RelayState'] - del request_data['get_data']['RelayState'] + relay_state = request_data["get_data"]["RelayState"] + del request_data["get_data"]["RelayState"] auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_slo() - self.assertIn('invalid_logout_request_signature', auth.get_errors()) + self.assertIn("invalid_logout_request_signature", auth.get_errors()) - request_data['get_data']['RelayState'] = relay_state + request_data["get_data"]["RelayState"] = relay_state settings.set_strict(True) auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_slo() - self.assertIn('invalid_logout_request', auth.get_errors()) + self.assertIn("invalid_logout_request", auth.get_errors()) settings.set_strict(False) - old_signature = request_data['get_data']['Signature'] - request_data['get_data']['Signature'] = 'vfWbbc47PkP3ejx4bjKsRX7lo9Ml1WRoE5J5owF/0mnyKHfSY6XbhO1wwjBV5vWdrUVX+xp6slHyAf4YoAsXFS0qhan6txDiZY4Oec6yE+l10iZbzvie06I4GPak4QrQ4gAyXOSzwCrRmJu4gnpeUxZ6IqKtdrKfAYRAcVf3333=' + old_signature = request_data["get_data"]["Signature"] + request_data["get_data"][ + "Signature" + ] = "vfWbbc47PkP3ejx4bjKsRX7lo9Ml1WRoE5J5owF/0mnyKHfSY6XbhO1wwjBV5vWdrUVX+xp6slHyAf4YoAsXFS0qhan6txDiZY4Oec6yE+l10iZbzvie06I4GPak4QrQ4gAyXOSzwCrRmJu4gnpeUxZ6IqKtdrKfAYRAcVf3333=" auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_slo() - self.assertIn('invalid_logout_request_signature', auth.get_errors()) + self.assertIn("invalid_logout_request_signature", auth.get_errors()) - request_data['get_data']['Signature'] = old_signature - old_signature_algorithm = request_data['get_data']['SigAlg'] - del request_data['get_data']['SigAlg'] + request_data["get_data"]["Signature"] = old_signature + old_signature_algorithm = request_data["get_data"]["SigAlg"] + del request_data["get_data"]["SigAlg"] auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_slo() self.assertEqual([], auth.get_errors()) settings.set_strict(True) - request_2 = request.replace('https://pitbulk.no-ip.org/newonelogin/demo1/index.php?sls', current_url) - request_2 = request_2.replace('https://pitbulk.no-ip.org/simplesaml/saml2/idp/metadata.php', 'http://idp.example.com/') - request_data['get_data']['SAMLRequest'] = OneLogin_Saml2_Utils.deflate_and_base64_encode(request_2) + request_2 = request.replace("https://pitbulk.no-ip.org/newonelogin/demo1/index.php?sls", current_url) + request_2 = request_2.replace("https://pitbulk.no-ip.org/simplesaml/saml2/idp/metadata.php", "http://idp.example.com/") + request_data["get_data"]["SAMLRequest"] = OneLogin_Saml2_Utils.deflate_and_base64_encode(request_2) auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_slo() - self.assertIn('invalid_logout_request_signature', auth.get_errors()) + self.assertIn("invalid_logout_request_signature", auth.get_errors()) settings.set_strict(False) auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_slo() - self.assertIn('invalid_logout_request_signature', auth.get_errors()) + self.assertIn("invalid_logout_request_signature", auth.get_errors()) - request_data['get_data']['SigAlg'] = 'http://www.w3.org/2000/09/xmldsig#dsa-sha1' + request_data["get_data"]["SigAlg"] = "http://www.w3.org/2000/09/xmldsig#dsa-sha1" auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_slo() - self.assertIn('invalid_logout_request_signature', auth.get_errors()) + self.assertIn("invalid_logout_request_signature", auth.get_errors()) settings_info = self.loadSettingsJSON() - settings_info['strict'] = True - settings_info['security']['wantMessagesSigned'] = True + settings_info["strict"] = True + settings_info["security"]["wantMessagesSigned"] = True settings = OneLogin_Saml2_Settings(settings_info) - request_data['get_data']['SigAlg'] = old_signature_algorithm - old_signature = request_data['get_data']['Signature'] - del request_data['get_data']['Signature'] + request_data["get_data"]["SigAlg"] = old_signature_algorithm + old_signature = request_data["get_data"]["Signature"] + del request_data["get_data"]["Signature"] auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_slo() - self.assertIn('Signature validation failed. Logout Request rejected', auth.get_errors()) + self.assertIn("Signature validation failed. Logout Request rejected", auth.get_errors()) - request_data['get_data']['Signature'] = old_signature - settings_info['idp']['certFingerprint'] = 'afe71c28ef740bc87425be13a2263d37971da1f9' - del settings_info['idp']['x509cert'] + request_data["get_data"]["Signature"] = old_signature + settings_info["idp"]["certFingerprint"] = "afe71c28ef740bc87425be13a2263d37971da1f9" + del settings_info["idp"]["x509cert"] settings_2 = OneLogin_Saml2_Settings(settings_info) auth = OneLogin_Saml2_Auth(request_data, old_settings=settings_2) auth.process_slo() - self.assertIn('Signature validation failed. Logout Request rejected', auth.get_errors()) + 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=' - } + "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_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()) + 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() @@ -1363,95 +1321,92 @@ def testGetLastRequestID(self): def testGetLastSAMLResponse(self): settings = self.loadSettingsJSON() - message = self.file_contents(join(self.data_path, 'responses', 'signed_message_response.xml.base64')) - message_wrapper = {'post_data': {'SAMLResponse': message}} + message = self.file_contents(join(self.data_path, "responses", "signed_message_response.xml.base64")) + message_wrapper = {"post_data": {"SAMLResponse": message}} auth = OneLogin_Saml2_Auth(message_wrapper, old_settings=settings) auth.process_response() - expected_message = self.file_contents(join(self.data_path, 'responses', 'pretty_signed_message_response.xml')) + expected_message = self.file_contents(join(self.data_path, "responses", "pretty_signed_message_response.xml")) self.assertEqual(auth.get_last_response_xml(True), expected_message) # with encrypted assertion - message = self.file_contents(join(self.data_path, 'responses', 'valid_encrypted_assertion.xml.base64')) - message_wrapper = {'post_data': {'SAMLResponse': message}} + message = self.file_contents(join(self.data_path, "responses", "valid_encrypted_assertion.xml.base64")) + message_wrapper = {"post_data": {"SAMLResponse": message}} auth = OneLogin_Saml2_Auth(message_wrapper, old_settings=settings) auth.process_response() - decrypted_response = self.file_contents(join(self.data_path, 'responses', 'decrypted_valid_encrypted_assertion.xml')) + decrypted_response = self.file_contents(join(self.data_path, "responses", "decrypted_valid_encrypted_assertion.xml")) self.assertEqual(auth.get_last_response_xml(False), decrypted_response) - pretty_decrypted_response = self.file_contents(join(self.data_path, 'responses', 'pretty_decrypted_valid_encrypted_assertion.xml')) + pretty_decrypted_response = self.file_contents(join(self.data_path, "responses", "pretty_decrypted_valid_encrypted_assertion.xml")) self.assertEqual(auth.get_last_response_xml(True), pretty_decrypted_response) def testGetLastAuthnRequest(self): settings = self.loadSettingsJSON() - auth = OneLogin_Saml2_Auth({'http_host': 'localhost', 'script_name': 'thing'}, old_settings=settings) + auth = OneLogin_Saml2_Auth({"http_host": "localhost", "script_name": "thing"}, old_settings=settings) auth.login() expectedFragment = ( ' Destination="http://idp.example.com/SSOService.php"\n' ' ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"\n' ' AssertionConsumerServiceURL="http://stuff.com/endpoints/endpoints/acs.php">\n' - ' http://stuff.com/endpoints/metadata.php\n' - ' http://stuff.com/endpoints/metadata.php\n" + " \n' ' \n' - ' urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport\n' - ' \n' + " urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport\n" + " \n" ) self.assertIn(expectedFragment, auth.get_last_request_xml()) def testGetLastAuthnContexts(self): settings = self.loadSettingsJSON() request_data = self.get_request() - message = self.file_contents( - join(self.data_path, 'responses', 'valid_response.xml.base64')) - del request_data['get_data'] - request_data['post_data'] = { - 'SAMLResponse': message - } + message = self.file_contents(join(self.data_path, "responses", "valid_response.xml.base64")) + del request_data["get_data"] + request_data["post_data"] = {"SAMLResponse": message} auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_response() - self.assertEqual(auth.get_last_authn_contexts(), ['urn:oasis:names:tc:SAML:2.0:ac:classes:Password']) + self.assertEqual(auth.get_last_authn_contexts(), ["urn:oasis:names:tc:SAML:2.0:ac:classes:Password"]) def testGetLastLogoutRequest(self): settings = self.loadSettingsJSON() - auth = OneLogin_Saml2_Auth({'http_host': 'localhost', 'script_name': 'thing'}, old_settings=settings) + auth = OneLogin_Saml2_Auth({"http_host": "localhost", "script_name": "thing"}, old_settings=settings) auth.logout() expectedFragment = ( ' Destination="http://idp.example.com/SingleLogoutService.php">\n' - ' http://stuff.com/endpoints/metadata.php\n' + " http://stuff.com/endpoints/metadata.php\n" ' http://idp.example.com/\n' - ' \n' + " \n" ) self.assertIn(expectedFragment, auth.get_last_request_xml()) - request = self.file_contents(join(self.data_path, 'logout_requests', 'logout_request.xml')) + request = self.file_contents(join(self.data_path, "logout_requests", "logout_request.xml")) message = OneLogin_Saml2_Utils.deflate_and_base64_encode(request) - message_wrapper = {'get_data': {'SAMLRequest': message}} + message_wrapper = {"get_data": {"SAMLRequest": message}} auth = OneLogin_Saml2_Auth(message_wrapper, old_settings=settings) auth.process_slo() self.assertEqual(request, auth.get_last_request_xml()) def testGetLastLogoutResponse(self): settings = self.loadSettingsJSON() - request = self.file_contents(join(self.data_path, 'logout_requests', 'logout_request.xml')) + request = self.file_contents(join(self.data_path, "logout_requests", "logout_request.xml")) message = OneLogin_Saml2_Utils.deflate_and_base64_encode(request) - message_wrapper = {'get_data': {'SAMLRequest': message}} + message_wrapper = {"get_data": {"SAMLRequest": message}} auth = OneLogin_Saml2_Auth(message_wrapper, old_settings=settings) auth.process_slo() expectedFragment = ( ' Destination="http://idp.example.com/SingleLogoutService.php"\n' ' InResponseTo="ONELOGIN_21584ccdfaca36a145ae990442dcd96bfe60151e">\n' - ' http://stuff.com/endpoints/metadata.php\n' - ' \n' + " http://stuff.com/endpoints/metadata.php\n" + " \n" ' \n' - ' \n' - '' + " \n" + "" ) self.assertIn(expectedFragment, auth.get_last_response_xml()) - response = self.file_contents(join(self.data_path, 'logout_responses', 'logout_response.xml')) + response = self.file_contents(join(self.data_path, "logout_responses", "logout_response.xml")) message = OneLogin_Saml2_Utils.deflate_and_base64_encode(response) - message_wrapper = {'get_data': {'SAMLResponse': message}} + message_wrapper = {"get_data": {"SAMLResponse": message}} auth = OneLogin_Saml2_Auth(message_wrapper, old_settings=settings) auth.process_slo() self.assertEqual(response, auth.get_last_response_xml()) @@ -1463,24 +1418,22 @@ def testGetInfoFromLastResponseReceived(self): """ settings = self.loadSettingsJSON() request_data = self.get_request() - message = self.file_contents(join(self.data_path, 'responses', 'valid_response.xml.base64')) - del request_data['get_data'] - request_data['post_data'] = { - 'SAMLResponse': message - } + message = self.file_contents(join(self.data_path, "responses", "valid_response.xml.base64")) + del request_data["get_data"] + request_data["post_data"] = {"SAMLResponse": message} 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.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()) self.assertEqual(auth.get_last_assertion_issue_instant(), 1392773821) # NotOnOrAfter is only calculated with strict = true # If invalid, response id and assertion id are not obtained - settings['strict'] = True + settings["strict"] = True auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_response() self.assertNotEqual(len(auth.get_errors()), 0) @@ -1490,15 +1443,15 @@ def testGetInfoFromLastResponseReceived(self): 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' - request_data['script_name'] = '/newonelogin/demo1/index.php?acs' + request_data["https"] = "on" + request_data["http_host"] = "pitbulk.no-ip.org" + request_data["script_name"] = "/newonelogin/demo1/index.php?acs" 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_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) self.assertEqual(auth.get_last_assertion_issue_instant(), 1392773821) @@ -1508,12 +1461,12 @@ def testGetIdFromLogoutRequest(self): Case Valid Logout request """ settings = self.loadSettingsJSON() - request = self.file_contents(join(self.data_path, 'logout_requests', 'logout_request.xml')) + request = self.file_contents(join(self.data_path, "logout_requests", "logout_request.xml")) message = OneLogin_Saml2_Utils.deflate_and_base64_encode(request) - message_wrapper = {'get_data': {'SAMLRequest': message}} + message_wrapper = {"get_data": {"SAMLRequest": message}} auth = OneLogin_Saml2_Auth(message_wrapper, old_settings=settings) auth.process_slo() - self.assertIn(auth.get_last_message_id(), 'ONELOGIN_21584ccdfaca36a145ae990442dcd96bfe60151e') + self.assertIn(auth.get_last_message_id(), "ONELOGIN_21584ccdfaca36a145ae990442dcd96bfe60151e") def testGetIdFromLogoutResponse(self): """ @@ -1521,9 +1474,9 @@ def testGetIdFromLogoutResponse(self): Case Valid Logout response """ settings = self.loadSettingsJSON() - response = self.file_contents(join(self.data_path, 'logout_responses', 'logout_response.xml')) + response = self.file_contents(join(self.data_path, "logout_responses", "logout_response.xml")) message = OneLogin_Saml2_Utils.deflate_and_base64_encode(response) - message_wrapper = {'get_data': {'SAMLResponse': message}} + message_wrapper = {"get_data": {"SAMLResponse": message}} auth = OneLogin_Saml2_Auth(message_wrapper, old_settings=settings) auth.process_slo() - self.assertIn(auth.get_last_message_id(), '_f9ee61bd9dbf63606faa9ae3b10548d5b3656fb859') + self.assertIn(auth.get_last_message_id(), "_f9ee61bd9dbf63606faa9ae3b10548d5b3656fb859") diff --git a/tests/src/OneLogin/saml2_tests/authn_request_test.py b/tests/src/OneLogin/saml2_tests/authn_request_test.py index af7aa2cc..b3e187a5 100644 --- a/tests/src/OneLogin/saml2_tests/authn_request_test.py +++ b/tests/src/OneLogin/saml2_tests/authn_request_test.py @@ -20,24 +20,24 @@ class OneLogin_Saml2_Authn_Request_Test(unittest.TestCase): - settings_path = join(dirname(dirname(dirname(dirname(__file__)))), 'settings') + settings_path = join(dirname(dirname(dirname(dirname(__file__)))), "settings") # assertRegexpMatches deprecated on python3 def assertRegex(self, text, regexp, msg=None): - if hasattr(unittest.TestCase, 'assertRegex'): + if hasattr(unittest.TestCase, "assertRegex"): return super(OneLogin_Saml2_Authn_Request_Test, self).assertRegex(text, regexp, msg) else: return self.assertRegexpMatches(text, regexp, msg) - def loadSettingsJSON(self, name='settings1.json'): + def loadSettingsJSON(self, name="settings1.json"): filename = join(self.settings_path, name) if exists(filename): - stream = open(filename, 'r') + stream = open(filename, "r") settings = json.load(stream) stream.close() return settings else: - raise Exception('Settings json file does not exist') + raise Exception("Settings json file does not exist") def setUp(self): self.__settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) @@ -50,26 +50,21 @@ def testCreateRequest(self): saml_settings = self.loadSettingsJSON() settings = OneLogin_Saml2_Settings(saml_settings) - settings._organization = { - u'en-US': { - u'url': u'http://sp.example.com', - u'name': u'sp_test' - } - } + settings._organization = {"en-US": {"url": "http://sp.example.com", "name": "sp_test"}} authn_request = OneLogin_Saml2_Authn_Request(settings) authn_request_encoded = authn_request.get_request() inflated = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(authn_request_encoded)) - self.assertRegex(inflated, '^', inflated) + self.assertRegex(inflated, "^", inflated) - authn_request_2 = OneLogin_Saml2_Authn_Request(settings, name_id_value_req='testuser@example.com') + authn_request_2 = OneLogin_Saml2_Authn_Request(settings, name_id_value_req="testuser@example.com") authn_request_encoded_2 = authn_request_2.get_request() inflated_2 = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(authn_request_encoded_2)) - self.assertRegex(inflated_2, '^', inflated_2) + self.assertRegex(inflated_2, "^", inflated_2) self.assertIn('Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">testuser@example.com', inflated_2) self.assertIn('', inflated_2) - saml_settings['sp']['NameIDFormat'] = 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress' + saml_settings["sp"]["NameIDFormat"] = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" settings = OneLogin_Saml2_Settings(saml_settings) - authn_request_3 = OneLogin_Saml2_Authn_Request(settings, name_id_value_req='testuser@example.com') + authn_request_3 = OneLogin_Saml2_Authn_Request(settings, name_id_value_req="testuser@example.com") authn_request_encoded_3 = authn_request_3.get_request() inflated_3 = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(authn_request_encoded_3)) - self.assertRegex(inflated_3, '^', inflated_3) + self.assertRegex(inflated_3, "^", inflated_3) self.assertIn('Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">testuser@example.com', inflated_3) self.assertIn('', inflated_3) @@ -292,16 +287,14 @@ def testCreateDeflatedSAMLRequestURLParameter(self): The creation of a deflated SAML Request """ authn_request = OneLogin_Saml2_Authn_Request(self.__settings) - parameters = { - 'SAMLRequest': authn_request.get_request() - } - auth_url = OneLogin_Saml2_Utils.redirect('http://idp.example.com/SSOService.php', parameters, True) - self.assertRegex(auth_url, r'^http://idp\.example\.com\/SSOService\.php\?SAMLRequest=') + parameters = {"SAMLRequest": authn_request.get_request()} + auth_url = OneLogin_Saml2_Utils.redirect("http://idp.example.com/SSOService.php", parameters, True) + self.assertRegex(auth_url, r"^http://idp\.example\.com\/SSOService\.php\?SAMLRequest=") exploded = urlparse(auth_url) exploded = parse_qs(exploded[4]) - payload = exploded['SAMLRequest'][0] + payload = exploded["SAMLRequest"][0] inflated = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(payload)) - self.assertRegex(inflated, '^') - self.assertRegex(inflated, 'http://stuff.com/endpoints/metadata.php') + self.assertRegex(inflated, "http://stuff.com/endpoints/metadata.php") self.assertRegex(inflated, 'Format="urn:oasis:names:tc:SAML:2.0:nameid-format:encrypted"') self.assertRegex(inflated, 'ProviderName="SP prueba"') @@ -345,7 +330,7 @@ def testGetID(self): authn_request_encoded = authn_request.get_request() inflated = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(authn_request_encoded)) document = OneLogin_Saml2_XML.to_etree(inflated) - self.assertEqual(authn_request.get_id(), document.get('ID', None)) + self.assertEqual(authn_request.get_id(), document.get("ID", None)) def testAttributeConsumingService(self): """ @@ -360,7 +345,7 @@ def testAttributeConsumingService(self): self.assertNotIn('AttributeConsumingServiceIndex="1"', inflated) - saml_settings = self.loadSettingsJSON('settings4.json') + saml_settings = self.loadSettingsJSON("settings4.json") settings = OneLogin_Saml2_Settings(saml_settings) authn_request = OneLogin_Saml2_Authn_Request(settings) diff --git a/tests/src/OneLogin/saml2_tests/error_test.py b/tests/src/OneLogin/saml2_tests/error_test.py index 7d311130..23ed4931 100644 --- a/tests/src/OneLogin/saml2_tests/error_test.py +++ b/tests/src/OneLogin/saml2_tests/error_test.py @@ -11,5 +11,5 @@ class OneLogin_Saml2_Error_Test(unittest.TestCase): """ def runTest(self): - exception = OneLogin_Saml2_Error('test') - self.assertEqual(str(exception), 'test') + exception = OneLogin_Saml2_Error("test") + self.assertEqual(str(exception), "test") 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 3d027ed1..cff9418b 100644 --- a/tests/src/OneLogin/saml2_tests/idp_metadata_parser_test.py +++ b/tests/src/OneLogin/saml2_tests/idp_metadata_parser_test.py @@ -22,21 +22,21 @@ class OneLogin_Saml2_IdPMetadataParser_Test(unittest.TestCase): # Set self.maxDiff to None to see it." from showing up. maxDiff = None - data_path = join(dirname(dirname(dirname(dirname(__file__)))), 'data') - settings_path = join(dirname(dirname(dirname(dirname(__file__)))), 'settings') + data_path = join(dirname(dirname(dirname(dirname(__file__)))), "data") + settings_path = join(dirname(dirname(dirname(dirname(__file__)))), "settings") - def loadSettingsJSON(self, filename='settings1.json'): + def loadSettingsJSON(self, filename="settings1.json"): filename = join(self.settings_path, filename) if exists(filename): - stream = open(filename, 'r') + stream = open(filename, "r") settings = json.load(stream) stream.close() return settings else: - raise Exception('Settings json file does not exist') + raise Exception("Settings json file does not exist") def file_contents(self, filename): - f = open(filename, 'r') + f = open(filename, "r") content = f.read() f.close() return content @@ -46,31 +46,32 @@ def testGetMetadata(self): Tests the get_metadata method of the OneLogin_Saml2_IdPMetadataParser """ with self.assertRaises(Exception): - data = OneLogin_Saml2_IdPMetadataParser.get_metadata('http://google.es') + data = OneLogin_Saml2_IdPMetadataParser.get_metadata("http://google.es", validate_cert=False) try: - data = OneLogin_Saml2_IdPMetadataParser.get_metadata('https://idp.testshib.org/idp/shibboleth') + data = OneLogin_Saml2_IdPMetadataParser.get_metadata("https://raw.githubusercontent.com/SAML-Toolkits/python3-saml/master/tests/data/metadata/testshib-providers.xml", validate_cert=False) self.assertTrue(data is not None and data is not {}) except URLError: pass def testGetMetadataWithHeaders(self): - data = OneLogin_Saml2_IdPMetadataParser.get_metadata('https://samltest.id/saml/providers', - headers={'User-Agent': 'Mozilla/5.0'}) + data = OneLogin_Saml2_IdPMetadataParser.get_metadata( + "https://raw.githubusercontent.com/SAML-Toolkits/python3-saml/master/tests/data/metadata/testshib-providers.xml", validate_cert=False, headers={"User-Agent": "Mozilla/5.0"} + ) self.assertIsNotNone(data) - self.assertIn(b'entityID=', data) + self.assertIn(b"entityID=", data) def testParseRemote(self): """ Tests the parse_remote method of the OneLogin_Saml2_IdPMetadataParser """ with self.assertRaises(Exception): - data = OneLogin_Saml2_IdPMetadataParser.parse_remote('http://google.es') + data = OneLogin_Saml2_IdPMetadataParser.parse_remote("http://google.es", validate_cert=False) try: - data = OneLogin_Saml2_IdPMetadataParser.parse_remote('https://idp.testshib.org/idp/shibboleth') + data = OneLogin_Saml2_IdPMetadataParser.parse_remote("https://raw.githubusercontent.com/SAML-Toolkits/python3-saml/master/tests/data/metadata/testshib-providers.xml", validate_cert=False) except URLError: - xml = self.file_contents(join(self.data_path, 'metadata', 'testshib-providers.xml')) + xml = self.file_contents(join(self.data_path, "metadata", "testshib-providers.xml")) data = OneLogin_Saml2_IdPMetadataParser.parse(xml) self.assertTrue(data is not None and data is not {}) @@ -96,23 +97,23 @@ 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']) + data = OneLogin_Saml2_IdPMetadataParser.parse_remote("https://raw.githubusercontent.com/SAML-Toolkits/python3-saml/master/tests/data/metadata/testshib-providers.xml", validate_cert=False) + self.assertEqual(data["idp"]["entityId"], "https://idp.testshib.org/idp/shibboleth") + self.assertIsNotNone(data["idp"]["singleSignOnService"]["url"]) + self.assertIsNotNone(data["idp"]["x509cert"]) def testParse(self): """ Tests the parse method of the OneLogin_Saml2_IdPMetadataParser """ with self.assertRaises(XMLSyntaxError): - data = OneLogin_Saml2_IdPMetadataParser.parse('') + data = OneLogin_Saml2_IdPMetadataParser.parse("") - xml_sp_metadata = self.file_contents(join(self.data_path, 'metadata', 'metadata_settings1.xml')) + xml_sp_metadata = self.file_contents(join(self.data_path, "metadata", "metadata_settings1.xml")) data = OneLogin_Saml2_IdPMetadataParser.parse(xml_sp_metadata) self.assertEqual({}, data) - xml_idp_metadata = self.file_contents(join(self.data_path, 'metadata', 'idp_metadata.xml')) + xml_idp_metadata = self.file_contents(join(self.data_path, "metadata", "idp_metadata.xml")) data = OneLogin_Saml2_IdPMetadataParser.parse(xml_idp_metadata) # W/o further specification, expect to get the redirect binding SSO @@ -160,18 +161,14 @@ def test_parse_testshib_required_binding_sso_redirect(self): } """ try: - xmldoc = OneLogin_Saml2_IdPMetadataParser.get_metadata( - 'https://idp.testshib.org/idp/shibboleth') + xmldoc = OneLogin_Saml2_IdPMetadataParser.get_metadata("https://idp.testshib.org/idp/shibboleth") except URLError: - xmldoc = self.file_contents(join(self.data_path, 'metadata', 'testshib-providers.xml')) + xmldoc = self.file_contents(join(self.data_path, "metadata", "testshib-providers.xml")) # Parse, require SSO REDIRECT binding, implicitly. settings1 = OneLogin_Saml2_IdPMetadataParser.parse(xmldoc) # Parse, require SSO REDIRECT binding, explicitly. - settings2 = OneLogin_Saml2_IdPMetadataParser.parse( - xmldoc, - required_sso_binding=OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT - ) + settings2 = OneLogin_Saml2_IdPMetadataParser.parse(xmldoc, required_sso_binding=OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT) expected_settings = json.loads(expected_settings_json) self.assertEqual(expected_settings, settings1) self.assertEqual(expected_settings, settings2) @@ -198,16 +195,12 @@ def test_parse_testshib_required_binding_sso_post(self): } """ try: - xmldoc = OneLogin_Saml2_IdPMetadataParser.get_metadata( - 'https://idp.testshib.org/idp/shibboleth') + xmldoc = OneLogin_Saml2_IdPMetadataParser.get_metadata("https://idp.testshib.org/idp/shibboleth") except URLError: - xmldoc = self.file_contents(join(self.data_path, 'metadata', 'testshib-providers.xml')) + xmldoc = self.file_contents(join(self.data_path, "metadata", "testshib-providers.xml")) # Parse, require POST binding. - settings = OneLogin_Saml2_IdPMetadataParser.parse( - xmldoc, - required_sso_binding=OneLogin_Saml2_Constants.BINDING_HTTP_POST - ) + settings = OneLogin_Saml2_IdPMetadataParser.parse(xmldoc, required_sso_binding=OneLogin_Saml2_Constants.BINDING_HTTP_POST) expected_settings = json.loads(expected_settings_json) self.assertEqual(expected_settings, settings) @@ -238,7 +231,7 @@ def test_parse_required_binding_all(self): } } """ - xmldoc = self.file_contents(join(self.data_path, 'metadata', 'idp_metadata2.xml')) + xmldoc = self.file_contents(join(self.data_path, "metadata", "idp_metadata2.xml")) expected_settings = json.loads(expected_settings_json) @@ -247,50 +240,30 @@ def test_parse_required_binding_all(self): # Parse, require SLO and SSO REDIRECT binding, explicitly. settings2 = OneLogin_Saml2_IdPMetadataParser.parse( - xmldoc, - required_sso_binding=OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT, - required_slo_binding=OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT + xmldoc, required_sso_binding=OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT, required_slo_binding=OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT ) expected_settings1_2 = deepcopy(expected_settings) self.assertEqual(expected_settings1_2, settings1) self.assertEqual(expected_settings1_2, settings2) - settings3 = OneLogin_Saml2_IdPMetadataParser.parse( - xmldoc, - required_sso_binding=OneLogin_Saml2_Constants.BINDING_HTTP_POST, - required_slo_binding=OneLogin_Saml2_Constants.BINDING_HTTP_POST - ) + settings3 = OneLogin_Saml2_IdPMetadataParser.parse(xmldoc, required_sso_binding=OneLogin_Saml2_Constants.BINDING_HTTP_POST, required_slo_binding=OneLogin_Saml2_Constants.BINDING_HTTP_POST) expected_settings3 = deepcopy(expected_settings) - del expected_settings3['idp']['singleLogoutService'] - del expected_settings3['idp']['singleSignOnService'] + del expected_settings3["idp"]["singleLogoutService"] + del expected_settings3["idp"]["singleSignOnService"] self.assertEqual(expected_settings3, settings3) - settings4 = OneLogin_Saml2_IdPMetadataParser.parse( - xmldoc, - required_sso_binding=OneLogin_Saml2_Constants.BINDING_HTTP_POST, - required_slo_binding=OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT - ) - settings5 = OneLogin_Saml2_IdPMetadataParser.parse( - xmldoc, - required_sso_binding=OneLogin_Saml2_Constants.BINDING_HTTP_POST - ) + settings4 = OneLogin_Saml2_IdPMetadataParser.parse(xmldoc, required_sso_binding=OneLogin_Saml2_Constants.BINDING_HTTP_POST, required_slo_binding=OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT) + settings5 = OneLogin_Saml2_IdPMetadataParser.parse(xmldoc, required_sso_binding=OneLogin_Saml2_Constants.BINDING_HTTP_POST) expected_settings4_5 = deepcopy(expected_settings) - del expected_settings4_5['idp']['singleSignOnService'] + del expected_settings4_5["idp"]["singleSignOnService"] self.assertEqual(expected_settings4_5, settings4) self.assertEqual(expected_settings4_5, settings5) - settings6 = OneLogin_Saml2_IdPMetadataParser.parse( - xmldoc, - required_sso_binding=OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT, - required_slo_binding=OneLogin_Saml2_Constants.BINDING_HTTP_POST - ) - settings7 = OneLogin_Saml2_IdPMetadataParser.parse( - xmldoc, - required_slo_binding=OneLogin_Saml2_Constants.BINDING_HTTP_POST - ) + settings6 = OneLogin_Saml2_IdPMetadataParser.parse(xmldoc, required_sso_binding=OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT, required_slo_binding=OneLogin_Saml2_Constants.BINDING_HTTP_POST) + settings7 = OneLogin_Saml2_IdPMetadataParser.parse(xmldoc, required_slo_binding=OneLogin_Saml2_Constants.BINDING_HTTP_POST) expected_settings6_7 = deepcopy(expected_settings) - del expected_settings6_7['idp']['singleLogoutService'] + del expected_settings6_7["idp"]["singleLogoutService"] self.assertEqual(expected_settings6_7, settings6) self.assertEqual(expected_settings6_7, settings7) @@ -300,7 +273,7 @@ def test_parse_with_entity_id(self): Case: Provide entity_id to identify the desired IdPDescriptor from EntitiesDescriptor """ - xml_idp_metadata = self.file_contents(join(self.data_path, 'metadata', 'idp_multiple_descriptors.xml')) + xml_idp_metadata = self.file_contents(join(self.data_path, "metadata", "idp_multiple_descriptors.xml")) # should find first descriptor data = OneLogin_Saml2_IdPMetadataParser.parse(xml_idp_metadata) @@ -361,7 +334,7 @@ def test_parse_multi_certs(self): Tests the parse method of the OneLogin_Saml2_IdPMetadataParser Case: IdP metadata contains multiple certs """ - xml_idp_metadata = self.file_contents(join(self.data_path, 'metadata', 'idp_metadata_multi_certs.xml')) + xml_idp_metadata = self.file_contents(join(self.data_path, "metadata", "idp_metadata_multi_certs.xml")) data = OneLogin_Saml2_IdPMetadataParser.parse(xml_idp_metadata) expected_settings_json = """ @@ -399,7 +372,7 @@ def test_parse_multi_singing_certs(self): Tests the parse method of the OneLogin_Saml2_IdPMetadataParser Case: IdP metadata contains multiple signing certs and no encryption certs """ - xml_idp_metadata = self.file_contents(join(self.data_path, 'metadata', 'idp_metadata_multi_signing_certs.xml')) + xml_idp_metadata = self.file_contents(join(self.data_path, "metadata", "idp_metadata_multi_signing_certs.xml")) data = OneLogin_Saml2_IdPMetadataParser.parse(xml_idp_metadata) expected_settings_json = """ @@ -436,7 +409,7 @@ def test_parse_multi_same_signing_and_encrypt_cert(self): Case: IdP metadata contains multiple signature cert and encrypt cert that is the same """ - xml_idp_metadata = self.file_contents(join(self.data_path, 'metadata', 'idp_metadata_same_sign_and_encrypt_cert.xml')) + xml_idp_metadata = self.file_contents(join(self.data_path, "metadata", "idp_metadata_same_sign_and_encrypt_cert.xml")) data = OneLogin_Saml2_IdPMetadataParser.parse(xml_idp_metadata) expected_settings_json = """ @@ -457,7 +430,7 @@ def test_parse_multi_same_signing_and_encrypt_cert(self): expected_settings = json.loads(expected_settings_json) self.assertEqual(expected_settings, data) - xml_idp_metadata_2 = self.file_contents(join(self.data_path, 'metadata', 'idp_metadata_different_sign_and_encrypt_cert.xml')) + xml_idp_metadata_2 = self.file_contents(join(self.data_path, "metadata", "idp_metadata_different_sign_and_encrypt_cert.xml")) data_2 = OneLogin_Saml2_IdPMetadataParser.parse(xml_idp_metadata_2) expected_settings_json_2 = """ { @@ -494,7 +467,7 @@ def test_merge_settings(self): with self.assertRaises(TypeError): settings_result = OneLogin_Saml2_IdPMetadataParser.merge_settings({}, None) - xml_idp_metadata = self.file_contents(join(self.data_path, 'metadata', 'idp_metadata.xml')) + xml_idp_metadata = self.file_contents(join(self.data_path, "metadata", "idp_metadata.xml")) # Parse XML metadata. data = OneLogin_Saml2_IdPMetadataParser.parse(xml_idp_metadata) @@ -620,7 +593,7 @@ def test_merge_settings(self): self.assertEqual(expected_settings2, settings_result2) # Test merging multiple certs - xml_idp_metadata = self.file_contents(join(self.data_path, 'metadata', 'idp_metadata_multi_certs.xml')) + xml_idp_metadata = self.file_contents(join(self.data_path, "metadata", "idp_metadata_multi_certs.xml")) data3 = OneLogin_Saml2_IdPMetadataParser.parse(xml_idp_metadata) settings_result3 = OneLogin_Saml2_IdPMetadataParser.merge_settings(settings, data3) expected_settings3_json = """ diff --git a/tests/src/OneLogin/saml2_tests/logout_request_test.py b/tests/src/OneLogin/saml2_tests/logout_request_test.py index cabe7b47..76994175 100644 --- a/tests/src/OneLogin/saml2_tests/logout_request_test.py +++ b/tests/src/OneLogin/saml2_tests/logout_request_test.py @@ -18,35 +18,35 @@ class OneLogin_Saml2_Logout_Request_Test(unittest.TestCase): - data_path = join(dirname(dirname(dirname(dirname(__file__)))), 'data') - settings_path = join(dirname(dirname(dirname(dirname(__file__)))), 'settings') + data_path = join(dirname(dirname(dirname(dirname(__file__)))), "data") + settings_path = join(dirname(dirname(dirname(dirname(__file__)))), "settings") # assertRegexpMatches deprecated on python3 def assertRegex(self, text, regexp, msg=None): - if hasattr(unittest.TestCase, 'assertRegex'): + if hasattr(unittest.TestCase, "assertRegex"): return super(OneLogin_Saml2_Logout_Request_Test, self).assertRegex(text, regexp, msg) else: return self.assertRegexpMatches(text, regexp, msg) # assertRaisesRegexp deprecated on python3 def assertRaisesRegex(self, exception, regexp, msg=None): - if hasattr(unittest.TestCase, 'assertRaisesRegex'): + if hasattr(unittest.TestCase, "assertRaisesRegex"): return super(OneLogin_Saml2_Logout_Request_Test, self).assertRaisesRegex(exception, regexp, msg=msg) else: return self.assertRaisesRegexp(exception, regexp) - def loadSettingsJSON(self, name='settings1.json'): + def loadSettingsJSON(self, name="settings1.json"): filename = join(self.settings_path, name) if exists(filename): - stream = open(filename, 'r') + stream = open(filename, "r") settings = json.load(stream) stream.close() return settings else: - raise Exception('Settings json file does not exist') + raise Exception("Settings json file does not exist") def file_contents(self, filename): - f = open(filename, 'r') + f = open(filename, "r") content = f.read() f.close() return content @@ -56,19 +56,19 @@ def testConstructor(self): Tests the OneLogin_Saml2_LogoutRequest Constructor. """ settings_info = self.loadSettingsJSON() - settings_info['security']['nameIdEncrypted'] = True + settings_info["security"]["nameIdEncrypted"] = True settings = OneLogin_Saml2_Settings(settings_info) logout_request = OneLogin_Saml2_Logout_Request(settings) - parameters = {'SAMLRequest': logout_request.get_request()} - logout_url = OneLogin_Saml2_Utils.redirect('http://idp.example.com/SingleLogoutService.php', parameters, True) - self.assertRegex(logout_url, r'^http://idp\.example\.com\/SingleLogoutService\.php\?SAMLRequest=') + parameters = {"SAMLRequest": logout_request.get_request()} + logout_url = OneLogin_Saml2_Utils.redirect("http://idp.example.com/SingleLogoutService.php", parameters, True) + self.assertRegex(logout_url, r"^http://idp\.example\.com\/SingleLogoutService\.php\?SAMLRequest=") url_parts = urlparse(logout_url) exploded = parse_qs(url_parts.query) - payload = exploded['SAMLRequest'][0] + payload = exploded["SAMLRequest"][0] inflated = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(payload)) - self.assertRegex(inflated, '^') + self.assertRegex(inflated, "^") def testGetIDFromSAMLLogoutRequest(self): """ Tests the get_id method of the OneLogin_Saml2_LogoutRequest """ - logout_request = self.file_contents(join(self.data_path, 'logout_requests', 'logout_request.xml')) + logout_request = self.file_contents(join(self.data_path, "logout_requests", "logout_request.xml")) id1 = OneLogin_Saml2_Logout_Request.get_id(logout_request) - self.assertEqual('ONELOGIN_21584ccdfaca36a145ae990442dcd96bfe60151e', id1) + self.assertEqual("ONELOGIN_21584ccdfaca36a145ae990442dcd96bfe60151e", id1) dom = parseString(logout_request) id2 = OneLogin_Saml2_Logout_Request.get_id(dom.toxml()) - self.assertEqual('ONELOGIN_21584ccdfaca36a145ae990442dcd96bfe60151e', id2) + self.assertEqual("ONELOGIN_21584ccdfaca36a145ae990442dcd96bfe60151e", id2) def testGetIDFromDeflatedSAMLLogoutRequest(self): """ Tests the get_id method of the OneLogin_Saml2_LogoutRequest """ - deflated_logout_request = self.file_contents(join(self.data_path, 'logout_requests', 'logout_request_deflated.xml.base64')) + deflated_logout_request = self.file_contents(join(self.data_path, "logout_requests", "logout_request_deflated.xml.base64")) logout_request = OneLogin_Saml2_Utils.decode_base64_and_inflate(deflated_logout_request) id1 = OneLogin_Saml2_Logout_Request.get_id(logout_request) - self.assertEqual('ONELOGIN_21584ccdfaca36a145ae990442dcd96bfe60151e', id1) + self.assertEqual("ONELOGIN_21584ccdfaca36a145ae990442dcd96bfe60151e", id1) def testGetNameIdData(self): """ Tests the get_nameid_data method of the OneLogin_Saml2_LogoutRequest """ expected_name_id_data = { - 'Value': 'ONELOGIN_1e442c129e1f822c8096086a1103c5ee2c7cae1c', - 'Format': 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified', - 'SPNameQualifier': 'http://idp.example.com/' + "Value": "ONELOGIN_1e442c129e1f822c8096086a1103c5ee2c7cae1c", + "Format": "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", + "SPNameQualifier": "http://idp.example.com/", } - request = self.file_contents(join(self.data_path, 'logout_requests', 'logout_request.xml')) + request = self.file_contents(join(self.data_path, "logout_requests", "logout_request.xml")) name_id_data = OneLogin_Saml2_Logout_Request.get_nameid_data(request) self.assertEqual(expected_name_id_data, name_id_data) @@ -184,54 +179,48 @@ def testGetNameIdData(self): name_id_data_2 = OneLogin_Saml2_Logout_Request.get_nameid_data(dom.toxml()) self.assertEqual(expected_name_id_data, name_id_data_2) - request_2 = self.file_contents(join(self.data_path, 'logout_requests', 'logout_request_encrypted_nameid.xml')) - with self.assertRaisesRegex(Exception, 'Key is required in order to decrypt the NameID'): + request_2 = self.file_contents(join(self.data_path, "logout_requests", "logout_request_encrypted_nameid.xml")) + with self.assertRaisesRegex(Exception, "Key is required in order to decrypt the NameID"): OneLogin_Saml2_Logout_Request.get_nameid(request_2) settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) key = settings.get_sp_key() name_id_data_4 = OneLogin_Saml2_Logout_Request.get_nameid_data(request_2, key) expected_name_id_data = { - 'Value': 'ONELOGIN_9c86c4542ab9d6fce07f2f7fd335287b9b3cdf69', - 'Format': 'urn:oasis:names:tc:SAML:2.0:nameid-format:emailAddress', - 'SPNameQualifier': 'https://pitbulk.no-ip.org/newonelogin/demo1/metadata.php' + "Value": "ONELOGIN_9c86c4542ab9d6fce07f2f7fd335287b9b3cdf69", + "Format": "urn:oasis:names:tc:SAML:2.0:nameid-format:emailAddress", + "SPNameQualifier": "https://pitbulk.no-ip.org/newonelogin/demo1/metadata.php", } self.assertEqual(expected_name_id_data, name_id_data_4) dom_2 = parseString(request_2) - encrypted_id_nodes = dom_2.getElementsByTagName('saml:EncryptedID') + encrypted_id_nodes = dom_2.getElementsByTagName("saml:EncryptedID") encrypted_data = encrypted_id_nodes[0].firstChild.nextSibling encrypted_id_nodes[0].removeChild(encrypted_data) - with self.assertRaisesRegex(Exception, 'NameID not found in the Logout Request'): + with self.assertRaisesRegex(Exception, "NameID not found in the Logout Request"): OneLogin_Saml2_Logout_Request.get_nameid(dom_2.toxml(), key) - inv_request = self.file_contents(join(self.data_path, 'logout_requests', 'invalids', 'no_nameId.xml')) - with self.assertRaisesRegex(Exception, 'NameID not found in the Logout Request'): + inv_request = self.file_contents(join(self.data_path, "logout_requests", "invalids", "no_nameId.xml")) + with self.assertRaisesRegex(Exception, "NameID not found in the Logout Request"): OneLogin_Saml2_Logout_Request.get_nameid(inv_request) idp_data = settings.get_idp_data() expected_name_id_data = { - 'Format': 'urn:oasis:names:tc:SAML:2.0:nameid-format:emailAddress', - 'NameQualifier': idp_data['entityId'], - 'Value': 'ONELOGIN_9c86c4542ab9d6fce07f2f7fd335287b9b3cdf69' + "Format": "urn:oasis:names:tc:SAML:2.0:nameid-format:emailAddress", + "NameQualifier": idp_data["entityId"], + "Value": "ONELOGIN_9c86c4542ab9d6fce07f2f7fd335287b9b3cdf69", } - logout_request = OneLogin_Saml2_Logout_Request(settings, None, expected_name_id_data['Value'], None, idp_data['entityId'], expected_name_id_data['Format']) + logout_request = OneLogin_Saml2_Logout_Request(settings, None, expected_name_id_data["Value"], None, idp_data["entityId"], expected_name_id_data["Format"]) name_id_data_3 = OneLogin_Saml2_Logout_Request.get_nameid_data(logout_request.get_xml()) self.assertEqual(expected_name_id_data, name_id_data_3) - expected_name_id_data = { - 'Format': 'urn:oasis:names:tc:SAML:2.0:nameid-format:emailAddress', - 'Value': 'ONELOGIN_9c86c4542ab9d6fce07f2f7fd335287b9b3cdf69' - } - logout_request = OneLogin_Saml2_Logout_Request(settings, None, expected_name_id_data['Value'], None, None, expected_name_id_data['Format']) + expected_name_id_data = {"Format": "urn:oasis:names:tc:SAML:2.0:nameid-format:emailAddress", "Value": "ONELOGIN_9c86c4542ab9d6fce07f2f7fd335287b9b3cdf69"} + logout_request = OneLogin_Saml2_Logout_Request(settings, None, expected_name_id_data["Value"], None, None, expected_name_id_data["Format"]) name_id_data_4 = OneLogin_Saml2_Logout_Request.get_nameid_data(logout_request.get_xml()) self.assertEqual(expected_name_id_data, name_id_data_4) - expected_name_id_data = { - 'Format': 'urn:oasis:names:tc:SAML:2.0:nameid-format:entity', - 'Value': 'http://idp.example.com/' - } + expected_name_id_data = {"Format": "urn:oasis:names:tc:SAML:2.0:nameid-format:entity", "Value": "http://idp.example.com/"} logout_request = OneLogin_Saml2_Logout_Request(settings) name_id_data_5 = OneLogin_Saml2_Logout_Request.get_nameid_data(logout_request.get_xml()) self.assertEqual(expected_name_id_data, name_id_data_5) @@ -240,33 +229,33 @@ def testGetNameId(self): """ Tests the get_nameid of the OneLogin_Saml2_LogoutRequest """ - request = self.file_contents(join(self.data_path, 'logout_requests', 'logout_request.xml')) + request = self.file_contents(join(self.data_path, "logout_requests", "logout_request.xml")) name_id = OneLogin_Saml2_Logout_Request.get_nameid(request) - self.assertEqual(name_id, 'ONELOGIN_1e442c129e1f822c8096086a1103c5ee2c7cae1c') + self.assertEqual(name_id, "ONELOGIN_1e442c129e1f822c8096086a1103c5ee2c7cae1c") - request_2 = self.file_contents(join(self.data_path, 'logout_requests', 'logout_request_encrypted_nameid.xml')) - with self.assertRaisesRegex(Exception, 'Key is required in order to decrypt the NameID'): + request_2 = self.file_contents(join(self.data_path, "logout_requests", "logout_request_encrypted_nameid.xml")) + with self.assertRaisesRegex(Exception, "Key is required in order to decrypt the NameID"): OneLogin_Saml2_Logout_Request.get_nameid(request_2) settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) key = settings.get_sp_key() name_id_3 = OneLogin_Saml2_Logout_Request.get_nameid(request_2, key) - self.assertEqual('ONELOGIN_9c86c4542ab9d6fce07f2f7fd335287b9b3cdf69', name_id_3) + self.assertEqual("ONELOGIN_9c86c4542ab9d6fce07f2f7fd335287b9b3cdf69", name_id_3) def testGetIssuer(self): """ Tests the get_issuer of the OneLogin_Saml2_LogoutRequest """ - request = self.file_contents(join(self.data_path, 'logout_requests', 'logout_request.xml')) + request = self.file_contents(join(self.data_path, "logout_requests", "logout_request.xml")) issuer = OneLogin_Saml2_Logout_Request.get_issuer(request) - self.assertEqual('http://idp.example.com/', issuer) + self.assertEqual("http://idp.example.com/", issuer) dom = parseString(request) issuer_2 = OneLogin_Saml2_Logout_Request.get_issuer(dom.toxml()) - self.assertEqual('http://idp.example.com/', issuer_2) + self.assertEqual("http://idp.example.com/", issuer_2) - issuer_node = dom.getElementsByTagName('saml:Issuer')[0] + issuer_node = dom.getElementsByTagName("saml:Issuer")[0] issuer_node.parentNode.removeChild(issuer_node) issuer_3 = OneLogin_Saml2_Logout_Request.get_issuer(dom.toxml()) self.assertIsNone(issuer_3) @@ -275,7 +264,7 @@ def testGetSessionIndexes(self): """ Tests the get_session_indexes of the OneLogin_Saml2_LogoutRequest """ - request = self.file_contents(join(self.data_path, 'logout_requests', 'logout_request.xml')) + request = self.file_contents(join(self.data_path, "logout_requests", "logout_request.xml")) session_indexes = OneLogin_Saml2_Logout_Request.get_session_indexes(request) self.assertEqual(len(session_indexes), 0) @@ -284,19 +273,19 @@ def testGetSessionIndexes(self): session_indexes_2 = OneLogin_Saml2_Logout_Request.get_session_indexes(dom.toxml()) self.assertEqual(len(session_indexes_2), 0) - request_2 = self.file_contents(join(self.data_path, 'logout_requests', 'logout_request_with_sessionindex.xml')) + request_2 = self.file_contents(join(self.data_path, "logout_requests", "logout_request_with_sessionindex.xml")) session_indexes_3 = OneLogin_Saml2_Logout_Request.get_session_indexes(request_2) - self.assertEqual(['_ac72a76526cb6ca19f8438e73879a0e6c8ae5131'], session_indexes_3) + self.assertEqual(["_ac72a76526cb6ca19f8438e73879a0e6c8ae5131"], session_indexes_3) def testIsInvalidXML(self): """ Tests the is_valid method of the OneLogin_Saml2_LogoutRequest Case Invalid XML """ - request = OneLogin_Saml2_Utils.b64encode('invalid') + request = OneLogin_Saml2_Utils.b64encode("invalid") request_data = { - 'http_host': 'example.com', - 'script_name': 'index.html', + "http_host": "example.com", + "script_name": "index.html", } settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) @@ -313,20 +302,17 @@ def testIsInvalidIssuer(self): Tests the is_valid method of the OneLogin_Saml2_LogoutRequest Case Invalid Issuer """ - request = self.file_contents(join(self.data_path, 'logout_requests', 'invalids', 'invalid_issuer.xml')) - request_data = { - 'http_host': 'example.com', - 'script_name': 'index.html' - } + request = self.file_contents(join(self.data_path, "logout_requests", "invalids", "invalid_issuer.xml")) + request_data = {"http_host": "example.com", "script_name": "index.html"} current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data) - request = request.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url) + request = request.replace("http://stuff.com/endpoints/endpoints/sls.php", current_url) 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)) - with self.assertRaisesRegex(Exception, 'Invalid issuer in the Logout Request'): + with self.assertRaisesRegex(Exception, "Invalid issuer in the Logout Request"): logout_request2.is_valid(request_data, raise_exceptions=True) def testIsInvalidDestination(self): @@ -334,26 +320,23 @@ def testIsInvalidDestination(self): Tests the is_valid method of the OneLogin_Saml2_LogoutRequest Case Invalid Destination """ - request_data = { - 'http_host': 'example.com', - 'script_name': 'index.html' - } - request = self.file_contents(join(self.data_path, 'logout_requests', 'logout_request.xml')) + 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)) - with self.assertRaisesRegex(Exception, 'The LogoutRequest was received at'): + with self.assertRaisesRegex(Exception, "The LogoutRequest was received at"): logout_request2.is_valid(request_data, raise_exceptions=True) dom = parseString(request) - dom.documentElement.setAttribute('Destination', None) + dom.documentElement.setAttribute("Destination", None) logout_request3 = OneLogin_Saml2_Logout_Request(settings, OneLogin_Saml2_Utils.b64encode(dom.toxml())) self.assertTrue(logout_request3.is_valid(request_data)) - dom.documentElement.removeAttribute('Destination') + dom.documentElement.removeAttribute("Destination") logout_request4 = OneLogin_Saml2_Logout_Request(settings, OneLogin_Saml2_Utils.b64encode(dom.toxml())) self.assertTrue(logout_request4.is_valid(request_data)) @@ -362,13 +345,10 @@ def testIsInvalidNotOnOrAfter(self): Tests the is_valid method of the OneLogin_Saml2_LogoutRequest Case Invalid NotOnOrAfter """ - request_data = { - 'http_host': 'example.com', - 'script_name': 'index.html' - } - request = self.file_contents(join(self.data_path, 'logout_requests', 'invalids', 'not_after_failed.xml')) + request_data = {"http_host": "example.com", "script_name": "index.html"} + request = self.file_contents(join(self.data_path, "logout_requests", "invalids", "not_after_failed.xml")) current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data) - request = request.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url) + request = request.replace("http://stuff.com/endpoints/endpoints/sls.php", current_url) settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) logout_request = OneLogin_Saml2_Logout_Request(settings, OneLogin_Saml2_Utils.b64encode(request)) @@ -376,18 +356,15 @@ def testIsInvalidNotOnOrAfter(self): settings.set_strict(True) logout_request2 = OneLogin_Saml2_Logout_Request(settings, OneLogin_Saml2_Utils.b64encode(request)) - with self.assertRaisesRegex(Exception, 'Could not validate timestamp: expired. Check system clock.'): + with self.assertRaisesRegex(Exception, "Could not validate timestamp: expired. Check system clock."): logout_request2.is_valid(request_data, raise_exceptions=True) def testIsValid(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')) + 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)) @@ -407,7 +384,7 @@ def testIsValid(self): 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) + request = request.replace("http://stuff.com/endpoints/endpoints/sls.php", current_url) logout_request5 = OneLogin_Saml2_Logout_Request(settings, OneLogin_Saml2_Utils.b64encode(request)) self.assertTrue(logout_request5.is_valid(request_data)) @@ -415,11 +392,8 @@ 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')) + 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)) @@ -439,7 +413,7 @@ def testIsValidWithCapitalization(self): 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()) + 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)) @@ -447,11 +421,8 @@ 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')) + 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)) @@ -471,7 +442,7 @@ def testIsInValidWithCapitalization(self): 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()) + 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)) @@ -479,11 +450,8 @@ def testIsValidWithXMLEncoding(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_with_encoding.xml')) + request_data = {"http_host": "example.com", "script_name": "index.html"} + request = self.file_contents(join(self.data_path, "logout_requests", "logout_request_with_encoding.xml")) settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) logout_request = OneLogin_Saml2_Logout_Request(settings, OneLogin_Saml2_Utils.b64encode(request)) @@ -503,15 +471,15 @@ def testIsValidWithXMLEncoding(self): 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) + request = request.replace("http://stuff.com/endpoints/endpoints/sls.php", current_url) logout_request5 = OneLogin_Saml2_Logout_Request(settings, OneLogin_Saml2_Utils.b64encode(request)) self.assertTrue(logout_request5.is_valid(request_data)) def testIsValidRaisesExceptionWhenRaisesArgumentIsTrue(self): - request = OneLogin_Saml2_Utils.b64encode('invalid') + request = OneLogin_Saml2_Utils.b64encode("invalid") request_data = { - 'http_host': 'example.com', - 'script_name': 'index.html', + "http_host": "example.com", + "script_name": "index.html", } settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) settings.set_strict(True) @@ -528,15 +496,15 @@ def testGetXML(self): Tests that we can get the logout request XML directly without going through intermediate steps """ - request = self.file_contents(join(self.data_path, 'logout_requests', 'logout_request.xml')) + request = self.file_contents(join(self.data_path, "logout_requests", "logout_request.xml")) settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) logout_request_generated = OneLogin_Saml2_Logout_Request(settings) expectedFragment = ( 'Destination="http://idp.example.com/SingleLogoutService.php">\n' - ' http://stuff.com/endpoints/metadata.php\n' + " http://stuff.com/endpoints/metadata.php\n" ' http://idp.example.com/\n' - ' \n' + " \n" ) self.assertIn(expectedFragment, logout_request_generated.get_xml()) diff --git a/tests/src/OneLogin/saml2_tests/logout_response_test.py b/tests/src/OneLogin/saml2_tests/logout_response_test.py index eea09882..45b8c6ed 100644 --- a/tests/src/OneLogin/saml2_tests/logout_response_test.py +++ b/tests/src/OneLogin/saml2_tests/logout_response_test.py @@ -20,35 +20,35 @@ class OneLogin_Saml2_Logout_Response_Test(unittest.TestCase): - data_path = join(dirname(dirname(dirname(dirname(__file__)))), 'data') - settings_path = join(dirname(dirname(dirname(dirname(__file__)))), 'settings') + data_path = join(dirname(dirname(dirname(dirname(__file__)))), "data") + settings_path = join(dirname(dirname(dirname(dirname(__file__)))), "settings") # assertRegexpMatches deprecated on python3 def assertRegex(self, text, regexp, msg=None): - if hasattr(unittest.TestCase, 'assertRegex'): + if hasattr(unittest.TestCase, "assertRegex"): return super(OneLogin_Saml2_Logout_Response_Test, self).assertRegex(text, regexp, msg) else: return self.assertRegexpMatches(text, regexp, msg) # assertRaisesRegexp deprecated on python3 def assertRaisesRegex(self, exception, regexp, msg=None): - if hasattr(unittest.TestCase, 'assertRaisesRegex'): + if hasattr(unittest.TestCase, "assertRaisesRegex"): return super(OneLogin_Saml2_Logout_Response_Test, self).assertRaisesRegex(exception, regexp, msg=msg) else: return self.assertRaisesRegexp(exception, regexp) - def loadSettingsJSON(self, name='settings1.json'): + def loadSettingsJSON(self, name="settings1.json"): filename = join(self.settings_path, name) if exists(filename): - stream = open(filename, 'r') + stream = open(filename, "r") settings = json.load(stream) stream.close() return settings else: - raise Exception('Settings json file does not exist') + raise Exception("Settings json file does not exist") def file_contents(self, filename): - f = open(filename, 'r') + f = open(filename, "r") content = f.read() f.close() return content @@ -58,9 +58,9 @@ def testConstructor(self): Tests the OneLogin_Saml2_LogoutResponse Constructor. """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - message = self.file_contents(join(self.data_path, 'logout_responses', 'logout_response_deflated.xml.base64')) + message = self.file_contents(join(self.data_path, "logout_responses", "logout_response_deflated.xml.base64")) response = OneLogin_Saml2_Logout_Response(settings, message) - self.assertRegex(compat.to_string(OneLogin_Saml2_XML.to_string(response.document)), 'invalid') - request_data = { - 'http_host': 'example.com', - 'script_name': 'index.html', - 'get_data': {} - } + message = OneLogin_Saml2_Utils.deflate_and_base64_encode("invalid") + request_data = {"http_host": "example.com", "script_name": "index.html", "get_data": {}} settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) response = OneLogin_Saml2_Logout_Response(settings, message) @@ -155,20 +151,16 @@ def testIsInValidRequestId(self): Tests the is_valid method of the OneLogin_Saml2_LogoutResponse Case invalid request Id """ - request_data = { - 'http_host': 'example.com', - 'script_name': 'index.html', - 'get_data': {} - } + 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')) + message = self.file_contents(join(self.data_path, "logout_responses", "logout_response_deflated.xml.base64")) 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) - plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url) + plain_message = plain_message.replace("http://stuff.com/endpoints/endpoints/sls.php", current_url) message = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message) - request_id = 'invalid_request_id' + request_id = "invalid_request_id" settings.set_strict(False) response = OneLogin_Saml2_Logout_Response(settings, message) @@ -177,8 +169,8 @@ def testIsInValidRequestId(self): settings.set_strict(True) response_2 = OneLogin_Saml2_Logout_Response(settings, message) self.assertFalse(response_2.is_valid(request_data, request_id)) - self.assertIn('The InResponseTo of the Logout Response:', response_2.get_error()) - with self.assertRaisesRegex(Exception, 'The InResponseTo of the Logout Response:'): + self.assertIn("The InResponseTo of the Logout Response:", response_2.get_error()) + with self.assertRaisesRegex(Exception, "The InResponseTo of the Logout Response:"): response_2.is_valid(request_data, request_id, raise_exceptions=True) def testIsInValidIssuer(self): @@ -186,18 +178,14 @@ def testIsInValidIssuer(self): Tests the is_valid method of the OneLogin_Saml2_LogoutResponse Case invalid Issuer """ - request_data = { - 'http_host': 'example.com', - 'script_name': 'index.html', - 'get_data': {} - } + 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')) + message = self.file_contents(join(self.data_path, "logout_responses", "logout_response_deflated.xml.base64")) 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) - plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url) - plain_message = plain_message.replace('http://idp.example.com/', 'http://invalid.issuer.example.com') + plain_message = plain_message.replace("http://stuff.com/endpoints/endpoints/sls.php", current_url) + plain_message = plain_message.replace("http://idp.example.com/", "http://invalid.issuer.example.com") message = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message) settings.set_strict(False) @@ -206,7 +194,7 @@ def testIsInValidIssuer(self): settings.set_strict(True) response_2 = OneLogin_Saml2_Logout_Response(settings, message) - with self.assertRaisesRegex(Exception, 'Invalid issuer in the Logout Response'): + with self.assertRaisesRegex(Exception, "Invalid issuer in the Logout Response"): response_2.is_valid(request_data, raise_exceptions=True) def testIsInValidDestination(self): @@ -214,13 +202,9 @@ def testIsInValidDestination(self): Tests the is_valid method of the OneLogin_Saml2_LogoutResponse Case invalid Destination """ - request_data = { - 'http_host': 'example.com', - 'script_name': 'index.html', - 'get_data': {} - } + 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')) + message = self.file_contents(join(self.data_path, "logout_responses", "logout_response_deflated.xml.base64")) settings.set_strict(False) response = OneLogin_Saml2_Logout_Response(settings, message) @@ -228,19 +212,19 @@ def testIsInValidDestination(self): settings.set_strict(True) response_2 = OneLogin_Saml2_Logout_Response(settings, message) - with self.assertRaisesRegex(Exception, 'The LogoutResponse was received at'): + with self.assertRaisesRegex(Exception, "The LogoutResponse was received at"): response_2.is_valid(request_data, raise_exceptions=True) # Empty destination dom = parseString(OneLogin_Saml2_Utils.decode_base64_and_inflate(message)) - dom.firstChild.setAttribute('Destination', '') + dom.firstChild.setAttribute("Destination", "") xml = dom.toxml() message_3 = OneLogin_Saml2_Utils.deflate_and_base64_encode(xml) response_3 = OneLogin_Saml2_Logout_Response(settings, message_3) self.assertTrue(response_3.is_valid(request_data)) # No destination - dom.firstChild.removeAttribute('Destination') + dom.firstChild.removeAttribute("Destination") xml = dom.toxml() message_4 = OneLogin_Saml2_Utils.deflate_and_base64_encode(xml) response_4 = OneLogin_Saml2_Logout_Response(settings, message_4) @@ -250,25 +234,21 @@ def testIsValid(self): """ Tests the is_valid method of the OneLogin_Saml2_LogoutResponse """ - request_data = { - 'http_host': 'example.com', - 'script_name': 'index.html', - 'get_data': {} - } + 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')) + 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'): + 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) - plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url) + 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) @@ -278,26 +258,22 @@ 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': {} - } + 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')) + 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'): + 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) + 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) @@ -307,25 +283,21 @@ 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': {} - } + 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')) + 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'): + 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) + 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) @@ -335,37 +307,29 @@ def testIsValidWithXMLEncoding(self): """ Tests the is_valid method of the OneLogin_Saml2_LogoutResponse """ - request_data = { - 'http_host': 'example.com', - 'script_name': 'index.html', - 'get_data': {} - } + 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_with_encoding_deflated.xml.base64')) + message = self.file_contents(join(self.data_path, "logout_responses", "logout_response_with_encoding_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'): + 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) - plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url) + 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 testIsValidRaisesExceptionWhenRaisesArgumentIsTrue(self): - message = OneLogin_Saml2_Utils.deflate_and_base64_encode('invalid') - request_data = { - 'http_host': 'example.com', - 'script_name': 'index.html', - 'get_data': {} - } + message = OneLogin_Saml2_Utils.deflate_and_base64_encode("invalid") + request_data = {"http_host": "example.com", "script_name": "index.html", "get_data": {}} settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) settings.set_strict(True) @@ -381,7 +345,7 @@ def testGetXML(self): Tests that we can get the logout response XML directly without going through intermediate steps """ - response = self.file_contents(join(self.data_path, 'logout_responses', 'logout_response.xml')) + response = self.file_contents(join(self.data_path, "logout_responses", "logout_response.xml")) settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) logout_response_generated = OneLogin_Saml2_Logout_Response(settings) @@ -389,11 +353,11 @@ def testGetXML(self): expectedFragment = ( 'Destination="http://idp.example.com/SingleLogoutService.php"\n' ' InResponseTo="InResponseValue">\n' - ' http://stuff.com/endpoints/metadata.php\n' - ' \n' + " http://stuff.com/endpoints/metadata.php\n" + " \n" ' \n' - ' \n' - '' + " \n" + "" ) self.assertIn(expectedFragment, logout_response_generated.get_xml()) @@ -412,10 +376,6 @@ def testBuildWithStatus(self): # 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' - ) + expectedFragment = " \n" ' \n' " \n" self.assertIn(expectedFragment, parsed_response.get_xml()) self.assertEqual(parsed_response.get_status(), OneLogin_Saml2_Constants.STATUS_REQUESTER) diff --git a/tests/src/OneLogin/saml2_tests/metadata_test.py b/tests/src/OneLogin/saml2_tests/metadata_test.py index 31d94cf8..14b37dcb 100644 --- a/tests/src/OneLogin/saml2_tests/metadata_test.py +++ b/tests/src/OneLogin/saml2_tests/metadata_test.py @@ -16,18 +16,18 @@ class OneLogin_Saml2_Metadata_Test(unittest.TestCase): - def loadSettingsJSON(self, filename='settings1.json'): - filename = join(dirname(__file__), '..', '..', '..', 'settings', filename) + def loadSettingsJSON(self, filename="settings1.json"): + filename = join(dirname(__file__), "..", "..", "..", "settings", filename) if exists(filename): - stream = open(filename, 'r') + stream = open(filename, "r") settings = json.load(stream) stream.close() return settings else: - raise Exception('Settings json file does not exist') + raise Exception("Settings json file does not exist") def file_contents(self, filename): - f = open(filename, 'r') + f = open(filename, "r") content = f.read() f.close() return content @@ -42,15 +42,11 @@ def testBuilder(self): organization = settings.get_organization() contacts = settings.get_contacts() - metadata = OneLogin_Saml2_Metadata.builder( - sp_data, security['authnRequestsSigned'], - security['wantAssertionsSigned'], None, None, contacts, - organization - ) + metadata = OneLogin_Saml2_Metadata.builder(sp_data, security["authnRequestsSigned"], security["wantAssertionsSigned"], None, None, contacts, organization) self.assertIsNotNone(metadata) - self.assertIn('urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified', metadata) + self.assertIn("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", metadata) self.assertIn('sp_test', metadata) self.assertIn('', metadata) - self.assertIn('technical_name', metadata) + self.assertIn("technical_name", metadata) - security['authnRequestsSigned'] = True - security['wantAssertionsSigned'] = True - del sp_data['singleLogoutService']['url'] + security["authnRequestsSigned"] = True + security["wantAssertionsSigned"] = True + del sp_data["singleLogoutService"]["url"] - metadata2 = OneLogin_Saml2_Metadata.builder( - sp_data, security['authnRequestsSigned'], - security['wantAssertionsSigned'] - ) + metadata2 = OneLogin_Saml2_Metadata.builder(sp_data, security["authnRequestsSigned"], security["wantAssertionsSigned"]) self.assertIsNotNone(metadata2) - self.assertIn('', metadata2) - metadata3 = OneLogin_Saml2_Metadata.builder( - sp_data, security['authnRequestsSigned'], - security['wantAssertionsSigned'], - '2014-10-01T11:04:29Z', - 'P1Y', - contacts, - organization - ) + metadata3 = OneLogin_Saml2_Metadata.builder(sp_data, security["authnRequestsSigned"], security["wantAssertionsSigned"], "2014-10-01T11:04:29Z", "P1Y", contacts, organization) self.assertIsNotNone(metadata3) - self.assertIn(' + metadata = OneLogin_Saml2_Metadata.builder(sp_data, security["authnRequestsSigned"], security["wantAssertionsSigned"], None, None, contacts, organization) + self.assertIn( + """ Test Service Test Service @@ -160,21 +122,20 @@ def testBuilderAttributeConsumingService(self): - """, metadata) + """, + metadata, + ) def testBuilderAttributeConsumingServiceWithMultipleAttributeValue(self): - settings = OneLogin_Saml2_Settings(self.loadSettingsJSON('settings5.json')) + settings = OneLogin_Saml2_Settings(self.loadSettingsJSON("settings5.json")) sp_data = settings.get_sp_data() security = settings.get_security_data() organization = settings.get_organization() contacts = settings.get_contacts() - metadata = OneLogin_Saml2_Metadata.builder( - sp_data, security['authnRequestsSigned'], - security['wantAssertionsSigned'], None, None, contacts, - organization - ) - self.assertIn(""" + metadata = OneLogin_Saml2_Metadata.builder(sp_data, security["authnRequestsSigned"], security["wantAssertionsSigned"], None, None, contacts, organization) + self.assertIn( + """ Test Service Test Service @@ -182,7 +143,9 @@ def testBuilderAttributeConsumingServiceWithMultipleAttributeValue(self): admin - """, metadata) + """, + metadata, + ) def testSignMetadata(self): """ @@ -192,21 +155,18 @@ def testSignMetadata(self): sp_data = settings.get_sp_data() security = settings.get_security_data() - metadata = OneLogin_Saml2_Metadata.builder( - sp_data, security['authnRequestsSigned'], - security['wantAssertionsSigned'] - ) + metadata = OneLogin_Saml2_Metadata.builder(sp_data, security["authnRequestsSigned"], security["wantAssertionsSigned"]) self.assertIsNotNone(metadata) cert_path = settings.get_cert_path() - key = self.file_contents(join(cert_path, 'sp.key')) - cert = self.file_contents(join(cert_path, 'sp.crt')) + key = self.file_contents(join(cert_path, "sp.key")) + cert = self.file_contents(join(cert_path, "sp.crt")) signed_metadata = compat.to_string(OneLogin_Saml2_Metadata.sign_metadata(metadata, key, cert)) self.assertTrue(OneLogin_Saml2_Utils.validate_metadata_sign(signed_metadata, cert)) - self.assertIn('', signed_metadata) - self.assertIn('urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified', signed_metadata) + 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('\n\n', signed_metadata) + self.assertIn("\n\n", signed_metadata) with self.assertRaises(Exception) as context: - OneLogin_Saml2_Metadata.sign_metadata('', key, cert) + OneLogin_Saml2_Metadata.sign_metadata("", key, cert) exception = context.exception self.assertIn("Empty string supplied as input", str(exception)) signed_metadata_2 = compat.to_string(OneLogin_Saml2_Metadata.sign_metadata(metadata, key, cert, OneLogin_Saml2_Constants.RSA_SHA256, OneLogin_Saml2_Constants.SHA384)) self.assertTrue(OneLogin_Saml2_Utils.validate_metadata_sign(signed_metadata_2, cert)) - self.assertIn('', signed_metadata_2) - self.assertIn('urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified', signed_metadata_2) + self.assertIn("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", signed_metadata_2) self.assertIn('\n', signed_metadata_2) self.assertIn('', signed_metadata_2) self.assertIn('', signed_metadata_2) - self.assertIn('\n\n', signed_metadata_2) + self.assertIn("\n\n", signed_metadata_2) root = OneLogin_Saml2_XML.to_etree(signed_metadata_2) - first_child = OneLogin_Saml2_XML.query(root, '/md:EntityDescriptor/*[1]')[0] - self.assertEqual('{http://www.w3.org/2000/09/xmldsig#}Signature', first_child.tag) + first_child = OneLogin_Saml2_XML.query(root, "/md:EntityDescriptor/*[1]")[0] + self.assertEqual("{http://www.w3.org/2000/09/xmldsig#}Signature", first_child.tag) def testAddX509KeyDescriptors(self): """ @@ -271,24 +231,24 @@ def testAddX509KeyDescriptors(self): self.assertNotIn(' something_is_wrong'): + with self.assertRaisesRegex(Exception, "The status code of the Response was not Success, was Responder -> something_is_wrong"): response_3.check_status() def testCheckOneCondition(self): @@ -548,7 +527,7 @@ def testCheckOneCondition(self): Tests the check_one_condition method of SamlResponse """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - xml = self.file_contents(join(self.data_path, 'responses', 'invalids', 'no_conditions.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "invalids", "no_conditions.xml.base64")) response = OneLogin_Saml2_Response(settings, xml) self.assertFalse(response.check_one_condition()) @@ -556,9 +535,9 @@ def testCheckOneCondition(self): settings.set_strict(True) response = OneLogin_Saml2_Response(settings, xml) self.assertFalse(response.is_valid(self.get_request_data())) - self.assertEqual('The Assertion must include a Conditions element', response.get_error()) + self.assertEqual("The Assertion must include a Conditions element", response.get_error()) - xml_2 = self.file_contents(join(self.data_path, 'responses', 'valid_response.xml.base64')) + xml_2 = self.file_contents(join(self.data_path, "responses", "valid_response.xml.base64")) response_2 = OneLogin_Saml2_Response(settings, xml_2) self.assertTrue(response_2.check_one_condition()) @@ -567,7 +546,7 @@ def testCheckOneAuthnStatement(self): Tests the check_one_authnstatement method of SamlResponse """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - xml = self.file_contents(join(self.data_path, 'responses', 'invalids', 'no_authnstatement.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "invalids", "no_authnstatement.xml.base64")) response = OneLogin_Saml2_Response(settings, xml) self.assertFalse(response.check_one_authnstatement()) @@ -575,9 +554,9 @@ def testCheckOneAuthnStatement(self): settings.set_strict(True) response = OneLogin_Saml2_Response(settings, xml) self.assertFalse(response.is_valid(self.get_request_data())) - self.assertEqual('The Assertion must include an AuthnStatement element', response.get_error()) + self.assertEqual("The Assertion must include an AuthnStatement element", response.get_error()) - xml_2 = self.file_contents(join(self.data_path, 'responses', 'valid_response.xml.base64')) + xml_2 = self.file_contents(join(self.data_path, "responses", "valid_response.xml.base64")) response_2 = OneLogin_Saml2_Response(settings, xml_2) self.assertTrue(response_2.check_one_authnstatement()) @@ -586,17 +565,17 @@ def testGetAudiences(self): Tests the get_audiences method of the OneLogin_Saml2_Response """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - xml = self.file_contents(join(self.data_path, 'responses', 'no_audience.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "no_audience.xml.base64")) response = OneLogin_Saml2_Response(settings, xml) self.assertEqual([], response.get_audiences()) - xml_2 = self.file_contents(join(self.data_path, 'responses', 'response1.xml.base64')) + xml_2 = self.file_contents(join(self.data_path, "responses", "response1.xml.base64")) response_2 = OneLogin_Saml2_Response(settings, xml_2) - self.assertEqual(['{audience}'], response_2.get_audiences()) + self.assertEqual(["{audience}"], response_2.get_audiences()) - xml_3 = self.file_contents(join(self.data_path, 'responses', 'valid_encrypted_assertion.xml.base64')) + xml_3 = self.file_contents(join(self.data_path, "responses", "valid_encrypted_assertion.xml.base64")) response_3 = OneLogin_Saml2_Response(settings, xml_3) - self.assertEqual(['http://stuff.com/endpoints/metadata.php'], response_3.get_audiences()) + self.assertEqual(["http://stuff.com/endpoints/metadata.php"], response_3.get_audiences()) def testQueryAssertions(self): """ @@ -604,59 +583,59 @@ def testQueryAssertions(self): OneLogin_Saml2_Response using the get_issuers call """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - xml = self.file_contents(join(self.data_path, 'responses', 'adfs_response.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "adfs_response.xml.base64")) response = OneLogin_Saml2_Response(settings, xml) - self.assertEqual(['http://login.example.com/issuer'], response.get_issuers()) + self.assertEqual(["http://login.example.com/issuer"], response.get_issuers()) - xml_2 = self.file_contents(join(self.data_path, 'responses', 'valid_encrypted_assertion.xml.base64')) + xml_2 = self.file_contents(join(self.data_path, "responses", "valid_encrypted_assertion.xml.base64")) response_2 = OneLogin_Saml2_Response(settings, xml_2) - self.assertEqual(['http://idp.example.com/'], response_2.get_issuers()) + self.assertEqual(["http://idp.example.com/"], response_2.get_issuers()) - xml_3 = self.file_contents(join(self.data_path, 'responses', 'double_signed_encrypted_assertion.xml.base64')) + xml_3 = self.file_contents(join(self.data_path, "responses", "double_signed_encrypted_assertion.xml.base64")) response_3 = OneLogin_Saml2_Response(settings, xml_3) - self.assertEqual(['http://idp.example.com/', 'https://pitbulk.no-ip.org/simplesaml/saml2/idp/metadata.php'], sorted(response_3.get_issuers())) + self.assertEqual(["http://idp.example.com/", "https://pitbulk.no-ip.org/simplesaml/saml2/idp/metadata.php"], sorted(response_3.get_issuers())) - xml_4 = self.file_contents(join(self.data_path, 'responses', 'double_signed_response.xml.base64')) + xml_4 = self.file_contents(join(self.data_path, "responses", "double_signed_response.xml.base64")) response_4 = OneLogin_Saml2_Response(settings, xml_4) - self.assertEqual(['https://pitbulk.no-ip.org/simplesaml/saml2/idp/metadata.php'], response_4.get_issuers()) + self.assertEqual(["https://pitbulk.no-ip.org/simplesaml/saml2/idp/metadata.php"], response_4.get_issuers()) - xml_5 = self.file_contents(join(self.data_path, 'responses', 'signed_message_encrypted_assertion.xml.base64')) + xml_5 = self.file_contents(join(self.data_path, "responses", "signed_message_encrypted_assertion.xml.base64")) response_5 = OneLogin_Saml2_Response(settings, xml_5) - self.assertEqual(['http://idp.example.com/', 'https://pitbulk.no-ip.org/simplesaml/saml2/idp/metadata.php'], sorted(response_5.get_issuers())) + self.assertEqual(["http://idp.example.com/", "https://pitbulk.no-ip.org/simplesaml/saml2/idp/metadata.php"], sorted(response_5.get_issuers())) - xml_6 = self.file_contents(join(self.data_path, 'responses', 'signed_assertion_response.xml.base64')) + xml_6 = self.file_contents(join(self.data_path, "responses", "signed_assertion_response.xml.base64")) response_6 = OneLogin_Saml2_Response(settings, xml_6) - self.assertEqual(['https://pitbulk.no-ip.org/simplesaml/saml2/idp/metadata.php'], response_6.get_issuers()) + self.assertEqual(["https://pitbulk.no-ip.org/simplesaml/saml2/idp/metadata.php"], response_6.get_issuers()) - xml_7 = self.file_contents(join(self.data_path, 'responses', 'signed_encrypted_assertion.xml.base64')) + xml_7 = self.file_contents(join(self.data_path, "responses", "signed_encrypted_assertion.xml.base64")) response_7 = OneLogin_Saml2_Response(settings, xml_7) - self.assertEqual(['http://idp.example.com/'], response_7.get_issuers()) + self.assertEqual(["http://idp.example.com/"], response_7.get_issuers()) def testGetIssuers(self): """ Tests the get_issuers method of the OneLogin_Saml2_Response """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - xml = self.file_contents(join(self.data_path, 'responses', 'adfs_response.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "adfs_response.xml.base64")) response = OneLogin_Saml2_Response(settings, xml) - self.assertEqual(['http://login.example.com/issuer'], response.get_issuers()) + self.assertEqual(["http://login.example.com/issuer"], response.get_issuers()) - xml_2 = self.file_contents(join(self.data_path, 'responses', 'valid_encrypted_assertion.xml.base64')) + xml_2 = self.file_contents(join(self.data_path, "responses", "valid_encrypted_assertion.xml.base64")) response_2 = OneLogin_Saml2_Response(settings, xml_2) - self.assertEqual(['http://idp.example.com/'], response_2.get_issuers()) + self.assertEqual(["http://idp.example.com/"], response_2.get_issuers()) - xml_3 = self.file_contents(join(self.data_path, 'responses', 'double_signed_encrypted_assertion.xml.base64')) + xml_3 = self.file_contents(join(self.data_path, "responses", "double_signed_encrypted_assertion.xml.base64")) response_3 = OneLogin_Saml2_Response(settings, xml_3) - self.assertEqual(['http://idp.example.com/', 'https://pitbulk.no-ip.org/simplesaml/saml2/idp/metadata.php'], sorted(response_3.get_issuers())) + self.assertEqual(["http://idp.example.com/", "https://pitbulk.no-ip.org/simplesaml/saml2/idp/metadata.php"], sorted(response_3.get_issuers())) - xml_4 = self.file_contents(join(self.data_path, 'responses', 'invalids', 'no_issuer_response.xml.base64')) + xml_4 = self.file_contents(join(self.data_path, "responses", "invalids", "no_issuer_response.xml.base64")) response_4 = OneLogin_Saml2_Response(settings, xml_4) response_4.get_issuers() - self.assertEqual(['https://pitbulk.no-ip.org/simplesaml/saml2/idp/metadata.php'], response_4.get_issuers()) + self.assertEqual(["https://pitbulk.no-ip.org/simplesaml/saml2/idp/metadata.php"], response_4.get_issuers()) - xml_5 = self.file_contents(join(self.data_path, 'responses', 'invalids', 'no_issuer_assertion.xml.base64')) + xml_5 = self.file_contents(join(self.data_path, "responses", "invalids", "no_issuer_assertion.xml.base64")) response_5 = OneLogin_Saml2_Response(settings, xml_5) - with self.assertRaisesRegex(Exception, 'Issuer of the Assertion not found or multiple.'): + with self.assertRaisesRegex(Exception, "Issuer of the Assertion not found or multiple."): response_5.get_issuers() def testGetSessionIndex(self): @@ -664,52 +643,44 @@ def testGetSessionIndex(self): Tests the get_session_index method of the OneLogin_Saml2_Response """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - xml = self.file_contents(join(self.data_path, 'responses', 'response1.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "response1.xml.base64")) response = OneLogin_Saml2_Response(settings, xml) - self.assertEqual('_531c32d283bdff7e04e487bcdbc4dd8d', response.get_session_index()) + self.assertEqual("_531c32d283bdff7e04e487bcdbc4dd8d", response.get_session_index()) - xml_2 = self.file_contents(join(self.data_path, 'responses', 'valid_encrypted_assertion.xml.base64')) + xml_2 = self.file_contents(join(self.data_path, "responses", "valid_encrypted_assertion.xml.base64")) response_2 = OneLogin_Saml2_Response(settings, xml_2) - self.assertEqual('_7164a9a9f97828bfdb8d0ebc004a05d2e7d873f70c', response_2.get_session_index()) + self.assertEqual("_7164a9a9f97828bfdb8d0ebc004a05d2e7d873f70c", response_2.get_session_index()) def testGetAttributes(self): """ 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')) + xml = self.file_contents(join(self.data_path, "responses", "response1.xml.base64")) response = OneLogin_Saml2_Response(settings, xml) - expected_attributes = { - 'uid': ['demo'], - 'another_value': ['value'] - } + expected_attributes = {"uid": ["demo"], "another_value": ["value"]} self.assertEqual(expected_attributes, response.get_attributes()) # An assertion that has no attributes should return an empty # array when asked for the attributes - xml_2 = self.file_contents(join(self.data_path, 'responses', 'response2.xml.base64')) + xml_2 = self.file_contents(join(self.data_path, "responses", "response2.xml.base64")) response_2 = OneLogin_Saml2_Response(settings, xml_2) self.assertEqual({}, response_2.get_attributes()) # Encrypted Attributes are not supported - xml_3 = self.file_contents(join(self.data_path, 'responses', 'invalids', 'encrypted_attrs.xml.base64')) + xml_3 = self.file_contents(join(self.data_path, "responses", "invalids", "encrypted_attrs.xml.base64")) 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')) + 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)) + 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']} + 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()) @@ -720,38 +691,32 @@ def testGetFriendlyAttributes(self): """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - xml = self.file_contents(join(self.data_path, 'responses', 'response1.xml.base64')) + 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')) + 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')) + 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')) + 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()) # Test retrieving duplicate attributes - xml_5 = self.file_contents(join(self.data_path, 'responses', - 'response1_with_duplicate_attributes.xml.base64')) + 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)) + 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'] - } + settings = OneLogin_Saml2_Settings(self.loadSettingsJSON("settings11.json")) + expected_attributes = {"username": ["demo"], "friendlytest": ["friendly1", "friendly2"]} response_6 = OneLogin_Saml2_Response(settings, xml_5) self.assertEqual(expected_attributes, response_6.get_friendlyname_attributes()) @@ -760,17 +725,20 @@ def testGetEncryptedAttributes(self): """ Tests the get_attributes method of the OneLogin_Saml2_Response with an encrypted response """ - settings = OneLogin_Saml2_Settings(self.loadSettingsJSON('settings8.json')) - xml = self.file_contents(join(self.data_path, 'responses', 'signed_message_encrypted_assertion2.xml.base64')) + settings = OneLogin_Saml2_Settings(self.loadSettingsJSON("settings8.json")) + xml = self.file_contents(join(self.data_path, "responses", "signed_message_encrypted_assertion2.xml.base64")) response = OneLogin_Saml2_Response(settings, xml) - self.assertEqual({ - 'uid': ['smartin'], - 'mail': ['smartin@yaco.es'], - 'cn': ['Sixto3'], - 'sn': ['Martin2'], - 'phone': [], - 'eduPersonAffiliation': ['user', 'admin'], - }, response.get_attributes()) + self.assertEqual( + { + "uid": ["smartin"], + "mail": ["smartin@yaco.es"], + "cn": ["Sixto3"], + "sn": ["Martin2"], + "phone": [], + "eduPersonAffiliation": ["user", "admin"], + }, + response.get_attributes(), + ) def testGetNestedNameIDAttributes(self): """ @@ -778,29 +746,12 @@ def testGetNestedNameIDAttributes(self): nameID data """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - xml = self.file_contents(join(self.data_path, 'responses', 'response_with_nested_nameid_values.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "response_with_nested_nameid_values.xml.base64")) response = OneLogin_Saml2_Response(settings, xml) - expected_attributes = { - 'uid': ['demo'], - 'another_value': [{ - 'NameID': { - 'Format': 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent', - 'NameQualifier': 'https://idpID', - 'value': 'value' - } - }] - } + expected_attributes = {"uid": ["demo"], "another_value": [{"NameID": {"Format": "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent", "NameQualifier": "https://idpID", "value": "value"}}]} 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' - } - }] - } + 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): @@ -809,12 +760,12 @@ def testOnlyRetrieveAssertionWithIDThatMatchesSignatureReference(self): The Assertion is unsigned so the method fails """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - xml = self.file_contents(join(self.data_path, 'responses', 'wrapped_response_2.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "wrapped_response_2.xml.base64")) response = OneLogin_Saml2_Response(settings, xml) - with self.assertRaisesRegex(Exception, 'Invalid Signature Element {urn:oasis:names:tc:SAML:2.0:metadata}EntityDescriptor SAML Response rejected'): + with self.assertRaisesRegex(Exception, "Invalid Signature Element {urn:oasis:names:tc:SAML:2.0:metadata}EntityDescriptor SAML Response rejected"): response.is_valid(self.get_request_data(), raise_exceptions=True) nameid = response.get_nameid() - self.assertEqual('root@example.com', nameid) + self.assertEqual("root@example.com", nameid) def testDoesNotAllowSignatureWrappingAttack(self): """ @@ -822,9 +773,9 @@ def testDoesNotAllowSignatureWrappingAttack(self): Test that the SignatureWrappingAttack is not allowed """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - xml = self.file_contents(join(self.data_path, 'responses', 'response4.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "response4.xml.base64")) response = OneLogin_Saml2_Response(settings, xml) - self.assertEqual('test@onelogin.com', response.get_nameid()) + self.assertEqual("test@onelogin.com", response.get_nameid()) self.assertFalse(response.is_valid(self.get_request_data())) def testNodeTextAttack(self): @@ -832,29 +783,29 @@ def testNodeTextAttack(self): Tests the get_nameid and get_attributes methods of the OneLogin_Saml2_Response Test that the node text with comment attack (VU#475445) is not allowed """ - xml = self.file_contents(join(self.data_path, 'responses', 'response_node_text_attack.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "response_node_text_attack.xml.base64")) settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) response = OneLogin_Saml2_Response(settings, xml) attributes = response.get_attributes() nameid = response.get_nameid() - self.assertEqual("smith", attributes.get('surname')[0]) - self.assertEqual('support@onelogin.com', nameid) + self.assertEqual("smith", attributes.get("surname")[0]) + self.assertEqual("support@onelogin.com", nameid) def testGetSessionNotOnOrAfter(self): """ Tests the get_session_not_on_or_after method of the OneLogin_Saml2_Response """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - xml = self.file_contents(join(self.data_path, 'responses', 'response1.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "response1.xml.base64")) response = OneLogin_Saml2_Response(settings, xml) self.assertEqual(1290203857, response.get_session_not_on_or_after()) # An assertion that do not specified Session timeout should return NULL - xml_2 = self.file_contents(join(self.data_path, 'responses', 'response2.xml.base64')) + xml_2 = self.file_contents(join(self.data_path, "responses", "response2.xml.base64")) response_2 = OneLogin_Saml2_Response(settings, xml_2) self.assertEqual(None, response_2.get_session_not_on_or_after()) - xml_3 = self.file_contents(join(self.data_path, 'responses', 'valid_encrypted_assertion.xml.base64')) + xml_3 = self.file_contents(join(self.data_path, "responses", "valid_encrypted_assertion.xml.base64")) response_3 = OneLogin_Saml2_Response(settings, xml_3) self.assertEqual(2696012228, response_3.get_session_not_on_or_after()) @@ -866,46 +817,46 @@ def testGetInResponseTo(self): settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) # Response without an InResponseTo element should return None - xml = self.file_contents(join(self.data_path, 'responses', 'response1.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "response1.xml.base64")) response = OneLogin_Saml2_Response(settings, xml) self.assertIsNone(response.get_in_response_to()) - xml_3 = self.file_contents(join(self.data_path, 'responses', 'valid_encrypted_assertion.xml.base64')) + xml_3 = self.file_contents(join(self.data_path, "responses", "valid_encrypted_assertion.xml.base64")) response_3 = OneLogin_Saml2_Response(settings, xml_3) - self.assertEqual('ONELOGIN_be60b8caf8e9d19b7a3551b244f116c947ff247d', response_3.get_in_response_to()) + self.assertEqual("ONELOGIN_be60b8caf8e9d19b7a3551b244f116c947ff247d", response_3.get_in_response_to()) def testIsInvalidXML(self): """ Tests the is_valid method of the OneLogin_Saml2_Response Case Invalid XML """ - message = compat.to_string(OneLogin_Saml2_Utils.b64encode('invalid')) - request_data = { - 'http_host': 'example.com', - 'script_name': 'index.html', - 'get_data': {} - } + message = compat.to_string( + OneLogin_Saml2_Utils.b64encode( + 'invalid' + ) + ) + request_data = {"http_host": "example.com", "script_name": "index.html", "get_data": {}} settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) response = OneLogin_Saml2_Response(settings, message) response.is_valid(request_data) - self.assertEqual('No Signature found. SAML Response rejected', response.get_error()) + self.assertEqual("No Signature found. SAML Response rejected", response.get_error()) settings.set_strict(True) response_2 = OneLogin_Saml2_Response(settings, message) self.assertFalse(response_2.is_valid(request_data)) - self.assertEqual('Invalid SAML Response. Not match the saml-schema-protocol-2.0.xsd', response_2.get_error()) + self.assertEqual("Invalid SAML Response. Not match the saml-schema-protocol-2.0.xsd", response_2.get_error()) def testValidateNumAssertions(self): """ Tests the validate_num_assertions method of the OneLogin_Saml2_Response """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - xml = self.file_contents(join(self.data_path, 'responses', 'response1.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "response1.xml.base64")) response = OneLogin_Saml2_Response(settings, xml) self.assertTrue(response.validate_num_assertions()) - xml_multi_assertion = self.file_contents(join(self.data_path, 'responses', 'invalids', 'multiple_assertions.xml.base64')) + xml_multi_assertion = self.file_contents(join(self.data_path, "responses", "invalids", "multiple_assertions.xml.base64")) response_2 = OneLogin_Saml2_Response(settings, xml_multi_assertion) self.assertFalse(response_2.validate_num_assertions()) @@ -914,23 +865,23 @@ def testValidateTimestamps(self): Tests the validate_timestamps method of the OneLogin_Saml2_Response """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - xml = self.file_contents(join(self.data_path, 'responses', 'valid_response.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "valid_response.xml.base64")) response = OneLogin_Saml2_Response(settings, xml) self.assertTrue(response.validate_timestamps()) - xml_2 = self.file_contents(join(self.data_path, 'responses', 'valid_encrypted_assertion.xml.base64')) + xml_2 = self.file_contents(join(self.data_path, "responses", "valid_encrypted_assertion.xml.base64")) response_2 = OneLogin_Saml2_Response(settings, xml_2) self.assertTrue(response_2.validate_timestamps()) - xml_3 = self.file_contents(join(self.data_path, 'responses', 'expired_response.xml.base64')) + xml_3 = self.file_contents(join(self.data_path, "responses", "expired_response.xml.base64")) response_3 = OneLogin_Saml2_Response(settings, xml_3) self.assertFalse(response_3.validate_timestamps()) - xml_4 = self.file_contents(join(self.data_path, 'responses', 'invalids', 'not_after_failed.xml.base64')) + xml_4 = self.file_contents(join(self.data_path, "responses", "invalids", "not_after_failed.xml.base64")) response_4 = OneLogin_Saml2_Response(settings, xml_4) self.assertFalse(response_4.validate_timestamps()) - xml_5 = self.file_contents(join(self.data_path, 'responses', 'invalids', 'not_before_failed.xml.base64')) + xml_5 = self.file_contents(join(self.data_path, "responses", "invalids", "not_before_failed.xml.base64")) response_5 = OneLogin_Saml2_Response(settings, xml_5) self.assertFalse(response_5.validate_timestamps()) @@ -940,9 +891,9 @@ def testValidateVersion(self): Case invalid version """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - xml = self.file_contents(join(self.data_path, 'responses', 'invalids', 'no_saml2.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "invalids", "no_saml2.xml.base64")) response = OneLogin_Saml2_Response(settings, xml) - with self.assertRaisesRegex(Exception, 'Unsupported SAML version'): + with self.assertRaisesRegex(Exception, "Unsupported SAML version"): response.is_valid(self.get_request_data(), raise_exceptions=True) def testValidateID(self): @@ -951,9 +902,9 @@ def testValidateID(self): Case invalid no ID """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - xml = self.file_contents(join(self.data_path, 'responses', 'invalids', 'no_id.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "invalids", "no_id.xml.base64")) response = OneLogin_Saml2_Response(settings, xml) - with self.assertRaisesRegex(Exception, 'Missing ID attribute on SAML Response'): + with self.assertRaisesRegex(Exception, "Missing ID attribute on SAML Response"): response.is_valid(self.get_request_data(), raise_exceptions=True) def testIsInValidReference(self): @@ -962,12 +913,12 @@ def testIsInValidReference(self): Case invalid reference """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - xml = self.file_contents(join(self.data_path, 'responses', 'response1.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "response1.xml.base64")) response = OneLogin_Saml2_Response(settings, xml) self.assertFalse(response.is_valid(self.get_request_data())) - self.assertEqual('Signature validation failed. SAML Response rejected', response.get_error()) + self.assertEqual("Signature validation failed. SAML Response rejected", response.get_error()) - with self.assertRaisesRegex(Exception, 'Signature validation failed. SAML Response rejected'): + with self.assertRaisesRegex(Exception, "Signature validation failed. SAML Response rejected"): response.is_valid(self.get_request_data(), raise_exceptions=True) def testIsInValidExpired(self): @@ -976,14 +927,14 @@ def testIsInValidExpired(self): Case expired response """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - xml = self.file_contents(join(self.data_path, 'responses', 'expired_response.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "expired_response.xml.base64")) response = OneLogin_Saml2_Response(settings, xml) response.is_valid(self.get_request_data()) - self.assertEqual('No Signature found. SAML Response rejected', response.get_error()) + self.assertEqual("No Signature found. SAML Response rejected", response.get_error()) settings.set_strict(True) response_2 = OneLogin_Saml2_Response(settings, xml) - with self.assertRaisesRegex(Exception, 'Could not validate timestamp: expired. Check system clock.'): + with self.assertRaisesRegex(Exception, "Could not validate timestamp: expired. Check system clock."): response_2.is_valid(self.get_request_data(), raise_exceptions=True) def testIsInValidNoStatement(self): @@ -992,15 +943,15 @@ def testIsInValidNoStatement(self): Case no statement """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - xml = self.file_contents(join(self.data_path, 'responses', 'invalids', 'no_signature.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "invalids", "no_signature.xml.base64")) response = OneLogin_Saml2_Response(settings, xml) response.is_valid(self.get_request_data()) - self.assertEqual('No Signature found. SAML Response rejected', response.get_error()) + self.assertEqual("No Signature found. SAML Response rejected", response.get_error()) settings.set_strict(True) response_2 = OneLogin_Saml2_Response(settings, xml) self.assertFalse(response_2.is_valid(self.get_request_data())) - self.assertEqual('There is no AttributeStatement on the Response', response_2.get_error()) + self.assertEqual("There is no AttributeStatement on the Response", response_2.get_error()) def testIsValidOptionalStatement(self): """ @@ -1013,25 +964,25 @@ def testIsValidOptionalStatement(self): settings.set_strict(True) # want AttributeStatement True by default - self.assertTrue(settings.get_security_data()['wantAttributeStatement']) + self.assertTrue(settings.get_security_data()["wantAttributeStatement"]) - xml = self.file_contents(join(self.data_path, 'responses', 'invalids', 'signed_assertion_response.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "invalids", "signed_assertion_response.xml.base64")) response = OneLogin_Saml2_Response(settings, xml) self.assertFalse(response.is_valid(self.get_request_data())) - self.assertEqual('There is no AttributeStatement on the Response', response.get_error()) + self.assertEqual("There is no AttributeStatement on the Response", response.get_error()) # change wantAttributeStatement to optional - json_settings['security']['wantAttributeStatement'] = False + json_settings["security"]["wantAttributeStatement"] = False settings = OneLogin_Saml2_Settings(json_settings) # check settings - self.assertFalse(settings.get_security_data()['wantAttributeStatement']) + self.assertFalse(settings.get_security_data()["wantAttributeStatement"]) response = OneLogin_Saml2_Response(settings, xml) response.is_valid(self.get_request_data()) - self.assertNotEqual('There is no AttributeStatement on the Response', response.get_error()) - self.assertEqual('Signature validation failed. SAML Response rejected', response.get_error()) + self.assertNotEqual("There is no AttributeStatement on the Response", response.get_error()) + self.assertEqual("Signature validation failed. SAML Response rejected", response.get_error()) def testIsInValidNoKey(self): """ @@ -1039,9 +990,9 @@ def testIsInValidNoKey(self): Case no key """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - xml = self.file_contents(join(self.data_path, 'responses', 'invalids', 'no_key.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "invalids", "no_key.xml.base64")) response = OneLogin_Saml2_Response(settings, xml) - with self.assertRaisesRegex(Exception, 'Signature validation failed. SAML Response rejected'): + with self.assertRaisesRegex(Exception, "Signature validation failed. SAML Response rejected"): response.is_valid(self.get_request_data(), raise_exceptions=True) def testIsInValidDeprecatedAlgorithm(self): @@ -1050,11 +1001,11 @@ def testIsInValidDeprecatedAlgorithm(self): Case Deprecated algorithm used """ settings_dict = self.loadSettingsJSON() - settings_dict['security']['rejectDeprecatedAlgorithm'] = True + settings_dict["security"]["rejectDeprecatedAlgorithm"] = True settings = OneLogin_Saml2_Settings(settings_dict) - xml = self.file_contents(join(self.data_path, 'responses', 'valid_response.xml.base64')) + 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'): + 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): @@ -1064,9 +1015,9 @@ def testIsInValidMultipleAssertions(self): """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - xml = self.file_contents(join(self.data_path, 'responses', 'invalids', 'multiple_assertions.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "invalids", "multiple_assertions.xml.base64")) response = OneLogin_Saml2_Response(settings, xml) - with self.assertRaisesRegex(Exception, 'SAML Response must contain 1 assertion'): + with self.assertRaisesRegex(Exception, "SAML Response must contain 1 assertion"): response.is_valid(self.get_request_data(), raise_exceptions=True) def testIsInValidEncAttrs(self): @@ -1075,14 +1026,14 @@ def testIsInValidEncAttrs(self): Case invalid Encrypted Attrs """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - xml = self.file_contents(join(self.data_path, 'responses', 'invalids', 'encrypted_attrs.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "invalids", "encrypted_attrs.xml.base64")) response = OneLogin_Saml2_Response(settings, xml) response.is_valid(self.get_request_data()) - self.assertEqual('No Signature found. SAML Response rejected', response.get_error()) + self.assertEqual("No Signature found. SAML Response rejected", response.get_error()) settings.set_strict(True) response_2 = OneLogin_Saml2_Response(settings, xml) - with self.assertRaisesRegex(Exception, 'There is an EncryptedAttribute in the Response and this SP not support them'): + with self.assertRaisesRegex(Exception, "There is an EncryptedAttribute in the Response and this SP not support them"): response_2.is_valid(self.get_request_data(), raise_exceptions=True) def testIsInValidDuplicatedAttrs(self): @@ -1091,9 +1042,9 @@ def testIsInValidDuplicatedAttrs(self): Case duplicated Attrs """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - xml = self.file_contents(join(self.data_path, 'responses', 'invalids', 'duplicated_attributes.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "invalids", "duplicated_attributes.xml.base64")) response = OneLogin_Saml2_Response(settings, xml) - with self.assertRaisesRegex(Exception, 'Found an Attribute element with duplicated Name'): + with self.assertRaisesRegex(Exception, "Found an Attribute element with duplicated Name"): response.get_attributes() def testIsInValidDestination(self): @@ -1102,40 +1053,40 @@ def testIsInValidDestination(self): Case Invalid Response, Invalid Destination """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - message = self.file_contents(join(self.data_path, 'responses', 'unsigned_response.xml.base64')) + message = self.file_contents(join(self.data_path, "responses", "unsigned_response.xml.base64")) response = OneLogin_Saml2_Response(settings, message) response.is_valid(self.get_request_data()) - self.assertEqual('No Signature found. SAML Response rejected', response.get_error()) + self.assertEqual("No Signature found. SAML Response rejected", response.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()) + self.assertIn("The response was received at", response_2.get_error()) # Empty Destination dom = parseString(b64decode(message)) - dom.firstChild.setAttribute('Destination', '') + dom.firstChild.setAttribute("Destination", "") message_2 = OneLogin_Saml2_Utils.b64encode(dom.toxml()) response_3 = OneLogin_Saml2_Response(settings, message_2) self.assertFalse(response_3.is_valid(self.get_request_data())) - self.assertIn('The response has an empty Destination value', response_3.get_error()) + self.assertIn("The response has an empty Destination value", response_3.get_error()) - message_3 = self.file_contents(join(self.data_path, 'responses', 'invalids', 'empty_destination.xml.base64')) + message_3 = self.file_contents(join(self.data_path, "responses", "invalids", "empty_destination.xml.base64")) response_4 = OneLogin_Saml2_Response(settings, message_3) self.assertFalse(response_4.is_valid(self.get_request_data())) - self.assertEqual('The response has an empty Destination value', response_4.get_error()) + self.assertEqual("The response has an empty Destination value", response_4.get_error()) # No Destination - dom.firstChild.removeAttribute('Destination') + dom.firstChild.removeAttribute("Destination") message_4 = OneLogin_Saml2_Utils.b64encode(dom.toxml()) response_5 = OneLogin_Saml2_Response(settings, message_4) 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()) + 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()) + self.assertIn("The response was received at", response_2.get_error()) def testIsInValidDestinationCapitalizationOfElements(self): """ @@ -1143,18 +1094,18 @@ def testIsInValidDestinationCapitalizationOfElements(self): 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')) + 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()) + 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()) + self.assertIn("The response was received at", response_2.get_error()) def testIsValidDestinationCapitalizationOfHost(self): """ @@ -1162,15 +1113,15 @@ def testIsValidDestinationCapitalizationOfHost(self): 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')) + 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()) + 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()) + self.assertIn("A valid SubjectConfirmation was not found", response.get_error()) def testIsInValidAudience(self): """ @@ -1178,21 +1129,21 @@ def testIsInValidAudience(self): Case Invalid Response, Invalid Audience """ request_data = { - 'http_host': 'stuff.com', - 'script_name': '/endpoints/endpoints/acs.php', + "http_host": "stuff.com", + "script_name": "/endpoints/endpoints/acs.php", } settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - message = self.file_contents(join(self.data_path, 'responses', 'invalids', 'invalid_audience.xml.base64')) + message = self.file_contents(join(self.data_path, "responses", "invalids", "invalid_audience.xml.base64")) response = OneLogin_Saml2_Response(settings, message) response.is_valid(request_data) - self.assertEqual('No Signature found. SAML Response rejected', response.get_error()) + self.assertEqual("No Signature found. SAML Response rejected", response.get_error()) settings.set_strict(True) response_2 = OneLogin_Saml2_Response(settings, message) self.assertFalse(response_2.is_valid(request_data)) - self.assertIn('is not a valid audience for this Response', response_2.get_error()) + self.assertIn("is not a valid audience for this Response", response_2.get_error()) def testIsInValidAuthenticationContext(self): """ @@ -1202,13 +1153,13 @@ def testIsInValidAuthenticationContext(self): that didn't complete the two-factor step. """ request_data = self.get_request_data() - message = self.file_contents(join(self.data_path, 'responses', 'valid_response.xml.base64')) - two_factor_context = 'urn:oasis:names:tc:SAML:2.0:ac:classes:TimeSyncToken' - password_context = 'urn:oasis:names:tc:SAML:2.0:ac:classes:Password' + message = self.file_contents(join(self.data_path, "responses", "valid_response.xml.base64")) + two_factor_context = "urn:oasis:names:tc:SAML:2.0:ac:classes:TimeSyncToken" + password_context = "urn:oasis:names:tc:SAML:2.0:ac:classes:Password" settings_dict = self.loadSettingsJSON() - settings_dict['security']['requestedAuthnContext'] = [two_factor_context] - settings_dict['security']['failOnAuthnContextMismatch'] = True - settings_dict['strict'] = True + settings_dict["security"]["requestedAuthnContext"] = [two_factor_context] + settings_dict["security"]["failOnAuthnContextMismatch"] = True + settings_dict["strict"] = True settings = OneLogin_Saml2_Settings(settings_dict) # check that we catch when the contexts don't match @@ -1223,14 +1174,14 @@ def testIsInValidAuthenticationContext(self): response = OneLogin_Saml2_Response(settings, two_factor_message) response.is_valid(request_data) # check that we got as far as destination validation, which comes later - self.assertIn('The response was received at', response.get_error()) + self.assertIn("The response was received at", response.get_error()) # with the default setting, check that we succeed with our original context - settings_dict['security']['requestedAuthnContext'] = True + settings_dict["security"]["requestedAuthnContext"] = True settings = OneLogin_Saml2_Settings(settings_dict) response = OneLogin_Saml2_Response(settings, message) response.is_valid(request_data) - self.assertIn('The response was received at', response.get_error()) + self.assertIn("The response was received at", response.get_error()) def testIsInValidIssuer(self): """ @@ -1238,36 +1189,33 @@ def testIsInValidIssuer(self): Case Invalid Response, Invalid Issuer """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - request_data = { - 'http_host': 'example.com', - 'script_name': 'index.html' - } + request_data = {"http_host": "example.com", "script_name": "index.html"} current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data) - xml = self.file_contents(join(self.data_path, 'responses', 'invalids', 'invalid_issuer_assertion.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "invalids", "invalid_issuer_assertion.xml.base64")) plain_message = compat.to_string(OneLogin_Saml2_Utils.b64decode(xml)) - plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/acs.php', current_url) + plain_message = plain_message.replace("http://stuff.com/endpoints/endpoints/acs.php", current_url) message = OneLogin_Saml2_Utils.b64encode(plain_message) - xml_2 = self.file_contents(join(self.data_path, 'responses', 'invalids', 'invalid_issuer_message.xml.base64')) + xml_2 = self.file_contents(join(self.data_path, "responses", "invalids", "invalid_issuer_message.xml.base64")) plain_message_2 = compat.to_string(OneLogin_Saml2_Utils.b64decode(xml_2)) - plain_message_2 = plain_message_2.replace('http://stuff.com/endpoints/endpoints/acs.php', current_url) + plain_message_2 = plain_message_2.replace("http://stuff.com/endpoints/endpoints/acs.php", current_url) message_2 = OneLogin_Saml2_Utils.b64encode(plain_message_2) response = OneLogin_Saml2_Response(settings, message) response.is_valid(request_data) - self.assertEqual('No Signature found. SAML Response rejected', response.get_error()) + self.assertEqual("No Signature found. SAML Response rejected", response.get_error()) response_2 = OneLogin_Saml2_Response(settings, message_2) response_2.is_valid(request_data) - self.assertEqual('No Signature found. SAML Response rejected', response_2.get_error()) + self.assertEqual("No Signature found. SAML Response rejected", response_2.get_error()) settings.set_strict(True) response_3 = OneLogin_Saml2_Response(settings, message) - with self.assertRaisesRegex(Exception, 'Invalid issuer in the Assertion/Response'): + with self.assertRaisesRegex(Exception, "Invalid issuer in the Assertion/Response"): response_3.is_valid(request_data, raise_exceptions=True) response_4 = OneLogin_Saml2_Response(settings, message_2) - with self.assertRaisesRegex(Exception, 'Invalid issuer in the Assertion/Response'): + with self.assertRaisesRegex(Exception, "Invalid issuer in the Assertion/Response"): response_4.is_valid(request_data, raise_exceptions=True) def testIsInValidSessionIndex(self): @@ -1276,23 +1224,20 @@ def testIsInValidSessionIndex(self): Case Invalid Response, Invalid SessionIndex """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - request_data = { - 'http_host': 'example.com', - 'script_name': 'index.html' - } + request_data = {"http_host": "example.com", "script_name": "index.html"} current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data) - xml = self.file_contents(join(self.data_path, 'responses', 'invalids', 'invalid_sessionindex.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "invalids", "invalid_sessionindex.xml.base64")) plain_message = compat.to_string(OneLogin_Saml2_Utils.b64decode(xml)) - plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/acs.php', current_url) + plain_message = plain_message.replace("http://stuff.com/endpoints/endpoints/acs.php", current_url) message = OneLogin_Saml2_Utils.b64encode(plain_message) response = OneLogin_Saml2_Response(settings, message) response.is_valid(request_data) - self.assertEqual('No Signature found. SAML Response rejected', response.get_error()) + self.assertEqual("No Signature found. SAML Response rejected", response.get_error()) settings.set_strict(True) response_2 = OneLogin_Saml2_Response(settings, message) - with self.assertRaisesRegex(Exception, 'The attributes have expired, based on the SessionNotOnOrAfter of the AttributeStatement of this Response'): + with self.assertRaisesRegex(Exception, "The attributes have expired, based on the SessionNotOnOrAfter of the AttributeStatement of this Response"): response_2.is_valid(request_data, raise_exceptions=True) def testDatetimeWithMiliseconds(self): @@ -1302,19 +1247,16 @@ def testDatetimeWithMiliseconds(self): test is to verify that the toolkit supports them """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - request_data = { - 'http_host': 'example.com', - 'script_name': 'index.html' - } + request_data = {"http_host": "example.com", "script_name": "index.html"} current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data) - xml = self.file_contents(join(self.data_path, 'responses', 'unsigned_response_with_miliseconds.xm.base64')) + xml = self.file_contents(join(self.data_path, "responses", "unsigned_response_with_miliseconds.xm.base64")) plain_message = compat.to_string(OneLogin_Saml2_Utils.b64decode(xml)) - plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/acs.php', current_url) + plain_message = plain_message.replace("http://stuff.com/endpoints/endpoints/acs.php", current_url) message = OneLogin_Saml2_Utils.b64encode(plain_message) response = OneLogin_Saml2_Response(settings, message) response.is_valid(request_data) - self.assertEqual('No Signature found. SAML Response rejected', response.get_error()) + self.assertEqual("No Signature found. SAML Response rejected", response.get_error()) def testIsInValidSubjectConfirmation(self): """ @@ -1322,88 +1264,85 @@ def testIsInValidSubjectConfirmation(self): Case Invalid Response, Invalid SubjectConfirmation """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - request_data = { - 'http_host': 'example.com', - 'script_name': 'index.html' - } + request_data = {"http_host": "example.com", "script_name": "index.html"} current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data) - xml = self.file_contents(join(self.data_path, 'responses', 'invalids', 'no_subjectconfirmation_method.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "invalids", "no_subjectconfirmation_method.xml.base64")) plain_message = compat.to_string(OneLogin_Saml2_Utils.b64decode(xml)) - plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/acs.php', current_url) + plain_message = plain_message.replace("http://stuff.com/endpoints/endpoints/acs.php", current_url) message = OneLogin_Saml2_Utils.b64encode(plain_message) - xml_2 = self.file_contents(join(self.data_path, 'responses', 'invalids', 'no_subjectconfirmation_data.xml.base64')) + xml_2 = self.file_contents(join(self.data_path, "responses", "invalids", "no_subjectconfirmation_data.xml.base64")) plain_message_2 = compat.to_string(OneLogin_Saml2_Utils.b64decode(xml_2)) - plain_message_2 = plain_message_2.replace('http://stuff.com/endpoints/endpoints/acs.php', current_url) + plain_message_2 = plain_message_2.replace("http://stuff.com/endpoints/endpoints/acs.php", current_url) message_2 = OneLogin_Saml2_Utils.b64encode(plain_message_2) - xml_3 = self.file_contents(join(self.data_path, 'responses', 'invalids', 'invalid_subjectconfirmation_inresponse.xml.base64')) + xml_3 = self.file_contents(join(self.data_path, "responses", "invalids", "invalid_subjectconfirmation_inresponse.xml.base64")) plain_message_3 = compat.to_string(OneLogin_Saml2_Utils.b64decode(xml_3)) - plain_message_3 = plain_message_3.replace('http://stuff.com/endpoints/endpoints/acs.php', current_url) + plain_message_3 = plain_message_3.replace("http://stuff.com/endpoints/endpoints/acs.php", current_url) message_3 = OneLogin_Saml2_Utils.b64encode(plain_message_3) - xml_4 = self.file_contents(join(self.data_path, 'responses', 'invalids', 'invalid_subjectconfirmation_recipient.xml.base64')) + xml_4 = self.file_contents(join(self.data_path, "responses", "invalids", "invalid_subjectconfirmation_recipient.xml.base64")) plain_message_4 = compat.to_string(OneLogin_Saml2_Utils.b64decode(xml_4)) - plain_message_4 = plain_message_4.replace('http://stuff.com/endpoints/endpoints/acs.php', current_url) + plain_message_4 = plain_message_4.replace("http://stuff.com/endpoints/endpoints/acs.php", current_url) message_4 = OneLogin_Saml2_Utils.b64encode(plain_message_4) - xml_5 = self.file_contents(join(self.data_path, 'responses', 'invalids', 'invalid_subjectconfirmation_noa.xml.base64')) + xml_5 = self.file_contents(join(self.data_path, "responses", "invalids", "invalid_subjectconfirmation_noa.xml.base64")) plain_message_5 = compat.to_string(OneLogin_Saml2_Utils.b64decode(xml_5)) - plain_message_5 = plain_message_5.replace('http://stuff.com/endpoints/endpoints/acs.php', current_url) + plain_message_5 = plain_message_5.replace("http://stuff.com/endpoints/endpoints/acs.php", current_url) message_5 = OneLogin_Saml2_Utils.b64encode(plain_message_5) - xml_6 = self.file_contents(join(self.data_path, 'responses', 'invalids', 'invalid_subjectconfirmation_nb.xml.base64')) + xml_6 = self.file_contents(join(self.data_path, "responses", "invalids", "invalid_subjectconfirmation_nb.xml.base64")) plain_message_6 = compat.to_string(OneLogin_Saml2_Utils.b64decode(xml_6)) - plain_message_6 = plain_message_6.replace('http://stuff.com/endpoints/endpoints/acs.php', current_url) + plain_message_6 = plain_message_6.replace("http://stuff.com/endpoints/endpoints/acs.php", current_url) message_6 = OneLogin_Saml2_Utils.b64encode(plain_message_6) response = OneLogin_Saml2_Response(settings, message) response.is_valid(request_data) - self.assertEqual('No Signature found. SAML Response rejected', response.get_error()) + self.assertEqual("No Signature found. SAML Response rejected", response.get_error()) response_2 = OneLogin_Saml2_Response(settings, message_2) response_2.is_valid(request_data) - self.assertEqual('No Signature found. SAML Response rejected', response_2.get_error()) + self.assertEqual("No Signature found. SAML Response rejected", response_2.get_error()) response_3 = OneLogin_Saml2_Response(settings, message_3) response_3.is_valid(request_data) - self.assertEqual('No Signature found. SAML Response rejected', response_3.get_error()) + self.assertEqual("No Signature found. SAML Response rejected", response_3.get_error()) response_4 = OneLogin_Saml2_Response(settings, message_4) response_4.is_valid(request_data) - self.assertEqual('No Signature found. SAML Response rejected', response_4.get_error()) + self.assertEqual("No Signature found. SAML Response rejected", response_4.get_error()) response_5 = OneLogin_Saml2_Response(settings, message_5) response_5.is_valid(request_data) - self.assertEqual('No Signature found. SAML Response rejected', response_5.get_error()) + self.assertEqual("No Signature found. SAML Response rejected", response_5.get_error()) response_6 = OneLogin_Saml2_Response(settings, message_6) response_6.is_valid(request_data) - self.assertEqual('No Signature found. SAML Response rejected', response_6.get_error()) + self.assertEqual("No Signature found. SAML Response rejected", response_6.get_error()) settings.set_strict(True) response = OneLogin_Saml2_Response(settings, message) - with self.assertRaisesRegex(Exception, 'A valid SubjectConfirmation was not found on this Response'): + with self.assertRaisesRegex(Exception, "A valid SubjectConfirmation was not found on this Response"): response.is_valid(request_data, raise_exceptions=True) response_2 = OneLogin_Saml2_Response(settings, message_2) - with self.assertRaisesRegex(Exception, 'A valid SubjectConfirmation was not found on this Response'): + with self.assertRaisesRegex(Exception, "A valid SubjectConfirmation was not found on this Response"): response_2.is_valid(request_data, raise_exceptions=True) response_3 = OneLogin_Saml2_Response(settings, message_3) - with self.assertRaisesRegex(Exception, 'A valid SubjectConfirmation was not found on this Response'): + with self.assertRaisesRegex(Exception, "A valid SubjectConfirmation was not found on this Response"): response_3.is_valid(request_data, raise_exceptions=True) response_4 = OneLogin_Saml2_Response(settings, message_4) - with self.assertRaisesRegex(Exception, 'A valid SubjectConfirmation was not found on this Response'): + with self.assertRaisesRegex(Exception, "A valid SubjectConfirmation was not found on this Response"): response_4.is_valid(request_data, raise_exceptions=True) response_5 = OneLogin_Saml2_Response(settings, message_5) - with self.assertRaisesRegex(Exception, 'A valid SubjectConfirmation was not found on this Response'): + with self.assertRaisesRegex(Exception, "A valid SubjectConfirmation was not found on this Response"): response_5.is_valid(request_data, raise_exceptions=True) response_6 = OneLogin_Saml2_Response(settings, message_6) - with self.assertRaisesRegex(Exception, 'A valid SubjectConfirmation was not found on this Response'): + with self.assertRaisesRegex(Exception, "A valid SubjectConfirmation was not found on this Response"): response_6.is_valid(request_data, raise_exceptions=True) def testIsInValidRequestId(self): @@ -1412,29 +1351,26 @@ def testIsInValidRequestId(self): Case Invalid Response, Invalid requestID """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - request_data = { - 'http_host': 'example.com', - 'script_name': 'index.html' - } + request_data = {"http_host": "example.com", "script_name": "index.html"} current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data) - xml = self.file_contents(join(self.data_path, 'responses', 'unsigned_response.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "unsigned_response.xml.base64")) plain_message = compat.to_string(OneLogin_Saml2_Utils.b64decode(xml)) - plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/acs.php', current_url) + plain_message = plain_message.replace("http://stuff.com/endpoints/endpoints/acs.php", current_url) message = OneLogin_Saml2_Utils.b64encode(plain_message) response = OneLogin_Saml2_Response(settings, message) - request_id = 'invalid' + request_id = "invalid" response.is_valid(request_data, request_id) - self.assertEqual('No Signature found. SAML Response rejected', response.get_error()) + self.assertEqual("No Signature found. SAML Response rejected", response.get_error()) settings.set_strict(True) response = OneLogin_Saml2_Response(settings, message) - with self.assertRaisesRegex(Exception, 'The InResponseTo of the Response'): + with self.assertRaisesRegex(Exception, "The InResponseTo of the Response"): response.is_valid(request_data, request_id, raise_exceptions=True) - valid_request_id = '_57bcbf70-7b1f-012e-c821-782bcb13bb38' + valid_request_id = "_57bcbf70-7b1f-012e-c821-782bcb13bb38" response.is_valid(request_data, valid_request_id) - self.assertEqual('No Signature found. SAML Response rejected', response.get_error()) + self.assertEqual("No Signature found. SAML Response rejected", response.get_error()) def testIsInValidSignIssues(self): """ @@ -1442,67 +1378,64 @@ def testIsInValidSignIssues(self): Case Invalid Response, Invalid signing issues """ settings_info = self.loadSettingsJSON() - request_data = { - 'http_host': 'example.com', - 'script_name': 'index.html' - } + request_data = {"http_host": "example.com", "script_name": "index.html"} current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data) - xml = self.file_contents(join(self.data_path, 'responses', 'unsigned_response.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "unsigned_response.xml.base64")) plain_message = compat.to_string(OneLogin_Saml2_Utils.b64decode(xml)) - plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/acs.php', current_url) + plain_message = plain_message.replace("http://stuff.com/endpoints/endpoints/acs.php", current_url) message = OneLogin_Saml2_Utils.b64encode(plain_message) - settings_info['security']['wantAssertionsSigned'] = False + settings_info["security"]["wantAssertionsSigned"] = False settings = OneLogin_Saml2_Settings(settings_info) response = OneLogin_Saml2_Response(settings, message) response.is_valid(request_data) - self.assertEqual('No Signature found. SAML Response rejected', response.get_error()) + self.assertEqual("No Signature found. SAML Response rejected", response.get_error()) - settings_info['security']['wantAssertionsSigned'] = True + settings_info["security"]["wantAssertionsSigned"] = True settings_2 = OneLogin_Saml2_Settings(settings_info) response_2 = OneLogin_Saml2_Response(settings_2, message) response_2.is_valid(request_data) - self.assertEqual('No Signature found. SAML Response rejected', response_2.get_error()) + self.assertEqual("No Signature found. SAML Response rejected", response_2.get_error()) - settings_info['strict'] = True - settings_info['security']['wantAssertionsSigned'] = False + settings_info["strict"] = True + settings_info["security"]["wantAssertionsSigned"] = False settings_3 = OneLogin_Saml2_Settings(settings_info) response_3 = OneLogin_Saml2_Response(settings_3, message) response_3.is_valid(request_data) - self.assertEqual('No Signature found. SAML Response rejected', response_3.get_error()) + self.assertEqual("No Signature found. SAML Response rejected", response_3.get_error()) - settings_info['security']['wantAssertionsSigned'] = True + settings_info["security"]["wantAssertionsSigned"] = True settings_4 = OneLogin_Saml2_Settings(settings_info) response_4 = OneLogin_Saml2_Response(settings_4, message) - with self.assertRaisesRegex(Exception, 'The Assertion of the Response is not signed and the SP require it'): + with self.assertRaisesRegex(Exception, "The Assertion of the Response is not signed and the SP require it"): response_4.is_valid(request_data, raise_exceptions=True) - settings_info['security']['wantAssertionsSigned'] = False - settings_info['strict'] = False + settings_info["security"]["wantAssertionsSigned"] = False + settings_info["strict"] = False - settings_info['security']['wantMessagesSigned'] = False + settings_info["security"]["wantMessagesSigned"] = False settings_5 = OneLogin_Saml2_Settings(settings_info) response_5 = OneLogin_Saml2_Response(settings_5, message) response_5.is_valid(request_data) - self.assertEqual('No Signature found. SAML Response rejected', response_5.get_error()) + self.assertEqual("No Signature found. SAML Response rejected", response_5.get_error()) - settings_info['security']['wantMessagesSigned'] = True + settings_info["security"]["wantMessagesSigned"] = True settings_6 = OneLogin_Saml2_Settings(settings_info) response_6 = OneLogin_Saml2_Response(settings_6, message) response_6.is_valid(request_data) - self.assertEqual('No Signature found. SAML Response rejected', response_6.get_error()) + self.assertEqual("No Signature found. SAML Response rejected", response_6.get_error()) - settings_info['strict'] = True - settings_info['security']['wantMessagesSigned'] = False + settings_info["strict"] = True + settings_info["security"]["wantMessagesSigned"] = False settings_7 = OneLogin_Saml2_Settings(settings_info) response_7 = OneLogin_Saml2_Response(settings_7, message) response_7.is_valid(request_data) - self.assertEqual('No Signature found. SAML Response rejected', response_7.get_error()) + self.assertEqual("No Signature found. SAML Response rejected", response_7.get_error()) - settings_info['security']['wantMessagesSigned'] = True + settings_info["security"]["wantMessagesSigned"] = True settings_8 = OneLogin_Saml2_Settings(settings_info) response_8 = OneLogin_Saml2_Response(settings_8, message) - with self.assertRaisesRegex(Exception, 'The Message of the Response is not signed and the SP require it'): + with self.assertRaisesRegex(Exception, "The Message of the Response is not signed and the SP require it"): response_8.is_valid(request_data, raise_exceptions=True) def testIsInValidEncIssues(self): @@ -1511,76 +1444,74 @@ def testIsInValidEncIssues(self): Case Invalid Response, Invalid encryptation issues """ settings_info = self.loadSettingsJSON() - request_data = { - 'http_host': 'example.com', - 'script_name': 'index.html' - } + request_data = {"http_host": "example.com", "script_name": "index.html"} current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data) - xml = self.file_contents(join(self.data_path, 'responses', 'unsigned_response.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "unsigned_response.xml.base64")) plain_message = compat.to_string(OneLogin_Saml2_Utils.b64decode(xml)) - plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/acs.php', current_url) + plain_message = plain_message.replace("http://stuff.com/endpoints/endpoints/acs.php", current_url) message = OneLogin_Saml2_Utils.b64encode(plain_message) - settings_info['security']['wantAssertionsEncrypted'] = True + settings_info["security"]["wantAssertionsEncrypted"] = True settings = OneLogin_Saml2_Settings(settings_info) response = OneLogin_Saml2_Response(settings, message) response.is_valid(request_data) - self.assertEqual('No Signature found. SAML Response rejected', response.get_error()) + self.assertEqual("No Signature found. SAML Response rejected", response.get_error()) - settings_info['strict'] = True - settings_info['security']['wantAssertionsEncrypted'] = False + settings_info["strict"] = True + settings_info["security"]["wantAssertionsEncrypted"] = False settings = OneLogin_Saml2_Settings(settings_info) response_2 = OneLogin_Saml2_Response(settings, message) response_2.is_valid(request_data) - self.assertEqual('No Signature found. SAML Response rejected', response_2.get_error()) + self.assertEqual("No Signature found. SAML Response rejected", response_2.get_error()) - settings_info['security']['wantAssertionsEncrypted'] = True + settings_info["security"]["wantAssertionsEncrypted"] = True settings = OneLogin_Saml2_Settings(settings_info) response_3 = OneLogin_Saml2_Response(settings, message) self.assertFalse(response_3.is_valid(request_data)) - self.assertEqual('The assertion of the Response is not encrypted and the SP require it', response_3.get_error()) + self.assertEqual("The assertion of the Response is not encrypted and the SP require it", response_3.get_error()) - settings_info['security']['wantAssertionsEncrypted'] = False - settings_info['security']['wantNameIdEncrypted'] = True - settings_info['strict'] = False + settings_info["security"]["wantAssertionsEncrypted"] = False + settings_info["security"]["wantNameIdEncrypted"] = True + settings_info["strict"] = False settings = OneLogin_Saml2_Settings(settings_info) response_4 = OneLogin_Saml2_Response(settings, message) response_4.is_valid(request_data) - self.assertEqual('No Signature found. SAML Response rejected', response_4.get_error()) + self.assertEqual("No Signature found. SAML Response rejected", response_4.get_error()) - settings_info['strict'] = True + settings_info["strict"] = True settings = OneLogin_Saml2_Settings(settings_info) response_5 = OneLogin_Saml2_Response(settings, message) 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()) + 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_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 = {"script_name": "", "request_uri": "?acs", "http_host": "pytoolkit.com", "server_port": 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) + + if sys.version_info > (3, 2, 0): + with self.assertWarns(Warning): + 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()) + request_data = { - 'script_name': '', - 'request_uri': '?acs', + "script_name": "", + "request_uri": "?acs", + "http_host": "pytoolkit.com:8000", } - 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()) + + 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): """ @@ -1588,12 +1519,12 @@ def testIsInValidCert(self): Case invalid cert """ settings_info = self.loadSettingsJSON() - settings_info['idp']['x509cert'] = 'NotValidCert' + settings_info["idp"]["x509cert"] = "NotValidCert" settings = OneLogin_Saml2_Settings(settings_info) - xml = self.file_contents(join(self.data_path, 'responses', 'valid_response.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "valid_response.xml.base64")) response = OneLogin_Saml2_Response(settings, xml) - with self.assertRaisesRegex(Exception, 'Signature validation failed. SAML Response rejected'): + with self.assertRaisesRegex(Exception, "Signature validation failed. SAML Response rejected"): response.is_valid(self.get_request_data(), raise_exceptions=True) def testIsInValidCert2(self): @@ -1602,9 +1533,11 @@ def testIsInValidCert2(self): Case invalid cert2 """ settings_info = self.loadSettingsJSON() - settings_info['idp']['x509cert'] = 'MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290 MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9 uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0 WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0 Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5 6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ=' + settings_info["idp"][ + "x509cert" + ] = "MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290 MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9 uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0 WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0 Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5 6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ=" settings = OneLogin_Saml2_Settings(settings_info) - xml = self.file_contents(join(self.data_path, 'responses', 'valid_response.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "valid_response.xml.base64")) response = OneLogin_Saml2_Response(settings, xml) self.assertFalse(response.is_valid(self.get_request_data())) @@ -1615,10 +1548,10 @@ def testIsValid(self): """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - xml = self.file_contents(join(self.data_path, 'responses', 'valid_unsigned_response.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "valid_unsigned_response.xml.base64")) response = OneLogin_Saml2_Response(settings, xml) response.is_valid(self.get_request_data()) - self.assertEqual('No Signature found. SAML Response rejected', response.get_error()) + self.assertEqual("No Signature found. SAML Response rejected", response.get_error()) def testIsValid2(self): """ @@ -1629,36 +1562,36 @@ def testIsValid2(self): settings = OneLogin_Saml2_Settings(settings_info) # expired cert - xml = self.file_contents(join(self.data_path, 'responses', 'valid_response.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "valid_response.xml.base64")) response = OneLogin_Saml2_Response(settings, xml) self.assertTrue(response.is_valid(self.get_request_data())) - settings_info_2 = self.loadSettingsJSON('settings2.json') + settings_info_2 = self.loadSettingsJSON("settings2.json") settings_2 = OneLogin_Saml2_Settings(settings_info_2) - xml_2 = self.file_contents(join(self.data_path, 'responses', 'valid_response2.xml.base64')) + xml_2 = self.file_contents(join(self.data_path, "responses", "valid_response2.xml.base64")) response_2 = OneLogin_Saml2_Response(settings_2, xml_2) self.assertTrue(response_2.is_valid(self.get_request_data())) - 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'] = '' + 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"] = "" settings_3 = OneLogin_Saml2_Settings(settings_info_3) response_3 = OneLogin_Saml2_Response(settings_3, xml_2) self.assertTrue(response_3.is_valid(self.get_request_data())) - settings_info_3['idp']['certFingerprintAlgorithm'] = 'sha1' + settings_info_3["idp"]["certFingerprintAlgorithm"] = "sha1" settings_4 = OneLogin_Saml2_Settings(settings_info_3) response_4 = OneLogin_Saml2_Response(settings_4, xml_2) self.assertTrue(response_4.is_valid(self.get_request_data())) - settings_info_3['idp']['certFingerprintAlgorithm'] = 'sha256' + settings_info_3["idp"]["certFingerprintAlgorithm"] = "sha256" settings_5 = OneLogin_Saml2_Settings(settings_info_3) response_5 = OneLogin_Saml2_Response(settings_5, xml_2) self.assertFalse(response_5.is_valid(self.get_request_data())) - settings_info_3['idp']['certFingerprint'] = OneLogin_Saml2_Utils.calculate_x509_fingerprint(idp_cert, 'sha256') + settings_info_3["idp"]["certFingerprint"] = OneLogin_Saml2_Utils.calculate_x509_fingerprint(idp_cert, "sha256") settings_6 = OneLogin_Saml2_Settings(settings_info_3) response_6 = OneLogin_Saml2_Response(settings_6, xml_2) self.assertTrue(response_6.is_valid(self.get_request_data())) @@ -1674,45 +1607,42 @@ def testIsValidEnc(self): settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) # expired cert - xml = self.file_contents(join(self.data_path, 'responses', 'double_signed_encrypted_assertion.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "double_signed_encrypted_assertion.xml.base64")) response = OneLogin_Saml2_Response(settings, xml) self.assertTrue(response.is_valid(self.get_request_data())) - xml_2 = self.file_contents(join(self.data_path, 'responses', 'signed_encrypted_assertion.xml.base64')) + xml_2 = self.file_contents(join(self.data_path, "responses", "signed_encrypted_assertion.xml.base64")) response_2 = OneLogin_Saml2_Response(settings, xml_2) self.assertTrue(response_2.is_valid(self.get_request_data())) - xml_3 = self.file_contents(join(self.data_path, 'responses', 'signed_message_encrypted_assertion.xml.base64')) + xml_3 = self.file_contents(join(self.data_path, "responses", "signed_message_encrypted_assertion.xml.base64")) response_3 = OneLogin_Saml2_Response(settings, xml_3) self.assertTrue(response_3.is_valid(self.get_request_data())) - settings_2 = OneLogin_Saml2_Settings(self.loadSettingsJSON('settings2.json')) - xml_4 = self.file_contents(join(self.data_path, 'responses', 'double_signed_encrypted_assertion2.xml.base64')) + settings_2 = OneLogin_Saml2_Settings(self.loadSettingsJSON("settings2.json")) + xml_4 = self.file_contents(join(self.data_path, "responses", "double_signed_encrypted_assertion2.xml.base64")) response_4 = OneLogin_Saml2_Response(settings_2, xml_4) self.assertTrue(response_4.is_valid(self.get_request_data())) - xml_5 = self.file_contents(join(self.data_path, 'responses', 'signed_encrypted_assertion2.xml.base64')) + xml_5 = self.file_contents(join(self.data_path, "responses", "signed_encrypted_assertion2.xml.base64")) response_5 = OneLogin_Saml2_Response(settings_2, xml_5) self.assertTrue(response_5.is_valid(self.get_request_data())) - xml_6 = self.file_contents(join(self.data_path, 'responses', 'signed_message_encrypted_assertion2.xml.base64')) + xml_6 = self.file_contents(join(self.data_path, "responses", "signed_message_encrypted_assertion2.xml.base64")) response_6 = OneLogin_Saml2_Response(settings_2, xml_6) self.assertTrue(response_6.is_valid(self.get_request_data())) settings.set_strict(True) - xml_7 = self.file_contents(join(self.data_path, 'responses', 'valid_encrypted_assertion.xml.base64')) + xml_7 = self.file_contents(join(self.data_path, "responses", "valid_encrypted_assertion.xml.base64")) # In order to avoid the destination problem plain_message = compat.to_string(OneLogin_Saml2_Utils.b64decode(xml_7)) - request_data = { - 'http_host': 'example.com', - 'script_name': 'index.html' - } + request_data = {"http_host": "example.com", "script_name": "index.html"} current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data) - plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/acs.php', current_url) + plain_message = plain_message.replace("http://stuff.com/endpoints/endpoints/acs.php", current_url) message = compat.to_string(OneLogin_Saml2_Utils.b64encode(plain_message)) response_7 = OneLogin_Saml2_Response(settings, message) response_7.is_valid(request_data) - self.assertEqual('No Signature found. SAML Response rejected', response_7.get_error()) + self.assertEqual("No Signature found. SAML Response rejected", response_7.get_error()) def testIsValidSign(self): """ @@ -1725,47 +1655,47 @@ def testIsValidSign(self): settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) # expired cert - xml = self.file_contents(join(self.data_path, 'responses', 'signed_message_response.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "signed_message_response.xml.base64")) response = OneLogin_Saml2_Response(settings, xml) self.assertTrue(response.is_valid(self.get_request_data())) - xml_2 = self.file_contents(join(self.data_path, 'responses', 'signed_assertion_response.xml.base64')) + xml_2 = self.file_contents(join(self.data_path, "responses", "signed_assertion_response.xml.base64")) response_2 = OneLogin_Saml2_Response(settings, xml_2) self.assertTrue(response_2.is_valid(self.get_request_data())) - xml_3 = self.file_contents(join(self.data_path, 'responses', 'double_signed_response.xml.base64')) + xml_3 = self.file_contents(join(self.data_path, "responses", "double_signed_response.xml.base64")) response_3 = OneLogin_Saml2_Response(settings, xml_3) self.assertTrue(response_3.is_valid(self.get_request_data())) - settings_2 = OneLogin_Saml2_Settings(self.loadSettingsJSON('settings2.json')) - xml_4 = self.file_contents(join(self.data_path, 'responses', 'signed_message_response2.xml.base64')) + settings_2 = OneLogin_Saml2_Settings(self.loadSettingsJSON("settings2.json")) + xml_4 = self.file_contents(join(self.data_path, "responses", "signed_message_response2.xml.base64")) response_4 = OneLogin_Saml2_Response(settings_2, xml_4) self.assertTrue(response_4.is_valid(self.get_request_data())) - xml_5 = self.file_contents(join(self.data_path, 'responses', 'signed_assertion_response2.xml.base64')) + xml_5 = self.file_contents(join(self.data_path, "responses", "signed_assertion_response2.xml.base64")) response_5 = OneLogin_Saml2_Response(settings_2, xml_5) self.assertTrue(response_5.is_valid(self.get_request_data())) - xml_6 = self.file_contents(join(self.data_path, 'responses', 'double_signed_response2.xml.base64')) + xml_6 = self.file_contents(join(self.data_path, "responses", "double_signed_response2.xml.base64")) response_6 = OneLogin_Saml2_Response(settings_2, xml_6) self.assertTrue(response_6.is_valid(self.get_request_data())) dom = parseString(b64decode(xml_4)) - dom.firstChild.firstChild.firstChild.nodeValue = 'https://example.com/other-idp' + dom.firstChild.firstChild.firstChild.nodeValue = "https://example.com/other-idp" xml_7 = OneLogin_Saml2_Utils.b64encode(dom.toxml()) response_7 = OneLogin_Saml2_Response(settings, xml_7) # Modified message self.assertFalse(response_7.is_valid(self.get_request_data())) dom_2 = parseString(OneLogin_Saml2_Utils.b64decode(xml_5)) - dom_2.firstChild.firstChild.firstChild.nodeValue = 'https://example.com/other-idp' + dom_2.firstChild.firstChild.firstChild.nodeValue = "https://example.com/other-idp" xml_8 = OneLogin_Saml2_Utils.b64encode(dom_2.toxml()) response_8 = OneLogin_Saml2_Response(settings, xml_8) # Modified message self.assertFalse(response_8.is_valid(self.get_request_data())) dom_3 = parseString(OneLogin_Saml2_Utils.b64decode(xml_6)) - dom_3.firstChild.firstChild.firstChild.nodeValue = 'https://example.com/other-idp' + dom_3.firstChild.firstChild.firstChild.nodeValue = "https://example.com/other-idp" xml_9 = OneLogin_Saml2_Utils.b64encode(dom_3.toxml()) response_9 = OneLogin_Saml2_Response(settings, xml_9) # Modified message @@ -1782,47 +1712,47 @@ def testIsValidSignFingerprint(self): settings = OneLogin_Saml2_Settings(self.loadSettingsJSON("settings6.json")) # expired cert - xml = self.file_contents(join(self.data_path, 'responses', 'signed_message_response.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "signed_message_response.xml.base64")) response = OneLogin_Saml2_Response(settings, xml) self.assertTrue(response.is_valid(self.get_request_data())) - xml_2 = self.file_contents(join(self.data_path, 'responses', 'signed_assertion_response.xml.base64')) + xml_2 = self.file_contents(join(self.data_path, "responses", "signed_assertion_response.xml.base64")) response_2 = OneLogin_Saml2_Response(settings, xml_2) self.assertTrue(response_2.is_valid(self.get_request_data())) - xml_3 = self.file_contents(join(self.data_path, 'responses', 'double_signed_response.xml.base64')) + xml_3 = self.file_contents(join(self.data_path, "responses", "double_signed_response.xml.base64")) response_3 = OneLogin_Saml2_Response(settings, xml_3) self.assertTrue(response_3.is_valid(self.get_request_data())) - settings_2 = OneLogin_Saml2_Settings(self.loadSettingsJSON('settings2.json')) - xml_4 = self.file_contents(join(self.data_path, 'responses', 'signed_message_response2.xml.base64')) + settings_2 = OneLogin_Saml2_Settings(self.loadSettingsJSON("settings2.json")) + xml_4 = self.file_contents(join(self.data_path, "responses", "signed_message_response2.xml.base64")) response_4 = OneLogin_Saml2_Response(settings_2, xml_4) self.assertTrue(response_4.is_valid(self.get_request_data())) - xml_5 = self.file_contents(join(self.data_path, 'responses', 'signed_assertion_response2.xml.base64')) + xml_5 = self.file_contents(join(self.data_path, "responses", "signed_assertion_response2.xml.base64")) response_5 = OneLogin_Saml2_Response(settings_2, xml_5) self.assertTrue(response_5.is_valid(self.get_request_data())) - xml_6 = self.file_contents(join(self.data_path, 'responses', 'double_signed_response2.xml.base64')) + xml_6 = self.file_contents(join(self.data_path, "responses", "double_signed_response2.xml.base64")) response_6 = OneLogin_Saml2_Response(settings_2, xml_6) self.assertTrue(response_6.is_valid(self.get_request_data())) dom = parseString(b64decode(xml_4)) - dom.firstChild.firstChild.firstChild.nodeValue = 'https://example.com/other-idp' + dom.firstChild.firstChild.firstChild.nodeValue = "https://example.com/other-idp" xml_7 = OneLogin_Saml2_Utils.b64encode(dom.toxml()) response_7 = OneLogin_Saml2_Response(settings, xml_7) # Modified message self.assertFalse(response_7.is_valid(self.get_request_data())) dom_2 = parseString(OneLogin_Saml2_Utils.b64decode(xml_5)) - dom_2.firstChild.firstChild.firstChild.nodeValue = 'https://example.com/other-idp' + dom_2.firstChild.firstChild.firstChild.nodeValue = "https://example.com/other-idp" xml_8 = OneLogin_Saml2_Utils.b64encode(dom_2.toxml()) response_8 = OneLogin_Saml2_Response(settings, xml_8) # Modified message self.assertFalse(response_8.is_valid(self.get_request_data())) dom_3 = parseString(OneLogin_Saml2_Utils.b64decode(xml_6)) - dom_3.firstChild.firstChild.firstChild.nodeValue = 'https://example.com/other-idp' + dom_3.firstChild.firstChild.firstChild.nodeValue = "https://example.com/other-idp" xml_9 = OneLogin_Saml2_Utils.b64encode(dom_3.toxml()) response_9 = OneLogin_Saml2_Response(settings, xml_9) # Modified message @@ -1830,19 +1760,19 @@ def testIsValidSignFingerprint(self): def testMessageSignedIsValidSignWithEmptyReferenceURI(self): settings_info = self.loadSettingsJSON("settings10.json") - del settings_info['idp']['x509cert'] - settings_info['idp']['certFingerprint'] = "657302a5e11a4794a1e50a705988d66c9377575d" + 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_reference_uri.xml.base64')) + 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('settings10.json') - del settings_info['idp']['x509cert'] - settings_info['idp']['certFingerprint'] = "657302a5e11a4794a1e50a705988d66c9377575d" + settings_info = self.loadSettingsJSON("settings10.json") + 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')) + 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())) @@ -1854,24 +1784,16 @@ def testIsValidWithoutInResponseTo(self): # prepare strict settings settings_info = self.loadSettingsJSON() - settings_info['strict'] = True - settings_info['idp']['entityId'] = 'https://pitbulk.no-ip.org/simplesaml/saml2/idp/metadata.php' - settings_info['sp']['entityId'] = 'https://pitbulk.no-ip.org/newonelogin/demo1/metadata.php' + settings_info["strict"] = True + settings_info["idp"]["entityId"] = "https://pitbulk.no-ip.org/simplesaml/saml2/idp/metadata.php" + settings_info["sp"]["entityId"] = "https://pitbulk.no-ip.org/newonelogin/demo1/metadata.php" settings = OneLogin_Saml2_Settings(settings_info) - xml = self.file_contents(join(self.data_path, 'responses', 'valid_response_without_inresponseto.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "valid_response_without_inresponseto.xml.base64")) response = OneLogin_Saml2_Response(settings, xml) - not_on_or_after = datetime.strptime('2014-02-19T09:37:01Z', '%Y-%m-%dT%H:%M:%SZ') - not_on_or_after -= timedelta(seconds=150) - - with freeze_time(not_on_or_after): - self.assertTrue(response.is_valid({ - 'https': 'on', - 'http_host': 'pitbulk.no-ip.org', - 'script_name': 'newonelogin/demo1/index.php?acs' - })) + self.assertTrue(response.is_valid({"https": "on", "http_host": "pitbulk.no-ip.org", "script_name": "newonelogin/demo1/index.php?acs"})) def testIsValidRaisesExceptionWhenRaisesArgumentIsTrue(self): """ @@ -1880,7 +1802,7 @@ def testIsValidRaisesExceptionWhenRaisesArgumentIsTrue(self): """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) settings.set_strict(True) - xml = self.file_contents(join(self.data_path, 'responses', 'invalids', 'no_conditions.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "invalids", "no_conditions.xml.base64")) response = OneLogin_Saml2_Response(settings, xml) self.assertFalse(response.is_valid(self.get_request_data())) @@ -1893,9 +1815,9 @@ def testStatusCheckBeforeAssertionCheck(self): Tests the status of a response is checked before the assertion count. As failed statuses will have no assertions """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - xml = self.file_contents(join(self.data_path, 'responses', 'invalids', 'status_code_responder.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "invalids", "status_code_responder.xml.base64")) response = OneLogin_Saml2_Response(settings, xml) - with self.assertRaisesRegex(Exception, 'The status code of the Response was not Success, was Responder'): + with self.assertRaisesRegex(Exception, "The status code of the Response was not Success, was Responder"): response.is_valid(self.get_request_data(), raise_exceptions=True) def testGetId(self): @@ -1903,18 +1825,18 @@ def testGetId(self): Tests that we can retrieve the ID of the Response """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - xml = self.file_contents(join(self.data_path, 'responses', 'signed_message_response.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "signed_message_response.xml.base64")) response = OneLogin_Saml2_Response(settings, xml) - self.assertEqual(response.get_id(), 'pfxf209cd60-f060-722b-02e9-4850ac5a2e41') + self.assertEqual(response.get_id(), "pfxf209cd60-f060-722b-02e9-4850ac5a2e41") def testGetAssertionId(self): """ Tests that we can retrieve the ID of the Assertion """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - xml = self.file_contents(join(self.data_path, 'responses', 'signed_message_response.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "signed_message_response.xml.base64")) response = OneLogin_Saml2_Response(settings, xml) - self.assertEqual(response.get_assertion_id(), '_cccd6024116641fe48e0ae2c51220d02755f96c98d') + self.assertEqual(response.get_assertion_id(), "_cccd6024116641fe48e0ae2c51220d02755f96c98d") def testGetAssertionNotOnOrAfter(self): """ @@ -1924,7 +1846,7 @@ def testGetAssertionNotOnOrAfter(self): settings_data = self.loadSettingsJSON() request_data = self.get_request_data() settings = OneLogin_Saml2_Settings(settings_data) - message = self.file_contents(join(self.data_path, 'responses', 'valid_response.xml.base64')) + message = self.file_contents(join(self.data_path, "responses", "valid_response.xml.base64")) response = OneLogin_Saml2_Response(settings, message) self.assertIsNone(response.get_assertion_not_on_or_after()) @@ -1932,7 +1854,7 @@ def testGetAssertionNotOnOrAfter(self): self.assertIsNone(response.get_error()) self.assertIsNone(response.get_assertion_not_on_or_after()) - settings_data['strict'] = True + settings_data["strict"] = True settings = OneLogin_Saml2_Settings(settings_data) response = OneLogin_Saml2_Response(settings, message) @@ -1940,9 +1862,9 @@ def testGetAssertionNotOnOrAfter(self): self.assertNotEqual(response.get_error(), None) self.assertIsNone(response.get_assertion_not_on_or_after()) - request_data['https'] = 'on' - request_data['http_host'] = 'pitbulk.no-ip.org' - request_data['script_name'] = '/newonelogin/demo1/index.php?acs' + request_data["https"] = "on" + request_data["http_host"] = "pitbulk.no-ip.org" + request_data["script_name"] = "/newonelogin/demo1/index.php?acs" response.is_valid(request_data) self.assertIsNone(response.get_error()) self.assertEqual(response.get_assertion_not_on_or_after(), 2671081021) diff --git a/tests/src/OneLogin/saml2_tests/settings_test.py b/tests/src/OneLogin/saml2_tests/settings_test.py index 2b5135db..85386fc3 100644 --- a/tests/src/OneLogin/saml2_tests/settings_test.py +++ b/tests/src/OneLogin/saml2_tests/settings_test.py @@ -12,21 +12,21 @@ class OneLogin_Saml2_Settings_Test(unittest.TestCase): - data_path = join(dirname(dirname(dirname(dirname(__file__)))), 'data') - settings_path = join(dirname(dirname(dirname(dirname(__file__)))), 'settings') + data_path = join(dirname(dirname(dirname(dirname(__file__)))), "data") + settings_path = join(dirname(dirname(dirname(dirname(__file__)))), "settings") - def loadSettingsJSON(self, name='settings1.json'): + def loadSettingsJSON(self, name="settings1.json"): filename = join(self.settings_path, name) if exists(filename): - stream = open(filename, 'r') + stream = open(filename, "r") settings = json.load(stream) stream.close() return settings else: - raise Exception('Settings json file does not exist') + raise Exception("Settings json file does not exist") def file_contents(self, filename): - f = open(filename, 'r') + f = open(filename, "r") content = f.read() f.close() return content @@ -40,60 +40,60 @@ def testLoadSettingsFromDict(self): settings = OneLogin_Saml2_Settings(settings_info) self.assertEqual(len(settings.get_errors()), 0) - del settings_info['contactPerson'] - del settings_info['organization'] + del settings_info["contactPerson"] + del settings_info["organization"] settings = OneLogin_Saml2_Settings(settings_info) self.assertEqual(len(settings.get_errors()), 0) - del settings_info['security'] + del settings_info["security"] settings = OneLogin_Saml2_Settings(settings_info) self.assertEqual(len(settings.get_errors()), 0) - del settings_info['sp']['NameIDFormat'] - del settings_info['idp']['x509cert'] - settings_info['idp']['certFingerprint'] = 'afe71c28ef740bc87425be13a2263d37971daA1f9' + del settings_info["sp"]["NameIDFormat"] + del settings_info["idp"]["x509cert"] + settings_info["idp"]["certFingerprint"] = "afe71c28ef740bc87425be13a2263d37971daA1f9" settings = OneLogin_Saml2_Settings(settings_info) self.assertEqual(len(settings.get_errors()), 0) - settings_info['idp']['singleSignOnService']['url'] = 'invalid_url' + settings_info["idp"]["singleSignOnService"]["url"] = "invalid_url" try: settings_2 = OneLogin_Saml2_Settings(settings_info) self.assertNotEqual(len(settings_2.get_errors()), 0) except Exception as e: - self.assertIn('Invalid dict settings: idp_sso_url_invalid', str(e)) + self.assertIn("Invalid dict settings: idp_sso_url_invalid", str(e)) - settings_info['idp']['singleSignOnService']['url'] = 'http://invalid_domain' + settings_info["idp"]["singleSignOnService"]["url"] = "http://invalid_domain" try: settings_3 = OneLogin_Saml2_Settings(settings_info) self.assertNotEqual(len(settings_3.get_errors()), 0) except Exception as e: - self.assertIn('Invalid dict settings: idp_sso_url_invalid', str(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_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'] + del settings_info["security"] + del settings_info["sp"] + del settings_info["idp"] try: 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)) - self.assertIn('sp_not_found', str(e)) + self.assertIn("Invalid dict settings", str(e)) + self.assertIn("idp_not_found", str(e)) + self.assertIn("sp_not_found", str(e)) settings_info = self.loadSettingsJSON() - settings_info['security']['authnRequestsSigned'] = True - settings_info['custom_base_path'] = dirname(__file__) + settings_info["security"]["authnRequestsSigned"] = True + settings_info["custom_base_path"] = dirname(__file__) try: 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)) + self.assertIn("Invalid dict settings: sp_cert_not_found_and_required", str(e)) # test if the cert-file is loaded correct with the default filename settings_info = self.loadSettingsJSON() @@ -113,20 +113,20 @@ def testLoadSettingsFromDict(self): settings_9 = OneLogin_Saml2_Settings(settings_info) self.assertNotEqual(len(settings_9.get_errors()), 0) except Exception as e: - self.assertIn('Invalid dict settings: idp_cert_not_found_and_required', str(e)) + self.assertIn("Invalid dict settings: idp_cert_not_found_and_required", str(e)) def testLoadSettingsFromInvalidData(self): """ Tests the OneLogin_Saml2_Settings Constructor. Case load setting """ - invalid_settings = ('param1', 'param2') + invalid_settings = ("param1", "param2") try: OneLogin_Saml2_Settings(invalid_settings) self.assertTrue(False) except Exception as e: - self.assertIn('Unsupported settings object', str(e)) + self.assertIn("Unsupported settings object", str(e)) settings = OneLogin_Saml2_Settings(custom_base_path=self.settings_path) self.assertEqual(len(settings.get_errors()), 0) @@ -136,7 +136,7 @@ def testLoadSettingsFromFile(self): Tests the OneLogin_Saml2_Settings Constructor. Case load setting from file """ - custom_base_path = join(dirname(__file__), '..', '..', '..', 'settings') + custom_base_path = join(dirname(__file__), "..", "..", "..", "settings") settings = OneLogin_Saml2_Settings(custom_base_path=custom_base_path) self.assertEqual(len(settings.get_errors()), 0) @@ -144,9 +144,9 @@ def testLoadSettingsFromFile(self): try: OneLogin_Saml2_Settings(custom_base_path=custom_base_path) except Exception as e: - self.assertIn('Settings file not found', str(e)) + self.assertIn("Settings file not found", str(e)) - custom_base_path = join(dirname(__file__), '..', '..', '..', 'data', 'customPath') + custom_base_path = join(dirname(__file__), "..", "..", "..", "data", "customPath") settings_3 = OneLogin_Saml2_Settings(custom_base_path=custom_base_path) self.assertEqual(len(settings_3.get_errors()), 0) @@ -155,17 +155,17 @@ def testGetCertPath(self): Tests getCertPath method of the OneLogin_Saml2_Settings """ settings = OneLogin_Saml2_Settings(custom_base_path=self.settings_path) - self.assertEqual(self.settings_path + sep + 'certs' + sep, settings.get_cert_path()) + self.assertEqual(self.settings_path + sep + "certs" + sep, settings.get_cert_path()) def testSetCertPath(self): """ Tests setCertPath method of the OneLogin_Saml2_Settings """ settings = OneLogin_Saml2_Settings(custom_base_path=self.settings_path) - self.assertEqual(self.settings_path + sep + 'certs' + sep, settings.get_cert_path()) + self.assertEqual(self.settings_path + sep + "certs" + sep, settings.get_cert_path()) - settings.set_cert_path('/tmp') - self.assertEqual('/tmp', settings.get_cert_path()) + settings.set_cert_path("/tmp") + self.assertEqual("/tmp", settings.get_cert_path()) def testGetLibPath(self): """ @@ -174,19 +174,19 @@ def testGetLibPath(self): 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/')) + 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'] + 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/')) + 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) 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/')) + 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): """ @@ -195,19 +195,19 @@ def testGetSchemasPath(self): 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/')) + 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'] + 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/')) + 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) 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(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): """ @@ -216,7 +216,7 @@ def testGetIdPSSOurl(self): settings_info = self.loadSettingsJSON() settings = OneLogin_Saml2_Settings(settings_info) - sso_url = settings_info['idp']['singleSignOnService']['url'] + sso_url = settings_info["idp"]["singleSignOnService"]["url"] self.assertEqual(settings.get_idp_sso_url(), sso_url) def testGetIdPSLOurl(self): @@ -226,7 +226,7 @@ def testGetIdPSLOurl(self): settings_info = self.loadSettingsJSON() settings = OneLogin_Saml2_Settings(settings_info) - slo_url = settings_info['idp']['singleLogoutService']['url'] + slo_url = settings_info["idp"]["singleLogoutService"]["url"] self.assertEqual(settings.get_idp_slo_url(), slo_url) def testGetIdPSLOresponseUrl(self): @@ -234,14 +234,14 @@ 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_info["idp"]["singleLogoutService"]["responseUrl"] = "http://idp.example.com/SingleLogoutReturn.php" settings = OneLogin_Saml2_Settings(settings_info) - slo_url = settings_info['idp']['singleLogoutService']['responseUrl'] + 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_info["idp"]["singleLogoutService"].pop("responseUrl") settings = OneLogin_Saml2_Settings(settings_info) - slo_url = settings_info['idp']['singleLogoutService']['url'] + slo_url = settings_info["idp"]["singleLogoutService"]["url"] self.assertEqual(settings.get_idp_slo_response_url(), slo_url) def testGetSPCert(self): @@ -254,12 +254,12 @@ def testGetSPCert(self): self.assertEqual(cert, settings.get_sp_cert()) cert_2 = "-----BEGIN CERTIFICATE-----\nMIICbDCCAdWgAwIBAgIBADANBgkqhkiG9w0BAQ0FADBTMQswCQYDVQQGEwJ1czET\nMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lbG9naW4gSW5jMRgwFgYD\nVQQDDA9pZHAuZXhhbXBsZS5jb20wHhcNMTQwOTIzMTIyNDA4WhcNNDIwMjA4MTIy\nNDA4WjBTMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UE\nCgwMT25lbG9naW4gSW5jMRgwFgYDVQQDDA9pZHAuZXhhbXBsZS5jb20wgZ8wDQYJ\nKoZIhvcNAQEBBQADgY0AMIGJAoGBAOWA+YHU7cvPOrBOfxCscsYTJB+kH3MaA9BF\nrSHFS+KcR6cw7oPSktIJxUgvDpQbtfNcOkE/tuOPBDoech7AXfvH6d7Bw7xtW8PP\nJ2mB5Hn/HGW2roYhxmfh3tR5SdwN6i4ERVF8eLkvwCHsNQyK2Ref0DAJvpBNZMHC\npS24916/AgMBAAGjUDBOMB0GA1UdDgQWBBQ77/qVeiigfhYDITplCNtJKZTM8DAf\nBgNVHSMEGDAWgBQ77/qVeiigfhYDITplCNtJKZTM8DAMBgNVHRMEBTADAQH/MA0G\nCSqGSIb3DQEBDQUAA4GBAJO2j/1uO80E5C2PM6Fk9mzerrbkxl7AZ/mvlbOn+sNZ\nE+VZ1AntYuG8ekbJpJtG1YfRfc7EA9mEtqvv4dhv7zBy4nK49OR+KpIBjItWB5kY\nvrqMLKBa32sMbgqqUqeF1ENXKjpvLSuPdfGJZA3dNa/+Dyb8GGqWe707zLyc5F8m\n-----END CERTIFICATE-----\n" - settings_data['sp']['x509cert'] = cert_2 + settings_data["sp"]["x509cert"] = cert_2 settings = OneLogin_Saml2_Settings(settings_data) self.assertEqual(cert_2, settings.get_sp_cert()) - del settings_data['sp']['x509cert'] - del settings_data['custom_base_path'] + del settings_data["sp"]["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) @@ -275,7 +275,7 @@ def testGetSPCertNew(self): self.assertEqual(cert, settings.get_sp_cert()) self.assertIsNone(settings.get_sp_cert_new()) - settings = OneLogin_Saml2_Settings(self.loadSettingsJSON('settings7.json')) + settings = OneLogin_Saml2_Settings(self.loadSettingsJSON("settings7.json")) cert_new = "-----BEGIN CERTIFICATE-----\nMIICVDCCAb2gAwIBAgIBADANBgkqhkiG9w0BAQ0FADBHMQswCQYDVQQGEwJ1czEQ\nMA4GA1UECAwHZXhhbXBsZTEQMA4GA1UECgwHZXhhbXBsZTEUMBIGA1UEAwwLZXhh\nbXBsZS5jb20wHhcNMTcwNDA3MDgzMDAzWhcNMjcwNDA1MDgzMDAzWjBHMQswCQYD\nVQQGEwJ1czEQMA4GA1UECAwHZXhhbXBsZTEQMA4GA1UECgwHZXhhbXBsZTEUMBIG\nA1UEAwwLZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKhP\nS4/0azxbQekHHewQGKD7Pivr3CDpsrKxY3xlVanxj427OwzOb5KUVzsDEazumt6s\nZFY8HfidsjXY4EYA4ZzyL7ciIAR5vlAsIYN9nJ4AwVDnN/RjVwj+TN6BqWPLpVIp\nHc6Dl005HyE0zJnk1DZDn2tQVrIzbD3FhCp7YeotAgMBAAGjUDBOMB0GA1UdDgQW\nBBRYZx4thASfNvR/E7NsCF2IaZ7wIDAfBgNVHSMEGDAWgBRYZx4thASfNvR/E7Ns\nCF2IaZ7wIDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4GBACz4aobx9aG3\nkh+rNyrlgM3K6dYfnKG1/YH5sJCAOvg8kDr0fQAQifH8lFVWumKUMoAe0bFTfwWt\np/VJ8MprrEJth6PFeZdczpuv+fpLcNj2VmNVJqvQYvS4m36OnBFh1QFZW8UrbFIf\ndtm2nuZ+twSKqfKwjLdqcoX0p39h7Uw/\n-----END CERTIFICATE-----\n" self.assertEqual(cert, settings.get_sp_cert()) self.assertEqual(cert_new, settings.get_sp_cert_new()) @@ -290,12 +290,12 @@ def testGetSPKey(self): self.assertEqual(key, settings.get_sp_key()) key_2 = "-----BEGIN PRIVATE KEY-----\nMIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAOWA+YHU7cvPOrBO\nfxCscsYTJB+kH3MaA9BFrSHFS+KcR6cw7oPSktIJxUgvDpQbtfNcOkE/tuOPBDoe\nch7AXfvH6d7Bw7xtW8PPJ2mB5Hn/HGW2roYhxmfh3tR5SdwN6i4ERVF8eLkvwCHs\nNQyK2Ref0DAJvpBNZMHCpS24916/AgMBAAECgYEA0wDXZPS9hKqMTNh+nnfONioX\nBjhA6fQ7GVtWKDxa3ofMoPyt7ejGL/Hnvcv13Vn02UAsFx1bKrCstDqVtYwrWrnm\nywXyH+o9paJnTmd+cRIjWU8mRvCrxzH5I/Bcvbp1qZoASuqZEaGwNjM6JpW2o3QT\nmHGMALcLUPfEvhApssECQQDy2e65E86HcFhi/Ta8TQ0odDCNbiWA0bI1Iu8B7z+N\nAy1D1+WnCd7w2u9U6CF/k2nFHCsvxEoeANM0z7h5T/XvAkEA8e4JqKmDrfdiakQT\n7nf9svU2jXZtxSbPiIRMafNikDvzZ1vJCZkvdmaWYL70GlDZIwc9ad67rHZ/n/fq\nX1d0MQJAbRpRsJ5gY+KqItbFt3UaWzlP8sowWR5cZJjsLb9RmsV5mYguKYw6t5R0\nf33GRu1wUFimYlBaR/5w5MIJi57LywJATO1a5uWX+G5MPewNxmsjIY91XEAHIYR4\nwzkGLz5z3dciS4BVCZdLD0QJlxPA/MkuckPwFET9uhYn+M7VGKHvUQJBANSDwsY+\nBdCGpi/WRV37HUfwLl07damaFbW3h08PQx8G8SuF7DpN+FPBcI6VhzrIWNRBxWpr\nkgeGioKNfFWzSaM=\n-----END PRIVATE KEY-----\n" - settings_data['sp']['privateKey'] = key_2 + settings_data["sp"]["privateKey"] = key_2 settings_2 = OneLogin_Saml2_Settings(settings_data) self.assertEqual(key_2, settings_2.get_sp_key()) - del settings_data['sp']['privateKey'] - del settings_data['custom_base_path'] + del settings_data["sp"]["privateKey"] + del settings_data["custom_base_path"] custom_base_path = dirname(__file__) settings_3 = OneLogin_Saml2_Settings(settings_data, custom_base_path=custom_base_path) @@ -306,18 +306,18 @@ def testGetIDPCert(self): Tests the get_idp_cert method of the OneLogin_Saml2_Settings """ - settings = OneLogin_Saml2_Settings(self.loadSettingsJSON('settings9.json')) + 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-----" self.assertEqual(cert, settings.get_idp_cert()) settings_data = self.loadSettingsJSON() settings = OneLogin_Saml2_Settings(settings_data) - settings_data['idp']['x509cert'] = cert + settings_data["idp"]["x509cert"] = cert self.assertEqual(cert, settings.get_idp_cert()) - del settings_data['idp']['x509cert'] - del settings_data['custom_base_path'] + 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) @@ -333,7 +333,7 @@ def testFormatIdPCert(self): self.assertEqual(cert, settings.get_idp_cert()) cert_2 = "-----BEGIN CERTIFICATE-----\nMIICbDCCAdWgAwIBAgIBADANBgkqhkiG9w0BAQ0FADBTMQswCQYDVQQGEwJ1czET\nMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lbG9naW4gSW5jMRgwFgYD\nVQQDDA9pZHAuZXhhbXBsZS5jb20wHhcNMTQwOTIzMTIyNDA4WhcNNDIwMjA4MTIy\nNDA4WjBTMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UE\nCgwMT25lbG9naW4gSW5jMRgwFgYDVQQDDA9pZHAuZXhhbXBsZS5jb20wgZ8wDQYJ\nKoZIhvcNAQEBBQADgY0AMIGJAoGBAOWA+YHU7cvPOrBOfxCscsYTJB+kH3MaA9BF\nrSHFS+KcR6cw7oPSktIJxUgvDpQbtfNcOkE/tuOPBDoech7AXfvH6d7Bw7xtW8PP\nJ2mB5Hn/HGW2roYhxmfh3tR5SdwN6i4ERVF8eLkvwCHsNQyK2Ref0DAJvpBNZMHC\npS24916/AgMBAAGjUDBOMB0GA1UdDgQWBBQ77/qVeiigfhYDITplCNtJKZTM8DAf\nBgNVHSMEGDAWgBQ77/qVeiigfhYDITplCNtJKZTM8DAMBgNVHRMEBTADAQH/MA0G\nCSqGSIb3DQEBDQUAA4GBAJO2j/1uO80E5C2PM6Fk9mzerrbkxl7AZ/mvlbOn+sNZ\nE+VZ1AntYuG8ekbJpJtG1YfRfc7EA9mEtqvv4dhv7zBy4nK49OR+KpIBjItWB5kY\nvrqMLKBa32sMbgqqUqeF1ENXKjpvLSuPdfGJZA3dNa/+Dyb8GGqWe707zLyc5F8m\n-----END CERTIFICATE-----\n" - settings_data['idp']['x509cert'] = cert_2 + settings_data["idp"]["x509cert"] = cert_2 settings = OneLogin_Saml2_Settings(settings_data) self.assertEqual(cert_2, settings.get_idp_cert()) @@ -347,12 +347,12 @@ def testFormatSPCert(self): settings = OneLogin_Saml2_Settings(settings_data) self.assertEqual(cert, settings.get_sp_cert()) - settings_data['sp']['x509cert'] = cert + settings_data["sp"]["x509cert"] = cert settings = OneLogin_Saml2_Settings(settings_data) self.assertEqual(cert, settings.get_sp_cert()) cert_2 = "-----BEGIN CERTIFICATE-----\nMIICbDCCAdWgAwIBAgIBADANBgkqhkiG9w0BAQ0FADBTMQswCQYDVQQGEwJ1czET\nMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lbG9naW4gSW5jMRgwFgYD\nVQQDDA9pZHAuZXhhbXBsZS5jb20wHhcNMTQwOTIzMTIyNDA4WhcNNDIwMjA4MTIy\nNDA4WjBTMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UE\nCgwMT25lbG9naW4gSW5jMRgwFgYDVQQDDA9pZHAuZXhhbXBsZS5jb20wgZ8wDQYJ\nKoZIhvcNAQEBBQADgY0AMIGJAoGBAOWA+YHU7cvPOrBOfxCscsYTJB+kH3MaA9BF\nrSHFS+KcR6cw7oPSktIJxUgvDpQbtfNcOkE/tuOPBDoech7AXfvH6d7Bw7xtW8PP\nJ2mB5Hn/HGW2roYhxmfh3tR5SdwN6i4ERVF8eLkvwCHsNQyK2Ref0DAJvpBNZMHC\npS24916/AgMBAAGjUDBOMB0GA1UdDgQWBBQ77/qVeiigfhYDITplCNtJKZTM8DAf\nBgNVHSMEGDAWgBQ77/qVeiigfhYDITplCNtJKZTM8DAMBgNVHRMEBTADAQH/MA0G\nCSqGSIb3DQEBDQUAA4GBAJO2j/1uO80E5C2PM6Fk9mzerrbkxl7AZ/mvlbOn+sNZ\nE+VZ1AntYuG8ekbJpJtG1YfRfc7EA9mEtqvv4dhv7zBy4nK49OR+KpIBjItWB5kY\nvrqMLKBa32sMbgqqUqeF1ENXKjpvLSuPdfGJZA3dNa/+Dyb8GGqWe707zLyc5F8m\n-----END CERTIFICATE-----\n" - settings_data['sp']['x509cert'] = cert_2 + settings_data["sp"]["x509cert"] = cert_2 settings = OneLogin_Saml2_Settings(settings_data) self.assertEqual(cert_2, settings.get_sp_cert()) @@ -362,12 +362,12 @@ def testFormatSPKey(self): """ settings_data = self.loadSettingsJSON() key = "-----BEGIN RSA PRIVATE KEY-----\nMIICXgIBAAKBgQDivbhR7P516x/S3BqKxupQe0LONoliupiBOesCO3SHbDrl3+q9\nIbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHISKOtPlAeTZSnb8QAu7aRjZq3+\nPbrP5uW3TcfCGPtKTytHOge/OlJbo078dVhXQ14d1EDwXJW1rRXuUt4C8QIDAQAB\nAoGAD4/Z4LWVWV6D1qMIp1Gzr0ZmdWTE1SPdZ7Ej8glGnCzPdguCPuzbhGXmIg0V\nJ5D+02wsqws1zd48JSMXXM8zkYZVwQYIPUsNn5FetQpwxDIMPmhHg+QNBgwOnk8J\nK2sIjjLPL7qY7Itv7LT7Gvm5qSOkZ33RCgXcgz+okEIQMYkCQQDzbTOyDL0c5WQV\n6A2k06T/azdhUdGXF9C0+WkWSfNaovmTgRXh1G+jMlr82Snz4p4/STt7P/XtyWzF\n3pkVgZr3AkEA7nPjXwHlttNEMo6AtxHd47nizK2NUN803ElIUT8P9KSCoERmSXq6\n6PDekGNic4ldpsSvOeYCk8MAYoDBy9kvVwJBAMLgX4xg6lzhv7hR5+pWjTb1rIY6\nrCHbrPfU264+UZXz9v2BT/VUznLF81WMvStD9xAPHpFS6R0OLghSZhdzhI0CQQDL\n8Duvfxzrn4b9QlmduV8wLERoT6rEVxKLsPVz316TGrxJvBZLk/cV0SRZE1cZf4uk\nXSWMfEcJ/0Zt+LdG1CqjAkEAqwLSglJ9Dy3HpgMz4vAAyZWzAxvyA1zW0no9GOLc\nPQnYaNUN/Fy2SYtETXTb0CQ9X1rt8ffkFP7ya+5TC83aMg==\n-----END RSA PRIVATE KEY-----\n" - settings_data['sp']['privateKey'] = key + settings_data["sp"]["privateKey"] = key settings = OneLogin_Saml2_Settings(settings_data) self.assertEqual(key, settings.get_sp_key()) key_2 = "-----BEGIN PRIVATE KEY-----\nMIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAOWA+YHU7cvPOrBO\nfxCscsYTJB+kH3MaA9BFrSHFS+KcR6cw7oPSktIJxUgvDpQbtfNcOkE/tuOPBDoe\nch7AXfvH6d7Bw7xtW8PPJ2mB5Hn/HGW2roYhxmfh3tR5SdwN6i4ERVF8eLkvwCHs\nNQyK2Ref0DAJvpBNZMHCpS24916/AgMBAAECgYEA0wDXZPS9hKqMTNh+nnfONioX\nBjhA6fQ7GVtWKDxa3ofMoPyt7ejGL/Hnvcv13Vn02UAsFx1bKrCstDqVtYwrWrnm\nywXyH+o9paJnTmd+cRIjWU8mRvCrxzH5I/Bcvbp1qZoASuqZEaGwNjM6JpW2o3QT\nmHGMALcLUPfEvhApssECQQDy2e65E86HcFhi/Ta8TQ0odDCNbiWA0bI1Iu8B7z+N\nAy1D1+WnCd7w2u9U6CF/k2nFHCsvxEoeANM0z7h5T/XvAkEA8e4JqKmDrfdiakQT\n7nf9svU2jXZtxSbPiIRMafNikDvzZ1vJCZkvdmaWYL70GlDZIwc9ad67rHZ/n/fq\nX1d0MQJAbRpRsJ5gY+KqItbFt3UaWzlP8sowWR5cZJjsLb9RmsV5mYguKYw6t5R0\nf33GRu1wUFimYlBaR/5w5MIJi57LywJATO1a5uWX+G5MPewNxmsjIY91XEAHIYR4\nwzkGLz5z3dciS4BVCZdLD0QJlxPA/MkuckPwFET9uhYn+M7VGKHvUQJBANSDwsY+\nBdCGpi/WRV37HUfwLl07damaFbW3h08PQx8G8SuF7DpN+FPBcI6VhzrIWNRBxWpr\nkgeGioKNfFWzSaM=\n-----END PRIVATE KEY-----\n" - settings_data['sp']['privateKey'] = key_2 + settings_data["sp"]["privateKey"] = key_2 settings_2 = OneLogin_Saml2_Settings(settings_data) self.assertEqual(key_2, settings_2.get_sp_key()) @@ -388,112 +388,93 @@ def testCheckSettings(self): OneLogin_Saml2_Settings(settings_info) self.assertTrue(False) except Exception as e: - self.assertIn('Invalid dict settings: invalid_syntax', str(e)) + self.assertIn("Invalid dict settings: invalid_syntax", str(e)) - settings_info['strict'] = True + settings_info["strict"] = True try: OneLogin_Saml2_Settings(settings_info) self.assertTrue(False) except Exception as e: - self.assertIn('idp_not_found', str(e)) - self.assertIn('sp_not_found', str(e)) - - settings_info['idp'] = {} - settings_info['idp']['x509cert'] = '' - settings_info['sp'] = {} - settings_info['sp']['entityID'] = 'SPentityId' - settings_info['security'] = {} - settings_info['security']['signMetadata'] = False + self.assertIn("idp_not_found", str(e)) + self.assertIn("sp_not_found", str(e)) + + settings_info["idp"] = {} + settings_info["idp"]["x509cert"] = "" + settings_info["sp"] = {} + settings_info["sp"]["entityID"] = "SPentityId" + settings_info["security"] = {} + settings_info["security"]["signMetadata"] = False try: OneLogin_Saml2_Settings(settings_info) self.assertTrue(False) except Exception as e: - self.assertIn('idp_entityId_not_found', str(e)) - self.assertIn('idp_sso_not_found', str(e)) - self.assertIn('sp_entityId_not_found', str(e)) - self.assertIn('sp_acs_not_found', str(e)) + self.assertIn("idp_entityId_not_found", str(e)) + self.assertIn("idp_sso_not_found", str(e)) + self.assertIn("sp_entityId_not_found", str(e)) + self.assertIn("sp_acs_not_found", str(e)) # AttributeConsumingService tests # serviceName, requestedAttributes are required - settings_info['sp']['attributeConsumingService'] = { - "serviceDescription": "Test Service" - } + settings_info["sp"]["attributeConsumingService"] = {"serviceDescription": "Test Service"} try: OneLogin_Saml2_Settings(settings_info) self.assertTrue(False) except Exception as e: - self.assertIn('sp_attributeConsumingService_serviceName_not_found', str(e)) - self.assertIn('sp_attributeConsumingService_requestedAttributes_not_found', str(e)) + self.assertIn("sp_attributeConsumingService_serviceName_not_found", str(e)) + self.assertIn("sp_attributeConsumingService_requestedAttributes_not_found", str(e)) # requestedAttributes/name is required - settings_info['sp']['attributeConsumingService'] = { + settings_info["sp"]["attributeConsumingService"] = { "serviceName": {}, "serviceDescription": ["Test Service"], - "requestedAttributes": [{ - "nameFormat": "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", - "friendlyName": "givenName", - "isRequired": "False" - } - ] + "requestedAttributes": [{"nameFormat": "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", "friendlyName": "givenName", "isRequired": "False"}], } try: OneLogin_Saml2_Settings(settings_info) self.assertTrue(False) except Exception as e: - self.assertIn('sp_attributeConsumingService_requestedAttributes_name_not_found', str(e)) - self.assertIn('sp_attributeConsumingService_requestedAttributes_isRequired_type_invalid', str(e)) - self.assertIn('sp_attributeConsumingService_serviceDescription_type_invalid', str(e)) - self.assertIn('sp_attributeConsumingService_serviceName_type_invalid', str(e)) - - settings_info['idp']['entityID'] = 'entityId' - settings_info['idp']['singleSignOnService'] = {} - settings_info['idp']['singleSignOnService']['url'] = 'invalid_value' - settings_info['idp']['singleLogoutService'] = {} - settings_info['idp']['singleLogoutService']['url'] = 'invalid_value' - settings_info['sp']['assertionConsumerService'] = {} - settings_info['sp']['assertionConsumerService']['url'] = 'invalid_value' - settings_info['sp']['singleLogoutService'] = {} - settings_info['sp']['singleLogoutService']['url'] = 'invalid_value' + self.assertIn("sp_attributeConsumingService_requestedAttributes_name_not_found", str(e)) + self.assertIn("sp_attributeConsumingService_requestedAttributes_isRequired_type_invalid", str(e)) + self.assertIn("sp_attributeConsumingService_serviceDescription_type_invalid", str(e)) + self.assertIn("sp_attributeConsumingService_serviceName_type_invalid", str(e)) + + settings_info["idp"]["entityID"] = "entityId" + settings_info["idp"]["singleSignOnService"] = {} + settings_info["idp"]["singleSignOnService"]["url"] = "invalid_value" + settings_info["idp"]["singleLogoutService"] = {} + settings_info["idp"]["singleLogoutService"]["url"] = "invalid_value" + settings_info["sp"]["assertionConsumerService"] = {} + settings_info["sp"]["assertionConsumerService"]["url"] = "invalid_value" + settings_info["sp"]["singleLogoutService"] = {} + settings_info["sp"]["singleLogoutService"]["url"] = "invalid_value" try: OneLogin_Saml2_Settings(settings_info) self.assertTrue(False) except Exception as e: - self.assertIn('idp_sso_url_invalid', str(e)) - self.assertIn('idp_slo_url_invalid', str(e)) - self.assertIn('sp_acs_url_invalid', str(e)) - self.assertIn('sp_sls_url_invalid', str(e)) + self.assertIn("idp_sso_url_invalid", str(e)) + self.assertIn("idp_slo_url_invalid", str(e)) + self.assertIn("sp_acs_url_invalid", str(e)) + self.assertIn("sp_sls_url_invalid", str(e)) - settings_info['security']['wantAssertionsSigned'] = True + settings_info["security"]["wantAssertionsSigned"] = True try: OneLogin_Saml2_Settings(settings_info) self.assertTrue(False) except Exception as e: - self.assertIn('idp_cert_or_fingerprint_not_found_and_required', str(e)) + self.assertIn("idp_cert_or_fingerprint_not_found_and_required", str(e)) settings_info = self.loadSettingsJSON() - settings_info['security']['signMetadata'] = {} - settings_info['security']['signMetadata']['keyFileName'] = 'metadata.key' - settings_info['organization'] = { - 'en-US': { - 'name': 'miss_information' - } - } - settings_info['contactPerson'] = { - 'support': { - 'givenName': 'support_name' - }, - 'auxiliar': { - 'givenName': 'auxiliar_name', - 'emailAddress': 'auxiliar@example.com' - } - } + settings_info["security"]["signMetadata"] = {} + settings_info["security"]["signMetadata"]["keyFileName"] = "metadata.key" + settings_info["organization"] = {"en-US": {"name": "miss_information"}} + settings_info["contactPerson"] = {"support": {"givenName": "support_name"}, "auxiliar": {"givenName": "auxiliar_name", "emailAddress": "auxiliar@example.com"}} try: OneLogin_Saml2_Settings(settings_info) self.assertTrue(False) except Exception as e: - self.assertIn('sp_signMetadata_invalid', str(e)) - self.assertIn('organization_not_enought_data', str(e)) - self.assertIn('contact_type_invalid', str(e)) + self.assertIn("sp_signMetadata_invalid", str(e)) + self.assertIn("organization_not_enought_data", str(e)) + self.assertIn("contact_type_invalid", str(e)) def testGetSPMetadata(self): """ @@ -501,28 +482,28 @@ def testGetSPMetadata(self): Case unsigned metadata """ settings_info = self.loadSettingsJSON() - settings_info['security']['wantNameIdEncrypted'] = False - settings_info['security']['wantAssertionsEncrypted'] = False + settings_info["security"]["wantNameIdEncrypted"] = False + settings_info["security"]["wantAssertionsEncrypted"] = False settings = OneLogin_Saml2_Settings(settings_info) metadata = compat.to_string(settings.get_sp_metadata()) self.assertNotEqual(len(metadata), 0) - self.assertIn('', metadata) self.assertIn('', metadata) - self.assertIn('urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified', metadata) - self.assertEqual(1, metadata.count('urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", metadata) + self.assertEqual(1, metadata.count("', metadata) self.assertIn('', metadata) - self.assertIn('urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified', metadata) + self.assertIn("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", metadata) self.assertIn('\n', metadata) self.assertIn('', metadata) - self.assertIn('\n\n', metadata) + self.assertIn("\n\n", metadata) def testGetSPMetadataSignedNoMetadataCert(self): """ @@ -612,36 +593,30 @@ def testGetSPMetadataSignedNoMetadataCert(self): Case signed metadata with specific certs """ settings_info = self.loadSettingsJSON() - if 'security' not in settings_info: - settings_info['security'] = {} - settings_info['security']['signMetadata'] = {} + if "security" not in settings_info: + settings_info["security"] = {} + settings_info["security"]["signMetadata"] = {} with self.assertRaises(Exception) as context: OneLogin_Saml2_Settings(settings_info) exception = context.exception self.assertIn("sp_signMetadata_invalid", str(exception)) - settings_info['security']['signMetadata'] = { - 'keyFileName': 'noexist.key', - 'certFileName': 'sp.crt' - } + settings_info["security"]["signMetadata"] = {"keyFileName": "noexist.key", "certFileName": "sp.crt"} settings = OneLogin_Saml2_Settings(settings_info) with self.assertRaises(Exception) as context: settings.get_sp_metadata() exception = context.exception self.assertIn("Private key file not readable", str(exception)) - settings_info['security']['signMetadata'] = { - 'keyFileName': 'sp.key', - 'certFileName': 'noexist.crt' - } + settings_info["security"]["signMetadata"] = {"keyFileName": "sp.key", "certFileName": "noexist.crt"} settings = OneLogin_Saml2_Settings(settings_info) with self.assertRaises(Exception) as context: settings.get_sp_metadata() exception = context.exception self.assertIn("Public cert file not readable", str(exception)) - settings_info['security']['signMetadata'] = 'invalid_value' + settings_info["security"]["signMetadata"] = "invalid_value" settings = OneLogin_Saml2_Settings(settings_info) with self.assertRaises(Exception) as context: settings.get_sp_metadata() @@ -657,19 +632,19 @@ def testValidateMetadata(self): metadata = settings.get_sp_metadata() self.assertEqual(len(settings.validate_metadata(metadata)), 0) - xml = self.file_contents(join(self.data_path, 'metadata', 'metadata_settings1.xml')) + xml = self.file_contents(join(self.data_path, "metadata", "metadata_settings1.xml")) self.assertEqual(len(settings.validate_metadata(xml)), 0) - xml_2 = 'invalid' - self.assertIn('invalid_xml', settings.validate_metadata(xml_2)) + xml_2 = "invalid" + self.assertIn("invalid_xml", settings.validate_metadata(xml_2)) - xml_3 = self.file_contents(join(self.data_path, 'metadata', 'entities_metadata.xml')) - self.assertIn('noEntityDescriptor_xml', settings.validate_metadata(xml_3)) + xml_3 = self.file_contents(join(self.data_path, "metadata", "entities_metadata.xml")) + self.assertIn("noEntityDescriptor_xml", settings.validate_metadata(xml_3)) - xml_4 = self.file_contents(join(self.data_path, 'metadata', 'idp_metadata.xml')) - self.assertIn('onlySPSSODescriptor_allowed_xml', settings.validate_metadata(xml_4)) + xml_4 = self.file_contents(join(self.data_path, "metadata", "idp_metadata.xml")) + self.assertIn("onlySPSSODescriptor_allowed_xml", settings.validate_metadata(xml_4)) - xml_5 = self.file_contents(join(self.data_path, 'metadata', 'no_expiration_mark_metadata.xml')) + xml_5 = self.file_contents(join(self.data_path, "metadata", "no_expiration_mark_metadata.xml")) self.assertEqual(len(settings.validate_metadata(xml_5)), 0) def testValidateMetadataExpired(self): @@ -677,10 +652,10 @@ def testValidateMetadataExpired(self): Tests the validateMetadata method of the OneLogin_Saml2_Settings """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - metadata = self.file_contents(join(self.data_path, 'metadata', 'expired_metadata_settings1.xml')) + metadata = self.file_contents(join(self.data_path, "metadata", "expired_metadata_settings1.xml")) errors = settings.validate_metadata(metadata) self.assertNotEqual(len(metadata), 0) - self.assertIn('expired_xml', errors) + self.assertIn("expired_xml", errors) def testValidateMetadataNoXML(self): """ @@ -688,17 +663,17 @@ def testValidateMetadataNoXML(self): Case no metadata """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - metadata = '' + metadata = "" with self.assertRaises(Exception) as context: settings.validate_metadata(metadata) exception = context.exception self.assertIn("t", str(exception)) - metadata = '' + metadata = "" errors = settings.validate_metadata(metadata) self.assertNotEqual(len(errors), 0) - self.assertIn('unloaded_xml', errors) + self.assertIn("unloaded_xml", errors) def testValidateMetadataNoEntity(self): """ @@ -706,10 +681,10 @@ def testValidateMetadataNoEntity(self): Case invalid xml metadata """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - metadata = self.file_contents(join(self.data_path, 'metadata', 'noentity_metadata_settings1.xml')) + metadata = self.file_contents(join(self.data_path, "metadata", "noentity_metadata_settings1.xml")) errors = settings.validate_metadata(metadata) self.assertNotEqual(len(metadata), 0) - self.assertIn('invalid_xml', errors) + self.assertIn("invalid_xml", errors) def testGetIdPData(self): """ @@ -719,18 +694,18 @@ def testGetIdPData(self): idp_data = settings.get_idp_data() self.assertNotEqual(len(idp_data), 0) - self.assertIn('entityId', idp_data) - self.assertIn('singleSignOnService', idp_data) - self.assertIn('singleLogoutService', idp_data) - self.assertIn('x509cert', idp_data) + self.assertIn("entityId", idp_data) + self.assertIn("singleSignOnService", idp_data) + self.assertIn("singleLogoutService", idp_data) + self.assertIn("x509cert", idp_data) - self.assertEqual('http://idp.example.com/', idp_data['entityId']) - self.assertEqual('http://idp.example.com/SSOService.php', idp_data['singleSignOnService']['url']) - self.assertEqual('http://idp.example.com/SingleLogoutService.php', idp_data['singleLogoutService']['url']) + self.assertEqual("http://idp.example.com/", idp_data["entityId"]) + self.assertEqual("http://idp.example.com/SSOService.php", idp_data["singleSignOnService"]["url"]) + self.assertEqual("http://idp.example.com/SingleLogoutService.php", idp_data["singleLogoutService"]["url"]) - x509cert = 'MIICgTCCAeoCCQCbOlrWDdX7FTANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMCTk8xGDAWBgNVBAgTD0FuZHJlYXMgU29sYmVyZzEMMAoGA1UEBxMDRm9vMRAwDgYDVQQKEwdVTklORVRUMRgwFgYDVQQDEw9mZWlkZS5lcmxhbmcubm8xITAfBgkqhkiG9w0BCQEWEmFuZHJlYXNAdW5pbmV0dC5ubzAeFw0wNzA2MTUxMjAxMzVaFw0wNzA4MTQxMjAxMzVaMIGEMQswCQYDVQQGEwJOTzEYMBYGA1UECBMPQW5kcmVhcyBTb2xiZXJnMQwwCgYDVQQHEwNGb28xEDAOBgNVBAoTB1VOSU5FVFQxGDAWBgNVBAMTD2ZlaWRlLmVybGFuZy5ubzEhMB8GCSqGSIb3DQEJARYSYW5kcmVhc0B1bmluZXR0Lm5vMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDivbhR7P516x/S3BqKxupQe0LONoliupiBOesCO3SHbDrl3+q9IbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHISKOtPlAeTZSnb8QAu7aRjZq3+PbrP5uW3TcfCGPtKTytHOge/OlJbo078dVhXQ14d1EDwXJW1rRXuUt4C8QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACDVfp86HObqY+e8BUoWQ9+VMQx1ASDohBjwOsg2WykUqRXF+dLfcUH9dWR63CtZIKFDbStNomPnQz7nbK+onygwBspVEbnHuUihZq3ZUdmumQqCw4Uvs/1Uvq3orOo/WJVhTyvLgFVK2QarQ4/67OZfHd7R+POBXhophSMv1ZOo' + x509cert = "MIICgTCCAeoCCQCbOlrWDdX7FTANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMCTk8xGDAWBgNVBAgTD0FuZHJlYXMgU29sYmVyZzEMMAoGA1UEBxMDRm9vMRAwDgYDVQQKEwdVTklORVRUMRgwFgYDVQQDEw9mZWlkZS5lcmxhbmcubm8xITAfBgkqhkiG9w0BCQEWEmFuZHJlYXNAdW5pbmV0dC5ubzAeFw0wNzA2MTUxMjAxMzVaFw0wNzA4MTQxMjAxMzVaMIGEMQswCQYDVQQGEwJOTzEYMBYGA1UECBMPQW5kcmVhcyBTb2xiZXJnMQwwCgYDVQQHEwNGb28xEDAOBgNVBAoTB1VOSU5FVFQxGDAWBgNVBAMTD2ZlaWRlLmVybGFuZy5ubzEhMB8GCSqGSIb3DQEJARYSYW5kcmVhc0B1bmluZXR0Lm5vMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDivbhR7P516x/S3BqKxupQe0LONoliupiBOesCO3SHbDrl3+q9IbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHISKOtPlAeTZSnb8QAu7aRjZq3+PbrP5uW3TcfCGPtKTytHOge/OlJbo078dVhXQ14d1EDwXJW1rRXuUt4C8QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACDVfp86HObqY+e8BUoWQ9+VMQx1ASDohBjwOsg2WykUqRXF+dLfcUH9dWR63CtZIKFDbStNomPnQz7nbK+onygwBspVEbnHuUihZq3ZUdmumQqCw4Uvs/1Uvq3orOo/WJVhTyvLgFVK2QarQ4/67OZfHd7R+POBXhophSMv1ZOo" formated_x509_cert = OneLogin_Saml2_Utils.format_cert(x509cert) - self.assertEqual(formated_x509_cert, idp_data['x509cert']) + self.assertEqual(formated_x509_cert, idp_data["x509cert"]) def testGetSPData(self): """ @@ -740,15 +715,15 @@ def testGetSPData(self): sp_data = settings.get_sp_data() self.assertNotEqual(len(sp_data), 0) - self.assertIn('entityId', sp_data) - self.assertIn('assertionConsumerService', sp_data) - self.assertIn('singleLogoutService', sp_data) - self.assertIn('NameIDFormat', sp_data) + self.assertIn("entityId", sp_data) + self.assertIn("assertionConsumerService", sp_data) + self.assertIn("singleLogoutService", sp_data) + self.assertIn("NameIDFormat", sp_data) - self.assertEqual('http://stuff.com/endpoints/metadata.php', sp_data['entityId']) - self.assertEqual('http://stuff.com/endpoints/endpoints/acs.php', sp_data['assertionConsumerService']['url']) - self.assertEqual('http://stuff.com/endpoints/endpoints/sls.php', sp_data['singleLogoutService']['url']) - self.assertEqual('urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified', sp_data['NameIDFormat']) + self.assertEqual("http://stuff.com/endpoints/metadata.php", sp_data["entityId"]) + self.assertEqual("http://stuff.com/endpoints/endpoints/acs.php", sp_data["assertionConsumerService"]["url"]) + self.assertEqual("http://stuff.com/endpoints/endpoints/sls.php", sp_data["singleLogoutService"]["url"]) + self.assertEqual("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", sp_data["NameIDFormat"]) def testGetSecurityData(self): """ @@ -758,55 +733,55 @@ def testGetSecurityData(self): security = settings.get_security_data() self.assertNotEqual(len(security), 0) - self.assertIn('nameIdEncrypted', security) - self.assertIn('authnRequestsSigned', security) - self.assertIn('logoutRequestSigned', security) - self.assertIn('logoutResponseSigned', security) - self.assertIn('signMetadata', security) - self.assertIn('wantMessagesSigned', security) - self.assertIn('wantAssertionsSigned', security) - self.assertIn('requestedAuthnContext', security) - self.assertIn('wantNameId', security) - self.assertIn('wantNameIdEncrypted', security) + self.assertIn("nameIdEncrypted", security) + self.assertIn("authnRequestsSigned", security) + self.assertIn("logoutRequestSigned", security) + self.assertIn("logoutResponseSigned", security) + self.assertIn("signMetadata", security) + self.assertIn("wantMessagesSigned", security) + self.assertIn("wantAssertionsSigned", security) + self.assertIn("requestedAuthnContext", security) + self.assertIn("wantNameId", security) + self.assertIn("wantNameIdEncrypted", security) def testGetDefaultSecurityValues(self): """ Tests default values of Security advanced sesettings """ settings_json = self.loadSettingsJSON() - del settings_json['security'] + del settings_json["security"] settings = OneLogin_Saml2_Settings(settings_json) security = settings.get_security_data() - self.assertIn('nameIdEncrypted', security) - self.assertFalse(security.get('nameIdEncrypted')) + self.assertIn("nameIdEncrypted", security) + self.assertFalse(security.get("nameIdEncrypted")) - self.assertIn('authnRequestsSigned', security) - self.assertFalse(security.get('authnRequestsSigned')) + self.assertIn("authnRequestsSigned", security) + self.assertFalse(security.get("authnRequestsSigned")) - self.assertIn('logoutRequestSigned', security) - self.assertFalse(security.get('logoutRequestSigned')) + self.assertIn("logoutRequestSigned", security) + self.assertFalse(security.get("logoutRequestSigned")) - self.assertIn('logoutResponseSigned', security) - self.assertFalse(security.get('logoutResponseSigned')) + self.assertIn("logoutResponseSigned", security) + self.assertFalse(security.get("logoutResponseSigned")) - self.assertIn('signMetadata', security) - self.assertFalse(security.get('signMetadata')) + self.assertIn("signMetadata", security) + self.assertFalse(security.get("signMetadata")) - self.assertIn('wantMessagesSigned', security) - self.assertFalse(security.get('wantMessagesSigned')) + self.assertIn("wantMessagesSigned", security) + self.assertFalse(security.get("wantMessagesSigned")) - self.assertIn('wantAssertionsSigned', security) - self.assertFalse(security.get('wantAssertionsSigned')) + self.assertIn("wantAssertionsSigned", security) + self.assertFalse(security.get("wantAssertionsSigned")) - self.assertIn('requestedAuthnContext', security) - self.assertTrue(security.get('requestedAuthnContext')) + self.assertIn("requestedAuthnContext", security) + self.assertTrue(security.get("requestedAuthnContext")) - self.assertIn('wantNameId', security) - self.assertTrue(security.get('wantNameId')) + self.assertIn("wantNameId", security) + self.assertTrue(security.get("wantNameId")) - self.assertIn('wantNameIdEncrypted', security) - self.assertFalse(security.get('wantNameIdEncrypted')) + self.assertIn("wantNameIdEncrypted", security) + self.assertFalse(security.get("wantNameIdEncrypted")) def testGetContacts(self): """ @@ -816,10 +791,10 @@ def testGetContacts(self): contacts = settings.get_contacts() self.assertNotEqual(len(contacts), 0) - self.assertEqual('technical_name', contacts['technical']['givenName']) - self.assertEqual('technical@example.com', contacts['technical']['emailAddress']) - self.assertEqual('support_name', contacts['support']['givenName']) - self.assertEqual('support@example.com', contacts['support']['emailAddress']) + self.assertEqual("technical_name", contacts["technical"]["givenName"]) + self.assertEqual("technical@example.com", contacts["technical"]["emailAddress"]) + self.assertEqual("support_name", contacts["support"]["givenName"]) + self.assertEqual("support@example.com", contacts["support"]["emailAddress"]) def testGetOrganization(self): """ @@ -829,9 +804,9 @@ def testGetOrganization(self): organization = settings.get_organization() self.assertNotEqual(len(organization), 0) - self.assertEqual('sp_test', organization['en-US']['name']) - self.assertEqual('SP test', organization['en-US']['displayname']) - self.assertEqual('http://sp.example.com', organization['en-US']['url']) + self.assertEqual("sp_test", organization["en-US"]["name"]) + self.assertEqual("SP test", organization["en-US"]["displayname"]) + self.assertEqual("http://sp.example.com", organization["en-US"]["url"]) def testSetStrict(self): """ @@ -846,23 +821,23 @@ def testSetStrict(self): settings.set_strict(False) self.assertFalse(settings.is_strict()) - self.assertRaises(AssertionError, settings.set_strict, 'a') + self.assertRaises(AssertionError, settings.set_strict, "a") def testIsStrict(self): """ Tests the isStrict method of the OneLogin_Saml2_Settings """ settings_info = self.loadSettingsJSON() - del settings_info['strict'] + del settings_info["strict"] settings = OneLogin_Saml2_Settings(settings_info) self.assertTrue(settings.is_strict()) - settings_info['strict'] = False + settings_info["strict"] = False settings_2 = OneLogin_Saml2_Settings(settings_info) self.assertFalse(settings_2.is_strict()) - settings_info['strict'] = True + settings_info["strict"] = True settings_3 = OneLogin_Saml2_Settings(settings_info) self.assertTrue(settings_3.is_strict()) @@ -871,15 +846,15 @@ def testIsDebugActive(self): Tests the isDebugActive method of the OneLogin_Saml2_Settings """ settings_info = self.loadSettingsJSON() - del settings_info['debug'] + del settings_info["debug"] settings = OneLogin_Saml2_Settings(settings_info) self.assertFalse(settings.is_debug_active()) - settings_info['debug'] = False + settings_info["debug"] = False settings_2 = OneLogin_Saml2_Settings(settings_info) self.assertFalse(settings_2.is_debug_active()) - settings_info['debug'] = True + settings_info["debug"] = True settings_3 = OneLogin_Saml2_Settings(settings_info) self.assertTrue(settings_3.is_debug_active()) diff --git a/tests/src/OneLogin/saml2_tests/signed_response_test.py b/tests/src/OneLogin/saml2_tests/signed_response_test.py index bed0e028..844fc930 100644 --- a/tests/src/OneLogin/saml2_tests/signed_response_test.py +++ b/tests/src/OneLogin/saml2_tests/signed_response_test.py @@ -11,20 +11,20 @@ class OneLogin_Saml2_SignedResponse_Test(unittest.TestCase): - data_path = join(dirname(__file__), '..', '..', '..', 'data') + data_path = join(dirname(__file__), "..", "..", "..", "data") def loadSettingsJSON(self): - filename = join(dirname(__file__), '..', '..', '..', 'settings', 'settings1.json') + filename = join(dirname(__file__), "..", "..", "..", "settings", "settings1.json") if exists(filename): - stream = open(filename, 'r') + stream = open(filename, "r") settings = json.load(stream) stream.close() return settings else: - raise Exception('Settings json file does not exist') + raise Exception("Settings json file does not exist") def file_contents(self, filename): - f = open(filename, 'r') + f = open(filename, "r") content = f.read() f.close() return content @@ -35,10 +35,10 @@ def testResponseSignedAssertionNot(self): Case valid signed response, unsigned assertion """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - message = self.file_contents(join(self.data_path, 'responses', 'open_saml_response.xml')) + message = self.file_contents(join(self.data_path, "responses", "open_saml_response.xml")) response = OneLogin_Saml2_Response(settings, OneLogin_Saml2_Utils.b64encode(message)) - self.assertEqual('someone@example.org', response.get_nameid()) + self.assertEqual("someone@example.org", response.get_nameid()) def testResponseAndAssertionSigned(self): """ @@ -46,7 +46,7 @@ def testResponseAndAssertionSigned(self): Case valid signed response, signed assertion """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - message = self.file_contents(join(self.data_path, 'responses', 'simple_saml_php.xml')) + message = self.file_contents(join(self.data_path, "responses", "simple_saml_php.xml")) response = OneLogin_Saml2_Response(settings, OneLogin_Saml2_Utils.b64encode(message)) - self.assertEqual('someone@example.com', response.get_nameid()) + self.assertEqual("someone@example.com", response.get_nameid()) diff --git a/tests/src/OneLogin/saml2_tests/utils_test.py b/tests/src/OneLogin/saml2_tests/utils_test.py index f1e66453..60637e5f 100644 --- a/tests/src/OneLogin/saml2_tests/utils_test.py +++ b/tests/src/OneLogin/saml2_tests/utils_test.py @@ -6,6 +6,7 @@ from lxml import etree from os.path import dirname, join, exists import unittest +import sys from xml.dom.minidom import parseString from onelogin.saml2 import compat @@ -16,28 +17,28 @@ class OneLogin_Saml2_Utils_Test(unittest.TestCase): - data_path = join(dirname(dirname(dirname(dirname(__file__)))), 'data') - settings_path = join(dirname(dirname(dirname(dirname(__file__)))), 'settings') + data_path = join(dirname(dirname(dirname(dirname(__file__)))), "data") + settings_path = join(dirname(dirname(dirname(dirname(__file__)))), "settings") # assertRegexpMatches deprecated on python3 def assertRaisesRegex(self, exception, regexp, msg=None): - if hasattr(unittest.TestCase, 'assertRaisesRegex'): + if hasattr(unittest.TestCase, "assertRaisesRegex"): return super(OneLogin_Saml2_Utils_Test, self).assertRaisesRegex(exception, regexp, msg=msg) else: return self.assertRaisesRegexp(exception, regexp) - def loadSettingsJSON(self, name='settings1.json'): + def loadSettingsJSON(self, name="settings1.json"): filename = join(self.settings_path, name) if exists(filename): - stream = open(filename, 'r') + stream = open(filename, "r") settings = json.load(stream) stream.close() return settings else: - raise Exception('Settings json file does not exist') + raise Exception("Settings json file does not exist") def file_contents(self, filename): - f = open(filename, 'r') + f = open(filename, "r") content = f.read() f.close() return content @@ -47,21 +48,21 @@ def testFormatCert(self): Tests the format_cert method of the OneLogin_Saml2_Utils """ settings_info = self.loadSettingsJSON() - cert = settings_info['idp']['x509cert'] - self.assertNotIn('-----BEGIN CERTIFICATE-----', cert) - self.assertNotIn('-----END CERTIFICATE-----', cert) + cert = settings_info["idp"]["x509cert"] + self.assertNotIn("-----BEGIN CERTIFICATE-----", cert) + self.assertNotIn("-----END CERTIFICATE-----", cert) self.assertEqual(len(cert), 860) formated_cert1 = OneLogin_Saml2_Utils.format_cert(cert) - self.assertIn('-----BEGIN CERTIFICATE-----', formated_cert1) - self.assertIn('-----END CERTIFICATE-----', formated_cert1) + self.assertIn("-----BEGIN CERTIFICATE-----", formated_cert1) + self.assertIn("-----END CERTIFICATE-----", formated_cert1) formated_cert2 = OneLogin_Saml2_Utils.format_cert(cert, True) self.assertEqual(formated_cert1, formated_cert2) formated_cert3 = OneLogin_Saml2_Utils.format_cert(cert, False) - self.assertNotIn('-----BEGIN CERTIFICATE-----', formated_cert3) - self.assertNotIn('-----END CERTIFICATE-----', formated_cert3) + self.assertNotIn("-----BEGIN CERTIFICATE-----", formated_cert3) + self.assertNotIn("-----END CERTIFICATE-----", formated_cert3) self.assertEqual(len(formated_cert3), 860) def testFormatPrivateKey(self): @@ -70,51 +71,49 @@ def testFormatPrivateKey(self): """ key = "-----BEGIN RSA PRIVATE KEY-----\nMIICXgIBAAKBgQDivbhR7P516x/S3BqKxupQe0LONoliupiBOesCO3SHbDrl3+q9\nIbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHISKOtPlAeTZSnb8QAu7aRjZq3+\nPbrP5uW3TcfCGPtKTytHOge/OlJbo078dVhXQ14d1EDwXJW1rRXuUt4C8QIDAQAB\nAoGAD4/Z4LWVWV6D1qMIp1Gzr0ZmdWTE1SPdZ7Ej8glGnCzPdguCPuzbhGXmIg0V\nJ5D+02wsqws1zd48JSMXXM8zkYZVwQYIPUsNn5FetQpwxDIMPmhHg+QNBgwOnk8J\nK2sIjjLPL7qY7Itv7LT7Gvm5qSOkZ33RCgXcgz+okEIQMYkCQQDzbTOyDL0c5WQV\n6A2k06T/azdhUdGXF9C0+WkWSfNaovmTgRXh1G+jMlr82Snz4p4/STt7P/XtyWzF\n3pkVgZr3AkEA7nPjXwHlttNEMo6AtxHd47nizK2NUN803ElIUT8P9KSCoERmSXq6\n6PDekGNic4ldpsSvOeYCk8MAYoDBy9kvVwJBAMLgX4xg6lzhv7hR5+pWjTb1rIY6\nrCHbrPfU264+UZXz9v2BT/VUznLF81WMvStD9xAPHpFS6R0OLghSZhdzhI0CQQDL\n8Duvfxzrn4b9QlmduV8wLERoT6rEVxKLsPVz316TGrxJvBZLk/cV0SRZE1cZf4uk\nXSWMfEcJ/0Zt+LdG1CqjAkEAqwLSglJ9Dy3HpgMz4vAAyZWzAxvyA1zW0no9GOLc\nPQnYaNUN/Fy2SYtETXTb0CQ9X1rt8ffkFP7ya+5TC83aMg==\n-----END RSA PRIVATE KEY-----\n" formated_key = OneLogin_Saml2_Utils.format_private_key(key, True) - self.assertIn('-----BEGIN RSA PRIVATE KEY-----', formated_key) - self.assertIn('-----END RSA PRIVATE KEY-----', formated_key) + self.assertIn("-----BEGIN RSA PRIVATE KEY-----", formated_key) + self.assertIn("-----END RSA PRIVATE KEY-----", formated_key) self.assertEqual(len(formated_key), 891) formated_key = OneLogin_Saml2_Utils.format_private_key(key, False) - self.assertNotIn('-----BEGIN RSA PRIVATE KEY-----', formated_key) - self.assertNotIn('-----END RSA PRIVATE KEY-----', formated_key) + self.assertNotIn("-----BEGIN RSA PRIVATE KEY-----", formated_key) + self.assertNotIn("-----END RSA PRIVATE KEY-----", formated_key) self.assertEqual(len(formated_key), 816) key_2 = "-----BEGIN PRIVATE KEY-----\nMIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAM62buSW9Zgh7CmZ\nouJekK0ac9sgEZkspemjv7SyE6Hbdz+KmUr3C7MI6JuPfVyJbxvMDf3FbgBBK7r5\nyfGgehXwplLMZj8glvV3NkdLMLPWmaw9U5sOzRoym46pVvsEo1PUL2qDK5Wrsm1g\nuY1KIDSHL59NQ7PzDKgm1dxioeXFAgMBAAECgYA/fvRzTReloo3rfWD2Tfv84EpE\nPgaJ2ZghO4Zwl97F8icgIo/R4i760Lq6xgnI+gJiNHz7vcB7XYl0RrRMf3HgbA7z\npJxREmOVltESDHy6lH0TmCdv9xMmHltB+pbGOhqBvuGgFbEOR73lDDV0ln2rEITJ\nA2zjYF+hWe8b0JFeQQJBAOsIIIlHAMngjhCQDD6kla/vce972gCFU7ZeFw16ZMmb\n8W4rGRfQoQWYxSLAFIFsYewSBTccanyYbBNe3njki3ECQQDhJ4cgV6VpTwez4dkp\nU/xCHKoReedAEJhXucTNGpiIqu+TDgIz9aRbrgnUKkS1s06UJhcDRTl/+pCSRRt/\nCA2VAkBkPw4pn1hNwvK1S8t9OJQD+5xcKjZcvIFtKoqonAi7GUGL3OQSDVFw4q1K\n2iSk40aM+06wJ/WfeR+3z2ISrGBxAkAJ20YiF1QpcQlASbHNCl0vs7uKOlDyUAer\nR3mjFPf6e6kzQdi815MTZGIPxK3vWmMlPymgvgYPYTO1A4t5myulAkEA1QioAWcJ\noO26qhUlFRBCR8BMJoVPImV7ndVHE7usHdJvP7V2P9RyuRcMCTVul8RRmyoh/+yG\n4ghMaHo/v0YY5Q==\n-----END PRIVATE KEY-----\n" formated_key_2 = OneLogin_Saml2_Utils.format_private_key(key_2, True) - self.assertIn('-----BEGIN PRIVATE KEY-----', formated_key_2) - self.assertIn('-----END PRIVATE KEY-----', formated_key_2) + self.assertIn("-----BEGIN PRIVATE KEY-----", formated_key_2) + self.assertIn("-----END PRIVATE KEY-----", formated_key_2) self.assertEqual(len(formated_key_2), 916) formated_key_2 = OneLogin_Saml2_Utils.format_private_key(key_2, False) - self.assertNotIn('-----BEGIN PRIVATE KEY-----', formated_key_2) - self.assertNotIn('-----END PRIVATE KEY-----', formated_key_2) + self.assertNotIn("-----BEGIN PRIVATE KEY-----", formated_key_2) + self.assertNotIn("-----END PRIVATE KEY-----", formated_key_2) self.assertEqual(len(formated_key_2), 848) - key_3 = 'MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAM62buSW9Zgh7CmZouJekK0ac9sgEZkspemjv7SyE6Hbdz+KmUr3C7MI6JuPfVyJbxvMDf3FbgBBK7r5yfGgehXwplLMZj8glvV3NkdLMLPWmaw9U5sOzRoym46pVvsEo1PUL2qDK5Wrsm1guY1KIDSHL59NQ7PzDKgm1dxioeXFAgMBAAECgYA/fvRzTReloo3rfWD2Tfv84EpEPgaJ2ZghO4Zwl97F8icgIo/R4i760Lq6xgnI+gJiNHz7vcB7XYl0RrRMf3HgbA7zpJxREmOVltESDHy6lH0TmCdv9xMmHltB+pbGOhqBvuGgFbEOR73lDDV0ln2rEITJA2zjYF+hWe8b0JFeQQJBAOsIIIlHAMngjhCQDD6kla/vce972gCFU7ZeFw16ZMmb8W4rGRfQoQWYxSLAFIFsYewSBTccanyYbBNe3njki3ECQQDhJ4cgV6VpTwez4dkpU/xCHKoReedAEJhXucTNGpiIqu+TDgIz9aRbrgnUKkS1s06UJhcDRTl/+pCSRRt/CA2VAkBkPw4pn1hNwvK1S8t9OJQD+5xcKjZcvIFtKoqonAi7GUGL3OQSDVFw4q1K2iSk40aM+06wJ/WfeR+3z2ISrGBxAkAJ20YiF1QpcQlASbHNCl0vs7uKOlDyUAerR3mjFPf6e6kzQdi815MTZGIPxK3vWmMlPymgvgYPYTO1A4t5myulAkEA1QioAWcJoO26qhUlFRBCR8BMJoVPImV7ndVHE7usHdJvP7V2P9RyuRcMCTVul8RRmyoh/+yG4ghMaHo/v0YY5Q==' + key_3 = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAM62buSW9Zgh7CmZouJekK0ac9sgEZkspemjv7SyE6Hbdz+KmUr3C7MI6JuPfVyJbxvMDf3FbgBBK7r5yfGgehXwplLMZj8glvV3NkdLMLPWmaw9U5sOzRoym46pVvsEo1PUL2qDK5Wrsm1guY1KIDSHL59NQ7PzDKgm1dxioeXFAgMBAAECgYA/fvRzTReloo3rfWD2Tfv84EpEPgaJ2ZghO4Zwl97F8icgIo/R4i760Lq6xgnI+gJiNHz7vcB7XYl0RrRMf3HgbA7zpJxREmOVltESDHy6lH0TmCdv9xMmHltB+pbGOhqBvuGgFbEOR73lDDV0ln2rEITJA2zjYF+hWe8b0JFeQQJBAOsIIIlHAMngjhCQDD6kla/vce972gCFU7ZeFw16ZMmb8W4rGRfQoQWYxSLAFIFsYewSBTccanyYbBNe3njki3ECQQDhJ4cgV6VpTwez4dkpU/xCHKoReedAEJhXucTNGpiIqu+TDgIz9aRbrgnUKkS1s06UJhcDRTl/+pCSRRt/CA2VAkBkPw4pn1hNwvK1S8t9OJQD+5xcKjZcvIFtKoqonAi7GUGL3OQSDVFw4q1K2iSk40aM+06wJ/WfeR+3z2ISrGBxAkAJ20YiF1QpcQlASbHNCl0vs7uKOlDyUAerR3mjFPf6e6kzQdi815MTZGIPxK3vWmMlPymgvgYPYTO1A4t5myulAkEA1QioAWcJoO26qhUlFRBCR8BMJoVPImV7ndVHE7usHdJvP7V2P9RyuRcMCTVul8RRmyoh/+yG4ghMaHo/v0YY5Q==" formated_key_3 = OneLogin_Saml2_Utils.format_private_key(key_3, True) - self.assertIn('-----BEGIN RSA PRIVATE KEY-----', formated_key_3) - self.assertIn('-----END RSA PRIVATE KEY-----', formated_key_3) + self.assertIn("-----BEGIN RSA PRIVATE KEY-----", formated_key_3) + self.assertIn("-----END RSA PRIVATE KEY-----", formated_key_3) self.assertEqual(len(formated_key_3), 924) formated_key_3 = OneLogin_Saml2_Utils.format_private_key(key_3, False) - self.assertNotIn('-----BEGIN PRIVATE KEY-----', formated_key_3) - self.assertNotIn('-----END PRIVATE KEY-----', formated_key_3) - self.assertNotIn('-----BEGIN RSA PRIVATE KEY-----', formated_key_3) - self.assertNotIn('-----END RSA PRIVATE KEY-----', formated_key_3) + self.assertNotIn("-----BEGIN PRIVATE KEY-----", formated_key_3) + self.assertNotIn("-----END PRIVATE KEY-----", formated_key_3) + self.assertNotIn("-----BEGIN RSA PRIVATE KEY-----", formated_key_3) + self.assertNotIn("-----END RSA PRIVATE KEY-----", formated_key_3) self.assertEqual(len(formated_key_3), 848) def testRedirect(self): """ Tests the redirect method of the OneLogin_Saml2_Utils """ - request_data = { - 'http_host': 'example.com' - } + request_data = {"http_host": "example.com"} # Check relative and absolute hostname = OneLogin_Saml2_Utils.get_self_host(request_data) - url = 'http://%s/example' % hostname - url2 = '/example' + url = "http://%s/example" % hostname + url2 = "/example" target_url = OneLogin_Saml2_Utils.redirect(url, {}, request_data) target_url2 = OneLogin_Saml2_Utils.redirect(url2, {}, request_data) @@ -122,48 +121,42 @@ def testRedirect(self): self.assertEqual(target_url, target_url2) # Check that accept http/https and reject other protocols - url3 = 'https://%s/example?test=true' % hostname - url4 = 'ftp://%s/example' % hostname + url3 = "https://%s/example?test=true" % hostname + url4 = "ftp://%s/example" % hostname target_url3 = OneLogin_Saml2_Utils.redirect(url3, {}, request_data) - self.assertIn('test=true', target_url3) + self.assertIn("test=true", target_url3) with self.assertRaises(Exception) as context: OneLogin_Saml2_Utils.redirect(url4, {}, request_data) exception = context.exception self.assertIn("Redirect to invalid URL", str(exception)) # Review parameter prefix - parameters1 = { - 'value1': 'a' - } + parameters1 = {"value1": "a"} target_url5 = OneLogin_Saml2_Utils.redirect(url, parameters1, request_data) - self.assertEqual('http://%s/example?value1=a' % hostname, target_url5) + self.assertEqual("http://%s/example?value1=a" % hostname, target_url5) target_url6 = OneLogin_Saml2_Utils.redirect(url3, parameters1, request_data) - self.assertEqual('https://%s/example?test=true&value1=a' % hostname, target_url6) + self.assertEqual("https://%s/example?test=true&value1=a" % hostname, target_url6) # Review parameters - parameters2 = { - 'alphavalue': 'a', - 'numvaluelist': ['1', '2'], - 'testing': None - } + parameters2 = {"alphavalue": "a", "numvaluelist": ["1", "2"], "testing": None} target_url7 = OneLogin_Saml2_Utils.redirect(url, parameters2, request_data) parameters2_decoded = {"alphavalue": "alphavalue=a", "numvaluelist": "numvaluelist[]=1&numvaluelist[]=2", "testing": "testing"} parameters2_str = "&".join(parameters2_decoded[x] for x in parameters2) - self.assertEqual('http://%s/example?%s' % (hostname, parameters2_str), target_url7) + self.assertEqual("http://%s/example?%s" % (hostname, parameters2_str), target_url7) parameters3 = { - 'alphavalue': 'a', - 'emptynumvaluelist': [], - 'numvaluelist': [''], + "alphavalue": "a", + "emptynumvaluelist": [], + "numvaluelist": [""], } parameters3_decoded = {"alphavalue": "alphavalue=a", "numvaluelist": "numvaluelist[]="} parameters3_str = "&".join((parameters3_decoded[x] for x in parameters3.keys() if x in parameters3_decoded)) target_url8 = OneLogin_Saml2_Utils.redirect(url, parameters3, request_data) - self.assertEqual('http://%s/example?%s' % (hostname, parameters3_str), target_url8) + self.assertEqual("http://%s/example?%s" % (hostname, parameters3_str), target_url8) def testGetselfhost(self): """ @@ -175,89 +168,86 @@ def testGetselfhost(self): exception = context.exception self.assertIn("No hostname defined", str(exception)) - request_data = { - 'server_name': 'example.com' - } - self.assertEqual('example.com', OneLogin_Saml2_Utils.get_self_host(request_data)) + if sys.version_info > (3, 2, 0): + request_data = {"server_name": "example.com"} + with self.assertWarns(Warning) as context: + self_host = OneLogin_Saml2_Utils.get_self_host(request_data) - request_data = { - 'http_host': 'example.com' - } - self.assertEqual('example.com', OneLogin_Saml2_Utils.get_self_host(request_data)) + self.assertEqual("example.com", self_host) - request_data = { - 'http_host': 'example.com:443' - } - self.assertEqual('example.com:443', OneLogin_Saml2_Utils.get_self_host(request_data)) + request_data = {"http_host": "example.com"} + self.assertEqual("example.com", OneLogin_Saml2_Utils.get_self_host(request_data)) - request_data = { - 'http_host': 'example.com:ok' - } - self.assertEqual('example.com:ok', OneLogin_Saml2_Utils.get_self_host(request_data)) + request_data = {"http_host": "example.com:443"} + self.assertEqual("example.com:443", OneLogin_Saml2_Utils.get_self_host(request_data)) + + request_data = {"http_host": "example.com:ok"} + self.assertEqual("example.com:ok", OneLogin_Saml2_Utils.get_self_host(request_data)) def testisHTTPS(self): """ Tests the is_https method of the OneLogin_Saml2_Utils """ - request_data = { - 'https': 'off' - } + request_data = {"https": "off"} self.assertFalse(OneLogin_Saml2_Utils.is_https(request_data)) - request_data = { - 'https': 'on' - } + request_data = {"https": "on"} self.assertTrue(OneLogin_Saml2_Utils.is_https(request_data)) - request_data = { - 'server_port': '443' - } + request_data = {"server_port": "443"} self.assertTrue(OneLogin_Saml2_Utils.is_https(request_data)) def testGetSelfURLhost(self): """ Tests the get_self_url_host method of the OneLogin_Saml2_Utils """ - request_data = { - 'http_host': 'example.com' - } - self.assertEqual('http://example.com', OneLogin_Saml2_Utils.get_self_url_host(request_data)) + request_data = {"http_host": "example.com"} + self.assertEqual("http://example.com", OneLogin_Saml2_Utils.get_self_url_host(request_data)) - request_data['server_port'] = '80' - self.assertEqual('http://example.com', OneLogin_Saml2_Utils.get_self_url_host(request_data)) + if sys.version_info > (3, 2, 0): + with self.assertWarns(Warning) as context: + request_data["server_port"] = "80" + self.assertEqual("http://example.com", OneLogin_Saml2_Utils.get_self_url_host(request_data)) - request_data['server_port'] = '81' - self.assertEqual('http://example.com:81', OneLogin_Saml2_Utils.get_self_url_host(request_data)) + with self.assertWarns(Warning) as context: + request_data["server_port"] = "81" + self.assertEqual("http://example.com:81", OneLogin_Saml2_Utils.get_self_url_host(request_data)) - request_data['server_port'] = '443' - self.assertEqual('https://example.com', OneLogin_Saml2_Utils.get_self_url_host(request_data)) + with self.assertWarns(Warning) as context: + request_data["server_port"] = "443" + self.assertEqual("https://example.com", OneLogin_Saml2_Utils.get_self_url_host(request_data)) - del request_data['server_port'] - request_data['https'] = 'on' - self.assertEqual('https://example.com', OneLogin_Saml2_Utils.get_self_url_host(request_data)) + del request_data["server_port"] + request_data["https"] = "on" + self.assertEqual("https://example.com", OneLogin_Saml2_Utils.get_self_url_host(request_data)) - request_data['server_port'] = '444' - self.assertEqual('https://example.com:444', OneLogin_Saml2_Utils.get_self_url_host(request_data)) + if sys.version_info > (3, 2, 0): + with self.assertWarns(Warning) as context: + request_data["server_port"] = "444" + self.assertEqual("https://example.com:444", OneLogin_Saml2_Utils.get_self_url_host(request_data)) - request_data['server_port'] = '443' - request_data['request_uri'] = '' - self.assertEqual('https://example.com', OneLogin_Saml2_Utils.get_self_url_host(request_data)) + with self.assertWarns(Warning) as context: + request_data["server_port"] = "443" + request_data["request_uri"] = "" + self.assertEqual("https://example.com", OneLogin_Saml2_Utils.get_self_url_host(request_data)) - request_data['request_uri'] = '/' - self.assertEqual('https://example.com', OneLogin_Saml2_Utils.get_self_url_host(request_data)) + with self.assertWarns(Warning) as context: + request_data["request_uri"] = "/" + self.assertEqual("https://example.com", OneLogin_Saml2_Utils.get_self_url_host(request_data)) - request_data['request_uri'] = 'onelogin/' - self.assertEqual('https://example.com', OneLogin_Saml2_Utils.get_self_url_host(request_data)) + with self.assertWarns(Warning) as context: + request_data["request_uri"] = "onelogin/" + self.assertEqual("https://example.com", OneLogin_Saml2_Utils.get_self_url_host(request_data)) - request_data['request_uri'] = '/onelogin' - self.assertEqual('https://example.com', OneLogin_Saml2_Utils.get_self_url_host(request_data)) + with self.assertWarns(Warning) as context: + request_data["request_uri"] = "/onelogin" + self.assertEqual("https://example.com", OneLogin_Saml2_Utils.get_self_url_host(request_data)) - request_data['request_uri'] = 'https://example.com/onelogin/sso' - self.assertEqual('https://example.com', OneLogin_Saml2_Utils.get_self_url_host(request_data)) + with self.assertWarns(Warning) as context: + request_data["request_uri"] = "https://example.com/onelogin/sso" + self.assertEqual("https://example.com", OneLogin_Saml2_Utils.get_self_url_host(request_data)) - request_data2 = { - 'request_uri': 'example.com/onelogin/sso' - } + request_data2 = {"request_uri": "example.com/onelogin/sso"} with self.assertRaises(Exception) as context: OneLogin_Saml2_Utils.get_self_url_host(request_data2) exception = context.exception @@ -267,144 +257,127 @@ def testGetSelfURL(self): """ Tests the get_self_url method of the OneLogin_Saml2_Utils """ - request_data = { - 'http_host': 'example.com' - } + request_data = {"http_host": "example.com"} url = OneLogin_Saml2_Utils.get_self_url_host(request_data) self.assertEqual(url, OneLogin_Saml2_Utils.get_self_url(request_data)) - request_data['request_uri'] = '' + request_data["request_uri"] = "" self.assertEqual(url, OneLogin_Saml2_Utils.get_self_url(request_data)) - request_data['request_uri'] = '/' - self.assertEqual(url + '/', OneLogin_Saml2_Utils.get_self_url(request_data)) + request_data["request_uri"] = "/" + self.assertEqual(url + "/", OneLogin_Saml2_Utils.get_self_url(request_data)) - request_data['request_uri'] = 'index.html' - self.assertEqual(url + 'index.html', OneLogin_Saml2_Utils.get_self_url(request_data)) + request_data["request_uri"] = "index.html" + self.assertEqual(url + "index.html", OneLogin_Saml2_Utils.get_self_url(request_data)) - request_data['request_uri'] = '?index.html' - self.assertEqual(url + '?index.html', OneLogin_Saml2_Utils.get_self_url(request_data)) + request_data["request_uri"] = "?index.html" + self.assertEqual(url + "?index.html", OneLogin_Saml2_Utils.get_self_url(request_data)) - request_data['request_uri'] = '/index.html' - self.assertEqual(url + '/index.html', OneLogin_Saml2_Utils.get_self_url(request_data)) + request_data["request_uri"] = "/index.html" + self.assertEqual(url + "/index.html", OneLogin_Saml2_Utils.get_self_url(request_data)) - request_data['request_uri'] = '/index.html?testing' - self.assertEqual(url + '/index.html?testing', OneLogin_Saml2_Utils.get_self_url(request_data)) + request_data["request_uri"] = "/index.html?testing" + self.assertEqual(url + "/index.html?testing", OneLogin_Saml2_Utils.get_self_url(request_data)) - request_data['request_uri'] = '/test/index.html?testing' - self.assertEqual(url + '/test/index.html?testing', OneLogin_Saml2_Utils.get_self_url(request_data)) + request_data["request_uri"] = "/test/index.html?testing" + self.assertEqual(url + "/test/index.html?testing", OneLogin_Saml2_Utils.get_self_url(request_data)) - request_data['request_uri'] = 'https://example.com/testing' - self.assertEqual(url + '/testing', OneLogin_Saml2_Utils.get_self_url(request_data)) + request_data["request_uri"] = "https://example.com/testing" + self.assertEqual(url + "/testing", OneLogin_Saml2_Utils.get_self_url(request_data)) def testGetSelfURLNoQuery(self): """ Tests the get_self_url_no_query method of the OneLogin_Saml2_Utils """ - request_data = { - 'http_host': 'example.com', - 'script_name': '/index.html' - } - url = OneLogin_Saml2_Utils.get_self_url_host(request_data) + request_data['script_name'] + request_data = {"http_host": "example.com", "script_name": "/index.html"} + url = OneLogin_Saml2_Utils.get_self_url_host(request_data) + request_data["script_name"] self.assertEqual(url, OneLogin_Saml2_Utils.get_self_url_no_query(request_data)) - request_data['path_info'] = '/test' - self.assertEqual(url + '/test', OneLogin_Saml2_Utils.get_self_url_no_query(request_data)) + request_data["path_info"] = "/test" + self.assertEqual(url + "/test", OneLogin_Saml2_Utils.get_self_url_no_query(request_data)) def testGetSelfRoutedURLNoQuery(self): """ Tests the get_self_routed_url_no_query method of the OneLogin_Saml2_Utils """ - request_data = { - 'http_host': 'example.com', - 'request_uri': '/example1/route?x=test', - 'query_string': '?x=test' - } - url = OneLogin_Saml2_Utils.get_self_url_host(request_data) + '/example1/route' + request_data = {"http_host": "example.com", "request_uri": "/example1/route?x=test", "query_string": "?x=test"} + url = OneLogin_Saml2_Utils.get_self_url_host(request_data) + "/example1/route" self.assertEqual(url, OneLogin_Saml2_Utils.get_self_routed_url_no_query(request_data)) request_data_2 = { - 'http_host': 'example.com', - 'request_uri': '', + "http_host": "example.com", + "request_uri": "", } url_2 = OneLogin_Saml2_Utils.get_self_url_host(request_data_2) self.assertEqual(url_2, OneLogin_Saml2_Utils.get_self_routed_url_no_query(request_data_2)) request_data_3 = { - 'http_host': 'example.com', + "http_host": "example.com", } url_3 = OneLogin_Saml2_Utils.get_self_url_host(request_data_3) self.assertEqual(url_3, OneLogin_Saml2_Utils.get_self_routed_url_no_query(request_data_3)) - request_data_4 = { - 'http_host': 'example.com', - 'request_uri': '/example1/route/test/', - 'query_string': '?invalid=1' - } - url_4 = OneLogin_Saml2_Utils.get_self_url_host(request_data_4) + '/example1/route/test/' + request_data_4 = {"http_host": "example.com", "request_uri": "/example1/route/test/", "query_string": "?invalid=1"} + url_4 = OneLogin_Saml2_Utils.get_self_url_host(request_data_4) + "/example1/route/test/" self.assertEqual(url_4, OneLogin_Saml2_Utils.get_self_routed_url_no_query(request_data_4)) - request_data_5 = { - 'http_host': 'example.com', - 'request_uri': '/example1/route/test/', - 'query_string': '' - } - url_5 = OneLogin_Saml2_Utils.get_self_url_host(request_data_5) + '/example1/route/test/' + request_data_5 = {"http_host": "example.com", "request_uri": "/example1/route/test/", "query_string": ""} + url_5 = OneLogin_Saml2_Utils.get_self_url_host(request_data_5) + "/example1/route/test/" self.assertEqual(url_5, OneLogin_Saml2_Utils.get_self_routed_url_no_query(request_data_5)) request_data_6 = { - 'http_host': 'example.com', - 'request_uri': '/example1/route/test/', + "http_host": "example.com", + "request_uri": "/example1/route/test/", } - url_6 = OneLogin_Saml2_Utils.get_self_url_host(request_data_6) + '/example1/route/test/' + url_6 = OneLogin_Saml2_Utils.get_self_url_host(request_data_6) + "/example1/route/test/" self.assertEqual(url_6, OneLogin_Saml2_Utils.get_self_routed_url_no_query(request_data_6)) def testGetStatus(self): """ Gets the status of a message """ - xml = self.file_contents(join(self.data_path, 'responses', 'response1.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "response1.xml.base64")) xml = b64decode(xml) dom = etree.fromstring(xml) status = OneLogin_Saml2_Utils.get_status(dom) - self.assertEqual(OneLogin_Saml2_Constants.STATUS_SUCCESS, status['code']) + self.assertEqual(OneLogin_Saml2_Constants.STATUS_SUCCESS, status["code"]) - xml2 = self.file_contents(join(self.data_path, 'responses', 'invalids', 'status_code_responder.xml.base64')) + xml2 = self.file_contents(join(self.data_path, "responses", "invalids", "status_code_responder.xml.base64")) xml2 = b64decode(xml2) dom2 = etree.fromstring(xml2) status2 = OneLogin_Saml2_Utils.get_status(dom2) - self.assertEqual(OneLogin_Saml2_Constants.STATUS_RESPONDER, status2['code']) - self.assertEqual('', status2['msg']) + self.assertEqual(OneLogin_Saml2_Constants.STATUS_RESPONDER, status2["code"]) + self.assertEqual("", status2["msg"]) - xml3 = self.file_contents(join(self.data_path, 'responses', 'invalids', 'status_code_responer_and_msg.xml.base64')) + xml3 = self.file_contents(join(self.data_path, "responses", "invalids", "status_code_responer_and_msg.xml.base64")) xml3 = b64decode(xml3) dom3 = etree.fromstring(xml3) status3 = OneLogin_Saml2_Utils.get_status(dom3) - self.assertEqual(OneLogin_Saml2_Constants.STATUS_RESPONDER, status3['code']) - self.assertEqual('something_is_wrong', status3['msg']) + self.assertEqual(OneLogin_Saml2_Constants.STATUS_RESPONDER, status3["code"]) + self.assertEqual("something_is_wrong", status3["msg"]) - xml_inv = self.file_contents(join(self.data_path, 'responses', 'invalids', 'no_status.xml.base64')) + xml_inv = self.file_contents(join(self.data_path, "responses", "invalids", "no_status.xml.base64")) xml_inv = b64decode(xml_inv) dom_inv = etree.fromstring(xml_inv) - with self.assertRaisesRegex(Exception, 'Missing Status on response'): + with self.assertRaisesRegex(Exception, "Missing Status on response"): OneLogin_Saml2_Utils.get_status(dom_inv) - xml_inv2 = self.file_contents(join(self.data_path, 'responses', 'invalids', 'no_status_code.xml.base64')) + xml_inv2 = self.file_contents(join(self.data_path, "responses", "invalids", "no_status_code.xml.base64")) xml_inv2 = b64decode(xml_inv2) dom_inv2 = etree.fromstring(xml_inv2) - with self.assertRaisesRegex(Exception, 'Missing Status Code on response'): + with self.assertRaisesRegex(Exception, "Missing Status Code on response"): OneLogin_Saml2_Utils.get_status(dom_inv2) def testParseDuration(self): """ Tests the parse_duration method of the OneLogin_Saml2_Utils """ - duration = 'PT1393462294S' + duration = "PT1393462294S" timestamp = 1393876825 parsed_duration = OneLogin_Saml2_Utils.parse_duration(duration, timestamp) @@ -413,17 +386,17 @@ def testParseDuration(self): parsed_duration_2 = OneLogin_Saml2_Utils.parse_duration(duration) self.assertTrue(parsed_duration_2 > parsed_duration) - invalid_duration = 'PT1Y' + invalid_duration = "PT1Y" with self.assertRaises(Exception) as context: OneLogin_Saml2_Utils.parse_duration(invalid_duration) exception = context.exception self.assertIn("Unrecognised ISO 8601 date format", str(exception)) - new_duration = 'P1Y1M' + new_duration = "P1Y1M" parsed_duration_4 = OneLogin_Saml2_Utils.parse_duration(new_duration, timestamp) self.assertEqual(1428091225, parsed_duration_4) - neg_duration = '-P14M' + neg_duration = "-P14M" parsed_duration_5 = OneLogin_Saml2_Utils.parse_duration(neg_duration, timestamp) self.assertEqual(1357243225, parsed_duration_5) @@ -432,23 +405,23 @@ def testParseSAML2Time(self): Tests the parse_SAML_to_time method of the OneLogin_Saml2_Utils """ time = 1386650371 - saml_time = '2013-12-10T04:39:31Z' + saml_time = "2013-12-10T04:39:31Z" self.assertEqual(time, OneLogin_Saml2_Utils.parse_SAML_to_time(saml_time)) with self.assertRaises(Exception) as context: - OneLogin_Saml2_Utils.parse_SAML_to_time('invalidSAMLTime') + OneLogin_Saml2_Utils.parse_SAML_to_time("invalidSAMLTime") exception = context.exception self.assertIn("does not match format", str(exception)) # Now test if toolkit supports miliseconds - saml_time2 = '2013-12-10T04:39:31.120Z' + 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' + 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' + saml_time4 = "2013-12-10T04:39:31.120240360Z" self.assertEqual(time, OneLogin_Saml2_Utils.parse_SAML_to_time(saml_time4)) def testParseTime2SAML(self): @@ -456,10 +429,10 @@ def testParseTime2SAML(self): Tests the parse_time_to_SAML method of the OneLogin_Saml2_Utils """ time = 1386650371 - saml_time = '2013-12-10T04:39:31Z' + saml_time = "2013-12-10T04:39:31Z" self.assertEqual(saml_time, OneLogin_Saml2_Utils.parse_time_to_SAML(time)) with self.assertRaises(Exception) as context: - OneLogin_Saml2_Utils.parse_time_to_SAML('invalidtime') + OneLogin_Saml2_Utils.parse_time_to_SAML("invalidtime") exception = context.exception self.assertIn("could not convert string to float", str(exception)) @@ -468,18 +441,18 @@ def testGetExpireTime(self): Tests the get_expire_time method of the OneLogin_Saml2_Utils """ self.assertEqual(None, OneLogin_Saml2_Utils.get_expire_time()) - self.assertNotEqual(None, OneLogin_Saml2_Utils.get_expire_time('PT360000S')) + self.assertNotEqual(None, OneLogin_Saml2_Utils.get_expire_time("PT360000S")) - self.assertEqual('1291955971', OneLogin_Saml2_Utils.get_expire_time('PT360000S', '2010-12-10T04:39:31Z')) - self.assertEqual('1291955971', OneLogin_Saml2_Utils.get_expire_time('PT360000S', 1291955971)) + self.assertEqual("1291955971", OneLogin_Saml2_Utils.get_expire_time("PT360000S", "2010-12-10T04:39:31Z")) + self.assertEqual("1291955971", OneLogin_Saml2_Utils.get_expire_time("PT360000S", 1291955971)) - self.assertNotEqual('3311642371', OneLogin_Saml2_Utils.get_expire_time('PT360000S', '2074-12-10T04:39:31Z')) - self.assertNotEqual('3311642371', OneLogin_Saml2_Utils.get_expire_time('PT360000S', 1418186371)) + self.assertNotEqual("3311642371", OneLogin_Saml2_Utils.get_expire_time("PT360000S", "2074-12-10T04:39:31Z")) + self.assertNotEqual("3311642371", OneLogin_Saml2_Utils.get_expire_time("PT360000S", 1418186371)) def _generate_name_id_element(self, name_qualifier): - name_id_value = 'value' - entity_id = 'sp-entity-id' - name_id_format = 'name-id-format' + name_id_value = "value" + entity_id = "sp-entity-id" + name_id_format = "name-id-format" raw_name_id = OneLogin_Saml2_Utils.generate_name_id( name_id_value, @@ -494,8 +467,8 @@ def testNameidGenerationIncludesNameQualifierAttribute(self): """ Tests the inclusion of NameQualifier in the generateNameId method of the OneLogin_Saml2_Utils """ - idp_name_qualifier = 'idp-name-qualifier' - idp_name_qualifier_attribute = ('NameQualifier', idp_name_qualifier) + idp_name_qualifier = "idp-name-qualifier" + idp_name_qualifier_attribute = ("NameQualifier", idp_name_qualifier) name_id = self._generate_name_id_element(idp_name_qualifier) @@ -506,7 +479,7 @@ def testNameidGenerationDoesNotIncludeNameQualifierAttribute(self): Tests the (not) inclusion of NameQualifier in the generateNameId method of the OneLogin_Saml2_Utils """ idp_name_qualifier = None - not_expected_attribute = 'NameQualifier' + not_expected_attribute = "NameQualifier" name_id = self._generate_name_id_element(idp_name_qualifier) @@ -516,27 +489,27 @@ def testGenerateNameIdWithoutFormat(self): """ Tests the generateNameId method of the OneLogin_Saml2_Utils """ - name_id_value = 'ONELOGIN_ce998811003f4e60f8b07a311dc641621379cfde' + name_id_value = "ONELOGIN_ce998811003f4e60f8b07a311dc641621379cfde" name_id_format = None name_id = OneLogin_Saml2_Utils.generate_name_id(name_id_value, None, name_id_format) - expected_name_id = 'ONELOGIN_ce998811003f4e60f8b07a311dc641621379cfde' + expected_name_id = "ONELOGIN_ce998811003f4e60f8b07a311dc641621379cfde" self.assertEqual(name_id, expected_name_id) def testGenerateNameIdWithSPNameQualifier(self): """ Tests the generateNameId method of the OneLogin_Saml2_Utils """ - name_id_value = 'ONELOGIN_ce998811003f4e60f8b07a311dc641621379cfde' - entity_id = 'http://stuff.com/endpoints/metadata.php' - name_id_format = 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified' + name_id_value = "ONELOGIN_ce998811003f4e60f8b07a311dc641621379cfde" + entity_id = "http://stuff.com/endpoints/metadata.php" + name_id_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" name_id = OneLogin_Saml2_Utils.generate_name_id(name_id_value, entity_id, name_id_format) expected_name_id = 'ONELOGIN_ce998811003f4e60f8b07a311dc641621379cfde' self.assertEqual(expected_name_id, name_id) settings_info = self.loadSettingsJSON() - x509cert = settings_info['idp']['x509cert'] + x509cert = settings_info["idp"]["x509cert"] key = OneLogin_Saml2_Utils.format_cert(x509cert) name_id_enc = OneLogin_Saml2_Utils.generate_name_id(name_id_value, entity_id, name_id_format, key) @@ -547,15 +520,15 @@ def testGenerateNameIdWithoutSPNameQualifier(self): """ Tests the generateNameId method of the OneLogin_Saml2_Utils """ - name_id_value = 'ONELOGIN_ce998811003f4e60f8b07a311dc641621379cfde' - name_id_format = 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified' + name_id_value = "ONELOGIN_ce998811003f4e60f8b07a311dc641621379cfde" + name_id_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" name_id = OneLogin_Saml2_Utils.generate_name_id(name_id_value, None, name_id_format) expected_name_id = 'ONELOGIN_ce998811003f4e60f8b07a311dc641621379cfde' self.assertEqual(expected_name_id, name_id) settings_info = self.loadSettingsJSON() - x509cert = settings_info['idp']['x509cert'] + x509cert = settings_info["idp"]["x509cert"] key = OneLogin_Saml2_Utils.format_cert(x509cert) name_id_enc = OneLogin_Saml2_Utils.generate_name_id(name_id_value, None, name_id_format, key) @@ -569,18 +542,21 @@ def testCalculateX509Fingerprint(self): settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) cert_path = settings.get_cert_path() - key = self.file_contents(cert_path + 'sp.key') - cert = self.file_contents(cert_path + 'sp.crt') + key = self.file_contents(cert_path + "sp.key") + cert = self.file_contents(cert_path + "sp.crt") self.assertEqual(None, OneLogin_Saml2_Utils.calculate_x509_fingerprint(key)) - self.assertEqual('afe71c28ef740bc87425be13a2263d37971da1f9', OneLogin_Saml2_Utils.calculate_x509_fingerprint(cert)) - self.assertEqual('afe71c28ef740bc87425be13a2263d37971da1f9', OneLogin_Saml2_Utils.calculate_x509_fingerprint(cert, 'sha1')) + self.assertEqual("afe71c28ef740bc87425be13a2263d37971da1f9", OneLogin_Saml2_Utils.calculate_x509_fingerprint(cert)) + self.assertEqual("afe71c28ef740bc87425be13a2263d37971da1f9", OneLogin_Saml2_Utils.calculate_x509_fingerprint(cert, "sha1")) - self.assertEqual('c51cfa06c7a49767f6eab18238eae1c56708e29264da3d11f538a12cd2c357ba', OneLogin_Saml2_Utils.calculate_x509_fingerprint(cert, 'sha256')) + self.assertEqual("c51cfa06c7a49767f6eab18238eae1c56708e29264da3d11f538a12cd2c357ba", OneLogin_Saml2_Utils.calculate_x509_fingerprint(cert, "sha256")) - self.assertEqual('bc5826e6f9429247254bae5e3c650e6968a36a62d23075eb168134978d88600559c10830c28711b2c29c7947c0c2eb1d', OneLogin_Saml2_Utils.calculate_x509_fingerprint(cert, 'sha384')) + self.assertEqual("bc5826e6f9429247254bae5e3c650e6968a36a62d23075eb168134978d88600559c10830c28711b2c29c7947c0c2eb1d", OneLogin_Saml2_Utils.calculate_x509_fingerprint(cert, "sha384")) - self.assertEqual('3db29251b97559c67988ea0754cb0573fc409b6f75d89282d57cfb75089539b0bbdb2dcd9ec6e032549ecbc466439d5992e18db2cf5494ca2fe1b2e16f348dff', OneLogin_Saml2_Utils.calculate_x509_fingerprint(cert, 'sha512')) + self.assertEqual( + "3db29251b97559c67988ea0754cb0573fc409b6f75d89282d57cfb75089539b0bbdb2dcd9ec6e032549ecbc466439d5992e18db2cf5494ca2fe1b2e16f348dff", + OneLogin_Saml2_Utils.calculate_x509_fingerprint(cert, "sha512"), + ) def testDeleteLocalSession(self): """ @@ -607,11 +583,11 @@ def testFormatFingerPrint(self): """ Tests the format_finger_print method of the OneLogin_Saml2_Utils """ - finger_print_1 = 'AF:E7:1C:28:EF:74:0B:C8:74:25:BE:13:A2:26:3D:37:97:1D:A1:F9' - self.assertEqual('afe71c28ef740bc87425be13a2263d37971da1f9', OneLogin_Saml2_Utils.format_finger_print(finger_print_1)) + finger_print_1 = "AF:E7:1C:28:EF:74:0B:C8:74:25:BE:13:A2:26:3D:37:97:1D:A1:F9" + self.assertEqual("afe71c28ef740bc87425be13a2263d37971da1f9", OneLogin_Saml2_Utils.format_finger_print(finger_print_1)) - finger_print_2 = 'afe71c28ef740bc87425be13a2263d37971da1f9' - self.assertEqual('afe71c28ef740bc87425be13a2263d37971da1f9', OneLogin_Saml2_Utils.format_finger_print(finger_print_2)) + finger_print_2 = "afe71c28ef740bc87425be13a2263d37971da1f9" + self.assertEqual("afe71c28ef740bc87425be13a2263d37971da1f9", OneLogin_Saml2_Utils.format_finger_print(finger_print_2)) def testDecryptElement(self): """ @@ -621,68 +597,68 @@ def testDecryptElement(self): key = settings.get_sp_key() - xml_nameid_enc = b64decode(self.file_contents(join(self.data_path, 'responses', 'response_encrypted_nameid.xml.base64'))) + xml_nameid_enc = b64decode(self.file_contents(join(self.data_path, "responses", "response_encrypted_nameid.xml.base64"))) dom_nameid_enc = etree.fromstring(xml_nameid_enc) - encrypted_nameid_nodes = dom_nameid_enc.find('.//saml:EncryptedID', namespaces=OneLogin_Saml2_Constants.NSMAP) + encrypted_nameid_nodes = dom_nameid_enc.find(".//saml:EncryptedID", namespaces=OneLogin_Saml2_Constants.NSMAP) encrypted_data = encrypted_nameid_nodes[0] decrypted_nameid = OneLogin_Saml2_Utils.decrypt_element(encrypted_data, key) - self.assertEqual('saml:NameID', decrypted_nameid.tag) - self.assertEqual('2de11defd199f8d5bb63f9b7deb265ba5c675c10', decrypted_nameid.text) + self.assertEqual("saml:NameID", decrypted_nameid.tag) + self.assertEqual("2de11defd199f8d5bb63f9b7deb265ba5c675c10", decrypted_nameid.text) - xml_assertion_enc = b64decode(self.file_contents(join(self.data_path, 'responses', 'valid_encrypted_assertion_encrypted_nameid.xml.base64'))) + xml_assertion_enc = b64decode(self.file_contents(join(self.data_path, "responses", "valid_encrypted_assertion_encrypted_nameid.xml.base64"))) dom_assertion_enc = etree.fromstring(xml_assertion_enc) - encrypted_assertion_enc_nodes = dom_assertion_enc.find('.//saml:EncryptedAssertion', namespaces=OneLogin_Saml2_Constants.NSMAP) + encrypted_assertion_enc_nodes = dom_assertion_enc.find(".//saml:EncryptedAssertion", namespaces=OneLogin_Saml2_Constants.NSMAP) encrypted_data_assert = encrypted_assertion_enc_nodes[0] decrypted_assertion = OneLogin_Saml2_Utils.decrypt_element(encrypted_data_assert, key) - self.assertEqual('{%s}Assertion' % OneLogin_Saml2_Constants.NS_SAML, decrypted_assertion.tag) - self.assertEqual('_6fe189b1c241827773902f2b1d3a843418206a5c97', decrypted_assertion.get('ID')) + self.assertEqual("{%s}Assertion" % OneLogin_Saml2_Constants.NS_SAML, decrypted_assertion.tag) + self.assertEqual("_6fe189b1c241827773902f2b1d3a843418206a5c97", decrypted_assertion.get("ID")) - encrypted_nameid_nodes = decrypted_assertion.xpath('./saml:Subject/saml:EncryptedID', namespaces=OneLogin_Saml2_Constants.NSMAP) + encrypted_nameid_nodes = decrypted_assertion.xpath("./saml:Subject/saml:EncryptedID", namespaces=OneLogin_Saml2_Constants.NSMAP) encrypted_data = encrypted_nameid_nodes[0][0] decrypted_nameid = OneLogin_Saml2_Utils.decrypt_element(encrypted_data, key) - self.assertEqual('{%s}NameID' % OneLogin_Saml2_Constants.NS_SAML, decrypted_nameid.tag) - self.assertEqual('457bdb600de717891c77647b0806ce59c089d5b8', decrypted_nameid.text) + self.assertEqual("{%s}NameID" % OneLogin_Saml2_Constants.NS_SAML, decrypted_nameid.tag) + self.assertEqual("457bdb600de717891c77647b0806ce59c089d5b8", decrypted_nameid.text) - key_2_file_name = join(self.data_path, 'misc', 'sp2.key') - f = open(key_2_file_name, 'r') + key_2_file_name = join(self.data_path, "misc", "sp2.key") + f = open(key_2_file_name, "r") key2 = f.read() f.close() # sp.key and sp2.key are equivalent we should be able to decrypt the nameID again decrypted_nameid = OneLogin_Saml2_Utils.decrypt_element(encrypted_data, key2) - self.assertIn('{%s}NameID' % (OneLogin_Saml2_Constants.NS_SAML), decrypted_nameid.tag) - self.assertEqual('457bdb600de717891c77647b0806ce59c089d5b8', decrypted_nameid.text) + self.assertIn("{%s}NameID" % (OneLogin_Saml2_Constants.NS_SAML), decrypted_nameid.tag) + self.assertEqual("457bdb600de717891c77647b0806ce59c089d5b8", decrypted_nameid.text) - key_3_file_name = join(self.data_path, 'misc', 'sp3.key') - f = open(key_3_file_name, 'r') + key_3_file_name = join(self.data_path, "misc", "sp3.key") + f = open(key_3_file_name, "r") key3 = f.read() f.close() # sp.key and sp3.key are equivalent we should be able to decrypt the nameID again decrypted_nameid = OneLogin_Saml2_Utils.decrypt_element(encrypted_data, key3) - self.assertIn('{%s}NameID' % (OneLogin_Saml2_Constants.NS_SAML), decrypted_nameid.tag) - self.assertEqual('457bdb600de717891c77647b0806ce59c089d5b8', decrypted_nameid.text) + self.assertIn("{%s}NameID" % (OneLogin_Saml2_Constants.NS_SAML), decrypted_nameid.tag) + self.assertEqual("457bdb600de717891c77647b0806ce59c089d5b8", decrypted_nameid.text) - key_4_file_name = join(self.data_path, 'misc', 'sp4.key') - f = open(key_4_file_name, 'r') + key_4_file_name = join(self.data_path, "misc", "sp4.key") + f = open(key_4_file_name, "r") key4 = f.read() f.close() with self.assertRaisesRegex(Exception, "(1, 'failed to decrypt')"): OneLogin_Saml2_Utils.decrypt_element(encrypted_data, key4) - xml_nameid_enc_2 = b64decode(self.file_contents(join(self.data_path, 'responses', 'invalids', 'encrypted_nameID_without_EncMethod.xml.base64'))) + xml_nameid_enc_2 = b64decode(self.file_contents(join(self.data_path, "responses", "invalids", "encrypted_nameID_without_EncMethod.xml.base64"))) dom_nameid_enc_2 = parseString(xml_nameid_enc_2) - encrypted_nameid_nodes_2 = dom_nameid_enc_2.getElementsByTagName('saml:EncryptedID') + encrypted_nameid_nodes_2 = dom_nameid_enc_2.getElementsByTagName("saml:EncryptedID") encrypted_data_2 = encrypted_nameid_nodes_2[0].firstChild with self.assertRaisesRegex(Exception, "(1, 'failed to decrypt')"): OneLogin_Saml2_Utils.decrypt_element(encrypted_data_2, key) - xml_nameid_enc_3 = b64decode(self.file_contents(join(self.data_path, 'responses', 'invalids', 'encrypted_nameID_without_keyinfo.xml.base64'))) + xml_nameid_enc_3 = b64decode(self.file_contents(join(self.data_path, "responses", "invalids", "encrypted_nameID_without_keyinfo.xml.base64"))) dom_nameid_enc_3 = parseString(xml_nameid_enc_3) - encrypted_nameid_nodes_3 = dom_nameid_enc_3.getElementsByTagName('saml:EncryptedID') + encrypted_nameid_nodes_3 = dom_nameid_enc_3.getElementsByTagName("saml:EncryptedID") encrypted_data_3 = encrypted_nameid_nodes_3[0].firstChild with self.assertRaisesRegex(Exception, "(1, 'failed to decrypt')"): @@ -696,20 +672,20 @@ def testDecryptElementInplace(self): key = settings.get_sp_key() - xml_nameid_enc = b64decode(self.file_contents(join(self.data_path, 'responses', 'response_encrypted_nameid.xml.base64'))) + xml_nameid_enc = b64decode(self.file_contents(join(self.data_path, "responses", "response_encrypted_nameid.xml.base64"))) dom = fromstring(xml_nameid_enc) - encrypted_node = dom.xpath('//saml:EncryptedID/xenc:EncryptedData', namespaces=OneLogin_Saml2_Constants.NSMAP)[0] + encrypted_node = dom.xpath("//saml:EncryptedID/xenc:EncryptedData", namespaces=OneLogin_Saml2_Constants.NSMAP)[0] # can be decrypted twice when copy the node first for _ in range(2): decrypted_nameid = OneLogin_Saml2_Utils.decrypt_element(encrypted_node, key, inplace=False) - self.assertIn('NameID', decrypted_nameid.tag) - self.assertEqual('2de11defd199f8d5bb63f9b7deb265ba5c675c10', decrypted_nameid.text) + self.assertIn("NameID", decrypted_nameid.tag) + self.assertEqual("2de11defd199f8d5bb63f9b7deb265ba5c675c10", decrypted_nameid.text) # can only be decrypted once in place decrypted_nameid = OneLogin_Saml2_Utils.decrypt_element(encrypted_node, key, inplace=True) - self.assertIn('NameID', decrypted_nameid.tag) - self.assertEqual('2de11defd199f8d5bb63f9b7deb265ba5c675c10', decrypted_nameid.text) + self.assertIn("NameID", decrypted_nameid.tag) + self.assertEqual("2de11defd199f8d5bb63f9b7deb265ba5c675c10", decrypted_nameid.text) # can't be decrypted twice since it has been decrypted inplace with self.assertRaisesRegex(Exception, "(1, 'failed to decrypt')"): @@ -723,60 +699,60 @@ def testAddSign(self): key = settings.get_sp_key() cert = settings.get_sp_cert() - xml_authn = b64decode(self.file_contents(join(self.data_path, 'requests', 'authn_request.xml.base64'))) + 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) res = parseString(xml_authn_signed) - ds_signature = res.firstChild.firstChild.nextSibling.nextSibling - self.assertIn('ds:Signature', ds_signature.tagName) + ds_signature = res.firstChild.firstChild.nextSibling.nextSibling.nextSibling + self.assertIn("ds:Signature", ds_signature.tagName) xml_authn_dom = parseString(xml_authn) xml_authn_signed_2 = compat.to_string(OneLogin_Saml2_Utils.add_sign(xml_authn_dom.toxml(), key, cert)) - self.assertIn('', xml_authn_signed_2) + self.assertIn("", xml_authn_signed_2) res_2 = parseString(xml_authn_signed_2) - ds_signature_2 = res_2.firstChild.firstChild.nextSibling.nextSibling - self.assertIn('ds:Signature', ds_signature_2.tagName) + ds_signature_2 = res_2.firstChild.firstChild.nextSibling.nextSibling.nextSibling + self.assertIn("ds:Signature", ds_signature_2.tagName) xml_authn_signed_3 = compat.to_string(OneLogin_Saml2_Utils.add_sign(xml_authn_dom.firstChild.toxml(), key, cert)) - self.assertIn('', xml_authn_signed_3) + self.assertIn("", xml_authn_signed_3) res_3 = parseString(xml_authn_signed_3) - ds_signature_3 = res_3.firstChild.firstChild.nextSibling.nextSibling - self.assertIn('ds:Signature', ds_signature_3.tagName) + ds_signature_3 = res_3.firstChild.firstChild.nextSibling.nextSibling.nextSibling + self.assertIn("ds:Signature", ds_signature_3.tagName) xml_authn_etree = etree.fromstring(xml_authn) xml_authn_signed_4 = compat.to_string(OneLogin_Saml2_Utils.add_sign(xml_authn_etree, key, cert)) - self.assertIn('', xml_authn_signed_4) + self.assertIn("", xml_authn_signed_4) res_4 = parseString(xml_authn_signed_4) - ds_signature_4 = res_4.firstChild.firstChild.nextSibling.nextSibling - self.assertIn('ds:Signature', ds_signature_4.tagName) + ds_signature_4 = res_4.firstChild.firstChild.nextSibling.nextSibling.nextSibling + self.assertIn("ds:Signature", ds_signature_4.tagName) xml_authn_signed_5 = compat.to_string(OneLogin_Saml2_Utils.add_sign(xml_authn_etree, key, cert)) - self.assertIn('', xml_authn_signed_5) + self.assertIn("", xml_authn_signed_5) res_5 = parseString(xml_authn_signed_5) - ds_signature_5 = res_5.firstChild.firstChild.nextSibling.nextSibling - self.assertIn('ds:Signature', ds_signature_5.tagName) + ds_signature_5 = res_5.firstChild.firstChild.nextSibling.nextSibling.nextSibling.nextSibling + self.assertIn("ds:Signature", ds_signature_5.tagName) - xml_logout_req = b64decode(self.file_contents(join(self.data_path, 'logout_requests', 'logout_request.xml.base64'))) + xml_logout_req = b64decode(self.file_contents(join(self.data_path, "logout_requests", "logout_request.xml.base64"))) xml_logout_req_signed = compat.to_string(OneLogin_Saml2_Utils.add_sign(xml_logout_req, key, cert)) - self.assertIn('', xml_logout_req_signed) + self.assertIn("", xml_logout_req_signed) res_6 = parseString(xml_logout_req_signed) - ds_signature_6 = res_6.firstChild.firstChild.nextSibling.nextSibling - self.assertIn('ds:Signature', ds_signature_6.tagName) + ds_signature_6 = res_6.firstChild.firstChild.nextSibling.nextSibling.nextSibling + self.assertIn("ds:Signature", ds_signature_6.tagName) - xml_logout_res = b64decode(self.file_contents(join(self.data_path, 'logout_responses', 'logout_response.xml.base64'))) + xml_logout_res = b64decode(self.file_contents(join(self.data_path, "logout_responses", "logout_response.xml.base64"))) xml_logout_res_signed = compat.to_string(OneLogin_Saml2_Utils.add_sign(xml_logout_res, key, cert)) - self.assertIn('', xml_logout_res_signed) + self.assertIn("", xml_logout_res_signed) res_7 = parseString(xml_logout_res_signed) - ds_signature_7 = res_7.firstChild.firstChild.nextSibling.nextSibling - self.assertIn('ds:Signature', ds_signature_7.tagName) + ds_signature_7 = res_7.firstChild.firstChild.nextSibling.nextSibling.nextSibling + self.assertIn("ds:Signature", ds_signature_7.tagName) - xml_metadata = self.file_contents(join(self.data_path, 'metadata', 'metadata_settings1.xml')) + xml_metadata = self.file_contents(join(self.data_path, "metadata", "metadata_settings1.xml")) xml_metadata_signed = compat.to_string(OneLogin_Saml2_Utils.add_sign(xml_metadata, key, cert)) - self.assertIn('', xml_metadata_signed) + self.assertIn("", xml_metadata_signed) res_8 = parseString(xml_metadata_signed) ds_signature_8 = res_8.firstChild.firstChild.nextSibling - self.assertIn('ds:Signature', ds_signature_8.tagName) + self.assertIn("ds:Signature", ds_signature_8.tagName) def testAddSignCheckAlg(self): """ @@ -787,19 +763,19 @@ def testAddSignCheckAlg(self): key = settings.get_sp_key() cert = settings.get_sp_cert() - xml_authn = b64decode(self.file_contents(join(self.data_path, 'requests', 'authn_request.xml.base64'))) + 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) 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) + self.assertIn("", xml_authn_signed_2) self.assertIn('', xml_authn_signed_2) self.assertIn('', xml_authn_signed_2) xml_authn_signed_3 = compat.to_string(OneLogin_Saml2_Utils.add_sign(xml_authn, key, cert, False, OneLogin_Saml2_Constants.RSA_SHA384, OneLogin_Saml2_Constants.SHA512)) - self.assertIn('', xml_authn_signed_3) + self.assertIn("", xml_authn_signed_3) self.assertIn('', xml_authn_signed_3) self.assertIn('', xml_authn_signed_3) @@ -809,30 +785,30 @@ def testValidateSign(self): """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) idp_data = settings.get_idp_data() - cert = idp_data['x509cert'] + cert = idp_data["x509cert"] - settings_2 = OneLogin_Saml2_Settings(self.loadSettingsJSON('settings2.json')) + settings_2 = OneLogin_Saml2_Settings(self.loadSettingsJSON("settings2.json")) idp_data2 = settings_2.get_idp_data() - cert_2 = idp_data2['x509cert'] + cert_2 = idp_data2["x509cert"] fingerprint_2 = OneLogin_Saml2_Utils.calculate_x509_fingerprint(cert_2) - fingerprint_2_256 = OneLogin_Saml2_Utils.calculate_x509_fingerprint(cert_2, 'sha256') + fingerprint_2_256 = OneLogin_Saml2_Utils.calculate_x509_fingerprint(cert_2, "sha256") try: - self.assertFalse(OneLogin_Saml2_Utils.validate_sign('', cert)) + self.assertFalse(OneLogin_Saml2_Utils.validate_sign("", cert)) except Exception as e: - self.assertEqual('Empty string supplied as input', str(e)) + self.assertEqual("Empty string supplied as input", str(e)) # expired cert - xml_metadata_signed = self.file_contents(join(self.data_path, 'metadata', 'signed_metadata_settings1.xml')) + xml_metadata_signed = self.file_contents(join(self.data_path, "metadata", "signed_metadata_settings1.xml")) self.assertTrue(OneLogin_Saml2_Utils.validate_metadata_sign(xml_metadata_signed, cert)) # expired cert, verified it self.assertFalse(OneLogin_Saml2_Utils.validate_metadata_sign(xml_metadata_signed, cert, validatecert=True)) - xml_metadata_signed_2 = self.file_contents(join(self.data_path, 'metadata', 'signed_metadata_settings2.xml')) + xml_metadata_signed_2 = self.file_contents(join(self.data_path, "metadata", "signed_metadata_settings2.xml")) self.assertTrue(OneLogin_Saml2_Utils.validate_metadata_sign(xml_metadata_signed_2, cert_2)) self.assertTrue(OneLogin_Saml2_Utils.validate_metadata_sign(xml_metadata_signed_2, None, fingerprint_2)) - xml_response_msg_signed = b64decode(self.file_contents(join(self.data_path, 'responses', 'signed_message_response.xml.base64'))) + xml_response_msg_signed = b64decode(self.file_contents(join(self.data_path, "responses", "signed_message_response.xml.base64"))) # expired cert self.assertTrue(OneLogin_Saml2_Utils.validate_sign(xml_response_msg_signed, cert)) @@ -840,83 +816,83 @@ def testValidateSign(self): self.assertFalse(OneLogin_Saml2_Utils.validate_sign(xml_response_msg_signed, cert, validatecert=True)) # modified cert - other_cert_path = join(dirname(__file__), '..', '..', '..', 'certs') - f = open(other_cert_path + '/certificate1', 'r') + other_cert_path = join(dirname(__file__), "..", "..", "..", "certs") + f = open(other_cert_path + "/certificate1", "r") cert_x = f.read() f.close() self.assertFalse(OneLogin_Saml2_Utils.validate_sign(xml_response_msg_signed, cert_x)) self.assertFalse(OneLogin_Saml2_Utils.validate_sign(xml_response_msg_signed, cert_x, validatecert=True)) - xml_response_msg_signed_2 = b64decode(self.file_contents(join(self.data_path, 'responses', 'signed_message_response2.xml.base64'))) + xml_response_msg_signed_2 = b64decode(self.file_contents(join(self.data_path, "responses", "signed_message_response2.xml.base64"))) self.assertTrue(OneLogin_Saml2_Utils.validate_sign(xml_response_msg_signed_2, cert_2)) self.assertTrue(OneLogin_Saml2_Utils.validate_sign(xml_response_msg_signed_2, None, fingerprint_2)) - self.assertTrue(OneLogin_Saml2_Utils.validate_sign(xml_response_msg_signed_2, None, fingerprint_2, 'sha1')) - self.assertTrue(OneLogin_Saml2_Utils.validate_sign(xml_response_msg_signed_2, None, fingerprint_2_256, 'sha256')) + self.assertTrue(OneLogin_Saml2_Utils.validate_sign(xml_response_msg_signed_2, None, fingerprint_2, "sha1")) + self.assertTrue(OneLogin_Saml2_Utils.validate_sign(xml_response_msg_signed_2, None, fingerprint_2_256, "sha256")) - xml_response_assert_signed = b64decode(self.file_contents(join(self.data_path, 'responses', 'signed_assertion_response.xml.base64'))) + xml_response_assert_signed = b64decode(self.file_contents(join(self.data_path, "responses", "signed_assertion_response.xml.base64"))) # expired cert self.assertTrue(OneLogin_Saml2_Utils.validate_sign(xml_response_assert_signed, cert)) # expired cert, verified it self.assertFalse(OneLogin_Saml2_Utils.validate_sign(xml_response_assert_signed, cert, validatecert=True)) - xml_response_assert_signed_2 = b64decode(self.file_contents(join(self.data_path, 'responses', 'signed_assertion_response2.xml.base64'))) + xml_response_assert_signed_2 = b64decode(self.file_contents(join(self.data_path, "responses", "signed_assertion_response2.xml.base64"))) self.assertTrue(OneLogin_Saml2_Utils.validate_sign(xml_response_assert_signed_2, cert_2)) self.assertTrue(OneLogin_Saml2_Utils.validate_sign(xml_response_assert_signed_2, None, fingerprint_2)) - xml_response_double_signed = b64decode(self.file_contents(join(self.data_path, 'responses', 'double_signed_response.xml.base64'))) + xml_response_double_signed = b64decode(self.file_contents(join(self.data_path, "responses", "double_signed_response.xml.base64"))) # expired cert self.assertTrue(OneLogin_Saml2_Utils.validate_sign(xml_response_double_signed, cert)) # expired cert, verified it self.assertFalse(OneLogin_Saml2_Utils.validate_sign(xml_response_double_signed, cert, validatecert=True)) - xml_response_double_signed_2 = b64decode(self.file_contents(join(self.data_path, 'responses', 'double_signed_response2.xml.base64'))) + xml_response_double_signed_2 = b64decode(self.file_contents(join(self.data_path, "responses", "double_signed_response2.xml.base64"))) self.assertTrue(OneLogin_Saml2_Utils.validate_sign(xml_response_double_signed_2, cert_2)) self.assertTrue(OneLogin_Saml2_Utils.validate_sign(xml_response_double_signed_2, None, fingerprint_2)) dom = parseString(xml_response_msg_signed_2) self.assertTrue(OneLogin_Saml2_Utils.validate_sign(dom.toxml(), cert_2)) - dom.firstChild.firstChild.firstChild.nodeValue = 'https://idp.example.com/simplesaml/saml2/idp/metadata.php' + dom.firstChild.firstChild.firstChild.nodeValue = "https://idp.example.com/simplesaml/saml2/idp/metadata.php" - dom.firstChild.getAttributeNode('ID').nodeValue = u'_34fg27g212d63k1f923845324475802ac0fc24530b' + dom.firstChild.getAttributeNode("ID").nodeValue = "_34fg27g212d63k1f923845324475802ac0fc24530b" # Reference validation failed self.assertFalse(OneLogin_Saml2_Utils.validate_sign(dom.toxml(), cert_2)) - invalid_fingerprint = 'afe71c34ef740bc87434be13a2263d31271da1f9' + invalid_fingerprint = "afe71c34ef740bc87434be13a2263d31271da1f9" # Wrong fingerprint self.assertFalse(OneLogin_Saml2_Utils.validate_metadata_sign(xml_metadata_signed_2, None, invalid_fingerprint)) dom_2 = parseString(xml_response_double_signed_2) self.assertTrue(OneLogin_Saml2_Utils.validate_sign(dom_2.toxml(), cert_2)) - dom_2.firstChild.firstChild.firstChild.nodeValue = 'https://example.com/other-idp' + dom_2.firstChild.firstChild.firstChild.nodeValue = "https://example.com/other-idp" # Modified message self.assertFalse(OneLogin_Saml2_Utils.validate_sign(dom_2.toxml(), cert_2)) # Try to validate directly the Assertion dom_3 = parseString(xml_response_double_signed_2) assert_elem_3 = dom_3.firstChild.firstChild.nextSibling.nextSibling.nextSibling - assert_elem_3.setAttributeNS(OneLogin_Saml2_Constants.NS_SAML, 'xmlns:saml', OneLogin_Saml2_Constants.NS_SAML) + assert_elem_3.setAttributeNS(OneLogin_Saml2_Constants.NS_SAML, "xmlns:saml", OneLogin_Saml2_Constants.NS_SAML) self.assertFalse(OneLogin_Saml2_Utils.validate_sign(assert_elem_3.toxml(), cert_2)) # Wrong scheme - no_signed = b64decode(self.file_contents(join(self.data_path, 'responses', 'invalids', 'no_signature.xml.base64'))) + no_signed = b64decode(self.file_contents(join(self.data_path, "responses", "invalids", "no_signature.xml.base64"))) self.assertFalse(OneLogin_Saml2_Utils.validate_sign(no_signed, cert)) - no_key = b64decode(self.file_contents(join(self.data_path, 'responses', 'invalids', 'no_key.xml.base64'))) + no_key = b64decode(self.file_contents(join(self.data_path, "responses", "invalids", "no_key.xml.base64"))) self.assertFalse(OneLogin_Saml2_Utils.validate_sign(no_key, cert)) # Signature Wrapping attack - wrapping_attack1 = b64decode(self.file_contents(join(self.data_path, 'responses', 'invalids', 'signature_wrapping_attack.xml.base64'))) + 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' + 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)) diff --git a/tests/src/OneLogin/saml2_tests/xml_utils_test.py b/tests/src/OneLogin/saml2_tests/xml_utils_test.py index bc563621..2135aca3 100644 --- a/tests/src/OneLogin/saml2_tests/xml_utils_test.py +++ b/tests/src/OneLogin/saml2_tests/xml_utils_test.py @@ -12,23 +12,23 @@ class TestOneLoginSaml2Xml(unittest.TestCase): - data_path = join(dirname(__file__), '..', '..', '..', 'data') + data_path = join(dirname(__file__), "..", "..", "..", "data") def loadSettingsJSON(self, filename=None): if filename: - filename = join(dirname(__file__), '..', '..', '..', 'settings', filename) + filename = join(dirname(__file__), "..", "..", "..", "settings", filename) else: - filename = join(dirname(__file__), '..', '..', '..', 'settings', 'settings1.json') + filename = join(dirname(__file__), "..", "..", "..", "settings", "settings1.json") if exists(filename): - stream = open(filename, 'r') + stream = open(filename, "r") settings = json.load(stream) stream.close() return settings else: - raise Exception('Settings json file does not exist') + raise Exception("Settings json file does not exist") def file_contents(self, filename): - f = open(filename, 'r') + f = open(filename, "r") content = f.read() f.close() return content @@ -43,13 +43,8 @@ def testLibxml2(self): 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" - ) + 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) @@ -60,32 +55,32 @@ def testValidateXML(self): """ Tests the validate_xml method of the OneLogin_Saml2_XML """ - metadata_unloaded = '' - res = OneLogin_Saml2_XML.validate_xml(metadata_unloaded, 'saml-schema-metadata-2.0.xsd') + metadata_unloaded = "" + res = OneLogin_Saml2_XML.validate_xml(metadata_unloaded, "saml-schema-metadata-2.0.xsd") self.assertIsInstance(res, str) - self.assertIn('unloaded_xml', res) + self.assertIn("unloaded_xml", res) - metadata_invalid = self.file_contents(join(self.data_path, 'metadata', 'noentity_metadata_settings1.xml')) + metadata_invalid = self.file_contents(join(self.data_path, "metadata", "noentity_metadata_settings1.xml")) - res = OneLogin_Saml2_XML.validate_xml(metadata_invalid, 'saml-schema-metadata-2.0.xsd') + res = OneLogin_Saml2_XML.validate_xml(metadata_invalid, "saml-schema-metadata-2.0.xsd") self.assertIsInstance(res, str) - self.assertIn('invalid_xml', res) + self.assertIn("invalid_xml", res) - metadata_expired = self.file_contents(join(self.data_path, 'metadata', 'expired_metadata_settings1.xml')) - res = OneLogin_Saml2_XML.validate_xml(metadata_expired, 'saml-schema-metadata-2.0.xsd') + metadata_expired = self.file_contents(join(self.data_path, "metadata", "expired_metadata_settings1.xml")) + res = OneLogin_Saml2_XML.validate_xml(metadata_expired, "saml-schema-metadata-2.0.xsd") self.assertIsInstance(res, OneLogin_Saml2_XML._element_class) - metadata_ok = self.file_contents(join(self.data_path, 'metadata', 'metadata_settings1.xml')) - res = OneLogin_Saml2_XML.validate_xml(metadata_ok, 'saml-schema-metadata-2.0.xsd') + metadata_ok = self.file_contents(join(self.data_path, "metadata", "metadata_settings1.xml")) + res = OneLogin_Saml2_XML.validate_xml(metadata_ok, "saml-schema-metadata-2.0.xsd") self.assertIsInstance(res, OneLogin_Saml2_XML._element_class) def testToString(self): """ Tests the to_string method of the OneLogin_Saml2_XML """ - xml = 'test1' + xml = "test1" elem = etree.fromstring(xml) - bxml = xml.encode('utf8') + bxml = xml.encode("utf8") self.assertIs(xml, OneLogin_Saml2_XML.to_string(xml)) self.assertIs(bxml, OneLogin_Saml2_XML.to_string(bxml)) @@ -99,7 +94,7 @@ def testToElement(self): """ Tests the to_etree method of the OneLogin_Saml2_XML """ - xml = 'test1' + xml = "test1" elem = etree.fromstring(xml) xml_expected = etree.tostring(elem) @@ -107,7 +102,7 @@ def testToElement(self): self.assertIsInstance(res, etree._Element) self.assertEqual(xml_expected, etree.tostring(res)) - res = OneLogin_Saml2_XML.to_etree(xml.encode('utf8')) + res = OneLogin_Saml2_XML.to_etree(xml.encode("utf8")) self.assertIsInstance(res, etree._Element) self.assertEqual(xml_expected, etree.tostring(res)) @@ -126,46 +121,46 @@ def testQuery(self): """ Tests the query method of the OneLogin_Saml2_Utils """ - xml = self.file_contents(join(self.data_path, 'responses', 'valid_response.xml.base64')) + xml = self.file_contents(join(self.data_path, "responses", "valid_response.xml.base64")) xml = b64decode(xml) dom = etree.fromstring(xml) - assertion_nodes = OneLogin_Saml2_XML.query(dom, '/samlp:Response/saml:Assertion') + assertion_nodes = OneLogin_Saml2_XML.query(dom, "/samlp:Response/saml:Assertion") self.assertEqual(1, len(assertion_nodes)) assertion = assertion_nodes[0] - self.assertIn('Assertion', assertion.tag) + self.assertIn("Assertion", assertion.tag) - attribute_statement_nodes = OneLogin_Saml2_XML.query(dom, '/samlp:Response/saml:Assertion/saml:AttributeStatement') + attribute_statement_nodes = OneLogin_Saml2_XML.query(dom, "/samlp:Response/saml:Assertion/saml:AttributeStatement") self.assertEqual(1, len(assertion_nodes)) attribute_statement = attribute_statement_nodes[0] - self.assertIn('AttributeStatement', attribute_statement.tag) + self.assertIn("AttributeStatement", attribute_statement.tag) - attribute_statement_nodes_2 = OneLogin_Saml2_XML.query(dom, './saml:AttributeStatement', assertion) + attribute_statement_nodes_2 = OneLogin_Saml2_XML.query(dom, "./saml:AttributeStatement", assertion) self.assertEqual(1, len(attribute_statement_nodes_2)) attribute_statement_2 = attribute_statement_nodes_2[0] self.assertEqual(attribute_statement, attribute_statement_2) - signature_res_nodes = OneLogin_Saml2_XML.query(dom, '/samlp:Response/ds:Signature') + signature_res_nodes = OneLogin_Saml2_XML.query(dom, "/samlp:Response/ds:Signature") self.assertEqual(1, len(signature_res_nodes)) signature_res = signature_res_nodes[0] - self.assertIn('Signature', signature_res.tag) + self.assertIn("Signature", signature_res.tag) - signature_nodes = OneLogin_Saml2_XML.query(dom, '/samlp:Response/saml:Assertion/ds:Signature') + signature_nodes = OneLogin_Saml2_XML.query(dom, "/samlp:Response/saml:Assertion/ds:Signature") self.assertEqual(1, len(signature_nodes)) signature = signature_nodes[0] - self.assertIn('Signature', signature.tag) + self.assertIn("Signature", signature.tag) - signature_nodes_2 = OneLogin_Saml2_XML.query(dom, './ds:Signature', assertion) + signature_nodes_2 = OneLogin_Saml2_XML.query(dom, "./ds:Signature", assertion) self.assertEqual(1, len(signature_nodes_2)) signature2 = signature_nodes_2[0] self.assertNotEqual(signature_res, signature2) self.assertEqual(signature, signature2) - signature_nodes_3 = OneLogin_Saml2_XML.query(dom, './ds:SignatureValue', assertion) + signature_nodes_3 = OneLogin_Saml2_XML.query(dom, "./ds:SignatureValue", assertion) self.assertEqual(0, len(signature_nodes_3)) - signature_nodes_4 = OneLogin_Saml2_XML.query(dom, './ds:Signature/ds:SignatureValue', assertion) + signature_nodes_4 = OneLogin_Saml2_XML.query(dom, "./ds:Signature/ds:SignatureValue", assertion) self.assertEqual(1, len(signature_nodes_4)) - signature_nodes_5 = OneLogin_Saml2_XML.query(dom, './/ds:SignatureValue', assertion) + signature_nodes_5 = OneLogin_Saml2_XML.query(dom, ".//ds:SignatureValue", assertion) self.assertEqual(1, len(signature_nodes_5))