From 587ada2f69b49106742c311335ec79d78d5f020a Mon Sep 17 00:00:00 2001 From: dezhidki Date: Fri, 4 Nov 2022 09:17:53 +0200 Subject: [PATCH 1/7] Add PySAML2 package --- poetry.lock | 1014 +++++++++++++++++++++++++-------------------- pyproject.toml | 2 + timApp/Dockerfile | 3 + 3 files changed, 578 insertions(+), 441 deletions(-) diff --git a/poetry.lock b/poetry.lock index 4a2086c55f..ae2488959d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -33,7 +33,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" vine = ">=1.1.3,<5.0.0a1" [[package]] -name = "async_generator" +name = "async-generator" version = "1.10" description = "Async generators and context managers for Python 3.5+" category = "main" @@ -60,10 +60,10 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six", "sphinx", "sphinx-notfound-page", "zope.interface"] docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"] tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six", "zope.interface"] -tests_no_zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six"] +tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six"] [[package]] -name = "Authlib" +name = "authlib" version = "1.0.0rc1" description = "The ultimate Python library in building OAuth and OpenID Connect servers and clients." category = "main" @@ -86,8 +86,8 @@ pycodestyle = ">=2.9.1" toml = "*" [[package]] -name = "Babel" -version = "2.10.3" +name = "babel" +version = "2.11.0" description = "Internationalization utilities" category = "main" optional = false @@ -136,11 +136,11 @@ python-versions = "*" [[package]] name = "black" -version = "22.8.0" +version = "22.10.0" description = "The uncompromising code formatter." category = "dev" optional = false -python-versions = ">=3.6.2" +python-versions = ">=3.7" [package.dependencies] click = ">=8.0.0" @@ -172,7 +172,7 @@ css = ["tinycss2 (>=1.1.0,<1.2)"] dev = ["Sphinx (==4.3.2)", "black (==22.3.0)", "build (==0.8.0)", "flake8 (==4.0.1)", "hashin (==0.17.0)", "mypy (==0.961)", "pip-tools (==6.6.2)", "pytest (==7.1.2)", "tox (==3.25.0)", "twine (==4.0.1)", "wheel (==0.37.1)"] [[package]] -name = "Brotli" +name = "brotli" version = "1.0.9" description = "Python bindings for the Brotli compression library" category = "main" @@ -264,7 +264,7 @@ optional = false python-versions = ">=3.6.0" [package.extras] -unicode_backport = ["unicodedata2"] +unicode-backport = ["unicodedata2"] [[package]] name = "click" @@ -279,11 +279,11 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} [[package]] name = "colorama" -version = "0.4.5" +version = "0.4.6" description = "Cross-platform colored terminal text." category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" [[package]] name = "commonmark" @@ -298,7 +298,7 @@ test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"] [[package]] name = "cryptography" -version = "38.0.1" +version = "38.0.3" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." category = "main" optional = false @@ -317,11 +317,11 @@ test = ["hypothesis (>=1.11.4,!=3.79.2)", "iso8601", "pretend", "pytest (>=6.2.0 [[package]] name = "cssselect" -version = "1.1.0" +version = "1.2.0" description = "cssselect parses CSS3 Selectors and translates them to XPath 1.0" category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.7" [[package]] name = "defusedxml" @@ -332,7 +332,7 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] -name = "Deprecated" +name = "deprecated" version = "1.2.13" description = "Python @deprecated decorator to deprecate old python classes, functions or methods." category = "main" @@ -368,9 +368,20 @@ category = "main" optional = false python-versions = ">=3.7" +[[package]] +name = "elementpath" +version = "3.0.2" +description = "XPath 1.0/2.0/3.0 parsers and selectors for ElementTree and lxml" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.extras] +dev = ["Sphinx", "coverage", "flake8", "lxml", "lxml-stubs", "memory-profiler", "memray", "mypy (==0.971)", "tox", "xmlschema (>=2.0.0)"] + [[package]] name = "exceptiongroup" -version = "1.0.0rc9" +version = "1.0.0" description = "Backport of PEP 654 (exception groups)" category = "main" optional = false @@ -392,7 +403,7 @@ docs = ["furo (>=2022.6.21)", "sphinx (>=5.1.1)", "sphinx-autodoc-typehints (>=1 testing = ["covdefaults (>=2.2)", "coverage (>=6.4.2)", "pytest (>=7.1.2)", "pytest-cov (>=3)", "pytest-timeout (>=2.1)"] [[package]] -name = "Flask" +name = "flask" version = "2.1.3" description = "A simple framework for building complex web applications." category = "main" @@ -410,7 +421,7 @@ async = ["asgiref (>=3.2)"] dotenv = ["python-dotenv"] [[package]] -name = "Flask-Assets" +name = "flask-assets" version = "2.0" description = "Asset management for Flask, to compress and merge CSS and Javascript files." category = "main" @@ -422,7 +433,7 @@ Flask = ">=0.8" webassets = ">=2.0" [[package]] -name = "Flask-Caching" +name = "flask-caching" version = "2.0.1" description = "Adds caching support to Flask applications." category = "main" @@ -434,7 +445,7 @@ cachelib = ">=0.9.0" Flask = "<3" [[package]] -name = "Flask-Compress" +name = "flask-compress" version = "1.13" description = "Compress responses in your Flask app with gzip, deflate or brotli." category = "main" @@ -446,7 +457,7 @@ brotli = "*" flask = "*" [[package]] -name = "Flask-Migrate" +name = "flask-migrate" version = "3.1.0" description = "SQLAlchemy database migrations for Flask applications using Alembic." category = "main" @@ -473,7 +484,7 @@ oauth2client = "*" six = "*" [[package]] -name = "Flask-OpenID" +name = "flask-openid" version = "1.3.0" description = "OpenID support for Flask" category = "main" @@ -485,7 +496,7 @@ Flask = ">=0.10.1" python3-openid = ">=2.0" [[package]] -name = "Flask-SQLAlchemy" +name = "flask-sqlalchemy" version = "2.5.1" description = "Adds SQLAlchemy support to your Flask application." category = "main" @@ -497,7 +508,7 @@ Flask = ">=0.10" SQLAlchemy = ">=0.8.0" [[package]] -name = "Flask-Testing" +name = "flask-testing" version = "0.8.1" description = "Unit testing for Flask" category = "main" @@ -508,7 +519,7 @@ python-versions = "*" Flask = "*" [[package]] -name = "Flask-WTF" +name = "flask-wtf" version = "1.0.1" description = "Form rendering, validation, and CSRF protection for Flask with WTForms." category = "main" @@ -547,7 +558,7 @@ test = ["backports.socketpair", "cffi (>=1.12.2)", "contextvars (==2.4)", "cover [[package]] name = "greenlet" -version = "1.1.3" +version = "1.1.3.post0" description = "Lightweight in-process concurrent programming" category = "main" optional = false @@ -601,7 +612,7 @@ lxml = ["lxml"] [[package]] name = "httpagentparser" -version = "1.9.3" +version = "1.9.5" description = "Extracts OS Browser etc information from http user agent string" category = "main" optional = false @@ -609,7 +620,7 @@ python-versions = "*" [[package]] name = "httplib2" -version = "0.20.4" +version = "0.21.0" description = "A comprehensive HTTP client library." category = "main" optional = false @@ -665,7 +676,7 @@ optional = false python-versions = ">=3.7" [[package]] -name = "Jinja2" +name = "jinja2" version = "3.1.2" description = "A very fast and expressive template engine." category = "main" @@ -757,7 +768,7 @@ source = ["Cython (>=0.29.7)"] [[package]] name = "mailmanclient" -version = "3.3.3" +version = "3.3.4" description = "mailmanclient -- Python bindings for Mailman REST API" category = "main" optional = false @@ -767,12 +778,12 @@ python-versions = "*" requests = "*" [package.extras] -docs = ["sphinx", "sphinx-issues", "sphinx-rtd-theme"] +docs = ["pydoctor", "sphinx", "sphinx-issues", "sphinx-rtd-theme"] lint = ["flake8 (>3.0)", "flake8-bugbear"] testing = ["falcon (>1.4.1)", "httpx", "mailman (>=3.3.1)", "pytest", "pytest-services"] [[package]] -name = "Mako" +name = "mako" version = "1.2.3" description = "A super-fast templating language that borrows the best ideas from the existing templating languages." category = "main" @@ -789,7 +800,7 @@ testing = ["pytest"] [[package]] name = "marisa-trie" -version = "0.7.7" +version = "0.7.8" description = "Static memory-efficient and fast Trie-like structures for Python." category = "main" optional = false @@ -802,7 +813,7 @@ setuptools = "*" test = ["hypothesis", "pytest", "readme-renderer"] [[package]] -name = "MarkupSafe" +name = "markupsafe" version = "2.1.1" description = "Safely add untrusted strings to HTML/XML markup." category = "main" @@ -947,8 +958,8 @@ optional = false python-versions = ">=3.7" [[package]] -name = "Pillow" -version = "9.2.0" +name = "pillow" +version = "9.3.0" description = "Python Imaging Library (Fork)" category = "main" optional = false @@ -980,7 +991,7 @@ python-versions = "*" [[package]] name = "psycopg2-binary" -version = "2.9.3" +version = "2.9.5" description = "psycopg2 - Python-PostgreSQL Database Adapter" category = "main" optional = false @@ -1033,7 +1044,7 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] -name = "Pygments" +name = "pygments" version = "2.13.0" description = "Pygments is a syntax highlighting package written in Python." category = "main" @@ -1044,7 +1055,7 @@ python-versions = ">=3.6" plugins = ["importlib-metadata"] [[package]] -name = "PyLaTeX" +name = "pylatex" version = "1.4.1" description = "A Python library for creating LaTeX files and snippets" category = "main" @@ -1056,16 +1067,31 @@ ordered-set = "*" [package.extras] all = ["coverage", "flake8 (<3.0.0)", "flake8-putty", "flake8_docstrings (==1.3.0)", "future (>=0.15.2)", "matplotlib", "numpy", "pep8-naming (==0.8.2)", "pycodestyle (==2.0.0)", "pydocstyle (==3.0.0)", "pyflakes (==1.2.3)", "pytest (>=4.6)", "pytest-cov", "quantities", "sphinx"] -convert_to_py2 = ["future (>=0.15.2)"] +convert-to-py2 = ["future (>=0.15.2)"] docs = ["sphinx"] matplotlib = ["matplotlib"] matrices = ["numpy"] quantities = ["numpy", "quantities"] testing = ["coverage", "flake8 (<3.0.0)", "flake8-putty", "flake8_docstrings (==1.3.0)", "pep8-naming (==0.8.2)", "pycodestyle (==2.0.0)", "pydocstyle (==3.0.0)", "pyflakes (==1.2.3)", "pytest (>=4.6)", "pytest-cov"] +[[package]] +name = "pyopenssl" +version = "22.1.0" +description = "Python wrapper module around the OpenSSL library" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +cryptography = ">=38.0.0,<39" + +[package.extras] +docs = ["sphinx (!=5.2.0,!=5.2.0.post0)", "sphinx-rtd-theme"] +test = ["flaky", "pretend", "pytest (>=3.0.1)"] + [[package]] name = "pypandoc" -version = "1.9" +version = "1.10" description = "Thin wrapper for pandoc." category = "main" optional = false @@ -1083,7 +1109,29 @@ python-versions = ">=3.6.8" diagrams = ["jinja2", "railroad-diagrams"] [[package]] -name = "PySocks" +name = "pysaml2" +version = "7.2.1" +description = "Python implementation of SAML Version 2 Standard" +category = "main" +optional = false +python-versions = "<4,>=3.6" + +[package.dependencies] +cryptography = ">=3.1" +defusedxml = "*" +pyOpenSSL = "*" +python-dateutil = "*" +pytz = "*" +requests = ">=1.0.0" +setuptools = "*" +six = "*" +xmlschema = ">=1.2.1" + +[package.extras] +s2repoze = ["paste", "repoze.who", "zope.interface"] + +[[package]] +name = "pysocks" version = "1.7.1" description = "A Python SOCKS client module. See https://github.com/Anorov/PySocks for more information." category = "main" @@ -1150,14 +1198,14 @@ test = ["coverage (>=4.5.2)", "flake8 (>=3.6.0)", "freezegun (>=0.3.11,<=1.1.0)" [[package]] name = "pytz" -version = "2022.2.1" +version = "2022.6" description = "World timezone definitions, modern and historical" category = "main" optional = false python-versions = "*" [[package]] -name = "PyYAML" +name = "pyyaml" version = "6.0" description = "YAML parser and emitter for Python" category = "main" @@ -1210,7 +1258,7 @@ urllib3 = ">=1.21.1,<1.27" [package.extras] socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use_chardet_on_py3 = ["chardet (>=3.0.2,<6)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "responses" @@ -1314,8 +1362,8 @@ optional = false python-versions = ">=3.6" [[package]] -name = "Sphinx" -version = "5.2.2" +name = "sphinx" +version = "5.3.0" description = "Python documentation generator" category = "main" optional = false @@ -1416,7 +1464,7 @@ lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] [[package]] -name = "SQLAlchemy" +name = "sqlalchemy" version = "1.3.24" description = "Database Abstraction Library" category = "main" @@ -1425,18 +1473,18 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.extras] mssql = ["pyodbc"] -mssql_pymssql = ["pymssql"] -mssql_pyodbc = ["pyodbc"] +mssql-pymssql = ["pymssql"] +mssql-pyodbc = ["pyodbc"] mysql = ["mysqlclient"] oracle = ["cx_oracle"] postgresql = ["psycopg2"] -postgresql_pg8000 = ["pg8000 (<1.16.6)"] -postgresql_psycopg2binary = ["psycopg2-binary"] -postgresql_psycopg2cffi = ["psycopg2cffi"] +postgresql-pg8000 = ["pg8000 (<1.16.6)"] +postgresql-psycopg2binary = ["psycopg2-binary"] +postgresql-psycopg2cffi = ["psycopg2cffi"] pymysql = ["pymysql", "pymysql (<1)"] [[package]] -name = "SQLAlchemy-Utils" +name = "sqlalchemy-utils" version = "0.38.3" description = "Various utility functions for SQLAlchemy." category = "main" @@ -1456,7 +1504,7 @@ password = ["passlib (>=1.6,<2.0)"] pendulum = ["pendulum (>=2.0.5)"] phone = ["phonenumbers (>=5.9.2)"] test = ["Jinja2 (>=2.3)", "Pygments (>=1.2)", "backports.zoneinfo", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "isort (>=4.2.2)", "pg8000 (>=1.12.4)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (>=2.7.1)", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] -test_all = ["Babel (>=1.3)", "Jinja2 (>=2.3)", "Pygments (>=1.2)", "arrow (>=0.3.4)", "backports.zoneinfo", "colour (>=0.0.4)", "cryptography (>=0.6)", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "furl (>=0.4.1)", "intervals (>=0.7.1)", "isort (>=4.2.2)", "passlib (>=1.6,<2.0)", "pendulum (>=2.0.5)", "pg8000 (>=1.12.4)", "phonenumbers (>=5.9.2)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (>=2.7.1)", "python-dateutil", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] +test-all = ["Babel (>=1.3)", "Jinja2 (>=2.3)", "Pygments (>=1.2)", "arrow (>=0.3.4)", "backports.zoneinfo", "colour (>=0.0.4)", "cryptography (>=0.6)", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "furl (>=0.4.1)", "intervals (>=0.7.1)", "isort (>=4.2.2)", "passlib (>=1.6,<2.0)", "pendulum (>=2.0.5)", "pg8000 (>=1.12.4)", "phonenumbers (>=5.9.2)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (>=2.7.1)", "python-dateutil", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] timezone = ["python-dateutil"] url = ["furl (>=0.4.1)"] @@ -1517,7 +1565,7 @@ python-versions = "*" [[package]] name = "types-bleach" -version = "5.0.3" +version = "5.0.3.1" description = "Typing stubs for bleach" category = "dev" optional = false @@ -1531,9 +1579,20 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "types-pysaml2" +version = "1.0.0" +description = "Type Stubs for pysaml2" +category = "dev" +optional = false +python-versions = ">=3.7, <4" + +[package.extras] +dev = ["mypy (==0.942)", "pipenv-setup (==3.2.0)", "pysaml2 (==7.1.2)", "twine (==4.0.0)", "types-requests (==2.27.16)", "types-six (==1.16.13)"] + [[package]] name = "types-python-dateutil" -version = "2.8.19" +version = "2.8.19.2" description = "Typing stubs for python-dateutil" category = "dev" optional = false @@ -1548,8 +1607,8 @@ optional = false python-versions = "*" [[package]] -name = "types-PyYAML" -version = "6.0.12" +name = "types-pyyaml" +version = "6.0.12.1" description = "Typing stubs for PyYAML" category = "dev" optional = false @@ -1557,7 +1616,7 @@ python-versions = "*" [[package]] name = "types-redis" -version = "4.3.21" +version = "4.3.21.3" description = "Typing stubs for redis" category = "dev" optional = false @@ -1565,7 +1624,7 @@ python-versions = "*" [[package]] name = "types-requests" -version = "2.28.11" +version = "2.28.11.2" description = "Typing stubs for requests" category = "dev" optional = false @@ -1576,7 +1635,7 @@ types-urllib3 = "<1.27" [[package]] name = "types-urllib3" -version = "1.26.25" +version = "1.26.25.1" description = "Typing stubs for urllib3" category = "dev" optional = false @@ -1584,7 +1643,7 @@ python-versions = "*" [[package]] name = "typing-extensions" -version = "4.3.0" +version = "4.4.0" description = "Backported and Experimental Type Hints for Python 3.7+" category = "main" optional = false @@ -1643,7 +1702,7 @@ optional = false python-versions = ">=3" [[package]] -name = "Wand" +name = "wand" version = "0.6.10" description = "Ctypes-based simple MagickWand API binding for Python" category = "main" @@ -1697,7 +1756,7 @@ optional = false python-versions = "*" [[package]] -name = "Werkzeug" +name = "werkzeug" version = "2.2.2" description = "The comprehensive WSGI web application library." category = "main" @@ -1741,7 +1800,7 @@ python-versions = ">=3.7.0" h11 = ">=0.9.0,<1" [[package]] -name = "WTForms" +name = "wtforms" version = "3.0.1" description = "Form validation and rendering for Python web development." category = "main" @@ -1754,6 +1813,22 @@ MarkupSafe = "*" [package.extras] email = ["email-validator"] +[[package]] +name = "xmlschema" +version = "2.1.1" +description = "An XML Schema validator and decoder" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +elementpath = ">=3.0.0,<4.0.0" + +[package.extras] +codegen = ["elementpath (>=3.0.0,<4.0.0)", "jinja2"] +dev = ["Sphinx", "coverage", "elementpath (>=3.0.0,<4.0.0)", "flake8", "jinja2", "lxml", "lxml-stubs", "memory-profiler", "mypy", "sphinx-rtd-theme", "tox"] +docs = ["Sphinx", "elementpath (>=3.0.0,<4.0.0)", "jinja2", "sphinx-rtd-theme"] + [[package]] name = "xmlsec" version = "1.3.13" @@ -1766,7 +1841,7 @@ python-versions = ">=3.5" lxml = ">=3.8" [[package]] -name = "zope.event" +name = "zope-event" version = "4.5.0" description = "Very basic event publishing system" category = "main" @@ -1781,8 +1856,8 @@ docs = ["Sphinx"] test = ["zope.testrunner"] [[package]] -name = "zope.interface" -version = "5.4.0" +name = "zope-interface" +version = "5.5.1" description = "Interfaces for Python" category = "main" optional = false @@ -1799,7 +1874,7 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] [metadata] lock-version = "1.1" python-versions = "^3.10" -content-hash = "c701a6d264a0eeda993340401dc7daeffe6008d7e431797901e821b5d3b6f05e" +content-hash = "6d91f855148ba53d492f34db528ba4b9e8ae3f0bebf1c74da63b9667292230f2" [metadata.files] alabaster = [ @@ -1814,7 +1889,7 @@ amqp = [ {file = "amqp-2.6.1-py2.py3-none-any.whl", hash = "sha256:aa7f313fb887c91f15474c1229907a04dac0b8135822d6603437803424c0aa59"}, {file = "amqp-2.6.1.tar.gz", hash = "sha256:70cdb10628468ff14e57ec2f751c7aa9e48e7e3651cfd62d431213c0c4e58f21"}, ] -async_generator = [ +async-generator = [ {file = "async_generator-1.10-py3-none-any.whl", hash = "sha256:01c7bf666359b4967d2cda0000cc2e4af16a0ae098cbffcb8472fb9e8ad6585b"}, {file = "async_generator-1.10.tar.gz", hash = "sha256:6ebb3d106c12920aaae42ccb6f787ef5eefdcdd166ea3d628fa8476abe712144"}, ] @@ -1826,7 +1901,7 @@ attrs = [ {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, ] -Authlib = [ +authlib = [ {file = "Authlib-1.0.0rc1-py2.py3-none-any.whl", hash = "sha256:add1855e7acad8da8b93b3895fe608d4304116e9cfbc6038591a0c483f44cc7b"}, {file = "Authlib-1.0.0rc1.tar.gz", hash = "sha256:abc68aadc0d305576975d77d8a7f9c6d9e3b5434f23facb424d0e9d545e8b649"}, ] @@ -1834,9 +1909,9 @@ autopep8 = [ {file = "autopep8-1.7.0-py2.py3-none-any.whl", hash = "sha256:6f09e90a2be784317e84dc1add17ebfc7abe3924239957a37e5040e27d812087"}, {file = "autopep8-1.7.0.tar.gz", hash = "sha256:ca9b1a83e53a7fad65d731dc7a2a2d50aa48f43850407c59f6a1a306c4201142"}, ] -Babel = [ - {file = "Babel-2.10.3-py3-none-any.whl", hash = "sha256:ff56f4892c1c4bf0d814575ea23471c230d544203c7748e8c68f0089478d48eb"}, - {file = "Babel-2.10.3.tar.gz", hash = "sha256:7614553711ee97490f732126dc077f8d0ae084ebc6a96e23db1482afabdb2c51"}, +babel = [ + {file = "Babel-2.11.0-py3-none-any.whl", hash = "sha256:1ad3eca1c885218f6dce2ab67291178944f810a10a9b5f3cb8382a5a232b64fe"}, + {file = "Babel-2.11.0.tar.gz", hash = "sha256:5ef4b3226b0180dedded4229651c8b0e1a3a6a2837d45a073272f313e4cf97f6"}, ] bcrypt = [ {file = "bcrypt-3.2.2-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:7180d98a96f00b1050e93f5b0f556e658605dd9f524d0b0e68ae7944673f525e"}, @@ -1860,35 +1935,33 @@ billiard = [ {file = "billiard-3.6.4.0.tar.gz", hash = "sha256:299de5a8da28a783d51b197d496bef4f1595dd023a93a4f59dde1886ae905547"}, ] black = [ - {file = "black-22.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ce957f1d6b78a8a231b18e0dd2d94a33d2ba738cd88a7fe64f53f659eea49fdd"}, - {file = "black-22.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5107ea36b2b61917956d018bd25129baf9ad1125e39324a9b18248d362156a27"}, - {file = "black-22.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e8166b7bfe5dcb56d325385bd1d1e0f635f24aae14b3ae437102dedc0c186747"}, - {file = "black-22.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd82842bb272297503cbec1a2600b6bfb338dae017186f8f215c8958f8acf869"}, - {file = "black-22.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d839150f61d09e7217f52917259831fe2b689f5c8e5e32611736351b89bb2a90"}, - {file = "black-22.8.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a05da0430bd5ced89176db098567973be52ce175a55677436a271102d7eaa3fe"}, - {file = "black-22.8.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a098a69a02596e1f2a58a2a1c8d5a05d5a74461af552b371e82f9fa4ada8342"}, - {file = "black-22.8.0-cp36-cp36m-win_amd64.whl", hash = "sha256:5594efbdc35426e35a7defa1ea1a1cb97c7dbd34c0e49af7fb593a36bd45edab"}, - {file = "black-22.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a983526af1bea1e4cf6768e649990f28ee4f4137266921c2c3cee8116ae42ec3"}, - {file = "black-22.8.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b2c25f8dea5e8444bdc6788a2f543e1fb01494e144480bc17f806178378005e"}, - {file = "black-22.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:78dd85caaab7c3153054756b9fe8c611efa63d9e7aecfa33e533060cb14b6d16"}, - {file = "black-22.8.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:cea1b2542d4e2c02c332e83150e41e3ca80dc0fb8de20df3c5e98e242156222c"}, - {file = "black-22.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5b879eb439094751185d1cfdca43023bc6786bd3c60372462b6f051efa6281a5"}, - {file = "black-22.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0a12e4e1353819af41df998b02c6742643cfef58282915f781d0e4dd7a200411"}, - {file = "black-22.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3a73f66b6d5ba7288cd5d6dad9b4c9b43f4e8a4b789a94bf5abfb878c663eb3"}, - {file = "black-22.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:e981e20ec152dfb3e77418fb616077937378b322d7b26aa1ff87717fb18b4875"}, - {file = "black-22.8.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8ce13ffed7e66dda0da3e0b2eb1bdfc83f5812f66e09aca2b0978593ed636b6c"}, - {file = "black-22.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:32a4b17f644fc288c6ee2bafdf5e3b045f4eff84693ac069d87b1a347d861497"}, - {file = "black-22.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0ad827325a3a634bae88ae7747db1a395d5ee02cf05d9aa7a9bd77dfb10e940c"}, - {file = "black-22.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53198e28a1fb865e9fe97f88220da2e44df6da82b18833b588b1883b16bb5d41"}, - {file = "black-22.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:bc4d4123830a2d190e9cc42a2e43570f82ace35c3aeb26a512a2102bce5af7ec"}, - {file = "black-22.8.0-py3-none-any.whl", hash = "sha256:d2c21d439b2baf7aa80d6dd4e3659259be64c6f49dfd0f32091063db0e006db4"}, - {file = "black-22.8.0.tar.gz", hash = "sha256:792f7eb540ba9a17e8656538701d3eb1afcb134e3b45b71f20b25c77a8db7e6e"}, + {file = "black-22.10.0-1fixedarch-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:5cc42ca67989e9c3cf859e84c2bf014f6633db63d1cbdf8fdb666dcd9e77e3fa"}, + {file = "black-22.10.0-1fixedarch-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:5d8f74030e67087b219b032aa33a919fae8806d49c867846bfacde57f43972ef"}, + {file = "black-22.10.0-1fixedarch-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:197df8509263b0b8614e1df1756b1dd41be6738eed2ba9e9769f3880c2b9d7b6"}, + {file = "black-22.10.0-1fixedarch-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:2644b5d63633702bc2c5f3754b1b475378fbbfb481f62319388235d0cd104c2d"}, + {file = "black-22.10.0-1fixedarch-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:e41a86c6c650bcecc6633ee3180d80a025db041a8e2398dcc059b3afa8382cd4"}, + {file = "black-22.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2039230db3c6c639bd84efe3292ec7b06e9214a2992cd9beb293d639c6402edb"}, + {file = "black-22.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14ff67aec0a47c424bc99b71005202045dc09270da44a27848d534600ac64fc7"}, + {file = "black-22.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:819dc789f4498ecc91438a7de64427c73b45035e2e3680c92e18795a839ebb66"}, + {file = "black-22.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5b9b29da4f564ba8787c119f37d174f2b69cdfdf9015b7d8c5c16121ddc054ae"}, + {file = "black-22.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8b49776299fece66bffaafe357d929ca9451450f5466e997a7285ab0fe28e3b"}, + {file = "black-22.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:21199526696b8f09c3997e2b4db8d0b108d801a348414264d2eb8eb2532e540d"}, + {file = "black-22.10.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e464456d24e23d11fced2bc8c47ef66d471f845c7b7a42f3bd77bf3d1789650"}, + {file = "black-22.10.0-cp37-cp37m-win_amd64.whl", hash = "sha256:9311e99228ae10023300ecac05be5a296f60d2fd10fff31cf5c1fa4ca4b1988d"}, + {file = "black-22.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fba8a281e570adafb79f7755ac8721b6cf1bbf691186a287e990c7929c7692ff"}, + {file = "black-22.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:915ace4ff03fdfff953962fa672d44be269deb2eaf88499a0f8805221bc68c87"}, + {file = "black-22.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:444ebfb4e441254e87bad00c661fe32df9969b2bf224373a448d8aca2132b395"}, + {file = "black-22.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:974308c58d057a651d182208a484ce80a26dac0caef2895836a92dd6ebd725e0"}, + {file = "black-22.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72ef3925f30e12a184889aac03d77d031056860ccae8a1e519f6cbb742736383"}, + {file = "black-22.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:432247333090c8c5366e69627ccb363bc58514ae3e63f7fc75c54b1ea80fa7de"}, + {file = "black-22.10.0-py3-none-any.whl", hash = "sha256:c957b2b4ea88587b46cf49d1dc17681c1e672864fd7af32fc1e9664d572b3458"}, + {file = "black-22.10.0.tar.gz", hash = "sha256:f513588da599943e0cde4e32cc9879e825d58720d6557062d1098c5ad80080e1"}, ] bleach = [ {file = "bleach-5.0.1-py3-none-any.whl", hash = "sha256:085f7f33c15bd408dd9b17a4ad77c577db66d76203e5984b1bd59baeee948b2a"}, {file = "bleach-5.0.1.tar.gz", hash = "sha256:0d03255c47eb9bd2f26aa9bb7f2107732e7e8fe195ca2f64709fcf3b0a4a085c"}, ] -Brotli = [ +brotli = [ {file = "Brotli-1.0.9-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:268fe94547ba25b58ebc724680609c8ee3e5a843202e9a381f6f9c5e8bdb5c70"}, {file = "Brotli-1.0.9-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:c2415d9d082152460f2bd4e382a1e85aed233abc92db5a3880da2257dc7daf7b"}, {file = "Brotli-1.0.9-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5913a1177fc36e30fcf6dc868ce23b0453952c78c04c266d3149b3d39e1410d6"}, @@ -2039,50 +2112,50 @@ click = [ {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, ] colorama = [ - {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, - {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] commonmark = [ {file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"}, {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, ] cryptography = [ - {file = "cryptography-38.0.1-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:10d1f29d6292fc95acb597bacefd5b9e812099d75a6469004fd38ba5471a977f"}, - {file = "cryptography-38.0.1-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:3fc26e22840b77326a764ceb5f02ca2d342305fba08f002a8c1f139540cdfaad"}, - {file = "cryptography-38.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:3b72c360427889b40f36dc214630e688c2fe03e16c162ef0aa41da7ab1455153"}, - {file = "cryptography-38.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:194044c6b89a2f9f169df475cc167f6157eb9151cc69af8a2a163481d45cc407"}, - {file = "cryptography-38.0.1-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca9f6784ea96b55ff41708b92c3f6aeaebde4c560308e5fbbd3173fbc466e94e"}, - {file = "cryptography-38.0.1-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:16fa61e7481f4b77ef53991075de29fc5bacb582a1244046d2e8b4bb72ef66d0"}, - {file = "cryptography-38.0.1-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d4ef6cc305394ed669d4d9eebf10d3a101059bdcf2669c366ec1d14e4fb227bd"}, - {file = "cryptography-38.0.1-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3261725c0ef84e7592597606f6583385fed2a5ec3909f43bc475ade9729a41d6"}, - {file = "cryptography-38.0.1-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:0297ffc478bdd237f5ca3a7dc96fc0d315670bfa099c04dc3a4a2172008a405a"}, - {file = "cryptography-38.0.1-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:89ed49784ba88c221756ff4d4755dbc03b3c8d2c5103f6d6b4f83a0fb1e85294"}, - {file = "cryptography-38.0.1-cp36-abi3-win32.whl", hash = "sha256:ac7e48f7e7261207d750fa7e55eac2d45f720027d5703cd9007e9b37bbb59ac0"}, - {file = "cryptography-38.0.1-cp36-abi3-win_amd64.whl", hash = "sha256:ad7353f6ddf285aeadfaf79e5a6829110106ff8189391704c1d8801aa0bae45a"}, - {file = "cryptography-38.0.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:896dd3a66959d3a5ddcfc140a53391f69ff1e8f25d93f0e2e7830c6de90ceb9d"}, - {file = "cryptography-38.0.1-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:d3971e2749a723e9084dd507584e2a2761f78ad2c638aa31e80bc7a15c9db4f9"}, - {file = "cryptography-38.0.1-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:79473cf8a5cbc471979bd9378c9f425384980fcf2ab6534b18ed7d0d9843987d"}, - {file = "cryptography-38.0.1-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:d9e69ae01f99abe6ad646947bba8941e896cb3aa805be2597a0400e0764b5818"}, - {file = "cryptography-38.0.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5067ee7f2bce36b11d0e334abcd1ccf8c541fc0bbdaf57cdd511fdee53e879b6"}, - {file = "cryptography-38.0.1-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:3e3a2599e640927089f932295a9a247fc40a5bdf69b0484532f530471a382750"}, - {file = "cryptography-38.0.1-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c2e5856248a416767322c8668ef1845ad46ee62629266f84a8f007a317141013"}, - {file = "cryptography-38.0.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:64760ba5331e3f1794d0bcaabc0d0c39e8c60bf67d09c93dc0e54189dfd7cfe5"}, - {file = "cryptography-38.0.1-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b6c9b706316d7b5a137c35e14f4103e2115b088c412140fdbd5f87c73284df61"}, - {file = "cryptography-38.0.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0163a849b6f315bf52815e238bc2b2346604413fa7c1601eea84bcddb5fb9ac"}, - {file = "cryptography-38.0.1-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:d1a5bd52d684e49a36582193e0b89ff267704cd4025abefb9e26803adeb3e5fb"}, - {file = "cryptography-38.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:765fa194a0f3372d83005ab83ab35d7c5526c4e22951e46059b8ac678b44fa5a"}, - {file = "cryptography-38.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:52e7bee800ec869b4031093875279f1ff2ed12c1e2f74923e8f49c916afd1d3b"}, - {file = "cryptography-38.0.1.tar.gz", hash = "sha256:1db3d807a14931fa317f96435695d9ec386be7b84b618cc61cfa5d08b0ae33d7"}, + {file = "cryptography-38.0.3-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:984fe150f350a3c91e84de405fe49e688aa6092b3525f407a18b9646f6612320"}, + {file = "cryptography-38.0.3-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:ed7b00096790213e09eb11c97cc6e2b757f15f3d2f85833cd2d3ec3fe37c1722"}, + {file = "cryptography-38.0.3-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:bbf203f1a814007ce24bd4d51362991d5cb90ba0c177a9c08825f2cc304d871f"}, + {file = "cryptography-38.0.3-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:554bec92ee7d1e9d10ded2f7e92a5d70c1f74ba9524947c0ba0c850c7b011828"}, + {file = "cryptography-38.0.3-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1b52c9e5f8aa2b802d48bd693190341fae201ea51c7a167d69fc48b60e8a959"}, + {file = "cryptography-38.0.3-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:728f2694fa743a996d7784a6194da430f197d5c58e2f4e278612b359f455e4a2"}, + {file = "cryptography-38.0.3-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:dfb4f4dd568de1b6af9f4cda334adf7d72cf5bc052516e1b2608b683375dd95c"}, + {file = "cryptography-38.0.3-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:5419a127426084933076132d317911e3c6eb77568a1ce23c3ac1e12d111e61e0"}, + {file = "cryptography-38.0.3-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:9b24bcff7853ed18a63cfb0c2b008936a9554af24af2fb146e16d8e1aed75748"}, + {file = "cryptography-38.0.3-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:25c1d1f19729fb09d42e06b4bf9895212292cb27bb50229f5aa64d039ab29146"}, + {file = "cryptography-38.0.3-cp36-abi3-win32.whl", hash = "sha256:7f836217000342d448e1c9a342e9163149e45d5b5eca76a30e84503a5a96cab0"}, + {file = "cryptography-38.0.3-cp36-abi3-win_amd64.whl", hash = "sha256:c46837ea467ed1efea562bbeb543994c2d1f6e800785bd5a2c98bc096f5cb220"}, + {file = "cryptography-38.0.3-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06fc3cc7b6f6cca87bd56ec80a580c88f1da5306f505876a71c8cfa7050257dd"}, + {file = "cryptography-38.0.3-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:65535bc550b70bd6271984d9863a37741352b4aad6fb1b3344a54e6950249b55"}, + {file = "cryptography-38.0.3-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:5e89468fbd2fcd733b5899333bc54d0d06c80e04cd23d8c6f3e0542358c6060b"}, + {file = "cryptography-38.0.3-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:6ab9516b85bebe7aa83f309bacc5f44a61eeb90d0b4ec125d2d003ce41932d36"}, + {file = "cryptography-38.0.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:068147f32fa662c81aebab95c74679b401b12b57494872886eb5c1139250ec5d"}, + {file = "cryptography-38.0.3-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:402852a0aea73833d982cabb6d0c3bb582c15483d29fb7085ef2c42bfa7e38d7"}, + {file = "cryptography-38.0.3-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b1b35d9d3a65542ed2e9d90115dfd16bbc027b3f07ee3304fc83580f26e43249"}, + {file = "cryptography-38.0.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:6addc3b6d593cd980989261dc1cce38263c76954d758c3c94de51f1e010c9a50"}, + {file = "cryptography-38.0.3-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:be243c7e2bfcf6cc4cb350c0d5cdf15ca6383bbcb2a8ef51d3c9411a9d4386f0"}, + {file = "cryptography-38.0.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78cf5eefac2b52c10398a42765bfa981ce2372cbc0457e6bf9658f41ec3c41d8"}, + {file = "cryptography-38.0.3-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:4e269dcd9b102c5a3d72be3c45d8ce20377b8076a43cbed6f660a1afe365e436"}, + {file = "cryptography-38.0.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:8d41a46251bf0634e21fac50ffd643216ccecfaf3701a063257fe0b2be1b6548"}, + {file = "cryptography-38.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:785e4056b5a8b28f05a533fab69febf5004458e20dad7e2e13a3120d8ecec75a"}, + {file = "cryptography-38.0.3.tar.gz", hash = "sha256:bfbe6ee19615b07a98b1d2287d6a6073f734735b49ee45b11324d85efc4d5cbd"}, ] cssselect = [ - {file = "cssselect-1.1.0-py2.py3-none-any.whl", hash = "sha256:f612ee47b749c877ebae5bb77035d8f4202c6ad0f0fc1271b3c18ad6c4468ecf"}, - {file = "cssselect-1.1.0.tar.gz", hash = "sha256:f95f8dedd925fd8f54edb3d2dfb44c190d9d18512377d3c1e2388d16126879bc"}, + {file = "cssselect-1.2.0-py2.py3-none-any.whl", hash = "sha256:da1885f0c10b60c03ed5eccbb6b68d6eff248d91976fcde348f395d54c9fd35e"}, + {file = "cssselect-1.2.0.tar.gz", hash = "sha256:666b19839cfaddb9ce9d36bfe4c969132c647b92fc9088c4e23f786b30f1b3dc"}, ] defusedxml = [ {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"}, {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, ] -Deprecated = [ +deprecated = [ {file = "Deprecated-1.2.13-py2.py3-none-any.whl", hash = "sha256:64756e3e14c8c5eea9795d93c524551432a0be75629f8f29e67ab8caf076c76d"}, {file = "Deprecated-1.2.13.tar.gz", hash = "sha256:43ac5335da90c31c24ba028af536a91d41d53f9e6901ddb021bcc572ce44e38d"}, ] @@ -2094,49 +2167,53 @@ docutils = [ {file = "docutils-0.19-py3-none-any.whl", hash = "sha256:5e1de4d849fee02c63b040a4a3fd567f4ab104defd8a5511fbbc24a8a017efbc"}, {file = "docutils-0.19.tar.gz", hash = "sha256:33995a6753c30b7f577febfc2c50411fec6aac7f7ffeb7c4cfe5991072dcf9e6"}, ] +elementpath = [ + {file = "elementpath-3.0.2-py3-none-any.whl", hash = "sha256:6122419481a4c73101918714274b2cec907feecd04a44b623b4bae4292853328"}, + {file = "elementpath-3.0.2.tar.gz", hash = "sha256:cca18742dc0f354f79874c41a906e6ce4cc15230b7858d22a861e1ec5946940f"}, +] exceptiongroup = [ - {file = "exceptiongroup-1.0.0rc9-py3-none-any.whl", hash = "sha256:2e3c3fc1538a094aab74fad52d6c33fc94de3dfee3ee01f187c0e0c72aec5337"}, - {file = "exceptiongroup-1.0.0rc9.tar.gz", hash = "sha256:9086a4a21ef9b31c72181c77c040a074ba0889ee56a7b289ff0afb0d97655f96"}, + {file = "exceptiongroup-1.0.0-py3-none-any.whl", hash = "sha256:2ac84b496be68464a2da60da518af3785fff8b7ec0d090a581604bc870bdee41"}, + {file = "exceptiongroup-1.0.0.tar.gz", hash = "sha256:affbabf13fb6e98988c38d9c5650e701569fe3c1de3233cfb61c5f33774690ad"}, ] filelock = [ {file = "filelock-3.8.0-py3-none-any.whl", hash = "sha256:617eb4e5eedc82fc5f47b6d61e4d11cb837c56cb4544e39081099fa17ad109d4"}, {file = "filelock-3.8.0.tar.gz", hash = "sha256:55447caa666f2198c5b6b13a26d2084d26fa5b115c00d065664b2124680c4edc"}, ] -Flask = [ +flask = [ {file = "Flask-2.1.3-py3-none-any.whl", hash = "sha256:9013281a7402ad527f8fd56375164f3aa021ecfaff89bfe3825346c24f87e04c"}, {file = "Flask-2.1.3.tar.gz", hash = "sha256:15972e5017df0575c3d6c090ba168b6db90259e620ac8d7ea813a396bad5b6cb"}, ] -Flask-Assets = [ +flask-assets = [ {file = "Flask-Assets-2.0.tar.gz", hash = "sha256:1dfdea35e40744d46aada72831f7613d67bf38e8b20ccaaa9e91fdc37aa3b8c2"}, {file = "Flask_Assets-2.0-py3-none-any.whl", hash = "sha256:2845bd3b479be9db8556801e7ebc2746ce2d9edb4e7b64a1c786ecbfc1e5867b"}, ] -Flask-Caching = [ +flask-caching = [ {file = "Flask-Caching-2.0.1.tar.gz", hash = "sha256:10df200a03f032af60077befe41779dd94898b67c82040d34e87210b71ba2638"}, {file = "Flask_Caching-2.0.1-py3-none-any.whl", hash = "sha256:703df847cbe904d8ddffd5f5fb320e236a31cb7bebac4a93d6b1701dd16dbf37"}, ] -Flask-Compress = [ +flask-compress = [ {file = "Flask-Compress-1.13.tar.gz", hash = "sha256:ee96f18bf9b00f2deb4e3406ca4a05093aa80e2ef0578525a3b4d32ecdff129d"}, {file = "Flask_Compress-1.13-py3-none-any.whl", hash = "sha256:1128f71fbd788393ce26830c51f8b5a1a7a4d085e79a21a5cddf4c057dcd559b"}, ] -Flask-Migrate = [ +flask-migrate = [ {file = "Flask-Migrate-3.1.0.tar.gz", hash = "sha256:57d6060839e3a7f150eaab6fe4e726d9e3e7cffe2150fb223d73f92421c6d1d9"}, {file = "Flask_Migrate-3.1.0-py3-none-any.whl", hash = "sha256:a6498706241aba6be7a251078de9cf166d74307bca41a4ca3e403c9d39e2f897"}, ] flask-oidc = [ {file = "flask-oidc-1.4.0.tar.gz", hash = "sha256:0c12151139d47a562e1c5ae203fb9dbc759fe7474cc01e0238bef828ece58f4e"}, ] -Flask-OpenID = [ +flask-openid = [ {file = "Flask-OpenID-1.3.0.tar.gz", hash = "sha256:539289ed2d19af61ae38d8fe46aec9e4de2b56f9f8b46da0b98c0d387f1d975a"}, {file = "Flask_OpenID-1.3.0-py3-none-any.whl", hash = "sha256:2d4560721b7bf2d014caf5180b3b10b746d221c534d2e63c4469f83af25f9791"}, ] -Flask-SQLAlchemy = [ +flask-sqlalchemy = [ {file = "Flask-SQLAlchemy-2.5.1.tar.gz", hash = "sha256:2bda44b43e7cacb15d4e05ff3cc1f8bc97936cc464623424102bfc2c35e95912"}, {file = "Flask_SQLAlchemy-2.5.1-py2.py3-none-any.whl", hash = "sha256:f12c3d4cc5cc7fdcc148b9527ea05671718c3ea45d50c7e732cceb33f574b390"}, ] -Flask-Testing = [ +flask-testing = [ {file = "Flask-Testing-0.8.1.tar.gz", hash = "sha256:0a734d7b68e63a9410b413cd7b1f96456f9a858bd09a6222d465650cc782eb01"}, ] -Flask-WTF = [ +flask-wtf = [ {file = "Flask-WTF-1.0.1.tar.gz", hash = "sha256:34fe5c6fee0f69b50e30f81a3b7ea16aa1492a771fe9ad0974d164610c09a6c9"}, {file = "Flask_WTF-1.0.1-py3-none-any.whl", hash = "sha256:9d733658c80be551ce7d5bc13c7a7ac0d80df509be1e23827c847d9520f4359a"}, ] @@ -2176,60 +2253,72 @@ gevent = [ {file = "gevent-21.12.0.tar.gz", hash = "sha256:f48b64578c367b91fa793bf8eaaaf4995cb93c8bc45860e473bf868070ad094e"}, ] greenlet = [ - {file = "greenlet-1.1.3-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:8c287ae7ac921dfde88b1c125bd9590b7ec3c900c2d3db5197f1286e144e712b"}, - {file = "greenlet-1.1.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:870a48007872d12e95a996fca3c03a64290d3ea2e61076aa35d3b253cf34cd32"}, - {file = "greenlet-1.1.3-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:7c5227963409551ae4a6938beb70d56bf1918c554a287d3da6853526212fbe0a"}, - {file = "greenlet-1.1.3-cp27-cp27m-win32.whl", hash = "sha256:9fae214f6c43cd47f7bef98c56919b9222481e833be2915f6857a1e9e8a15318"}, - {file = "greenlet-1.1.3-cp27-cp27m-win_amd64.whl", hash = "sha256:de431765bd5fe62119e0bc6bc6e7b17ac53017ae1782acf88fcf6b7eae475a49"}, - {file = "greenlet-1.1.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:510c3b15587afce9800198b4b142202b323bf4b4b5f9d6c79cb9a35e5e3c30d2"}, - {file = "greenlet-1.1.3-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:9951dcbd37850da32b2cb6e391f621c1ee456191c6ae5528af4a34afe357c30e"}, - {file = "greenlet-1.1.3-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:07c58e169bbe1e87b8bbf15a5c1b779a7616df9fd3e61cadc9d691740015b4f8"}, - {file = "greenlet-1.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df02fdec0c533301497acb0bc0f27f479a3a63dcdc3a099ae33a902857f07477"}, - {file = "greenlet-1.1.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9c88e134d51d5e82315a7c32b914a58751b7353eb5268dbd02eabf020b4c4700"}, - {file = "greenlet-1.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b41d19c0cfe5c259fe6c539fd75051cd39a5d33d05482f885faf43f7f5e7d26"}, - {file = "greenlet-1.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:6f5d4b2280ceea76c55c893827961ed0a6eadd5a584a7c4e6e6dd7bc10dfdd96"}, - {file = "greenlet-1.1.3-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:184416e481295832350a4bf731ba619a92f5689bf5d0fa4341e98b98b1265bd7"}, - {file = "greenlet-1.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd0404d154084a371e6d2bafc787201612a1359c2dee688ae334f9118aa0bf47"}, - {file = "greenlet-1.1.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7a43bbfa9b6cfdfaeefbd91038dde65ea2c421dc387ed171613df340650874f2"}, - {file = "greenlet-1.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce5b64dfe8d0cca407d88b0ee619d80d4215a2612c1af8c98a92180e7109f4b5"}, - {file = "greenlet-1.1.3-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:903fa5716b8fbb21019268b44f73f3748c41d1a30d71b4a49c84b642c2fed5fa"}, - {file = "greenlet-1.1.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:0118817c9341ef2b0f75f5af79ac377e4da6ff637e5ee4ac91802c0e379dadb4"}, - {file = "greenlet-1.1.3-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:466ce0928e33421ee84ae04c4ac6f253a3a3e6b8d600a79bd43fd4403e0a7a76"}, - {file = "greenlet-1.1.3-cp35-cp35m-win32.whl", hash = "sha256:65ad1a7a463a2a6f863661329a944a5802c7129f7ad33583dcc11069c17e622c"}, - {file = "greenlet-1.1.3-cp35-cp35m-win_amd64.whl", hash = "sha256:7532a46505470be30cbf1dbadb20379fb481244f1ca54207d7df3bf0bbab6a20"}, - {file = "greenlet-1.1.3-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:caff52cb5cd7626872d9696aee5b794abe172804beb7db52eed1fd5824b63910"}, - {file = "greenlet-1.1.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:db41f3845eb579b544c962864cce2c2a0257fe30f0f1e18e51b1e8cbb4e0ac6d"}, - {file = "greenlet-1.1.3-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:e8533f5111704d75de3139bf0b8136d3a6c1642c55c067866fa0a51c2155ee33"}, - {file = "greenlet-1.1.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9537e4baf0db67f382eb29255a03154fcd4984638303ff9baaa738b10371fa57"}, - {file = "greenlet-1.1.3-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8bfd36f368efe0ab2a6aa3db7f14598aac454b06849fb633b762ddbede1db90"}, - {file = "greenlet-1.1.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0877a9a2129a2c56a2eae2da016743db7d9d6a05d5e1c198f1b7808c602a30e"}, - {file = "greenlet-1.1.3-cp36-cp36m-win32.whl", hash = "sha256:88b04e12c9b041a1e0bcb886fec709c488192638a9a7a3677513ac6ba81d8e79"}, - {file = "greenlet-1.1.3-cp36-cp36m-win_amd64.whl", hash = "sha256:4f166b4aca8d7d489e82d74627a7069ab34211ef5ebb57c300ec4b9337b60fc0"}, - {file = "greenlet-1.1.3-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:cd16a89efe3a003029c87ff19e9fba635864e064da646bc749fc1908a4af18f3"}, - {file = "greenlet-1.1.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5b756e6730ea59b2745072e28ad27f4c837084688e6a6b3633c8b1e509e6ae0e"}, - {file = "greenlet-1.1.3-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:9b2f7d0408ddeb8ea1fd43d3db79a8cefaccadd2a812f021333b338ed6b10aba"}, - {file = "greenlet-1.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44b4817c34c9272c65550b788913620f1fdc80362b209bc9d7dd2f40d8793080"}, - {file = "greenlet-1.1.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d58a5a71c4c37354f9e0c24c9c8321f0185f6945ef027460b809f4bb474bfe41"}, - {file = "greenlet-1.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1dd51d2650e70c6c4af37f454737bf4a11e568945b27f74b471e8e2a9fd21268"}, - {file = "greenlet-1.1.3-cp37-cp37m-win32.whl", hash = "sha256:048d2bed76c2aa6de7af500ae0ea51dd2267aec0e0f2a436981159053d0bc7cc"}, - {file = "greenlet-1.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:77e41db75f9958f2083e03e9dd39da12247b3430c92267df3af77c83d8ff9eed"}, - {file = "greenlet-1.1.3-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:1626185d938d7381631e48e6f7713e8d4b964be246073e1a1d15c2f061ac9f08"}, - {file = "greenlet-1.1.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:1ec2779774d8e42ed0440cf8bc55540175187e8e934f2be25199bf4ed948cd9e"}, - {file = "greenlet-1.1.3-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:f2f908239b7098799b8845e5936c2ccb91d8c2323be02e82f8dcb4a80dcf4a25"}, - {file = "greenlet-1.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b181e9aa6cb2f5ec0cacc8cee6e5a3093416c841ba32c185c30c160487f0380"}, - {file = "greenlet-1.1.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2cf45e339cabea16c07586306a31cfcc5a3b5e1626d365714d283732afed6809"}, - {file = "greenlet-1.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6200a11f003ec26815f7e3d2ded01b43a3810be3528dd760d2f1fa777490c3cd"}, - {file = "greenlet-1.1.3-cp38-cp38-win32.whl", hash = "sha256:db5b25265010a1b3dca6a174a443a0ed4c4ab12d5e2883a11c97d6e6d59b12f9"}, - {file = "greenlet-1.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:095a980288fe05adf3d002fbb180c99bdcf0f930e220aa66fcd56e7914a38202"}, - {file = "greenlet-1.1.3-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:cbc1eb55342cbac8f7ec159088d54e2cfdd5ddf61c87b8bbe682d113789331b2"}, - {file = "greenlet-1.1.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:694ffa7144fa5cc526c8f4512665003a39fa09ef00d19bbca5c8d3406db72fbe"}, - {file = "greenlet-1.1.3-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:aa741c1a8a8cc25eb3a3a01a62bdb5095a773d8c6a86470bde7f607a447e7905"}, - {file = "greenlet-1.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3a669f11289a8995d24fbfc0e63f8289dd03c9aaa0cc8f1eab31d18ca61a382"}, - {file = "greenlet-1.1.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76a53bfa10b367ee734b95988bd82a9a5f0038a25030f9f23bbbc005010ca600"}, - {file = "greenlet-1.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fb0aa7f6996879551fd67461d5d3ab0c3c0245da98be90c89fcb7a18d437403"}, - {file = "greenlet-1.1.3-cp39-cp39-win32.whl", hash = "sha256:5fbe1ab72b998ca77ceabbae63a9b2e2dc2d963f4299b9b278252ddba142d3f1"}, - {file = "greenlet-1.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:ffe73f9e7aea404722058405ff24041e59d31ca23d1da0895af48050a07b6932"}, - {file = "greenlet-1.1.3.tar.gz", hash = "sha256:bcb6c6dd1d6be6d38d6db283747d07fda089ff8c559a835236560a4410340455"}, + {file = "greenlet-1.1.3.post0-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:949c9061b8c6d3e6e439466a9be1e787208dec6246f4ec5fffe9677b4c19fcc3"}, + {file = "greenlet-1.1.3.post0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:d7815e1519a8361c5ea2a7a5864945906f8e386fa1bc26797b4d443ab11a4589"}, + {file = "greenlet-1.1.3.post0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:9649891ab4153f217f319914455ccf0b86986b55fc0573ce803eb998ad7d6854"}, + {file = "greenlet-1.1.3.post0-cp27-cp27m-win32.whl", hash = "sha256:11fc7692d95cc7a6a8447bb160d98671ab291e0a8ea90572d582d57361360f05"}, + {file = "greenlet-1.1.3.post0-cp27-cp27m-win_amd64.whl", hash = "sha256:05ae7383f968bba4211b1fbfc90158f8e3da86804878442b4fb6c16ccbcaa519"}, + {file = "greenlet-1.1.3.post0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ccbe7129a282ec5797df0451ca1802f11578be018a32979131065565da89b392"}, + {file = "greenlet-1.1.3.post0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4a8b58232f5b72973350c2b917ea3df0bebd07c3c82a0a0e34775fc2c1f857e9"}, + {file = "greenlet-1.1.3.post0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:f6661b58412879a2aa099abb26d3c93e91dedaba55a6394d1fb1512a77e85de9"}, + {file = "greenlet-1.1.3.post0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c6e942ca9835c0b97814d14f78da453241837419e0d26f7403058e8db3e38f8"}, + {file = "greenlet-1.1.3.post0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a812df7282a8fc717eafd487fccc5ba40ea83bb5b13eb3c90c446d88dbdfd2be"}, + {file = "greenlet-1.1.3.post0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83a7a6560df073ec9de2b7cb685b199dfd12519bc0020c62db9d1bb522f989fa"}, + {file = "greenlet-1.1.3.post0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:17a69967561269b691747e7f436d75a4def47e5efcbc3c573180fc828e176d80"}, + {file = "greenlet-1.1.3.post0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:60839ab4ea7de6139a3be35b77e22e0398c270020050458b3d25db4c7c394df5"}, + {file = "greenlet-1.1.3.post0-cp310-cp310-win_amd64.whl", hash = "sha256:8926a78192b8b73c936f3e87929931455a6a6c6c385448a07b9f7d1072c19ff3"}, + {file = "greenlet-1.1.3.post0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:c6f90234e4438062d6d09f7d667f79edcc7c5e354ba3a145ff98176f974b8132"}, + {file = "greenlet-1.1.3.post0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:814f26b864ed2230d3a7efe0336f5766ad012f94aad6ba43a7c54ca88dd77cba"}, + {file = "greenlet-1.1.3.post0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8fda1139d87ce5f7bd80e80e54f9f2c6fe2f47983f1a6f128c47bf310197deb6"}, + {file = "greenlet-1.1.3.post0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0643250dd0756f4960633f5359884f609a234d4066686754e834073d84e9b51"}, + {file = "greenlet-1.1.3.post0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:cb863057bed786f6622982fb8b2c122c68e6e9eddccaa9fa98fd937e45ee6c4f"}, + {file = "greenlet-1.1.3.post0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8c0581077cf2734569f3e500fab09c0ff6a2ab99b1afcacbad09b3c2843ae743"}, + {file = "greenlet-1.1.3.post0-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:695d0d8b5ae42c800f1763c9fce9d7b94ae3b878919379150ee5ba458a460d57"}, + {file = "greenlet-1.1.3.post0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:5662492df0588a51d5690f6578f3bbbd803e7f8d99a99f3bf6128a401be9c269"}, + {file = "greenlet-1.1.3.post0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:bffba15cff4802ff493d6edcf20d7f94ab1c2aee7cfc1e1c7627c05f1102eee8"}, + {file = "greenlet-1.1.3.post0-cp35-cp35m-win32.whl", hash = "sha256:7afa706510ab079fd6d039cc6e369d4535a48e202d042c32e2097f030a16450f"}, + {file = "greenlet-1.1.3.post0-cp35-cp35m-win_amd64.whl", hash = "sha256:3a24f3213579dc8459e485e333330a921f579543a5214dbc935bc0763474ece3"}, + {file = "greenlet-1.1.3.post0-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:64e10f303ea354500c927da5b59c3802196a07468332d292aef9ddaca08d03dd"}, + {file = "greenlet-1.1.3.post0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:eb6ac495dccb1520667cfea50d89e26f9ffb49fa28496dea2b95720d8b45eb54"}, + {file = "greenlet-1.1.3.post0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:88720794390002b0c8fa29e9602b395093a9a766b229a847e8d88349e418b28a"}, + {file = "greenlet-1.1.3.post0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39464518a2abe9c505a727af7c0b4efff2cf242aa168be5f0daa47649f4d7ca8"}, + {file = "greenlet-1.1.3.post0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0914f02fcaa8f84f13b2df4a81645d9e82de21ed95633765dd5cc4d3af9d7403"}, + {file = "greenlet-1.1.3.post0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96656c5f7c95fc02c36d4f6ef32f4e94bb0b6b36e6a002c21c39785a4eec5f5d"}, + {file = "greenlet-1.1.3.post0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:4f74aa0092602da2069df0bc6553919a15169d77bcdab52a21f8c5242898f519"}, + {file = "greenlet-1.1.3.post0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:3aeac044c324c1a4027dca0cde550bd83a0c0fbff7ef2c98df9e718a5086c194"}, + {file = "greenlet-1.1.3.post0-cp36-cp36m-win32.whl", hash = "sha256:fe7c51f8a2ab616cb34bc33d810c887e89117771028e1e3d3b77ca25ddeace04"}, + {file = "greenlet-1.1.3.post0-cp36-cp36m-win_amd64.whl", hash = "sha256:70048d7b2c07c5eadf8393e6398595591df5f59a2f26abc2f81abca09610492f"}, + {file = "greenlet-1.1.3.post0-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:66aa4e9a726b70bcbfcc446b7ba89c8cec40f405e51422c39f42dfa206a96a05"}, + {file = "greenlet-1.1.3.post0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:025b8de2273d2809f027d347aa2541651d2e15d593bbce0d5f502ca438c54136"}, + {file = "greenlet-1.1.3.post0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:82a38d7d2077128a017094aff334e67e26194f46bd709f9dcdacbf3835d47ef5"}, + {file = "greenlet-1.1.3.post0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7d20c3267385236b4ce54575cc8e9f43e7673fc761b069c820097092e318e3b"}, + {file = "greenlet-1.1.3.post0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c8ece5d1a99a2adcb38f69af2f07d96fb615415d32820108cd340361f590d128"}, + {file = "greenlet-1.1.3.post0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2794eef1b04b5ba8948c72cc606aab62ac4b0c538b14806d9c0d88afd0576d6b"}, + {file = "greenlet-1.1.3.post0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:a8d24eb5cb67996fb84633fdc96dbc04f2d8b12bfcb20ab3222d6be271616b67"}, + {file = "greenlet-1.1.3.post0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0120a879aa2b1ac5118bce959ea2492ba18783f65ea15821680a256dfad04754"}, + {file = "greenlet-1.1.3.post0-cp37-cp37m-win32.whl", hash = "sha256:bef49c07fcb411c942da6ee7d7ea37430f830c482bf6e4b72d92fd506dd3a427"}, + {file = "greenlet-1.1.3.post0-cp37-cp37m-win_amd64.whl", hash = "sha256:62723e7eb85fa52e536e516ee2ac91433c7bb60d51099293671815ff49ed1c21"}, + {file = "greenlet-1.1.3.post0-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:d25cdedd72aa2271b984af54294e9527306966ec18963fd032cc851a725ddc1b"}, + {file = "greenlet-1.1.3.post0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:924df1e7e5db27d19b1359dc7d052a917529c95ba5b8b62f4af611176da7c8ad"}, + {file = "greenlet-1.1.3.post0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:ec615d2912b9ad807afd3be80bf32711c0ff9c2b00aa004a45fd5d5dde7853d9"}, + {file = "greenlet-1.1.3.post0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0971d37ae0eaf42344e8610d340aa0ad3d06cd2eee381891a10fe771879791f9"}, + {file = "greenlet-1.1.3.post0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:325f272eb997916b4a3fc1fea7313a8adb760934c2140ce13a2117e1b0a8095d"}, + {file = "greenlet-1.1.3.post0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d75afcbb214d429dacdf75e03a1d6d6c5bd1fa9c35e360df8ea5b6270fb2211c"}, + {file = "greenlet-1.1.3.post0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5c2d21c2b768d8c86ad935e404cc78c30d53dea009609c3ef3a9d49970c864b5"}, + {file = "greenlet-1.1.3.post0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:467b73ce5dcd89e381292fb4314aede9b12906c18fab903f995b86034d96d5c8"}, + {file = "greenlet-1.1.3.post0-cp38-cp38-win32.whl", hash = "sha256:8149a6865b14c33be7ae760bcdb73548bb01e8e47ae15e013bf7ef9290ca309a"}, + {file = "greenlet-1.1.3.post0-cp38-cp38-win_amd64.whl", hash = "sha256:104f29dd822be678ef6b16bf0035dcd43206a8a48668a6cae4d2fe9c7a7abdeb"}, + {file = "greenlet-1.1.3.post0-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:c8c9301e3274276d3d20ab6335aa7c5d9e5da2009cccb01127bddb5c951f8870"}, + {file = "greenlet-1.1.3.post0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:8415239c68b2ec9de10a5adf1130ee9cb0ebd3e19573c55ba160ff0ca809e012"}, + {file = "greenlet-1.1.3.post0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:3c22998bfef3fcc1b15694818fc9b1b87c6cc8398198b96b6d355a7bcb8c934e"}, + {file = "greenlet-1.1.3.post0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0aa1845944e62f358d63fcc911ad3b415f585612946b8edc824825929b40e59e"}, + {file = "greenlet-1.1.3.post0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:890f633dc8cb307761ec566bc0b4e350a93ddd77dc172839be122be12bae3e10"}, + {file = "greenlet-1.1.3.post0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cf37343e43404699d58808e51f347f57efd3010cc7cee134cdb9141bd1ad9ea"}, + {file = "greenlet-1.1.3.post0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5edf75e7fcfa9725064ae0d8407c849456553a181ebefedb7606bac19aa1478b"}, + {file = "greenlet-1.1.3.post0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a954002064ee919b444b19c1185e8cce307a1f20600f47d6f4b6d336972c809"}, + {file = "greenlet-1.1.3.post0-cp39-cp39-win32.whl", hash = "sha256:2ccdc818cc106cc238ff7eba0d71b9c77be868fdca31d6c3b1347a54c9b187b2"}, + {file = "greenlet-1.1.3.post0-cp39-cp39-win_amd64.whl", hash = "sha256:91a84faf718e6f8b888ca63d0b2d6d185c8e2a198d2a7322d75c303e7097c8b7"}, + {file = "greenlet-1.1.3.post0.tar.gz", hash = "sha256:f5e09dc5c6e1796969fd4b775ea1417d70e49a5df29aaa8e5d10675d9e11872c"}, ] gunicorn = [ {file = "gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"}, @@ -2244,11 +2333,11 @@ html5lib = [ {file = "html5lib-1.1.tar.gz", hash = "sha256:b2e5b40261e20f354d198eae92afc10d750afb487ed5e50f9c4eaf07c184146f"}, ] httpagentparser = [ - {file = "httpagentparser-1.9.3.tar.gz", hash = "sha256:370f466cbddca498290ea03d94e523c31e0cdb6218f53574d76e93ee1f9140f4"}, + {file = "httpagentparser-1.9.5.tar.gz", hash = "sha256:53cefd9d65990f6fe59c0378cad8ea1b9df8f770d2e8bd9d8762edae033be80a"}, ] httplib2 = [ - {file = "httplib2-0.20.4-py3-none-any.whl", hash = "sha256:8b6a905cb1c79eefd03f8669fd993c36dc341f7c558f056cb5a33b5c2f458543"}, - {file = "httplib2-0.20.4.tar.gz", hash = "sha256:58a98e45b4b1a48273073f905d2961666ecf0fbac4250ea5b47aef259eb5c585"}, + {file = "httplib2-0.21.0-py3-none-any.whl", hash = "sha256:987c8bb3eb82d3fa60c68699510a692aa2ad9c4bd4f123e51dfb1488c14cdd01"}, + {file = "httplib2-0.21.0.tar.gz", hash = "sha256:fc144f091c7286b82bec71bdbd9b27323ba709cc612568d3000893bfd9cb4b34"}, ] humanize = [ {file = "humanize-4.4.0-py3-none-any.whl", hash = "sha256:8830ebf2d65d0395c1bd4c79189ad71e023f277c2c7ae00f263124432e6f2ffa"}, @@ -2270,7 +2359,7 @@ itsdangerous = [ {file = "itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"}, {file = "itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"}, ] -Jinja2 = [ +jinja2 = [ {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, ] @@ -2361,51 +2450,78 @@ lxml = [ {file = "lxml-4.6.5.tar.gz", hash = "sha256:6e84edecc3a82f90d44ddee2ee2a2630d4994b8471816e226d2b771cda7ac4ca"}, ] mailmanclient = [ - {file = "mailmanclient-3.3.3.tar.gz", hash = "sha256:92fe624675e41f41f59de1208e0125dfaa8d062bbe6138bd7cd79e4dd0b6f85e"}, + {file = "mailmanclient-3.3.4.tar.gz", hash = "sha256:d32df51d78ef53f6f0cb4b343dc0ce96b5f545dc934e7938d5c783e00d11e29e"}, ] -Mako = [ +mako = [ {file = "Mako-1.2.3-py3-none-any.whl", hash = "sha256:c413a086e38cd885088d5e165305ee8eed04e8b3f8f62df343480da0a385735f"}, {file = "Mako-1.2.3.tar.gz", hash = "sha256:7fde96466fcfeedb0eed94f187f20b23d85e4cb41444be0e542e2c8c65c396cd"}, ] marisa-trie = [ - {file = "marisa-trie-0.7.7.tar.gz", hash = "sha256:bbeafb7d92839dc221365340e79d012cb50ee48a1f3f30dd916eb35a8b93db00"}, - {file = "marisa_trie-0.7.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7732907dfc1cf954d840cd95e4f63a52c61a9f6e864a3104e0427c4845fcfe82"}, - {file = "marisa_trie-0.7.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f86a718b4023904cf4a9e7ac1d967cb99b540ae05d081cf8b437129031f0845f"}, - {file = "marisa_trie-0.7.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:df23addeb641d3b6a80e4127a8f19074ebf1cbe9eb80206ee99e57da0f51e7f2"}, - {file = "marisa_trie-0.7.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e1f51f814b81847eafc4b622188b453c9fae765ace812caf67da140a6d435f99"}, - {file = "marisa_trie-0.7.7-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8863b9df15abd984d5faf11d129e626255468569ac2c7365519168d68f7dc7eb"}, - {file = "marisa_trie-0.7.7-cp310-cp310-win32.whl", hash = "sha256:5a53632478f839d2d02450ca2108350d983eb61319bb8da4c63d5f8e660ee8a0"}, - {file = "marisa_trie-0.7.7-cp310-cp310-win_amd64.whl", hash = "sha256:65e9ae1c080587dffeec3dd2e7da88120ecb090e6cad2714a8da1a5a7e56ac4f"}, - {file = "marisa_trie-0.7.7-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:62ca721b1be9aa2be1157b386be5dd55c0f78a7b92a25390a6fa663ed47ed222"}, - {file = "marisa_trie-0.7.7-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1c77def45f1daf6bec70af9667092e9d45457f15032a75b0921239792891faf7"}, - {file = "marisa_trie-0.7.7-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:739178a8f98d7584d5613c59a0346273d8a95154b0bcb0eb0bd11d6514a61dff"}, - {file = "marisa_trie-0.7.7-cp36-cp36m-win32.whl", hash = "sha256:64e76e9ed5c7b5929d9c598f343039f75ef4ae1881a8bb1b633d5fe624ae6b84"}, - {file = "marisa_trie-0.7.7-cp36-cp36m-win_amd64.whl", hash = "sha256:8db8e511e2d3b42221de0ed95ed6288ad514df0f7f88d0813990ea871ccd0baf"}, - {file = "marisa_trie-0.7.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:afa06ffe3a992de978582246550f3fe365cf3eaf0468b27e8cdf04ac2b5dbeeb"}, - {file = "marisa_trie-0.7.7-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:55c99a3fd4025a5f428f83ba678912844014806f36684c8690deb59ca08c93af"}, - {file = "marisa_trie-0.7.7-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:332ca107795dd67f21aafbc0141252dc33d1f8f1b4239cc7bb61b67cae33cf30"}, - {file = "marisa_trie-0.7.7-cp37-cp37m-win32.whl", hash = "sha256:150bbc78b98135e9c43eee43ac548fe4393bd069213c5104555e5cc108df0546"}, - {file = "marisa_trie-0.7.7-cp37-cp37m-win_amd64.whl", hash = "sha256:984ec658756a92d72e7bf664be02e6a1949a0e08658d5120e43fd26b9bd49187"}, - {file = "marisa_trie-0.7.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e060334862ba425568478fdbae2ec6599a6e123ac2b4c946b50f5e3417ed2072"}, - {file = "marisa_trie-0.7.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8ae6ef87610489f9b756916145550b99a1a87f672233111c20d7c9c91c3cf7b2"}, - {file = "marisa_trie-0.7.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:09bf4b0e2df7f07e5a59e2e07b6aa9ee781d48be7509eba11087bc2a8e1a94ad"}, - {file = "marisa_trie-0.7.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8df910508422ea2b331d2ce41a2c96f26f110663efb4c318525eedccd7b103af"}, - {file = "marisa_trie-0.7.7-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6a96100cc5cd6f50a1b02e20ece8dd05f476d1deb3062832259d87a6f4729ae4"}, - {file = "marisa_trie-0.7.7-cp38-cp38-win32.whl", hash = "sha256:d3d6e3174d9e9ed73ae9934b0c8e9c630a19a3182dca508825f44e3e5a1641cd"}, - {file = "marisa_trie-0.7.7-cp38-cp38-win_amd64.whl", hash = "sha256:7c978f7959218610780770725eb5bd88451e42f97af47ee220c1ae802fb9e402"}, - {file = "marisa_trie-0.7.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a0614aadb611e11c48d8d4d5710827e5b3a944ebfb4c51a999bfdde133dc4e9b"}, - {file = "marisa_trie-0.7.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:882ba0a90d961f93ec89b36c388dc0a6a7ffb871a6eb283a7a92485987212e22"}, - {file = "marisa_trie-0.7.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9ab818dcd557a6d673bc1f12e1f2dc69e62dacfe7ad444e723410aa745882210"}, - {file = "marisa_trie-0.7.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:16e9147753f2a211f9674e7b866bc931566950a6695d89b0d92bab8f6f34fe2b"}, - {file = "marisa_trie-0.7.7-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:643fb52f6658ef9f77f66b736207acdd4d372272381a95b0ecdc20c98614f9ba"}, - {file = "marisa_trie-0.7.7-cp39-cp39-win32.whl", hash = "sha256:f2540463270999718dc434d733e172b0918541fe123460494e19bcc185dbaefe"}, - {file = "marisa_trie-0.7.7-cp39-cp39-win_amd64.whl", hash = "sha256:a2638e9abd05b9b3cf19adfb30ac503e7fb32247b8defa97c9a5f09fa8330063"}, - {file = "marisa_trie-0.7.7-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:939c14a81c466ebbc0985d1ac2b8f167c52add5231789359e9262d6558be731c"}, - {file = "marisa_trie-0.7.7-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:197b41decda4865b9d6e24f3c0eccebf5804c89d7bbe35c77fee45dba3989c8a"}, - {file = "marisa_trie-0.7.7-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b2e25ec564f51d67e4406cdb52cba4942ebce07cb93b959ddadafbbdcd8523b1"}, - {file = "marisa_trie-0.7.7-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:37d121319813dbe9e9ff27f3a50ccff772d93296b27d6a8e32c08460827f477c"}, -] -MarkupSafe = [ + {file = "marisa-trie-0.7.8.tar.gz", hash = "sha256:aee3de5f2836074cfd803f1caf16f68390f262ef09cd7dc7d0e8aee9b6878643"}, + {file = "marisa_trie-0.7.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2f1cf9d5ead4471b149fdb93a1c84eddaa941d23e67b0782091adc222d198a87"}, + {file = "marisa_trie-0.7.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:73296b4d6d8ce2f6bc3898fe84348756beddb10cb56442391d050bff135e9c4c"}, + {file = "marisa_trie-0.7.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:782c1515caa603656e15779bc61d5db3b079fa4270ad77f464908796e0d940aa"}, + {file = "marisa_trie-0.7.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49131e51aad530e4d47c716cef1bbef15a4e5b8f75bddfcdd7903f5043ef2331"}, + {file = "marisa_trie-0.7.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:45b0a38e015d0149141f028b8892ab518946b828c7931685199549294f5893ca"}, + {file = "marisa_trie-0.7.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a537e0efff1ec880bc212390e97f1d35832a44bd78c96807ddb685d538875096"}, + {file = "marisa_trie-0.7.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5c2a33ede2655f1a6fb840729128cb4bc48829108711f79b7a645b6c0c54b5c2"}, + {file = "marisa_trie-0.7.8-cp310-cp310-win32.whl", hash = "sha256:7200cde8e2040811e98661a60463b296b76a6b224411f8899aa0850085e6af40"}, + {file = "marisa_trie-0.7.8-cp310-cp310-win_amd64.whl", hash = "sha256:a432607bae139183c7251da7eb22f761440bc07d92eacc9e9f7dc0d87f70c495"}, + {file = "marisa_trie-0.7.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a891d2841da153b98c6c7fbe0a89ea8edbc164bdc96a001f360bdcdd54e2070d"}, + {file = "marisa_trie-0.7.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c9ab632c5caef23a59cd43c76ab59e325f9eadd1e9c8b1c34005b9756ae716ee"}, + {file = "marisa_trie-0.7.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:68087942e95acb5801f2a5e9a874aa57af27a4afb52aca81fe1cbe22b2a2fd38"}, + {file = "marisa_trie-0.7.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ef2c4a5023bb6ddbaf1803187b7fb3108e9955aa9c60564504e5f622517c9e7"}, + {file = "marisa_trie-0.7.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:24e873619f61bef6a87c669ae459b79d98822270e8a10b21fc52dddf2acc9a46"}, + {file = "marisa_trie-0.7.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:34189c321f30cefb76a6b20c7f055b3f6cd0bc8378c16ba8b7283fd898bf4ac2"}, + {file = "marisa_trie-0.7.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:396555d5f52dc86c65717052573fa2875e10f9e5dd014f825677beadcaec8248"}, + {file = "marisa_trie-0.7.8-cp311-cp311-win32.whl", hash = "sha256:bfe649b02b6318bac572b86d9ddd8276c594411311f8e5ef2edc4bcd7285a06f"}, + {file = "marisa_trie-0.7.8-cp311-cp311-win_amd64.whl", hash = "sha256:84991b52a187d09b269c4caefc8b857a81156c44997eec7eac0e2862d108cc20"}, + {file = "marisa_trie-0.7.8-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:0555104fe9f414abb12e967322a13df778b21958d1727470f4c8dedfde76a8f2"}, + {file = "marisa_trie-0.7.8-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f96531013252bca14f7665f67aa642be113b6c348ada5e167ebf8db27b1551b5"}, + {file = "marisa_trie-0.7.8-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4ed76391b132c6261cfb402c1a08679e635d09a0a142dae2c1744d816f103c7f"}, + {file = "marisa_trie-0.7.8-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:e6232506b4d66da932f70cf359a4c5ba9e086228ccd97b602159e90c6ea53dab"}, + {file = "marisa_trie-0.7.8-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:34f927f2738d0b402b76821895254e6a164d5020042559f7d910f6632829cdfa"}, + {file = "marisa_trie-0.7.8-cp36-cp36m-win32.whl", hash = "sha256:645908879ae8fcadfb51650fc176902b9e68eee9a8c4d4d8c682cf99ce3ff029"}, + {file = "marisa_trie-0.7.8-cp36-cp36m-win_amd64.whl", hash = "sha256:a5bf2912810e135ce1e60a9b56a179ed62258306103bf5dd3186307f5c51b28f"}, + {file = "marisa_trie-0.7.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:bd86212d5037973deda057fc29d60e83dca05e68fa1e7ceaf014c513975c7a0d"}, + {file = "marisa_trie-0.7.8-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f280f059be417cff81ac030db6a002f8a93093c7ca4555e570d43a24ed45514"}, + {file = "marisa_trie-0.7.8-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1ae35c696f3c5b57c5fe4f73725102f3fe884bc658b854d484dfe6d7e72c86f5"}, + {file = "marisa_trie-0.7.8-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:524c02f398d361aaf85d8f7709b5ac6de68d020c588fb6c087fb171137643c13"}, + {file = "marisa_trie-0.7.8-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:353113e811ccfa176fbb611b83671f0b3b40f46b3896b096c10e43f65d35916d"}, + {file = "marisa_trie-0.7.8-cp37-cp37m-win32.whl", hash = "sha256:93172a7314d4d5993970dbafb746f23140d3abfa0d93cc174e766a302d125f7d"}, + {file = "marisa_trie-0.7.8-cp37-cp37m-win_amd64.whl", hash = "sha256:579d69981b18f427bd8e540199c4de400a2bd4ae98e96c814a12cbf766e7029b"}, + {file = "marisa_trie-0.7.8-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:08858920d0e09ca07d239252884fd72db2abb56c35ff463145ffc9c1277a4f34"}, + {file = "marisa_trie-0.7.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a1b4d07158a3f9b4e84ee709a1fa86b9e11f3dd3b1e6fc45493195105a029545"}, + {file = "marisa_trie-0.7.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f0359f392679774d1ff014f12efdf48da5d661e6241531ff55a3ae5a72a1137e"}, + {file = "marisa_trie-0.7.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c1daaa8c38423fbd119db6654f92740d5ee40d1185a2bbc47afae6712b9ebfc"}, + {file = "marisa_trie-0.7.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:266bf4b6e00b4cff2b8618533919d38b883127f4e5c0af0e0bd78a042093dd99"}, + {file = "marisa_trie-0.7.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:fd7e71d8d85d04d2a5d23611663b2d322b60c98c2edab7e9ef9a2019f7435c5b"}, + {file = "marisa_trie-0.7.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:66b13382be3c277f32143e6c814344118721c7954b2bfb57f5cfe93d17e63c9e"}, + {file = "marisa_trie-0.7.8-cp38-cp38-win32.whl", hash = "sha256:d75b5d642b3d1e47a0ab649fb5eb6bf3681a5e1d3793c8ea7546586ab72731fd"}, + {file = "marisa_trie-0.7.8-cp38-cp38-win_amd64.whl", hash = "sha256:07c14c88fde8a0ac55139f9fe763dc0deabc4b7950047719ae986ca62135e1fb"}, + {file = "marisa_trie-0.7.8-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c8df5238c7b29498f4ee24fd3ee25e0129b3c56beaed1dd1628bce0ebac8ec8c"}, + {file = "marisa_trie-0.7.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db2bdc480d83a1a566b3a64027f9fb34eae98bfe45788c41a45e99d430cbf48a"}, + {file = "marisa_trie-0.7.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:80b22bdbebc3e6677e83db1352e4f6d478364107874c031a34a961437ead4e93"}, + {file = "marisa_trie-0.7.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6412c816be723a0f11dd41225a30a08182cf2b3b7b3c882c44335003bde47003"}, + {file = "marisa_trie-0.7.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fcdb7f802db43857df3825c4c11acd14bb380deb961ff91e260950886531400"}, + {file = "marisa_trie-0.7.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:5cf04156f38dc46f0f14423f98559c5def7d83f3a30f8a580c27ad3b0311ce76"}, + {file = "marisa_trie-0.7.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c53b1d02f4974ecb52c6e8c6f4f1dbf3a15e79bc3861f4ad48b14e4e77c82342"}, + {file = "marisa_trie-0.7.8-cp39-cp39-win32.whl", hash = "sha256:75317347f20bf05ab2ce5537a90989b1439b5e1752f558aad7b5d6b43194429b"}, + {file = "marisa_trie-0.7.8-cp39-cp39-win_amd64.whl", hash = "sha256:82ba3caed5acfdff6a23d6881cc1927776b7320415261b6b24f48d0a190ab890"}, + {file = "marisa_trie-0.7.8-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:43abd082a21295b04859705b088d15acac8956587557680850e3149a79e36789"}, + {file = "marisa_trie-0.7.8-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0d891f0138e5aecc9c5afb7b0a57c758e22c5b5c7c0edb0a1f21ae933259815"}, + {file = "marisa_trie-0.7.8-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9031184fe2215b591a6cdefe5d6d4901806fd7359e813c485a7ff25ea69d603c"}, + {file = "marisa_trie-0.7.8-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:8ccb3ba8a2a589b8a7aed693d564f20a6d3bbbb552975f904ba311cea6b85706"}, + {file = "marisa_trie-0.7.8-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f49a2cba047e643e5cd295d75de59f1df710c5e919cd376ac06ead513439881b"}, + {file = "marisa_trie-0.7.8-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d37ea556bb99d9b0dfbe8fd6bdb17e91b91d04531be9e3b8b1b7b7f76ea55637"}, + {file = "marisa_trie-0.7.8-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:55a5aea422a4c0c9ef143d3703323f2a43b4a5315fc90bbb6e9ff18544b8d931"}, + {file = "marisa_trie-0.7.8-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:d19f363b981fe9b4a302060a8088fd1f00906bc315db24f5d6726b5c309cc47e"}, + {file = "marisa_trie-0.7.8-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e0d51c31fb41b6bc76c1abb7cf2d63a6e0ba7feffc96ea3d92b4d5084d71721a"}, + {file = "marisa_trie-0.7.8-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71ed6286e9d593dac035b8516e7ec35a1b54a7d9c6451a9319e918a8ef722714"}, + {file = "marisa_trie-0.7.8-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc1c1dca06c0fdcca5bb261a09eca2b3bcf41eaeb467caf600ac68e77d3ed2c0"}, + {file = "marisa_trie-0.7.8-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:891be5569cd6e3a059c2de53d63251aaaef513d68e8d2181f71378f9cb69e1ab"}, +] +markupsafe = [ {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"}, @@ -2540,65 +2656,66 @@ pathspec = [ {file = "pathspec-0.10.1-py3-none-any.whl", hash = "sha256:46846318467efc4556ccfd27816e004270a9eeeeb4d062ce5e6fc7a87c573f93"}, {file = "pathspec-0.10.1.tar.gz", hash = "sha256:7ace6161b621d31e7902eb6b5ae148d12cfd23f4a249b9ffb6b9fee12084323d"}, ] -Pillow = [ - {file = "Pillow-9.2.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:a9c9bc489f8ab30906d7a85afac4b4944a572a7432e00698a7239f44a44e6efb"}, - {file = "Pillow-9.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:510cef4a3f401c246cfd8227b300828715dd055463cdca6176c2e4036df8bd4f"}, - {file = "Pillow-9.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7888310f6214f19ab2b6df90f3f06afa3df7ef7355fc025e78a3044737fab1f5"}, - {file = "Pillow-9.2.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:831e648102c82f152e14c1a0938689dbb22480c548c8d4b8b248b3e50967b88c"}, - {file = "Pillow-9.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1cc1d2451e8a3b4bfdb9caf745b58e6c7a77d2e469159b0d527a4554d73694d1"}, - {file = "Pillow-9.2.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:136659638f61a251e8ed3b331fc6ccd124590eeff539de57c5f80ef3a9594e58"}, - {file = "Pillow-9.2.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:6e8c66f70fb539301e064f6478d7453e820d8a2c631da948a23384865cd95544"}, - {file = "Pillow-9.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:37ff6b522a26d0538b753f0b4e8e164fdada12db6c6f00f62145d732d8a3152e"}, - {file = "Pillow-9.2.0-cp310-cp310-win32.whl", hash = "sha256:c79698d4cd9318d9481d89a77e2d3fcaeff5486be641e60a4b49f3d2ecca4e28"}, - {file = "Pillow-9.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:254164c57bab4b459f14c64e93df11eff5ded575192c294a0c49270f22c5d93d"}, - {file = "Pillow-9.2.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:adabc0bce035467fb537ef3e5e74f2847c8af217ee0be0455d4fec8adc0462fc"}, - {file = "Pillow-9.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:336b9036127eab855beec9662ac3ea13a4544a523ae273cbf108b228ecac8437"}, - {file = "Pillow-9.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50dff9cc21826d2977ef2d2a205504034e3a4563ca6f5db739b0d1026658e004"}, - {file = "Pillow-9.2.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb6259196a589123d755380b65127ddc60f4c64b21fc3bb46ce3a6ea663659b0"}, - {file = "Pillow-9.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b0554af24df2bf96618dac71ddada02420f946be943b181108cac55a7a2dcd4"}, - {file = "Pillow-9.2.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:15928f824870535c85dbf949c09d6ae7d3d6ac2d6efec80f3227f73eefba741c"}, - {file = "Pillow-9.2.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:bdd0de2d64688ecae88dd8935012c4a72681e5df632af903a1dca8c5e7aa871a"}, - {file = "Pillow-9.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5b87da55a08acb586bad5c3aa3b86505f559b84f39035b233d5bf844b0834b1"}, - {file = "Pillow-9.2.0-cp311-cp311-win32.whl", hash = "sha256:b6d5e92df2b77665e07ddb2e4dbd6d644b78e4c0d2e9272a852627cdba0d75cf"}, - {file = "Pillow-9.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:6bf088c1ce160f50ea40764f825ec9b72ed9da25346216b91361eef8ad1b8f8c"}, - {file = "Pillow-9.2.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:2c58b24e3a63efd22554c676d81b0e57f80e0a7d3a5874a7e14ce90ec40d3069"}, - {file = "Pillow-9.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eef7592281f7c174d3d6cbfbb7ee5984a671fcd77e3fc78e973d492e9bf0eb3f"}, - {file = "Pillow-9.2.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dcd7b9c7139dc8258d164b55696ecd16c04607f1cc33ba7af86613881ffe4ac8"}, - {file = "Pillow-9.2.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a138441e95562b3c078746a22f8fca8ff1c22c014f856278bdbdd89ca36cff1b"}, - {file = "Pillow-9.2.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:93689632949aff41199090eff5474f3990b6823404e45d66a5d44304e9cdc467"}, - {file = "Pillow-9.2.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:f3fac744f9b540148fa7715a435d2283b71f68bfb6d4aae24482a890aed18b59"}, - {file = "Pillow-9.2.0-cp37-cp37m-win32.whl", hash = "sha256:fa768eff5f9f958270b081bb33581b4b569faabf8774726b283edb06617101dc"}, - {file = "Pillow-9.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:69bd1a15d7ba3694631e00df8de65a8cb031911ca11f44929c97fe05eb9b6c1d"}, - {file = "Pillow-9.2.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:030e3460861488e249731c3e7ab59b07c7853838ff3b8e16aac9561bb345da14"}, - {file = "Pillow-9.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:74a04183e6e64930b667d321524e3c5361094bb4af9083db5c301db64cd341f3"}, - {file = "Pillow-9.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d33a11f601213dcd5718109c09a52c2a1c893e7461f0be2d6febc2879ec2402"}, - {file = "Pillow-9.2.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fd6f5e3c0e4697fa7eb45b6e93996299f3feee73a3175fa451f49a74d092b9f"}, - {file = "Pillow-9.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a647c0d4478b995c5e54615a2e5360ccedd2f85e70ab57fbe817ca613d5e63b8"}, - {file = "Pillow-9.2.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:4134d3f1ba5f15027ff5c04296f13328fecd46921424084516bdb1b2548e66ff"}, - {file = "Pillow-9.2.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:bc431b065722a5ad1dfb4df354fb9333b7a582a5ee39a90e6ffff688d72f27a1"}, - {file = "Pillow-9.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:1536ad017a9f789430fb6b8be8bf99d2f214c76502becc196c6f2d9a75b01b76"}, - {file = "Pillow-9.2.0-cp38-cp38-win32.whl", hash = "sha256:2ad0d4df0f5ef2247e27fc790d5c9b5a0af8ade9ba340db4a73bb1a4a3e5fb4f"}, - {file = "Pillow-9.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:ec52c351b35ca269cb1f8069d610fc45c5bd38c3e91f9ab4cbbf0aebc136d9c8"}, - {file = "Pillow-9.2.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0ed2c4ef2451de908c90436d6e8092e13a43992f1860275b4d8082667fbb2ffc"}, - {file = "Pillow-9.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ad2f835e0ad81d1689f1b7e3fbac7b01bb8777d5a985c8962bedee0cc6d43da"}, - {file = "Pillow-9.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea98f633d45f7e815db648fd7ff0f19e328302ac36427343e4432c84432e7ff4"}, - {file = "Pillow-9.2.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7761afe0126d046974a01e030ae7529ed0ca6a196de3ec6937c11df0df1bc91c"}, - {file = "Pillow-9.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a54614049a18a2d6fe156e68e188da02a046a4a93cf24f373bffd977e943421"}, - {file = "Pillow-9.2.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:5aed7dde98403cd91d86a1115c78d8145c83078e864c1de1064f52e6feb61b20"}, - {file = "Pillow-9.2.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:13b725463f32df1bfeacbf3dd197fb358ae8ebcd8c5548faa75126ea425ccb60"}, - {file = "Pillow-9.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:808add66ea764ed97d44dda1ac4f2cfec4c1867d9efb16a33d158be79f32b8a4"}, - {file = "Pillow-9.2.0-cp39-cp39-win32.whl", hash = "sha256:337a74fd2f291c607d220c793a8135273c4c2ab001b03e601c36766005f36885"}, - {file = "Pillow-9.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:fac2d65901fb0fdf20363fbd345c01958a742f2dc62a8dd4495af66e3ff502a4"}, - {file = "Pillow-9.2.0-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:ad2277b185ebce47a63f4dc6302e30f05762b688f8dc3de55dbae4651872cdf3"}, - {file = "Pillow-9.2.0-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c7b502bc34f6e32ba022b4a209638f9e097d7a9098104ae420eb8186217ebbb"}, - {file = "Pillow-9.2.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d1f14f5f691f55e1b47f824ca4fdcb4b19b4323fe43cc7bb105988cad7496be"}, - {file = "Pillow-9.2.0-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:dfe4c1fedfde4e2fbc009d5ad420647f7730d719786388b7de0999bf32c0d9fd"}, - {file = "Pillow-9.2.0-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:f07f1f00e22b231dd3d9b9208692042e29792d6bd4f6639415d2f23158a80013"}, - {file = "Pillow-9.2.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1802f34298f5ba11d55e5bb09c31997dc0c6aed919658dfdf0198a2fe75d5490"}, - {file = "Pillow-9.2.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17d4cafe22f050b46d983b71c707162d63d796a1235cdf8b9d7a112e97b15bac"}, - {file = "Pillow-9.2.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:96b5e6874431df16aee0c1ba237574cb6dff1dcb173798faa6a9d8b399a05d0e"}, - {file = "Pillow-9.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:0030fdbd926fb85844b8b92e2f9449ba89607231d3dd597a21ae72dc7fe26927"}, - {file = "Pillow-9.2.0.tar.gz", hash = "sha256:75e636fd3e0fb872693f23ccb8a5ff2cd578801251f3a4f6854c6a5d437d3c04"}, +pillow = [ + {file = "Pillow-9.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:0b7257127d646ff8676ec8a15520013a698d1fdc48bc2a79ba4e53df792526f2"}, + {file = "Pillow-9.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b90f7616ea170e92820775ed47e136208e04c967271c9ef615b6fbd08d9af0e3"}, + {file = "Pillow-9.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68943d632f1f9e3dce98908e873b3a090f6cba1cbb1b892a9e8d97c938871fbe"}, + {file = "Pillow-9.3.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be55f8457cd1eac957af0c3f5ece7bc3f033f89b114ef30f710882717670b2a8"}, + {file = "Pillow-9.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d77adcd56a42d00cc1be30843d3426aa4e660cab4a61021dc84467123f7a00c"}, + {file = "Pillow-9.3.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:829f97c8e258593b9daa80638aee3789b7df9da5cf1336035016d76f03b8860c"}, + {file = "Pillow-9.3.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:801ec82e4188e935c7f5e22e006d01611d6b41661bba9fe45b60e7ac1a8f84de"}, + {file = "Pillow-9.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:871b72c3643e516db4ecf20efe735deb27fe30ca17800e661d769faab45a18d7"}, + {file = "Pillow-9.3.0-cp310-cp310-win32.whl", hash = "sha256:655a83b0058ba47c7c52e4e2df5ecf484c1b0b0349805896dd350cbc416bdd91"}, + {file = "Pillow-9.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:9f47eabcd2ded7698106b05c2c338672d16a6f2a485e74481f524e2a23c2794b"}, + {file = "Pillow-9.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:57751894f6618fd4308ed8e0c36c333e2f5469744c34729a27532b3db106ee20"}, + {file = "Pillow-9.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7db8b751ad307d7cf238f02101e8e36a128a6cb199326e867d1398067381bff4"}, + {file = "Pillow-9.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3033fbe1feb1b59394615a1cafaee85e49d01b51d54de0cbf6aa8e64182518a1"}, + {file = "Pillow-9.3.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22b012ea2d065fd163ca096f4e37e47cd8b59cf4b0fd47bfca6abb93df70b34c"}, + {file = "Pillow-9.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a65733d103311331875c1dca05cb4606997fd33d6acfed695b1232ba1df193"}, + {file = "Pillow-9.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:502526a2cbfa431d9fc2a079bdd9061a2397b842bb6bc4239bb176da00993812"}, + {file = "Pillow-9.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:90fb88843d3902fe7c9586d439d1e8c05258f41da473952aa8b328d8b907498c"}, + {file = "Pillow-9.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:89dca0ce00a2b49024df6325925555d406b14aa3efc2f752dbb5940c52c56b11"}, + {file = "Pillow-9.3.0-cp311-cp311-win32.whl", hash = "sha256:3168434d303babf495d4ba58fc22d6604f6e2afb97adc6a423e917dab828939c"}, + {file = "Pillow-9.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:18498994b29e1cf86d505edcb7edbe814d133d2232d256db8c7a8ceb34d18cef"}, + {file = "Pillow-9.3.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:772a91fc0e03eaf922c63badeca75e91baa80fe2f5f87bdaed4280662aad25c9"}, + {file = "Pillow-9.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa4107d1b306cdf8953edde0534562607fe8811b6c4d9a486298ad31de733b2"}, + {file = "Pillow-9.3.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b4012d06c846dc2b80651b120e2cdd787b013deb39c09f407727ba90015c684f"}, + {file = "Pillow-9.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77ec3e7be99629898c9a6d24a09de089fa5356ee408cdffffe62d67bb75fdd72"}, + {file = "Pillow-9.3.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:6c738585d7a9961d8c2821a1eb3dcb978d14e238be3d70f0a706f7fa9316946b"}, + {file = "Pillow-9.3.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:828989c45c245518065a110434246c44a56a8b2b2f6347d1409c787e6e4651ee"}, + {file = "Pillow-9.3.0-cp37-cp37m-win32.whl", hash = "sha256:82409ffe29d70fd733ff3c1025a602abb3e67405d41b9403b00b01debc4c9a29"}, + {file = "Pillow-9.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:41e0051336807468be450d52b8edd12ac60bebaa97fe10c8b660f116e50b30e4"}, + {file = "Pillow-9.3.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:b03ae6f1a1878233ac620c98f3459f79fd77c7e3c2b20d460284e1fb370557d4"}, + {file = "Pillow-9.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4390e9ce199fc1951fcfa65795f239a8a4944117b5935a9317fb320e7767b40f"}, + {file = "Pillow-9.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40e1ce476a7804b0fb74bcfa80b0a2206ea6a882938eaba917f7a0f004b42502"}, + {file = "Pillow-9.3.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a0a06a052c5f37b4ed81c613a455a81f9a3a69429b4fd7bb913c3fa98abefc20"}, + {file = "Pillow-9.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03150abd92771742d4a8cd6f2fa6246d847dcd2e332a18d0c15cc75bf6703040"}, + {file = "Pillow-9.3.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:15c42fb9dea42465dfd902fb0ecf584b8848ceb28b41ee2b58f866411be33f07"}, + {file = "Pillow-9.3.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:51e0e543a33ed92db9f5ef69a0356e0b1a7a6b6a71b80df99f1d181ae5875636"}, + {file = "Pillow-9.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3dd6caf940756101205dffc5367babf288a30043d35f80936f9bfb37f8355b32"}, + {file = "Pillow-9.3.0-cp38-cp38-win32.whl", hash = "sha256:f1ff2ee69f10f13a9596480335f406dd1f70c3650349e2be67ca3139280cade0"}, + {file = "Pillow-9.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:276a5ca930c913f714e372b2591a22c4bd3b81a418c0f6635ba832daec1cbcfc"}, + {file = "Pillow-9.3.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:73bd195e43f3fadecfc50c682f5055ec32ee2c933243cafbfdec69ab1aa87cad"}, + {file = "Pillow-9.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1c7c8ae3864846fc95f4611c78129301e203aaa2af813b703c55d10cc1628535"}, + {file = "Pillow-9.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e0918e03aa0c72ea56edbb00d4d664294815aa11291a11504a377ea018330d3"}, + {file = "Pillow-9.3.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0915e734b33a474d76c28e07292f196cdf2a590a0d25bcc06e64e545f2d146c"}, + {file = "Pillow-9.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af0372acb5d3598f36ec0914deed2a63f6bcdb7b606da04dc19a88d31bf0c05b"}, + {file = "Pillow-9.3.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:ad58d27a5b0262c0c19b47d54c5802db9b34d38bbf886665b626aff83c74bacd"}, + {file = "Pillow-9.3.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:97aabc5c50312afa5e0a2b07c17d4ac5e865b250986f8afe2b02d772567a380c"}, + {file = "Pillow-9.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9aaa107275d8527e9d6e7670b64aabaaa36e5b6bd71a1015ddd21da0d4e06448"}, + {file = "Pillow-9.3.0-cp39-cp39-win32.whl", hash = "sha256:bac18ab8d2d1e6b4ce25e3424f709aceef668347db8637c2296bcf41acb7cf48"}, + {file = "Pillow-9.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:b472b5ea442148d1c3e2209f20f1e0bb0eb556538690fa70b5e1f79fa0ba8dc2"}, + {file = "Pillow-9.3.0-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:ab388aaa3f6ce52ac1cb8e122c4bd46657c15905904b3120a6248b5b8b0bc228"}, + {file = "Pillow-9.3.0-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbb8e7f2abee51cef77673be97760abff1674ed32847ce04b4af90f610144c7b"}, + {file = "Pillow-9.3.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bca31dd6014cb8b0b2db1e46081b0ca7d936f856da3b39744aef499db5d84d02"}, + {file = "Pillow-9.3.0-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c7025dce65566eb6e89f56c9509d4f628fddcedb131d9465cacd3d8bac337e7e"}, + {file = "Pillow-9.3.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ebf2029c1f464c59b8bdbe5143c79fa2045a581ac53679733d3a91d400ff9efb"}, + {file = "Pillow-9.3.0-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b59430236b8e58840a0dfb4099a0e8717ffb779c952426a69ae435ca1f57210c"}, + {file = "Pillow-9.3.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12ce4932caf2ddf3e41d17fc9c02d67126935a44b86df6a206cf0d7161548627"}, + {file = "Pillow-9.3.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae5331c23ce118c53b172fa64a4c037eb83c9165aba3a7ba9ddd3ec9fa64a699"}, + {file = "Pillow-9.3.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:0b07fffc13f474264c336298d1b4ce01d9c5a011415b79d4ee5527bb69ae6f65"}, + {file = "Pillow-9.3.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:073adb2ae23431d3b9bcbcff3fe698b62ed47211d0716b067385538a1b0f28b8"}, + {file = "Pillow-9.3.0.tar.gz", hash = "sha256:c935a22a557a560108d780f9a0fc426dd7459940dc54faa49d83249c8d3e760f"}, ] platformdirs = [ {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, @@ -2608,65 +2725,75 @@ psycogreen = [ {file = "psycogreen-1.0.2.tar.gz", hash = "sha256:c429845a8a49cf2f76b71265008760bcd7c7c77d80b806db4dc81116dbcd130d"}, ] psycopg2-binary = [ - {file = "psycopg2-binary-2.9.3.tar.gz", hash = "sha256:761df5313dc15da1502b21453642d7599d26be88bff659382f8f9747c7ebea4e"}, - {file = "psycopg2_binary-2.9.3-cp310-cp310-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:539b28661b71da7c0e428692438efbcd048ca21ea81af618d845e06ebfd29478"}, - {file = "psycopg2_binary-2.9.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2f2534ab7dc7e776a263b463a16e189eb30e85ec9bbe1bff9e78dae802608932"}, - {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e82d38390a03da28c7985b394ec3f56873174e2c88130e6966cb1c946508e65"}, - {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57804fc02ca3ce0dbfbef35c4b3a4a774da66d66ea20f4bda601294ad2ea6092"}, - {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:083a55275f09a62b8ca4902dd11f4b33075b743cf0d360419e2051a8a5d5ff76"}, - {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_24_ppc64le.whl", hash = "sha256:0a29729145aaaf1ad8bafe663131890e2111f13416b60e460dae0a96af5905c9"}, - {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3a79d622f5206d695d7824cbf609a4f5b88ea6d6dab5f7c147fc6d333a8787e4"}, - {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:090f3348c0ab2cceb6dfbe6bf721ef61262ddf518cd6cc6ecc7d334996d64efa"}, - {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:a9e1f75f96ea388fbcef36c70640c4efbe4650658f3d6a2967b4cc70e907352e"}, - {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c3ae8e75eb7160851e59adc77b3a19a976e50622e44fd4fd47b8b18208189d42"}, - {file = "psycopg2_binary-2.9.3-cp310-cp310-win32.whl", hash = "sha256:7b1e9b80afca7b7a386ef087db614faebbf8839b7f4db5eb107d0f1a53225029"}, - {file = "psycopg2_binary-2.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:8b344adbb9a862de0c635f4f0425b7958bf5a4b927c8594e6e8d261775796d53"}, - {file = "psycopg2_binary-2.9.3-cp36-cp36m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:e847774f8ffd5b398a75bc1c18fbb56564cda3d629fe68fd81971fece2d3c67e"}, - {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:68641a34023d306be959101b345732360fc2ea4938982309b786f7be1b43a4a1"}, - {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3303f8807f342641851578ee7ed1f3efc9802d00a6f83c101d21c608cb864460"}, - {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_24_aarch64.whl", hash = "sha256:e3699852e22aa68c10de06524a3721ade969abf382da95884e6a10ff798f9281"}, - {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_24_ppc64le.whl", hash = "sha256:526ea0378246d9b080148f2d6681229f4b5964543c170dd10bf4faaab6e0d27f"}, - {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:b1c8068513f5b158cf7e29c43a77eb34b407db29aca749d3eb9293ee0d3103ca"}, - {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:15803fa813ea05bef089fa78835118b5434204f3a17cb9f1e5dbfd0b9deea5af"}, - {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:152f09f57417b831418304c7f30d727dc83a12761627bb826951692cc6491e57"}, - {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:404224e5fef3b193f892abdbf8961ce20e0b6642886cfe1fe1923f41aaa75c9d"}, - {file = "psycopg2_binary-2.9.3-cp36-cp36m-win32.whl", hash = "sha256:1f6b813106a3abdf7b03640d36e24669234120c72e91d5cbaeb87c5f7c36c65b"}, - {file = "psycopg2_binary-2.9.3-cp36-cp36m-win_amd64.whl", hash = "sha256:2d872e3c9d5d075a2e104540965a1cf898b52274a5923936e5bfddb58c59c7c2"}, - {file = "psycopg2_binary-2.9.3-cp37-cp37m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:10bb90fb4d523a2aa67773d4ff2b833ec00857f5912bafcfd5f5414e45280fb1"}, - {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:874a52ecab70af13e899f7847b3e074eeb16ebac5615665db33bce8a1009cf33"}, - {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a29b3ca4ec9defec6d42bf5feb36bb5817ba3c0230dd83b4edf4bf02684cd0ae"}, - {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:12b11322ea00ad8db8c46f18b7dfc47ae215e4df55b46c67a94b4effbaec7094"}, - {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_24_ppc64le.whl", hash = "sha256:53293533fcbb94c202b7c800a12c873cfe24599656b341f56e71dd2b557be063"}, - {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c381bda330ddf2fccbafab789d83ebc6c53db126e4383e73794c74eedce855ef"}, - {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9d29409b625a143649d03d0fd7b57e4b92e0ecad9726ba682244b73be91d2fdb"}, - {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:183a517a3a63503f70f808b58bfbf962f23d73b6dccddae5aa56152ef2bcb232"}, - {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:15c4e4cfa45f5a60599d9cec5f46cd7b1b29d86a6390ec23e8eebaae84e64554"}, - {file = "psycopg2_binary-2.9.3-cp37-cp37m-win32.whl", hash = "sha256:adf20d9a67e0b6393eac162eb81fb10bc9130a80540f4df7e7355c2dd4af9fba"}, - {file = "psycopg2_binary-2.9.3-cp37-cp37m-win_amd64.whl", hash = "sha256:2f9ffd643bc7349eeb664eba8864d9e01f057880f510e4681ba40a6532f93c71"}, - {file = "psycopg2_binary-2.9.3-cp38-cp38-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:def68d7c21984b0f8218e8a15d514f714d96904265164f75f8d3a70f9c295667"}, - {file = "psycopg2_binary-2.9.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e6aa71ae45f952a2205377773e76f4e3f27951df38e69a4c95440c779e013560"}, - {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dffc08ca91c9ac09008870c9eb77b00a46b3378719584059c034b8945e26b272"}, - {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:280b0bb5cbfe8039205c7981cceb006156a675362a00fe29b16fbc264e242834"}, - {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:af9813db73395fb1fc211bac696faea4ca9ef53f32dc0cfa27e4e7cf766dcf24"}, - {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_24_ppc64le.whl", hash = "sha256:63638d875be8c2784cfc952c9ac34e2b50e43f9f0a0660b65e2a87d656b3116c"}, - {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ffb7a888a047696e7f8240d649b43fb3644f14f0ee229077e7f6b9f9081635bd"}, - {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0c9d5450c566c80c396b7402895c4369a410cab5a82707b11aee1e624da7d004"}, - {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:d1c1b569ecafe3a69380a94e6ae09a4789bbb23666f3d3a08d06bbd2451f5ef1"}, - {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8fc53f9af09426a61db9ba357865c77f26076d48669f2e1bb24d85a22fb52307"}, - {file = "psycopg2_binary-2.9.3-cp38-cp38-win32.whl", hash = "sha256:6472a178e291b59e7f16ab49ec8b4f3bdada0a879c68d3817ff0963e722a82ce"}, - {file = "psycopg2_binary-2.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:35168209c9d51b145e459e05c31a9eaeffa9a6b0fd61689b48e07464ffd1a83e"}, - {file = "psycopg2_binary-2.9.3-cp39-cp39-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:47133f3f872faf28c1e87d4357220e809dfd3fa7c64295a4a148bcd1e6e34ec9"}, - {file = "psycopg2_binary-2.9.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b3a24a1982ae56461cc24f6680604fffa2c1b818e9dc55680da038792e004d18"}, - {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:91920527dea30175cc02a1099f331aa8c1ba39bf8b7762b7b56cbf54bc5cce42"}, - {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:887dd9aac71765ac0d0bac1d0d4b4f2c99d5f5c1382d8b770404f0f3d0ce8a39"}, - {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:1f14c8b0942714eb3c74e1e71700cbbcb415acbc311c730370e70c578a44a25c"}, - {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_24_ppc64le.whl", hash = "sha256:7af0dd86ddb2f8af5da57a976d27cd2cd15510518d582b478fbb2292428710b4"}, - {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:93cd1967a18aa0edd4b95b1dfd554cf15af657cb606280996d393dadc88c3c35"}, - {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bda845b664bb6c91446ca9609fc69f7db6c334ec5e4adc87571c34e4f47b7ddb"}, - {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:01310cf4cf26db9aea5158c217caa92d291f0500051a6469ac52166e1a16f5b7"}, - {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:99485cab9ba0fa9b84f1f9e1fef106f44a46ef6afdeec8885e0b88d0772b49e8"}, - {file = "psycopg2_binary-2.9.3-cp39-cp39-win32.whl", hash = "sha256:46f0e0a6b5fa5851bbd9ab1bc805eef362d3a230fbdfbc209f4a236d0a7a990d"}, - {file = "psycopg2_binary-2.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:accfe7e982411da3178ec690baaceaad3c278652998b2c45828aaac66cd8285f"}, + {file = "psycopg2-binary-2.9.5.tar.gz", hash = "sha256:33e632d0885b95a8b97165899006c40e9ecdc634a529dca7b991eb7de4ece41c"}, + {file = "psycopg2_binary-2.9.5-cp310-cp310-macosx_10_15_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:0775d6252ccb22b15da3b5d7adbbf8cfe284916b14b6dc0ff503a23edb01ee85"}, + {file = "psycopg2_binary-2.9.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2ec46ed947801652c9643e0b1dc334cfb2781232e375ba97312c2fc256597632"}, + {file = "psycopg2_binary-2.9.5-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3520d7af1ebc838cc6084a3281145d5cd5bdd43fdef139e6db5af01b92596cb7"}, + {file = "psycopg2_binary-2.9.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cbc554ba47ecca8cd3396ddaca85e1ecfe3e48dd57dc5e415e59551affe568e"}, + {file = "psycopg2_binary-2.9.5-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:5d28ecdf191db558d0c07d0f16524ee9d67896edf2b7990eea800abeb23ebd61"}, + {file = "psycopg2_binary-2.9.5-cp310-cp310-manylinux_2_24_ppc64le.whl", hash = "sha256:b9c33d4aef08dfecbd1736ceab8b7b3c4358bf10a0121483e5cd60d3d308cc64"}, + {file = "psycopg2_binary-2.9.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:05b3d479425e047c848b9782cd7aac9c6727ce23181eb9647baf64ffdfc3da41"}, + {file = "psycopg2_binary-2.9.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:1e491e6489a6cb1d079df8eaa15957c277fdedb102b6a68cfbf40c4994412fd0"}, + {file = "psycopg2_binary-2.9.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:9e32cedc389bcb76d9f24ea8a012b3cb8385ee362ea437e1d012ffaed106c17d"}, + {file = "psycopg2_binary-2.9.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:46850a640df62ae940e34a163f72e26aca1f88e2da79148e1862faaac985c302"}, + {file = "psycopg2_binary-2.9.5-cp310-cp310-win32.whl", hash = "sha256:3d790f84201c3698d1bfb404c917f36e40531577a6dda02e45ba29b64d539867"}, + {file = "psycopg2_binary-2.9.5-cp310-cp310-win_amd64.whl", hash = "sha256:1764546ffeaed4f9428707be61d68972eb5ede81239b46a45843e0071104d0dd"}, + {file = "psycopg2_binary-2.9.5-cp311-cp311-macosx_10_9_universal2.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:426c2ae999135d64e6a18849a7d1ad0e1bd007277e4a8f4752eaa40a96b550ff"}, + {file = "psycopg2_binary-2.9.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7cf1d44e710ca3a9ce952bda2855830fe9f9017ed6259e01fcd71ea6287565f5"}, + {file = "psycopg2_binary-2.9.5-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:024030b13bdcbd53d8a93891a2cf07719715724fc9fee40243f3bd78b4264b8f"}, + {file = "psycopg2_binary-2.9.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bcda1c84a1c533c528356da5490d464a139b6e84eb77cc0b432e38c5c6dd7882"}, + {file = "psycopg2_binary-2.9.5-cp311-cp311-manylinux_2_24_aarch64.whl", hash = "sha256:2ef892cabdccefe577088a79580301f09f2a713eb239f4f9f62b2b29cafb0577"}, + {file = "psycopg2_binary-2.9.5-cp311-cp311-manylinux_2_24_ppc64le.whl", hash = "sha256:af0516e1711995cb08dc19bbd05bec7dbdebf4185f68870595156718d237df3e"}, + {file = "psycopg2_binary-2.9.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e72c91bda9880f097c8aa3601a2c0de6c708763ba8128006151f496ca9065935"}, + {file = "psycopg2_binary-2.9.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e67b3c26e9b6d37b370c83aa790bbc121775c57bfb096c2e77eacca25fd0233b"}, + {file = "psycopg2_binary-2.9.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:5fc447058d083b8c6ac076fc26b446d44f0145308465d745fba93a28c14c9e32"}, + {file = "psycopg2_binary-2.9.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d892bfa1d023c3781a3cab8dd5af76b626c483484d782e8bd047c180db590e4c"}, + {file = "psycopg2_binary-2.9.5-cp36-cp36m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:6e63814ec71db9bdb42905c925639f319c80e7909fb76c3b84edc79dadef8d60"}, + {file = "psycopg2_binary-2.9.5-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:212757ffcecb3e1a5338d4e6761bf9c04f750e7d027117e74aa3cd8a75bb6fbd"}, + {file = "psycopg2_binary-2.9.5-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f8a9bcab7b6db2e3dbf65b214dfc795b4c6b3bb3af922901b6a67f7cb47d5f8"}, + {file = "psycopg2_binary-2.9.5-cp36-cp36m-manylinux_2_24_aarch64.whl", hash = "sha256:56b2957a145f816726b109ee3d4e6822c23f919a7d91af5a94593723ed667835"}, + {file = "psycopg2_binary-2.9.5-cp36-cp36m-manylinux_2_24_ppc64le.whl", hash = "sha256:f95b8aca2703d6a30249f83f4fe6a9abf2e627aa892a5caaab2267d56be7ab69"}, + {file = "psycopg2_binary-2.9.5-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:70831e03bd53702c941da1a1ad36c17d825a24fbb26857b40913d58df82ec18b"}, + {file = "psycopg2_binary-2.9.5-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:dbc332beaf8492b5731229a881807cd7b91b50dbbbaf7fe2faf46942eda64a24"}, + {file = "psycopg2_binary-2.9.5-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:2d964eb24c8b021623df1c93c626671420c6efadbdb8655cb2bd5e0c6fa422ba"}, + {file = "psycopg2_binary-2.9.5-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:95076399ec3b27a8f7fa1cc9a83417b1c920d55cf7a97f718a94efbb96c7f503"}, + {file = "psycopg2_binary-2.9.5-cp36-cp36m-win32.whl", hash = "sha256:3fc33295cfccad697a97a76dec3f1e94ad848b7b163c3228c1636977966b51e2"}, + {file = "psycopg2_binary-2.9.5-cp36-cp36m-win_amd64.whl", hash = "sha256:02551647542f2bf89073d129c73c05a25c372fc0a49aa50e0de65c3c143d8bd0"}, + {file = "psycopg2_binary-2.9.5-cp37-cp37m-macosx_10_15_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:63e318dbe52709ed10d516a356f22a635e07a2e34c68145484ed96a19b0c4c68"}, + {file = "psycopg2_binary-2.9.5-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7e518a0911c50f60313cb9e74a169a65b5d293770db4770ebf004245f24b5c5"}, + {file = "psycopg2_binary-2.9.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9d38a4656e4e715d637abdf7296e98d6267df0cc0a8e9a016f8ba07e4aa3eeb"}, + {file = "psycopg2_binary-2.9.5-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:68d81a2fe184030aa0c5c11e518292e15d342a667184d91e30644c9d533e53e1"}, + {file = "psycopg2_binary-2.9.5-cp37-cp37m-manylinux_2_24_ppc64le.whl", hash = "sha256:7ee3095d02d6f38bd7d9a5358fcc9ea78fcdb7176921528dd709cc63f40184f5"}, + {file = "psycopg2_binary-2.9.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:46512486be6fbceef51d7660dec017394ba3e170299d1dc30928cbedebbf103a"}, + {file = "psycopg2_binary-2.9.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b911dfb727e247340d36ae20c4b9259e4a64013ab9888ccb3cbba69b77fd9636"}, + {file = "psycopg2_binary-2.9.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:422e3d43b47ac20141bc84b3d342eead8d8099a62881a501e97d15f6addabfe9"}, + {file = "psycopg2_binary-2.9.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c5682a45df7d9642eff590abc73157c887a68f016df0a8ad722dcc0f888f56d7"}, + {file = "psycopg2_binary-2.9.5-cp37-cp37m-win32.whl", hash = "sha256:b8104f709590fff72af801e916817560dbe1698028cd0afe5a52d75ceb1fce5f"}, + {file = "psycopg2_binary-2.9.5-cp37-cp37m-win_amd64.whl", hash = "sha256:7b3751857da3e224f5629400736a7b11e940b5da5f95fa631d86219a1beaafec"}, + {file = "psycopg2_binary-2.9.5-cp38-cp38-macosx_10_15_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:043a9fd45a03858ff72364b4b75090679bd875ee44df9c0613dc862ca6b98460"}, + {file = "psycopg2_binary-2.9.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9ffdc51001136b699f9563b1c74cc1f8c07f66ef7219beb6417a4c8aaa896c28"}, + {file = "psycopg2_binary-2.9.5-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c15ba5982c177bc4b23a7940c7e4394197e2d6a424a2d282e7c236b66da6d896"}, + {file = "psycopg2_binary-2.9.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc85b3777068ed30aff8242be2813038a929f2084f69e43ef869daddae50f6ee"}, + {file = "psycopg2_binary-2.9.5-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:215d6bf7e66732a514f47614f828d8c0aaac9a648c46a831955cb103473c7147"}, + {file = "psycopg2_binary-2.9.5-cp38-cp38-manylinux_2_24_ppc64le.whl", hash = "sha256:7d07f552d1e412f4b4e64ce386d4c777a41da3b33f7098b6219012ba534fb2c2"}, + {file = "psycopg2_binary-2.9.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a0adef094c49f242122bb145c3c8af442070dc0e4312db17e49058c1702606d4"}, + {file = "psycopg2_binary-2.9.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:00475004e5ed3e3bf5e056d66e5dcdf41a0dc62efcd57997acd9135c40a08a50"}, + {file = "psycopg2_binary-2.9.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:7d88db096fa19d94f433420eaaf9f3c45382da2dd014b93e4bf3215639047c16"}, + {file = "psycopg2_binary-2.9.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:902844f9c4fb19b17dfa84d9e2ca053d4a4ba265723d62ea5c9c26b38e0aa1e6"}, + {file = "psycopg2_binary-2.9.5-cp38-cp38-win32.whl", hash = "sha256:4e7904d1920c0c89105c0517dc7e3f5c20fb4e56ba9cdef13048db76947f1d79"}, + {file = "psycopg2_binary-2.9.5-cp38-cp38-win_amd64.whl", hash = "sha256:a36a0e791805aa136e9cbd0ffa040d09adec8610453ee8a753f23481a0057af5"}, + {file = "psycopg2_binary-2.9.5-cp39-cp39-macosx_10_15_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:25382c7d174c679ce6927c16b6fbb68b10e56ee44b1acb40671e02d29f2fce7c"}, + {file = "psycopg2_binary-2.9.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9c38d3869238e9d3409239bc05bc27d6b7c99c2a460ea337d2814b35fb4fea1b"}, + {file = "psycopg2_binary-2.9.5-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5c6527c8efa5226a9e787507652dd5ba97b62d29b53c371a85cd13f957fe4d42"}, + {file = "psycopg2_binary-2.9.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e59137cdb970249ae60be2a49774c6dfb015bd0403f05af1fe61862e9626642d"}, + {file = "psycopg2_binary-2.9.5-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:d4c7b3a31502184e856df1f7bbb2c3735a05a8ce0ade34c5277e1577738a5c91"}, + {file = "psycopg2_binary-2.9.5-cp39-cp39-manylinux_2_24_ppc64le.whl", hash = "sha256:b9a794cef1d9c1772b94a72eec6da144c18e18041d294a9ab47669bc77a80c1d"}, + {file = "psycopg2_binary-2.9.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c5254cbd4f4855e11cebf678c1a848a3042d455a22a4ce61349c36aafd4c2267"}, + {file = "psycopg2_binary-2.9.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c5e65c6ac0ae4bf5bef1667029f81010b6017795dcb817ba5c7b8a8d61fab76f"}, + {file = "psycopg2_binary-2.9.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:74eddec4537ab1f701a1647214734bc52cee2794df748f6ae5908e00771f180a"}, + {file = "psycopg2_binary-2.9.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:01ad49d68dd8c5362e4bfb4158f2896dc6e0c02e87b8a3770fc003459f1a4425"}, + {file = "psycopg2_binary-2.9.5-cp39-cp39-win32.whl", hash = "sha256:937880290775033a743f4836aa253087b85e62784b63fd099ee725d567a48aa1"}, + {file = "psycopg2_binary-2.9.5-cp39-cp39-win_amd64.whl", hash = "sha256:484405b883630f3e74ed32041a87456c5e0e63a8e3429aa93e8714c366d62bd1"}, ] pyaml = [ {file = "pyaml-21.10.1-py2.py3-none-any.whl", hash = "sha256:19985ed303c3a985de4cf8fd329b6d0a5a5b5c9035ea240eccc709ebacbaf4a0"}, @@ -2688,22 +2815,30 @@ pycparser = [ {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, ] -Pygments = [ +pygments = [ {file = "Pygments-2.13.0-py3-none-any.whl", hash = "sha256:f643f331ab57ba3c9d89212ee4a2dabc6e94f117cf4eefde99a0574720d14c42"}, {file = "Pygments-2.13.0.tar.gz", hash = "sha256:56a8508ae95f98e2b9bdf93a6be5ae3f7d8af858b43e02c5a2ff083726be40c1"}, ] -PyLaTeX = [ +pylatex = [ {file = "PyLaTeX-1.4.1.tar.gz", hash = "sha256:d3c12efb8b260771260443dce78d1e9089c09f9d0b92e6273dfca0bf5e7302fb"}, ] +pyopenssl = [ + {file = "pyOpenSSL-22.1.0-py3-none-any.whl", hash = "sha256:b28437c9773bb6c6958628cf9c3bebe585de661dba6f63df17111966363dd15e"}, + {file = "pyOpenSSL-22.1.0.tar.gz", hash = "sha256:7a83b7b272dd595222d672f5ce29aa030f1fb837630ef229f62e72e395ce8968"}, +] pypandoc = [ - {file = "pypandoc-1.9-py3-none-any.whl", hash = "sha256:99013db9317fc631ba44bacdd61aed13cdabb77bfae3ee53de4684382c55c886"}, - {file = "pypandoc-1.9.tar.gz", hash = "sha256:dbd6208ff1ff923072f53307926e8c40c71a9c8ba84e1b665e6cd011bebd4c07"}, + {file = "pypandoc-1.10-py3-none-any.whl", hash = "sha256:ff9658cda18865a822695349a7c707756ecc454b59b71f920b554579ec54aaa7"}, + {file = "pypandoc-1.10.tar.gz", hash = "sha256:101164d154f0b9957372cdbf285396153b74144fda47f531fb556de244efc86e"}, ] pyparsing = [ {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, ] -PySocks = [ +pysaml2 = [ + {file = "pysaml2-7.2.1-py2.py3-none-any.whl", hash = "sha256:2ca155f4eeb1471b247a7b0cc79ccfd5780046d33d0b201e1199a00698dce795"}, + {file = "pysaml2-7.2.1.tar.gz", hash = "sha256:f40f9576dce9afef156469179277ffeeca36829248be333252af0517a26d0b1f"}, +] +pysocks = [ {file = "PySocks-1.7.1-py27-none-any.whl", hash = "sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299"}, {file = "PySocks-1.7.1-py3-none-any.whl", hash = "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5"}, {file = "PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0"}, @@ -2730,10 +2865,10 @@ python3-saml = [ {file = "python3_saml-1.14.0-py3-none-any.whl", hash = "sha256:0e886f5f2d9caf93972a34de72ae2302f4c44bc71472a567cb234e2ab96d91e3"}, ] pytz = [ - {file = "pytz-2022.2.1-py2.py3-none-any.whl", hash = "sha256:220f481bdafa09c3955dfbdddb7b57780e9a94f5127e35456a48589b9e0c0197"}, - {file = "pytz-2022.2.1.tar.gz", hash = "sha256:cea221417204f2d1a2aa03ddae3e867921971d0d76f14d87abb4414415bbdcf5"}, + {file = "pytz-2022.6-py2.py3-none-any.whl", hash = "sha256:222439474e9c98fced559f1709d89e6c9cbf8d79c794ff3eb9f8800064291427"}, + {file = "pytz-2022.6.tar.gz", hash = "sha256:e89512406b793ca39f5971bc999cc538ce125c0e51c27941bef4568b460095e2"}, ] -PyYAML = [ +pyyaml = [ {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, @@ -2885,9 +3020,9 @@ soupsieve = [ {file = "soupsieve-2.3.2.post1-py3-none-any.whl", hash = "sha256:3b2503d3c7084a42b1ebd08116e5f81aadfaea95863628c80a3b774a11b7c759"}, {file = "soupsieve-2.3.2.post1.tar.gz", hash = "sha256:fc53893b3da2c33de295667a0e19f078c14bf86544af307354de5fcf12a3f30d"}, ] -Sphinx = [ - {file = "Sphinx-5.2.2.tar.gz", hash = "sha256:7225c104dc06169eb73b061582c4bc84a9594042acae6c1582564de274b7df2f"}, - {file = "sphinx-5.2.2-py3-none-any.whl", hash = "sha256:9150a8ed2e98d70e778624373f183c5498bf429dd605cf7b63e80e2a166c35a5"}, +sphinx = [ + {file = "Sphinx-5.3.0.tar.gz", hash = "sha256:51026de0a9ff9fc13c05d74913ad66047e104f56a129ff73e174eb5c3ee794b5"}, + {file = "sphinx-5.3.0-py3-none-any.whl", hash = "sha256:060ca5c9f7ba57a08a1219e547b269fadf125ae25b06b9fa7f66768efb652d6d"}, ] sphinxcontrib-applehelp = [ {file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"}, @@ -2913,7 +3048,7 @@ sphinxcontrib-serializinghtml = [ {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, ] -SQLAlchemy = [ +sqlalchemy = [ {file = "SQLAlchemy-1.3.24-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:87a2725ad7d41cd7376373c15fd8bf674e9c33ca56d0b8036add2d634dba372e"}, {file = "SQLAlchemy-1.3.24-cp27-cp27m-win32.whl", hash = "sha256:f597a243b8550a3a0b15122b14e49d8a7e622ba1c9d29776af741f1845478d79"}, {file = "SQLAlchemy-1.3.24-cp27-cp27m-win_amd64.whl", hash = "sha256:fc4cddb0b474b12ed7bdce6be1b9edc65352e8ce66bc10ff8cbbfb3d4047dbf4"}, @@ -2949,7 +3084,7 @@ SQLAlchemy = [ {file = "SQLAlchemy-1.3.24-cp39-cp39-win_amd64.whl", hash = "sha256:09083c2487ca3c0865dc588e07aeaa25416da3d95f7482c07e92f47e080aa17b"}, {file = "SQLAlchemy-1.3.24.tar.gz", hash = "sha256:ebbb777cbf9312359b897bf81ba00dae0f5cb69fba2a18265dcc18a6f5ef7519"}, ] -SQLAlchemy-Utils = [ +sqlalchemy-utils = [ {file = "SQLAlchemy-Utils-0.38.3.tar.gz", hash = "sha256:9f9afba607a40455cf703adfa9846584bf26168a0c5a60a70063b70d65051f4d"}, {file = "SQLAlchemy_Utils-0.38.3-py3-none-any.whl", hash = "sha256:5c13b5d08adfaa85f3d4e8ec09a75136216fad41346980d02974a70a77988bf9"}, ] @@ -2973,40 +3108,44 @@ types-attrs = [ {file = "types_attrs-19.1.0-py2.py3-none-any.whl", hash = "sha256:d11acf7a2531a7c52a740c30fa3eb8d01d3066c10d34c01ff5e59502caac5352"}, ] types-bleach = [ - {file = "types-bleach-5.0.3.tar.gz", hash = "sha256:f7b3df8278efe176d9670d0f063a66c866c77577f71f54b9c7a320e31b1a7bbd"}, - {file = "types_bleach-5.0.3-py3-none-any.whl", hash = "sha256:5931525d03571f36b2bb40210c34b662c4d26c8fd6f2b1e1e83fe4d2d2fd63c7"}, + {file = "types-bleach-5.0.3.1.tar.gz", hash = "sha256:ce8772ea5126dab1883851b41e3aeff229aa5213ced36096990344e632e92373"}, + {file = "types_bleach-5.0.3.1-py3-none-any.whl", hash = "sha256:af5f1b3a54ff279f54c29eccb2e6988ebb6718bc4061469588a5fd4880a79287"}, ] types-filelock = [ {file = "types-filelock-3.2.7.tar.gz", hash = "sha256:0673a25b45725c5c45661fe3744c3d8058653a16e683f0ae4a74afb44a0cdef4"}, {file = "types_filelock-3.2.7-py3-none-any.whl", hash = "sha256:ee0ee2b4ec3d491ebe71f7343e21435c3a07f910881275a94788cac42acf4339"}, ] +types-pysaml2 = [ + {file = "types-pysaml2-1.0.0.tar.gz", hash = "sha256:334fe8e3bb385ec51706742fb4fb1b701910f4fd5b76f730d44b598d5f9f47ff"}, + {file = "types_pysaml2-1.0.0-py3-none-any.whl", hash = "sha256:b415a1b09fa3414f99957e8fd817023479c29f9aa7f49ad0a4b366fb71006ad9"}, +] types-python-dateutil = [ - {file = "types-python-dateutil-2.8.19.tar.gz", hash = "sha256:bfd3eb39c7253aea4ba23b10f69b017d30b013662bb4be4ab48b20bbd763f309"}, - {file = "types_python_dateutil-2.8.19-py3-none-any.whl", hash = "sha256:6284df1e4783d8fc6e587f0317a81333856b872a6669a282f8a325342bce7fa8"}, + {file = "types-python-dateutil-2.8.19.2.tar.gz", hash = "sha256:e6e32ce18f37765b08c46622287bc8d8136dc0c562d9ad5b8fd158c59963d7a7"}, + {file = "types_python_dateutil-2.8.19.2-py3-none-any.whl", hash = "sha256:3f4dbe465e7e0c6581db11fd7a4855d1355b78712b3f292bd399cd332247e9c0"}, ] types-pytz = [ {file = "types-pytz-2021.3.8.tar.gz", hash = "sha256:41253a3a2bf028b6a3f17b58749a692d955af0f74e975de94f6f4d2d3cd01dbd"}, {file = "types_pytz-2021.3.8-py3-none-any.whl", hash = "sha256:aef4a917ab28c585d3f474bfce4f4b44b91e95d9d47d4de29dd845e0db8e3910"}, ] -types-PyYAML = [ - {file = "types-PyYAML-6.0.12.tar.gz", hash = "sha256:f6f350418125872f3f0409d96a62a5a5ceb45231af5cc07ee0034ec48a3c82fa"}, - {file = "types_PyYAML-6.0.12-py3-none-any.whl", hash = "sha256:29228db9f82df4f1b7febee06bbfb601677882e98a3da98132e31c6874163e15"}, +types-pyyaml = [ + {file = "types-PyYAML-6.0.12.1.tar.gz", hash = "sha256:70ccaafcf3fb404d57bffc1529fdd86a13e8b4f2cf9fc3ee81a6408ce0ad59d2"}, + {file = "types_PyYAML-6.0.12.1-py3-none-any.whl", hash = "sha256:aaf5e51444c13bd34104695a89ad9c48412599a4f615d65a60e649109714f608"}, ] types-redis = [ - {file = "types-redis-4.3.21.tar.gz", hash = "sha256:fd017a6733af193d14f1bb29b4ff79c0abc906e8654158d17aba457a6381afe2"}, - {file = "types_redis-4.3.21-py3-none-any.whl", hash = "sha256:cca7e17e3efb5dfdc7c9841bd5963fdd717ab68c4d2d8c6f49a9175fe2f29ce4"}, + {file = "types-redis-4.3.21.3.tar.gz", hash = "sha256:2e1f184056188c8754ded0b5173dc01824d2bfe41975fe318068a68beedfb62c"}, + {file = "types_redis-4.3.21.3-py3-none-any.whl", hash = "sha256:77ee5b8ca3a779e09807348c7f0b33c0f38a46096cd6c8a2e0176b28b3784b34"}, ] types-requests = [ - {file = "types-requests-2.28.11.tar.gz", hash = "sha256:7ee827eb8ce611b02b5117cfec5da6455365b6a575f5e3ff19f655ba603e6b4e"}, - {file = "types_requests-2.28.11-py3-none-any.whl", hash = "sha256:af5f55e803cabcfb836dad752bd6d8a0fc8ef1cd84243061c0e27dee04ccf4fd"}, + {file = "types-requests-2.28.11.2.tar.gz", hash = "sha256:fdcd7bd148139fb8eef72cf4a41ac7273872cad9e6ada14b11ff5dfdeee60ed3"}, + {file = "types_requests-2.28.11.2-py3-none-any.whl", hash = "sha256:14941f8023a80b16441b3b46caffcbfce5265fd14555844d6029697824b5a2ef"}, ] types-urllib3 = [ - {file = "types-urllib3-1.26.25.tar.gz", hash = "sha256:5aef0e663724eef924afa8b320b62ffef2c1736c1fa6caecfc9bc6c8ae2c3def"}, - {file = "types_urllib3-1.26.25-py3-none-any.whl", hash = "sha256:c1d78cef7bd581e162e46c20a57b2e1aa6ebecdcf01fd0713bb90978ff3e3427"}, + {file = "types-urllib3-1.26.25.1.tar.gz", hash = "sha256:a948584944b2412c9a74b9cf64f6c48caf8652cb88b38361316f6d15d8a184cd"}, + {file = "types_urllib3-1.26.25.1-py3-none-any.whl", hash = "sha256:f6422596cc9ee5fdf68f9d547f541096a20c2dcfd587e37c804c9ea720bf5cb2"}, ] typing-extensions = [ - {file = "typing_extensions-4.3.0-py3-none-any.whl", hash = "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02"}, - {file = "typing_extensions-4.3.0.tar.gz", hash = "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"}, + {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"}, + {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, ] typing-inspect = [ {file = "typing_inspect-0.7.1-py2-none-any.whl", hash = "sha256:b1f56c0783ef0f25fb064a01be6e5407e54cf4a4bf4f3ba3fe51e0bd6dcea9e5"}, @@ -3028,7 +3167,7 @@ voikko = [ {file = "voikko-0.5-py3-none-any.whl", hash = "sha256:0fd5c10d3e6660e8dbc151649f5b307d2ff06e3181bf51a67f27820903534767"}, {file = "voikko-0.5.tar.gz", hash = "sha256:e933978d7686259c6afa07510c1d351108cb47dcd39f1b46a5dd9651d6523f32"}, ] -Wand = [ +wand = [ {file = "Wand-0.6.10-py2.py3-none-any.whl", hash = "sha256:ff69eeba36ec243d9a80dec31e738091653dcd01c2668a9c22dc3ff20392596b"}, {file = "Wand-0.6.10.tar.gz", hash = "sha256:373f4a7f2866c868c31ce910e1f9b36a92d132640a20068ec17cea3284fedc57"}, ] @@ -3042,7 +3181,7 @@ webencodings = [ {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, ] -Werkzeug = [ +werkzeug = [ {file = "Werkzeug-2.2.2-py3-none-any.whl", hash = "sha256:f979ab81f58d7318e064e99c4506445d60135ac5cd2e177a2de0089bfd4c9bd5"}, {file = "Werkzeug-2.2.2.tar.gz", hash = "sha256:7ea2d48322cc7c0f8b3a215ed73eabd7b5d75d0b50e31ab006286ccff9e00b8f"}, ] @@ -3120,10 +3259,14 @@ wsproto = [ {file = "wsproto-1.2.0-py3-none-any.whl", hash = "sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736"}, {file = "wsproto-1.2.0.tar.gz", hash = "sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065"}, ] -WTForms = [ +wtforms = [ {file = "WTForms-3.0.1-py3-none-any.whl", hash = "sha256:837f2f0e0ca79481b92884962b914eba4e72b7a2daaf1f939c890ed0124b834b"}, {file = "WTForms-3.0.1.tar.gz", hash = "sha256:6b351bbb12dd58af57ffef05bc78425d08d1914e0fd68ee14143b7ade023c5bc"}, ] +xmlschema = [ + {file = "xmlschema-2.1.1-py3-none-any.whl", hash = "sha256:5717a8a239637a9ad7d7563ce676dddf0a8989644c833f96bfc6d157c3cb3750"}, + {file = "xmlschema-2.1.1.tar.gz", hash = "sha256:5ca34ff15dd3276cfb2e3e7b4c8dde4b7d4d27080f333a93b6c3f817e90abddf"}, +] xmlsec = [ {file = "xmlsec-1.3.13-cp310-cp310-win32.whl", hash = "sha256:2174e8c88555383322d8b7d3927490a92ef72ad72a6ddaf4fa1b96a3f27c3e90"}, {file = "xmlsec-1.3.13-cp310-cp310-win_amd64.whl", hash = "sha256:46d1daf16a8f4430efca5bb9c6a15776f2671f69f48a1941d6bb335e6f8cb29d"}, @@ -3139,60 +3282,49 @@ xmlsec = [ {file = "xmlsec-1.3.13-cp39-cp39-win_amd64.whl", hash = "sha256:5162f416179350587c4ff64737af68a846a9b86f95fd465df4e68b589ce56618"}, {file = "xmlsec-1.3.13.tar.gz", hash = "sha256:916f5d78e8041f6cd9391abba659da8c94a4fef7196d126d40af1ff417f2cf86"}, ] -"zope.event" = [ +zope-event = [ {file = "zope.event-4.5.0-py2.py3-none-any.whl", hash = "sha256:2666401939cdaa5f4e0c08cf7f20c9b21423b95e88f4675b1443973bdb080c42"}, {file = "zope.event-4.5.0.tar.gz", hash = "sha256:5e76517f5b9b119acf37ca8819781db6c16ea433f7e2062c4afc2b6fbedb1330"}, ] -"zope.interface" = [ - {file = "zope.interface-5.4.0-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:7df1e1c05304f26faa49fa752a8c690126cf98b40b91d54e6e9cc3b7d6ffe8b7"}, - {file = "zope.interface-5.4.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:2c98384b254b37ce50eddd55db8d381a5c53b4c10ee66e1e7fe749824f894021"}, - {file = "zope.interface-5.4.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:08f9636e99a9d5410181ba0729e0408d3d8748026ea938f3b970a0249daa8192"}, - {file = "zope.interface-5.4.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:0ea1d73b7c9dcbc5080bb8aaffb776f1c68e807767069b9ccdd06f27a161914a"}, - {file = "zope.interface-5.4.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:273f158fabc5ea33cbc936da0ab3d4ba80ede5351babc4f577d768e057651531"}, - {file = "zope.interface-5.4.0-cp27-cp27m-win32.whl", hash = "sha256:a1e6e96217a0f72e2b8629e271e1b280c6fa3fe6e59fa8f6701bec14e3354325"}, - {file = "zope.interface-5.4.0-cp27-cp27m-win_amd64.whl", hash = "sha256:877473e675fdcc113c138813a5dd440da0769a2d81f4d86614e5d62b69497155"}, - {file = "zope.interface-5.4.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:f7ee479e96f7ee350db1cf24afa5685a5899e2b34992fb99e1f7c1b0b758d263"}, - {file = "zope.interface-5.4.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:b0297b1e05fd128d26cc2460c810d42e205d16d76799526dfa8c8ccd50e74959"}, - {file = "zope.interface-5.4.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:af310ec8335016b5e52cae60cda4a4f2a60a788cbb949a4fbea13d441aa5a09e"}, - {file = "zope.interface-5.4.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:9a9845c4c6bb56e508651f005c4aeb0404e518c6f000d5a1123ab077ab769f5c"}, - {file = "zope.interface-5.4.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:0b465ae0962d49c68aa9733ba92a001b2a0933c317780435f00be7ecb959c702"}, - {file = "zope.interface-5.4.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:5dd9ca406499444f4c8299f803d4a14edf7890ecc595c8b1c7115c2342cadc5f"}, - {file = "zope.interface-5.4.0-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:469e2407e0fe9880ac690a3666f03eb4c3c444411a5a5fddfdabc5d184a79f05"}, - {file = "zope.interface-5.4.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:52de7fc6c21b419078008f697fd4103dbc763288b1406b4562554bd47514c004"}, - {file = "zope.interface-5.4.0-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:3dd4952748521205697bc2802e4afac5ed4b02909bb799ba1fe239f77fd4e117"}, - {file = "zope.interface-5.4.0-cp35-cp35m-win32.whl", hash = "sha256:dd93ea5c0c7f3e25335ab7d22a507b1dc43976e1345508f845efc573d3d779d8"}, - {file = "zope.interface-5.4.0-cp35-cp35m-win_amd64.whl", hash = "sha256:3748fac0d0f6a304e674955ab1365d515993b3a0a865e16a11ec9d86fb307f63"}, - {file = "zope.interface-5.4.0-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:66c0061c91b3b9cf542131148ef7ecbecb2690d48d1612ec386de9d36766058f"}, - {file = "zope.interface-5.4.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:d0c1bc2fa9a7285719e5678584f6b92572a5b639d0e471bb8d4b650a1a910920"}, - {file = "zope.interface-5.4.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:2876246527c91e101184f63ccd1d716ec9c46519cc5f3d5375a3351c46467c46"}, - {file = "zope.interface-5.4.0-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:334701327f37c47fa628fc8b8d28c7d7730ce7daaf4bda1efb741679c2b087fc"}, - {file = "zope.interface-5.4.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:71aace0c42d53abe6fc7f726c5d3b60d90f3c5c055a447950ad6ea9cec2e37d9"}, - {file = "zope.interface-5.4.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:5bb3489b4558e49ad2c5118137cfeaf59434f9737fa9c5deefc72d22c23822e2"}, - {file = "zope.interface-5.4.0-cp36-cp36m-win32.whl", hash = "sha256:1c0e316c9add0db48a5b703833881351444398b04111188069a26a61cfb4df78"}, - {file = "zope.interface-5.4.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f0c02cbb9691b7c91d5009108f975f8ffeab5dff8f26d62e21c493060eff2a1"}, - {file = "zope.interface-5.4.0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:7d97a4306898b05404a0dcdc32d9709b7d8832c0c542b861d9a826301719794e"}, - {file = "zope.interface-5.4.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:867a5ad16892bf20e6c4ea2aab1971f45645ff3102ad29bd84c86027fa99997b"}, - {file = "zope.interface-5.4.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5f931a1c21dfa7a9c573ec1f50a31135ccce84e32507c54e1ea404894c5eb96f"}, - {file = "zope.interface-5.4.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:194d0bcb1374ac3e1e023961610dc8f2c78a0f5f634d0c737691e215569e640d"}, - {file = "zope.interface-5.4.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:8270252effc60b9642b423189a2fe90eb6b59e87cbee54549db3f5562ff8d1b8"}, - {file = "zope.interface-5.4.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:15e7d1f7a6ee16572e21e3576d2012b2778cbacf75eb4b7400be37455f5ca8bf"}, - {file = "zope.interface-5.4.0-cp37-cp37m-win32.whl", hash = "sha256:8892f89999ffd992208754851e5a052f6b5db70a1e3f7d54b17c5211e37a98c7"}, - {file = "zope.interface-5.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2e5a26f16503be6c826abca904e45f1a44ff275fdb7e9d1b75c10671c26f8b94"}, - {file = "zope.interface-5.4.0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:0f91b5b948686659a8e28b728ff5e74b1be6bf40cb04704453617e5f1e945ef3"}, - {file = "zope.interface-5.4.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:4de4bc9b6d35c5af65b454d3e9bc98c50eb3960d5a3762c9438df57427134b8e"}, - {file = "zope.interface-5.4.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:bf68f4b2b6683e52bec69273562df15af352e5ed25d1b6641e7efddc5951d1a7"}, - {file = "zope.interface-5.4.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:63b82bb63de7c821428d513607e84c6d97d58afd1fe2eb645030bdc185440120"}, - {file = "zope.interface-5.4.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:db1fa631737dab9fa0b37f3979d8d2631e348c3b4e8325d6873c2541d0ae5a48"}, - {file = "zope.interface-5.4.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:f44e517131a98f7a76696a7b21b164bcb85291cee106a23beccce454e1f433a4"}, - {file = "zope.interface-5.4.0-cp38-cp38-win32.whl", hash = "sha256:a9506a7e80bcf6eacfff7f804c0ad5350c8c95b9010e4356a4b36f5322f09abb"}, - {file = "zope.interface-5.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:3c02411a3b62668200910090a0dff17c0b25aaa36145082a5a6adf08fa281e54"}, - {file = "zope.interface-5.4.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:0cee5187b60ed26d56eb2960136288ce91bcf61e2a9405660d271d1f122a69a4"}, - {file = "zope.interface-5.4.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:a8156e6a7f5e2a0ff0c5b21d6bcb45145efece1909efcbbbf48c56f8da68221d"}, - {file = "zope.interface-5.4.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:205e40ccde0f37496904572035deea747390a8b7dc65146d30b96e2dd1359a83"}, - {file = "zope.interface-5.4.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:3f24df7124c323fceb53ff6168da70dbfbae1442b4f3da439cd441681f54fe25"}, - {file = "zope.interface-5.4.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:5208ebd5152e040640518a77827bdfcc73773a15a33d6644015b763b9c9febc1"}, - {file = "zope.interface-5.4.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:17776ecd3a1fdd2b2cd5373e5ef8b307162f581c693575ec62e7c5399d80794c"}, - {file = "zope.interface-5.4.0-cp39-cp39-win32.whl", hash = "sha256:d4d9d6c1a455d4babd320203b918ccc7fcbefe308615c521062bc2ba1aa4d26e"}, - {file = "zope.interface-5.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:0cba8477e300d64a11a9789ed40ee8932b59f9ee05f85276dbb4b59acee5dd09"}, - {file = "zope.interface-5.4.0.tar.gz", hash = "sha256:5dba5f530fec3f0988d83b78cc591b58c0b6eb8431a85edd1569a0539a8a5a0e"}, +zope-interface = [ + {file = "zope.interface-5.5.1-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:dd4b9251e95020c3d5d104b528dbf53629d09c146ce9c8dfaaf8f619ae1cce35"}, + {file = "zope.interface-5.5.1-cp27-cp27m-win32.whl", hash = "sha256:061a41a3f96f076686d7f1cb87f3deec6f0c9f0325dcc054ac7b504ae9bb0d82"}, + {file = "zope.interface-5.5.1-cp27-cp27m-win_amd64.whl", hash = "sha256:7f2e4ebe0a000c5727ee04227cf0ff5ae612fe599f88d494216e695b1dac744d"}, + {file = "zope.interface-5.5.1-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:c9552ee9e123b7997c7630fb95c466ee816d19e721c67e4da35351c5f4032726"}, + {file = "zope.interface-5.5.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6a923d2dec50f2b4d41ce198af3516517f2e458220942cf393839d2f9e22000"}, + {file = "zope.interface-5.5.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a20fc9cccbda2a28e8db8cabf2f47fead7e9e49d317547af6bf86a7269e4b9a1"}, + {file = "zope.interface-5.5.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a6f51ffbdcf865f140f55c484001415505f5e68eb0a9eab1d37d0743b503b423"}, + {file = "zope.interface-5.5.1-cp310-cp310-win32.whl", hash = "sha256:8de7bde839d72d96e0c92e8d1fdb4862e89b8fc52514d14b101ca317d9bcf87c"}, + {file = "zope.interface-5.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:90f611d4cdf82fb28837fe15c3940255755572a4edf4c72e2306dbce7dcb3092"}, + {file = "zope.interface-5.5.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d2f2ec42fbc21e1af5f129ec295e29fee6f93563e6388656975caebc5f851561"}, + {file = "zope.interface-5.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:489c4c46fcbd9364f60ff0dcb93ec9026eca64b2f43dc3b05d0724092f205e27"}, + {file = "zope.interface-5.5.1-cp311-cp311-win32.whl", hash = "sha256:9ad58724fabb429d1ebb6f334361f0a3b35f96be0e74bfca6f7de8530688b2df"}, + {file = "zope.interface-5.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:a69f6d8b639f2317ba54278b64fef51d8250ad2c87acac1408b9cc461e4d6bb6"}, + {file = "zope.interface-5.5.1-cp35-cp35m-win32.whl", hash = "sha256:d743b03a72fefed807a4512c079fb1aa5e7777036cc7a4b6ff79ae4650a14f73"}, + {file = "zope.interface-5.5.1-cp35-cp35m-win_amd64.whl", hash = "sha256:3e42b1c3f4fd863323a8275c52c78681281a8f2e1790f0e869d911c1c7b25c46"}, + {file = "zope.interface-5.5.1-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:7b4547a2f624a537e90fb99cec4d8b3b6be4af3f449c3477155aae65396724ad"}, + {file = "zope.interface-5.5.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59a96d499ff6faa9b85b1309f50bf3744eb786e24833f7b500cbb7052dc4ae29"}, + {file = "zope.interface-5.5.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:3c293c5c0e1cabe59c33e0d02fcee5c3eb365f79a20b8199a26ca784e406bd0d"}, + {file = "zope.interface-5.5.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e8c8764226daad39004b7873c3880eb4860c594ff549ea47c045cdf313e1bad5"}, + {file = "zope.interface-5.5.1-cp36-cp36m-win32.whl", hash = "sha256:4477930451521ac7da97cc31d49f7b83086d5ae76e52baf16aac659053119f6d"}, + {file = "zope.interface-5.5.1-cp36-cp36m-win_amd64.whl", hash = "sha256:27c53aa2f46d42940ccdcb015fd525a42bf73f94acd886296794a41f229d5946"}, + {file = "zope.interface-5.5.1-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:2204a9d545fdbe0d9b0bf4d5e2fc67e7977de59666f7131c1433fde292fc3b41"}, + {file = "zope.interface-5.5.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:475b6e371cdbeb024f2302e826222bdc202186531f6dc095e8986c034e4b7961"}, + {file = "zope.interface-5.5.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a1393229c9c126dd1b4356338421e8882347347ab6fe3230cb7044edc813e424"}, + {file = "zope.interface-5.5.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e4988d94962f517f6da2d52337170b84856905b31b7dc504ed9c7b7e4bab2fc3"}, + {file = "zope.interface-5.5.1-cp37-cp37m-win32.whl", hash = "sha256:0eda7f61da6606a28b5efa5d8ad79b4b5bb242488e53a58993b2ec46c924ffee"}, + {file = "zope.interface-5.5.1-cp37-cp37m-win_amd64.whl", hash = "sha256:185f0faf6c3d8f2203e8755f7ca16b8964d97da0abde89c367177a04e36f2568"}, + {file = "zope.interface-5.5.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:026e7da51147910435950a46c55159d68af319f6e909f14873d35d411f4961db"}, + {file = "zope.interface-5.5.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58331d2766e8e409360154d3178449d116220348d46386430097e63d02a1b6d2"}, + {file = "zope.interface-5.5.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d0587d238b7867544134f4dcca19328371b8fd03fc2c56d15786f410792d0a68"}, + {file = "zope.interface-5.5.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cd423d49abcf0ebf02c29c3daffe246ff756addb891f8aab717b3a4e2e1fd675"}, + {file = "zope.interface-5.5.1-cp38-cp38-win32.whl", hash = "sha256:13a7c6e3df8aa453583412de5725bf761217d06f66ff4ed776d44fbcd13ec4e4"}, + {file = "zope.interface-5.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:72a93445937cc71f0b8372b0c9e7c185328e0db5e94d06383a1cb56705df1df4"}, + {file = "zope.interface-5.5.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:6cb8f9a1db47017929634264b3fc7ea4c1a42a3e28d67a14f14aa7b71deaa0d2"}, + {file = "zope.interface-5.5.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e5540b7d703774fd171b7a7dc2a3cb70e98fc273b8b260b1bf2f7d3928f125b"}, + {file = "zope.interface-5.5.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d1f2d91c9c6cd54d750fa34f18bd73c71b372d0e6d06843bc7a5f21f5fd66fe0"}, + {file = "zope.interface-5.5.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:76cf472c79d15dce5f438a4905a1309be57d2d01bc1de2de30bda61972a79ab4"}, + {file = "zope.interface-5.5.1-cp39-cp39-win32.whl", hash = "sha256:509a8d39b64a5e8d473f3f3db981f3ca603d27d2bc023c482605c1b52ec15662"}, + {file = "zope.interface-5.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:8343536ea4ee15d6525e3e726bb49ffc3f2034f828a49237a36be96842c06e7c"}, + {file = "zope.interface-5.5.1.tar.gz", hash = "sha256:6d678475fdeb11394dc9aaa5c564213a1567cc663082e0ee85d52f78d1fbaab2"}, ] diff --git a/pyproject.toml b/pyproject.toml index cc8afc3b13..d88ed21b9c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -71,6 +71,7 @@ Wand = "^0.6.7" requests = "^2.27.1" six = "^1.16.0" python-gnupg = "^0.5.0" +pysaml2 = "^7.2.1" [tool.poetry.group.dev.dependencies] mypy = "^0.971" @@ -89,6 +90,7 @@ Flask = "<2.2.0" Authlib = "1.0.0rc1" langcodes = "^3.3.0" black = "^22.6.0" +types-pysaml2 = "^1.0.0" [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/timApp/Dockerfile b/timApp/Dockerfile index dbc30b0e68..82739c2693 100755 --- a/timApp/Dockerfile +++ b/timApp/Dockerfile @@ -118,6 +118,9 @@ RUN bash -c "${APT_INSTALL} \ RUN wget -q https://bootstrap.pypa.io/get-pip.py && python3.10 get-pip.py && rm get-pip.py +# Install xmlsec binary which is needed for PySAML2 +RUN bash -c "${APT_INSTALL} xmlsec1 && ${APT_CLEANUP}" + ENV PIP_INSTALL="python3.10 -m pip install" RUN bash -c "${PIP_INSTALL} --upgrade poetry==1.2.1 requests && ${APT_CLEANUP}" From 6cf4444f1b11769ff19720ffbccdf186e4ec1a67 Mon Sep 17 00:00:00 2001 From: dezhidki Date: Fri, 4 Nov 2022 16:04:43 +0200 Subject: [PATCH 2/7] saml: Refactor to use PySAML2 OneLogin's implementation is deprecated and not actively maintained at the moment (https://github.com/onelogin/python3-saml/issues/320) --- poetry.lock | 71 +--- pyproject.toml | 4 +- timApp/auth/saml.py | 563 ------------------------- timApp/auth/saml/__init__.py | 0 timApp/auth/saml/attributemaps/haka.py | 16 + timApp/auth/saml/attributes.py | 108 +++++ timApp/auth/saml/client.py | 111 +++++ timApp/auth/saml/dev/config.py | 36 ++ timApp/auth/saml/dev/settings.json | 57 --- timApp/auth/saml/identity_assurance.py | 55 +++ timApp/auth/saml/prod/readme.md | 4 +- timApp/auth/saml/routes.py | 220 ++++++++++ timApp/auth/saml/test/config.py | 36 ++ timApp/auth/saml/test/settings.json | 57 --- timApp/defaultconfig.py | 11 +- timApp/tim.py | 2 +- timApp/util/flask/requesthelper.py | 9 +- 17 files changed, 609 insertions(+), 751 deletions(-) delete mode 100644 timApp/auth/saml.py create mode 100644 timApp/auth/saml/__init__.py create mode 100644 timApp/auth/saml/attributemaps/haka.py create mode 100644 timApp/auth/saml/attributes.py create mode 100644 timApp/auth/saml/client.py create mode 100644 timApp/auth/saml/dev/config.py delete mode 100644 timApp/auth/saml/dev/settings.json create mode 100644 timApp/auth/saml/identity_assurance.py create mode 100644 timApp/auth/saml/routes.py create mode 100644 timApp/auth/saml/test/config.py delete mode 100644 timApp/auth/saml/test/settings.json diff --git a/poetry.lock b/poetry.lock index ae2488959d..5958da4bae 100644 --- a/poetry.lock +++ b/poetry.lock @@ -381,7 +381,7 @@ dev = ["Sphinx", "coverage", "flake8", "lxml", "lxml-stubs", "memory-profiler", [[package]] name = "exceptiongroup" -version = "1.0.0" +version = "1.0.1" description = "Backport of PEP 654 (exception groups)" category = "main" optional = false @@ -971,15 +971,15 @@ tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "pa [[package]] name = "platformdirs" -version = "2.5.2" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +version = "2.5.3" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." category = "dev" optional = false python-versions = ">=3.7" [package.extras] -docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx (>=4)", "sphinx-autodoc-typehints (>=1.12)"] -test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] +docs = ["furo (>=2022.9.29)", "proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.4)"] +test = ["appdirs (==1.4.4)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] [[package]] name = "psycogreen" @@ -1180,22 +1180,6 @@ defusedxml = "*" mysql = ["mysql-connector-python"] postgresql = ["psycopg2"] -[[package]] -name = "python3-saml" -version = "1.14.0" -description = "Onelogin Python Toolkit. Add SAML support to your Python software using this library" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -isodate = ">=0.6.1" -lxml = "<4.7.1" -xmlsec = ">=1.3.9" - -[package.extras] -test = ["coverage (>=4.5.2)", "flake8 (>=3.6.0)", "freezegun (>=0.3.11,<=1.1.0)", "pylint (==1.9.4)", "pytest (>=4.6)"] - [[package]] name = "pytz" version = "2022.6" @@ -1288,7 +1272,7 @@ pyasn1 = ">=0.1.3" [[package]] name = "selenium" -version = "4.5.0" +version = "4.6.0" description = "" category = "main" optional = false @@ -1829,17 +1813,6 @@ codegen = ["elementpath (>=3.0.0,<4.0.0)", "jinja2"] dev = ["Sphinx", "coverage", "elementpath (>=3.0.0,<4.0.0)", "flake8", "jinja2", "lxml", "lxml-stubs", "memory-profiler", "mypy", "sphinx-rtd-theme", "tox"] docs = ["Sphinx", "elementpath (>=3.0.0,<4.0.0)", "jinja2", "sphinx-rtd-theme"] -[[package]] -name = "xmlsec" -version = "1.3.13" -description = "Python bindings for the XML Security Library" -category = "main" -optional = false -python-versions = ">=3.5" - -[package.dependencies] -lxml = ">=3.8" - [[package]] name = "zope-event" version = "4.5.0" @@ -1874,7 +1847,7 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] [metadata] lock-version = "1.1" python-versions = "^3.10" -content-hash = "6d91f855148ba53d492f34db528ba4b9e8ae3f0bebf1c74da63b9667292230f2" +content-hash = "3e1882fb09c1f2f91e20e0bfd6e64670161da43a56370abed8887bb80abfefa6" [metadata.files] alabaster = [ @@ -2172,8 +2145,8 @@ elementpath = [ {file = "elementpath-3.0.2.tar.gz", hash = "sha256:cca18742dc0f354f79874c41a906e6ce4cc15230b7858d22a861e1ec5946940f"}, ] exceptiongroup = [ - {file = "exceptiongroup-1.0.0-py3-none-any.whl", hash = "sha256:2ac84b496be68464a2da60da518af3785fff8b7ec0d090a581604bc870bdee41"}, - {file = "exceptiongroup-1.0.0.tar.gz", hash = "sha256:affbabf13fb6e98988c38d9c5650e701569fe3c1de3233cfb61c5f33774690ad"}, + {file = "exceptiongroup-1.0.1-py3-none-any.whl", hash = "sha256:4d6c0aa6dd825810941c792f53d7b8d71da26f5e5f84f20f9508e8f2d33b140a"}, + {file = "exceptiongroup-1.0.1.tar.gz", hash = "sha256:73866f7f842ede6cb1daa42c4af078e2035e5f7607f0e2c762cc51bb31bbe7b2"}, ] filelock = [ {file = "filelock-3.8.0-py3-none-any.whl", hash = "sha256:617eb4e5eedc82fc5f47b6d61e4d11cb837c56cb4544e39081099fa17ad109d4"}, @@ -2718,8 +2691,8 @@ pillow = [ {file = "Pillow-9.3.0.tar.gz", hash = "sha256:c935a22a557a560108d780f9a0fc426dd7459940dc54faa49d83249c8d3e760f"}, ] platformdirs = [ - {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, - {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, + {file = "platformdirs-2.5.3-py3-none-any.whl", hash = "sha256:0cb405749187a194f444c25c82ef7225232f11564721eabffc6ec70df83b11cb"}, + {file = "platformdirs-2.5.3.tar.gz", hash = "sha256:6e52c21afff35cb659c6e52d8b4d61b9bd544557180440538f255d9382c8cbe0"}, ] psycogreen = [ {file = "psycogreen-1.0.2.tar.gz", hash = "sha256:c429845a8a49cf2f76b71265008760bcd7c7c77d80b806db4dc81116dbcd130d"}, @@ -2859,11 +2832,6 @@ python3-openid = [ {file = "python3-openid-3.2.0.tar.gz", hash = "sha256:33fbf6928f401e0b790151ed2b5290b02545e8775f982485205a066f874aaeaf"}, {file = "python3_openid-3.2.0-py3-none-any.whl", hash = "sha256:6626f771e0417486701e0b4daff762e7212e820ca5b29fcc0d05f6f8736dfa6b"}, ] -python3-saml = [ - {file = "python3-saml-1.14.0.tar.gz", hash = "sha256:e8d04f06549b30e29f9f1d6787faf67558c19f7ed2f3cc0656abb169c8240bc9"}, - {file = "python3_saml-1.14.0-py2-none-any.whl", hash = "sha256:c18913fa1d92b0db01e2b15fd9a0c7e21805aa5b19492dc33969b8751826e888"}, - {file = "python3_saml-1.14.0-py3-none-any.whl", hash = "sha256:0e886f5f2d9caf93972a34de72ae2302f4c44bc71472a567cb234e2ab96d91e3"}, -] pytz = [ {file = "pytz-2022.6-py2.py3-none-any.whl", hash = "sha256:222439474e9c98fced559f1709d89e6c9cbf8d79c794ff3eb9f8800064291427"}, {file = "pytz-2022.6.tar.gz", hash = "sha256:e89512406b793ca39f5971bc999cc538ce125c0e51c27941bef4568b460095e2"}, @@ -2931,7 +2899,7 @@ rsa = [ {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, ] selenium = [ - {file = "selenium-4.5.0-py3-none-any.whl", hash = "sha256:a733dd77d3171b846893f4d51b18967d809313f547a10974e26579f9ce797462"}, + {file = "selenium-4.6.0-py3-none-any.whl", hash = "sha256:3f1999875ef487ae676a254e7293a68041f1f1ec76be81402d8a1cd5a481bf3b"}, ] setuptools = [ {file = "setuptools-62.6.0-py3-none-any.whl", hash = "sha256:c1848f654aea2e3526d17fc3ce6aeaa5e7e24e66e645b5be2171f3f6b4e5a178"}, @@ -3267,21 +3235,6 @@ xmlschema = [ {file = "xmlschema-2.1.1-py3-none-any.whl", hash = "sha256:5717a8a239637a9ad7d7563ce676dddf0a8989644c833f96bfc6d157c3cb3750"}, {file = "xmlschema-2.1.1.tar.gz", hash = "sha256:5ca34ff15dd3276cfb2e3e7b4c8dde4b7d4d27080f333a93b6c3f817e90abddf"}, ] -xmlsec = [ - {file = "xmlsec-1.3.13-cp310-cp310-win32.whl", hash = "sha256:2174e8c88555383322d8b7d3927490a92ef72ad72a6ddaf4fa1b96a3f27c3e90"}, - {file = "xmlsec-1.3.13-cp310-cp310-win_amd64.whl", hash = "sha256:46d1daf16a8f4430efca5bb9c6a15776f2671f69f48a1941d6bb335e6f8cb29d"}, - {file = "xmlsec-1.3.13-cp35-cp35m-win32.whl", hash = "sha256:d47062c42775a025aa94fb8b15de97c1db86e301e549d3168157e0b1223d51b1"}, - {file = "xmlsec-1.3.13-cp35-cp35m-win_amd64.whl", hash = "sha256:7c7e8ef52688ddaf5b66750cc8d901f61716f46727014ff012f41d8858cedeb0"}, - {file = "xmlsec-1.3.13-cp36-cp36m-win32.whl", hash = "sha256:1725d70ee2bb2cd8dd66c7a7451be02bb59dc8280103db4f68e731f00135b1e0"}, - {file = "xmlsec-1.3.13-cp36-cp36m-win_amd64.whl", hash = "sha256:1f8c41162152d7086fd459926e61bc7cb2d52ffc829e760bf8b2c221a645d568"}, - {file = "xmlsec-1.3.13-cp37-cp37m-win32.whl", hash = "sha256:ff1c61f296e75cba5bac802d0000bfde09143eed946ced1a5162211867c335f8"}, - {file = "xmlsec-1.3.13-cp37-cp37m-win_amd64.whl", hash = "sha256:d249c0a2bf3ff13a231bca6a588e7d276b3f1e2cf09316b542f470a63855799e"}, - {file = "xmlsec-1.3.13-cp38-cp38-win32.whl", hash = "sha256:56cfcf3487b6ad269eb1fb543c04dee2c101f1bc91e06d6cf7bfab9ac486efd8"}, - {file = "xmlsec-1.3.13-cp38-cp38-win_amd64.whl", hash = "sha256:e6626bece0e97a8598b5df28c27bc6f2ae1e97d29dca3c1a4910a7598a4d1d0f"}, - {file = "xmlsec-1.3.13-cp39-cp39-win32.whl", hash = "sha256:091f23765729df6f3b3a55c8a6a96f9c713fa86e76b86a19cdb756aaa6dc0646"}, - {file = "xmlsec-1.3.13-cp39-cp39-win_amd64.whl", hash = "sha256:5162f416179350587c4ff64737af68a846a9b86f95fd465df4e68b589ce56618"}, - {file = "xmlsec-1.3.13.tar.gz", hash = "sha256:916f5d78e8041f6cd9391abba659da8c94a4fef7196d126d40af1ff417f2cf86"}, -] zope-event = [ {file = "zope.event-4.5.0-py2.py3-none-any.whl", hash = "sha256:2666401939cdaa5f4e0c08cf7f20c9b21423b95e88f4675b1443973bdb080c42"}, {file = "zope.event-4.5.0.tar.gz", hash = "sha256:5e76517f5b9b119acf37ca8819781db6c16ea433f7e2062c4afc2b6fbedb1330"}, diff --git a/pyproject.toml b/pyproject.toml index d88ed21b9c..8d46eb6f18 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,7 +57,6 @@ PyLaTeX = "^1.4.1" pypandoc = "^1.8.1" python-dateutil = "^2.8.2" python-magic = "^0.4.26" -python3-saml = "^1.14.0" pytz = "^2022.1" recommonmark = "^0.7.1" responses = "^0.21.0" @@ -165,6 +164,8 @@ module = [ "celery.schedules", "lxml", "lxml.*", + "saml2", + "saml2.*", "sqlalchemy", "sqlalchemy.dialects", "sqlalchemy.exc", @@ -193,7 +194,6 @@ module = [ "timApp.answer.routes", "timApp.auth.accesshelper", "timApp.auth.auth_models", - "timApp.auth.saml", "timApp.auth.sessioninfo", "timApp.defaultconfig", "timApp.document.attributeparser", diff --git a/timApp/auth/saml.py b/timApp/auth/saml.py deleted file mode 100644 index 844cbf3946..0000000000 --- a/timApp/auth/saml.py +++ /dev/null @@ -1,563 +0,0 @@ -import functools -import json -import os -from copy import copy -from dataclasses import dataclass, field -from enum import Enum -from functools import cached_property, total_ordering -from typing import Optional, Any -from urllib.parse import urlparse - -import xmlsec -from flask import request, redirect, session, make_response, Blueprint, Request, url_for -from lxml import etree -from lxml.cssselect import CSSSelector -from onelogin.saml2.auth import OneLogin_Saml2_Auth -from onelogin.saml2.errors import OneLogin_Saml2_Error, OneLogin_Saml2_ValidationError -from onelogin.saml2.idp_metadata_parser import OneLogin_Saml2_IdPMetadataParser -from onelogin.saml2.settings import OneLogin_Saml2_Settings -from onelogin.saml2.utils import OneLogin_Saml2_Utils, return_false_on_exception -from onelogin.saml2.xml_utils import OneLogin_Saml2_XML - -from timApp.auth.accesshelper import AccessDenied -from timApp.auth.login import create_or_update_user, set_user_to_session -from timApp.auth.sessioninfo import logged_in -from timApp.tim_app import app, csrf -from timApp.timdb.sqa import db -from timApp.user.personaluniquecode import SchacPersonalUniqueCode -from timApp.user.user import UserInfo, UserOrigin -from timApp.user.usercontact import ContactOrigin -from timApp.user.usergroup import UserGroup -from timApp.util.flask.cache import cache -from timApp.util.flask.requesthelper import use_model, RouteException -from timApp.util.flask.responsehelper import json_response -from timApp.util.logger import log_warning - -saml = Blueprint("saml", __name__, url_prefix="/saml") - - -class FingerPrintException(Exception): - pass - - -def load_sp_settings( - hostname=None, try_new_cert=False, sp_validation_only=False -) -> tuple[str, OneLogin_Saml2_Settings]: - """ - Loads OneLogin Saml2 settings for the given hostname. - - Behaves like OneLogin_Saml2_Settings constructor with custom_base_path set, but allows to dynamically change - Assertion Consumer Service URL. If the ACS URL has $hostname variable, it's replaced with the given hostname - argument. - - .. note: Templating is used because OneLogin does not support multiple ACSs at the moment, see - https://github.com/onelogin/python-saml/issues/207 - - :param hostname: Hostname for which to generate the ACS callback URL. If None, TIM_HOST is used. - :param try_new_cert: If True, settings and certificate from /new folder is used. - :param sp_validation_only: If True, the SP settings are only validated. - :return: Tuple (str, OneLogin_Saml2_Settings) contains path to current Saml2 settings and generated SP settings - object - """ - saml_path = app.config["SAML_PATH"] - if try_new_cert: - saml_path += "/new" - - filename = os.path.join(saml_path, "settings.json") - try: - with open(filename) as json_data: - settings = json.loads(json_data.read()) - except FileNotFoundError: - raise OneLogin_Saml2_Error( - "Settings file not found: %s", - OneLogin_Saml2_Error.SETTINGS_FILE_NOT_FOUND, - filename, - ) - - try: - advanced_filename = os.path.join(saml_path, "advanced_settings.json") - with open(advanced_filename) as json_data: - settings.update(json.loads(json_data.read())) - except FileNotFoundError: - pass - - acs_info = settings["sp"]["assertionConsumerService"] - acs_info["url"] = acs_info["url"].replace( - "$hostname", hostname or app.config["TIM_HOST"] - ) - - ol_settings = OneLogin_Saml2_Settings( - settings=settings, - custom_base_path=saml_path, - sp_validation_only=sp_validation_only, - ) - return saml_path, ol_settings - - -@return_false_on_exception -def validate_node_sign( - signature_node, - elem, - cert=None, - fingerprint=None, - fingerprintalg="sha1", - validatecert=False, - debug=False, -): - """ - Same as OneLogin_Saml2_Utils.validate_node_sign but with the following changes: - - * If the certificate fingerprint does not match, an exception is raised (to make debugging easier). - """ - if (cert is None or cert == "") and fingerprint: - x509_certificate_nodes = OneLogin_Saml2_XML.query( - signature_node, "//ds:Signature/ds:KeyInfo/ds:X509Data/ds:X509Certificate" - ) - if len(x509_certificate_nodes) > 0: - x509_certificate_node = x509_certificate_nodes[0] - x509_cert_value = OneLogin_Saml2_XML.element_text(x509_certificate_node) - x509_cert_value_formatted = OneLogin_Saml2_Utils.format_cert( - x509_cert_value - ) - x509_fingerprint_value = OneLogin_Saml2_Utils.calculate_x509_fingerprint( - x509_cert_value_formatted, fingerprintalg - ) - if fingerprint == x509_fingerprint_value: - cert = x509_cert_value_formatted - else: - raise FingerPrintException( - f"Expected certificate fingerprint {fingerprint} but got {x509_fingerprint_value}" - ) - - 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 validatecert: - manager = xmlsec.KeysManager() - manager.load_cert_from_memory( - cert, xmlsec.KeyFormat.CERT_PEM, xmlsec.KeyDataType.TRUSTED - ) - dsig_ctx = xmlsec.SignatureContext(manager) - else: - dsig_ctx = xmlsec.SignatureContext() - dsig_ctx.key = xmlsec.Key.from_memory(cert, xmlsec.KeyFormat.CERT_PEM, None) - - dsig_ctx.set_enabled_key_data([xmlsec.KeyData.X509]) - - try: - dsig_ctx.verify(signature_node) - except Exception as err: - raise OneLogin_Saml2_ValidationError( - "Signature validation failed. %s", - OneLogin_Saml2_ValidationError.INVALID_SIGNATURE, - str(err), - ) - - return True - - -OneLogin_Saml2_Utils.validate_node_sign = validate_node_sign - - -def do_validate_metadata(idp_metadata_xml: str, fingerprint: str) -> None: - try: - if not OneLogin_Saml2_Utils.validate_metadata_sign( - idp_metadata_xml, - validatecert=False, - fingerprint=fingerprint, - raise_exceptions=True, - fingerprintalg="sha256", - ): - raise RouteException("Failed to validate Haka metadata") - except OneLogin_Saml2_ValidationError as e: - raise RouteException(f"Failed to validate Haka metadata: {e}") - - -def init_saml_auth(req, entity_id: str, try_new_cert: bool) -> OneLogin_Saml2_Auth: - idp_metadata_xml = get_haka_metadata() - idp_data = OneLogin_Saml2_IdPMetadataParser.parse( - idp_metadata_xml, entity_id=entity_id - ) - if "idp" not in idp_data: - raise RouteException(f"IdP not found from Haka metadata: {entity_id}") - try: - do_validate_metadata(idp_metadata_xml, app.config["HAKA_METADATA_FINGERPRINT"]) - except FingerPrintException as e: - log_warning(f"{e} - trying with new fingerprint") - try: - do_validate_metadata( - idp_metadata_xml, app.config["HAKA_METADATA_FINGERPRINT_NEW"] - ) - except FingerPrintException as e: - raise RouteException(f"Failed to validate Haka metadata: {e}") - - saml_path, osett = load_sp_settings( - req["http_host"], try_new_cert, sp_validation_only=True - ) - sp = osett.get_sp_data() - - settings = OneLogin_Saml2_IdPMetadataParser.merge_settings({"sp": sp}, idp_data) - auth = OneLogin_Saml2_Auth(req, settings, custom_base_path=saml_path) - return auth - - -def get_haka_metadata() -> str: - return get_haka_metadata_from_url(app.config["HAKA_METADATA_URL"]) - - -@cache.memoize(timeout=3600 * 24) -def get_haka_metadata_from_url(url: str) -> str: - idp_metadata_xml = OneLogin_Saml2_IdPMetadataParser.get_metadata(url) - return idp_metadata_xml - - -def prepare_flask_request(r: Request): - url_data = urlparse(r.url) - return { - "https": "on", - "http_host": r.host, - "server_port": url_data.port, - "script_name": r.path, - "get_data": r.args.copy(), - "post_data": r.form.copy(), - } - - -@dataclass -class SSOData: - return_to: str - entityID: str - debug: bool = False - addUser: bool = False - - -def prepare_and_init(entity_id: str, try_new_cert: bool) -> OneLogin_Saml2_Auth: - req = prepare_flask_request(request) - auth = init_saml_auth(req, entity_id, try_new_cert) - return auth - - -REFEDS_PREFIX = "https://refeds.org/assurance" -REFEDS_LOCAL_ENTRPRISE_ASSURANCE = f"{REFEDS_PREFIX}/IAP/local-enterprise" - - -@total_ordering -class RefedsIapLevel(Enum): - """Valid Identity Assurance Proofing levels for REFEDS Assurance Framework ver 1.0 based on - https://wiki.refeds.org/display/ASS/REFEDS+Assurance+Framework+ver+1.0 - """ - - low = f"{REFEDS_PREFIX}/IAP/low" - medium = f"{REFEDS_PREFIX}/IAP/medium" - high = f"{REFEDS_PREFIX}/IAP/high" - - @staticmethod - @functools.cache - def as_list(): - return list(RefedsIapLevel) - - def __lt__(self, other: Any) -> bool: - if not isinstance(other, RefedsIapLevel): - return NotImplemented - return RefedsIapLevel.as_list().index(self) < RefedsIapLevel.as_list().index( - other - ) - - @staticmethod - def from_string(s: str) -> Optional["RefedsIapLevel"]: - try: - return RefedsIapLevel(s) - except ValueError: - return None - - -@dataclass(frozen=True) -class IdentityAssuranceProofing: - """Represents user's Identity Assurance Proofing (IAP) level. - IAP describes how the user's identity is assured (e.g. email, government ID, etc.) - - Attributes: - highest_refeds_level: Highest IAP level according to REFEDS Assurance Framework - local_enterprise: If True, user's identity proofing is good enough to access Home Organisations' - administrative systems. - """ - - highest_refeds_level: RefedsIapLevel - local_enterprise: bool - - -@dataclass -class TimRequestedAttributes: - saml_auth: OneLogin_Saml2_Auth - friendly_name_map: dict = field(init=False) - - def __post_init__(self): - self.friendly_name_map = {} - settings: OneLogin_Saml2_Settings = self.saml_auth.get_settings() - for ra in settings.get_sp_data()["attributeConsumingService"][ - "requestedAttributes" - ]: - self.friendly_name_map[ra["friendlyName"]] = ra["name"] - - def get_attribute_by_friendly_name(self, name: str) -> str | None: - values = self.get_attributes_by_friendly_name(name) - return values[0] if values else None - - def get_attributes_by_friendly_name(self, name: str) -> list[str] | None: - return self.saml_auth.get_attribute(self.friendly_name_map[name]) - - @property - def cn(self): - return self.get_attribute_by_friendly_name("cn") - - @property - def mail(self): - return self.get_attribute_by_friendly_name("mail") - - @property - def sn(self): - return self.get_attribute_by_friendly_name("sn") - - @property - def display_name(self): - return self.get_attribute_by_friendly_name("displayName") - - @property - def edu_person_principal_name(self): - return self.get_attribute_by_friendly_name("eduPersonPrincipalName") - - @property - def edu_person_assurance(self) -> list[str]: - return self.get_attributes_by_friendly_name("eduPersonAssurance") - - @property - def given_name(self): - return self.get_attribute_by_friendly_name("givenName") - - @property - def preferred_language(self): - return self.get_attribute_by_friendly_name("preferredLanguage") - - @property - def eppn_parts(self): - return self.edu_person_principal_name.split("@") - - @property - def org(self): - return self.eppn_parts[1] - - @property - def unique_codes(self) -> list[str] | None: - return self.get_attributes_by_friendly_name("schacPersonalUniqueCode") - - @property - def derived_username(self): - uname, org = self.eppn_parts - if org == app.config["HOME_ORGANIZATION"]: - return uname - return f"{org}:{uname}" - - @cached_property - def identity_assurance_proofing(self) -> IdentityAssuranceProofing: - """Parses and returns the best Identity Assurance Proofing (IAP) level - from eduPersonAssurance based on REFEDS Assurance Framework ver 1.0 spec. - - :return: Identity assurence proofing level - """ - iap_levels = [ - RefedsIapLevel.from_string(level) for level in self.edu_person_assurance - ] - # Don't check default because IAP must always be provided - best_level = max(level for level in iap_levels if level is not None) - return IdentityAssuranceProofing( - best_level, REFEDS_LOCAL_ENTRPRISE_ASSURANCE in self.edu_person_assurance - ) - - def to_json(self): - return { - "cn": self.cn, - "displayName": self.display_name, - "eduPersonPrincipalName": self.edu_person_principal_name, - "givenName": self.given_name, - "mail": self.mail, - "preferredLanguage": self.preferred_language, - "sn": self.sn, - "schacPersonalUniqueCode": self.unique_codes, - } - - -@saml.get("/sso") -@use_model(SSOData) -def sso(m: SSOData): - try: - auth = prepare_and_init(m.entityID, try_new_cert=True) - except OneLogin_Saml2_Error: - auth = prepare_and_init(m.entityID, try_new_cert=False) - session["entityID"] = m.entityID - login_url = auth.login(return_to=m.return_to) - session["requestID"] = auth.get_last_request_id() - if not logged_in() and m.addUser: - raise AccessDenied("You must be logged in before adding users to session.") - session["adding_user"] = m.addUser - if m.debug: - session["debugSSO"] = True - else: - session.pop("debugSSO", None) - return redirect(login_url) - - -@csrf.exempt -@saml.post("/acs") -def acs(): - entity_id = session.get("entityID") - if not entity_id: - raise RouteException("entityID not in session") - try: - auth = try_process_saml_response(entity_id, try_new_cert=True) - except (SamlProcessingError, OneLogin_Saml2_Error) as e: - # OneLogin_Saml2_Error happens if there is no new certificate in the file system. That means there is no - # rollover going on, so nothing interesting is happening. - # - # If instead we get a SamlProcessingError, that means there is a new certificate, but the IdP encrypted - # the SAML response with the old certificate, so we should account for that. - if isinstance(e, SamlProcessingError): - log_warning( - f"Failed to process SAML response with the new certificate; trying with old." - ) - try: - auth = try_process_saml_response(entity_id, try_new_cert=False) - except SamlProcessingError as e: - raise RouteException(str(e)) - errors = auth.get_errors() - if not auth.is_authenticated(): - err = f"Authentication failed: {auth.get_last_error_reason()}" - log_warning(err) - raise RouteException( - f'{err} (Please contact {app.config["HELP_EMAIL"]} if the problem persists.)' - ) - if errors: - err = str(errors) - log_warning(err) - raise RouteException(err) - session.pop("requestID", None) - timattrs = TimRequestedAttributes(auth) - org_group = UserGroup.get_organization_group(timattrs.org) - parsed_codes = [] - ucs = timattrs.unique_codes - if ucs: - for c in ucs: - parsed = SchacPersonalUniqueCode.parse(c) - if not parsed: - log_warning(f"Failed to parse unique code: {c}") - else: - parsed_codes.append(parsed) - elif ucs is None: - log_warning(f"{timattrs.derived_username} did not receive unique codes") - else: - log_warning(f"{timattrs.derived_username} received empty unique code list") - # Don't update email here to prevent setting is as primary automatically - user = create_or_update_user( - UserInfo( - username=timattrs.derived_username, - full_name=f"{timattrs.sn} {timattrs.given_name}", - email=timattrs.mail, - given_name=timattrs.given_name, - last_name=timattrs.sn, - origin=UserOrigin.Haka, - unique_codes=parsed_codes, - ), - group_to_add=org_group, - update_email=False, # Don't update the email here since we don't want to force the Haka mail as primary - ) - user.set_emails([timattrs.mail], ContactOrigin.Haka, can_update_primary=True) - haka = UserGroup.get_haka_group() - if haka not in user.groups: - user.groups.append(haka) - db.session.commit() - set_user_to_session(user) - if session.get("debugSSO"): - return json_response(auth.get_attributes()) - rs = request.form.get("RelayState") - if rs: - return redirect(auth.redirect_to(rs)) - return redirect(url_for("start_page")) - - -class SamlProcessingError(Exception): - pass - - -def try_process_saml_response(entity_id: str, try_new_cert: bool): - auth = prepare_and_init(entity_id, try_new_cert) - request_id = session.get("requestID") - if not request_id: - err = "requestID missing from session" - log_warning(err) - raise RouteException(err) - try: - auth.process_response(request_id=request_id) - except Exception as e: - err = f"Error processing SAML response: {str(e)}" - log_warning(err) - raise SamlProcessingError(err) - return auth - - -@saml.get("") -def get_metadata(): - _, settings = load_sp_settings(request.host, sp_validation_only=True) - metadata = settings.get_sp_metadata() - errors = settings.validate_metadata(metadata) - - if len(errors) == 0: - resp = make_response(metadata, 200) - resp.headers["Content-Type"] = "text/xml" - else: - resp = make_response(", ".join(errors), 400) - return resp - - -@saml.get("/feed") -def get_idps(): - idp_metadata_xml = get_haka_metadata() - root = etree.fromstring(idp_metadata_xml) - nsmap = copy(root.nsmap) - rootns = nsmap.pop(None) - nsmap["xhtml"] = rootns - select_idps = CSSSelector( - "xhtml|IDPSSODescriptor", - namespaces=nsmap, - ) - select_displaynames = CSSSelector( - "mdui|DisplayName", - namespaces=nsmap, - ) - feed = [] - for idp in select_idps(root): - names = [] - for n in select_displaynames(idp): - names.append( - { - "value": n.text, - "lang": n.attrib["{http://www.w3.org/XML/1998/namespace}lang"], - } - ) - scopes = [] - nsmap = idp.nsmap - nsmap.pop(None) - for n in CSSSelector( - "shibmd|Scope", - namespaces=nsmap, - )(idp): - scopes.append(n.text) - feed.append( - { - "entityID": idp.getparent().attrib["entityID"], - "displayNames": names, - "scopes": scopes, - } - ) - return json_response(feed) diff --git a/timApp/auth/saml/__init__.py b/timApp/auth/saml/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/timApp/auth/saml/attributemaps/haka.py b/timApp/auth/saml/attributemaps/haka.py new file mode 100644 index 0000000000..f9993b8d3e --- /dev/null +++ b/timApp/auth/saml/attributemaps/haka.py @@ -0,0 +1,16 @@ +from saml2.saml import NAME_FORMAT_URI + +MAP = { + "identifier": NAME_FORMAT_URI, + "fro": { + "urn:oid:2.5.4.3": "cn", + "urn:oid:2.16.840.1.113730.3.1.241": "displayName", + "urn:oid:1.3.6.1.4.1.5923.1.1.1.11": "eduPersonAssurance", + "urn:oid:1.3.6.1.4.1.5923.1.1.1.6": "eduPersonPrincipalName", + "urn:oid:2.5.4.42": "givenName", + "urn:oid:0.9.2342.19200300.100.1.3": "mail", + "urn:oid:2.16.840.1.113730.3.1.39": "preferredLanguage", + "urn:oid:2.5.4.4": "sn", + "urn:oid:1.3.6.1.4.1.25178.1.2.14": "schacPersonalUniqueCode", + }, +} diff --git a/timApp/auth/saml/attributes.py b/timApp/auth/saml/attributes.py new file mode 100644 index 0000000000..24f501c78d --- /dev/null +++ b/timApp/auth/saml/attributes.py @@ -0,0 +1,108 @@ +import functools +from dataclasses import dataclass +from typing import Any, Optional + +from timApp.auth.saml.identity_assurance import ( + IdentityAssuranceProofing, + RefedsIapLevel, + REFEDS_LOCAL_ENTRPRISE_ASSURANCE, + _T, +) +from timApp.tim_app import app +from timApp.util.flask.requesthelper import RouteException + + +@dataclass +class SAMLUserAttributes: + response_attribues: dict[str, Any] + + def _get_attribute_safe(self, name: str) -> Optional[_T]: + return self.response_attribues.get(name, [None])[0] + + def _get_attribute(self, name: str) -> _T: + value = self._get_attribute_safe(name) + if value is None: + raise RouteException(f"Missing required attribute {name}") + return value + + @property + def common_name(self) -> str: + return self._get_attribute("cn") + + @property + def email(self) -> str: + return self._get_attribute("mail") + + @property + def surname(self) -> str: + return self._get_attribute("sn") + + @property + def given_name(self) -> str: + return self._get_attribute("givenName") + + @property + def display_name(self) -> str: + return self._get_attribute("displayName") + + @property + def edu_person_principal_name(self) -> str: + return self._get_attribute("eduPersonPrincipalName") + + @property + def edu_person_assurance(self) -> list[str]: + res = self.response_attribues.get("eduPersonAssurance") + if not isinstance(res, list): + raise RouteException("eduPersonAssurance is not a list") + return res + + @property + def preferred_language(self) -> str: + return self._get_attribute("preferredLanguage") + + @property + def eppn_parts(self) -> list[str]: + return self.edu_person_principal_name.split("@") + + @property + def org(self) -> str: + return self.eppn_parts[1] + + @property + def unique_codes(self) -> list[str] | None: + return self.response_attribues.get("schacPersonalUniqueCode") + + @property + def derived_username(self) -> str: + uname, org = self.eppn_parts + if org == app.config["HOME_ORGANIZATION"]: + return uname + return f"{org}:{uname}" + + @functools.cached_property + def identity_assurance_proofing(self) -> IdentityAssuranceProofing: + """Parses and returns the best Identity Assurance Proofing (IAP) level + from eduPersonAssurance based on REFEDS Assurance Framework ver 1.0 spec. + + :return: Identity assurence proofing level + """ + iap_levels = [ + RefedsIapLevel.from_string(level) for level in self.edu_person_assurance + ] + # Don't check default because IAP must always be provided + best_level = max(level for level in iap_levels if level is not None) + return IdentityAssuranceProofing( + best_level, REFEDS_LOCAL_ENTRPRISE_ASSURANCE in self.edu_person_assurance + ) + + def to_json(self) -> dict[str, Any]: + return { + "cn": self.common_name, + "displayName": self.display_name, + "eduPersonPrincipalName": self.edu_person_principal_name, + "givenName": self.given_name, + "mail": self.email, + "preferredLanguage": self.preferred_language, + "sn": self.surname, + "schacPersonalUniqueCode": self.unique_codes, + } diff --git a/timApp/auth/saml/client.py b/timApp/auth/saml/client.py new file mode 100644 index 0000000000..fe8c2db544 --- /dev/null +++ b/timApp/auth/saml/client.py @@ -0,0 +1,111 @@ +import itertools +from pathlib import Path +from typing import Callable + +from saml2.client import Saml2Client +from saml2.config import Config as Saml2Config +from saml2.mdstore import MetadataStore, MetaDataLoader +from saml2.sigver import SignatureError + +from timApp.tim_app import app +from timApp.util.error_handlers import report_error +from timApp.util.flask.requesthelper import RouteException +from timApp.util.logger import log_warning + +# Add an external loader that allows checking for certs +_metadata_load = MetadataStore.load + + +def _metadatastore_load(self: MetadataStore, *args: list, **kwargs: dict) -> None: + typ = args[0] + + if typ == "loadex": + key = kwargs["loader"] + md = MetaDataLoader( + self.attrc, + kwargs["loader"], + cert=kwargs.get("cert"), + security=self.security, + filename="metadata.xml", + ) + md.load() + self.metadata[key] = md + else: + _metadata_load(self, *args, **kwargs) + + +MetadataStore.load = _metadatastore_load + + +def get_saml_config(metadata_loader: Callable[[], bytes]) -> Saml2Config: + def _do_get_saml_config(try_new_cert: bool, try_new_metadata: bool) -> Saml2Config: + saml_path = Path(app.config["SAML_PATH"]) + if try_new_cert: + saml_path /= "new" + + config_file = saml_path / "config.py" + saml2_config = Saml2Config() + + # We load the config file manually so that we can fill it with the extra info + try: + globals_dict = globals().copy() + locals_dict: dict = {} + exec(config_file.read_text(), globals_dict, locals_dict) + saml2_config.load_file(str(config_file)) + config_dict = locals_dict["CONFIG"] + except FileNotFoundError: + raise FileNotFoundError(f"Could not find SAML config file.") + except KeyError: + raise KeyError(f"Could not find CONFIG dict in SAML config file.") + + metadata_file_name = "metadata_new.crt" if try_new_metadata else "metadata.crt" + + config_dict["key_file"] = str(saml_path / "certs" / "sp.key") + config_dict["cert_file"] = str(saml_path / "certs" / "sp.crt") + # Encryption keypairs seem to be a different option, but e.g., in HAKA the same keys are used for encrypting + # requests and decrypting responses + config_dict["encryption_keypairs"] = [ + { + "key_file": str(saml_path / "certs" / "sp.key"), + "cert_file": str(saml_path / "certs" / "sp.crt"), + } + ] + config_dict["metadata"] = { + "loadex": [ + { + "loader": metadata_loader, + "cert": str(saml_path / "certs" / metadata_file_name), + } + ] + } + config_dict["attribute_map_dir"] = str(saml_path.parent / "attributemaps") + config_dict["allow_unknown_attributes"] = True + saml2_config.load(config_dict) + + return saml2_config + + errors = [] + for new_cert, new_meta in itertools.product((False, True), (False, True)): + try: + return _do_get_saml_config(try_new_cert=new_cert, try_new_metadata=new_meta) + except FileNotFoundError as e: + err = f"SAML (new_cert={new_cert}, new_meta={new_meta}): Could not load SAML config: {e}" + log_warning(err) + errors.append(err) + except SignatureError as e: + err = f"SAML (new_cert={new_cert}, new_meta={new_meta}): Could not load SAML config: {e}" + log_warning(err) + errors.append(err) + + report_error( + "Failed to validate SAML metadata signature. SAML login (HAKA) is not available.\n\n" + f"Errors:\n" + "\n".join(f"* {e}" for e in errors) + ) + raise RouteException( + "Failed to validate SAML metadata signature. Administrators have been notified." + ) + + +def get_saml_client(metadata_loader: Callable[[], bytes]) -> Saml2Client: + saml2_client = Saml2Client(config=get_saml_config(metadata_loader)) + return saml2_client diff --git a/timApp/auth/saml/dev/config.py b/timApp/auth/saml/dev/config.py new file mode 100644 index 0000000000..8ea84722ca --- /dev/null +++ b/timApp/auth/saml/dev/config.py @@ -0,0 +1,36 @@ +from saml2 import BINDING_HTTP_POST +from saml2.saml import NAMEID_FORMAT_PERSISTENT + +from timApp.util.flask.requesthelper import get_active_host_url + +CONFIG = { + "entityid": "https://timdevs02.it.jyu.fi/saml", + "name": "Jyvaskylan yliopiston TIM (testipalvelin)", + "description": "", + "service": { + "sp": { + "endpoints": { + "assertion_consumer_service": [ + (f"{get_active_host_url()}saml/acs", BINDING_HTTP_POST), + ], + }, + "required_attributes": [ + "cn", + "displayName", + "eduPersonAssurance", + "eduPersonPrincipalName", + "givenName", + "mail", + "preferredLanguage", + "sn", + ], + "optional_attributes": [ + "schacPersonalUniqueCode", + ], + "want_response_signed": False, + "want_assertions_signed": False, + "want_assertions_or_response_signed": True, + "name_id_format": NAMEID_FORMAT_PERSISTENT, + }, + }, +} diff --git a/timApp/auth/saml/dev/settings.json b/timApp/auth/saml/dev/settings.json deleted file mode 100644 index 253fde2b04..0000000000 --- a/timApp/auth/saml/dev/settings.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "strict": true, - "sp": { - "entityId": "https://timdevs02-5.it.jyu.fi/saml", - "assertionConsumerService": { - "url": "https://timdevs02-5.it.jyu.fi/saml/acs", - "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" - }, - "attributeConsumingService": { - "serviceName": "Jyvaskylan yliopiston TIM (testipalvelin)", - "serviceDescription": "", - "requestedAttributes": [ - { - "name": "urn:oid:2.5.4.3", - "isRequired": true, - "friendlyName": "cn" - }, - { - "name": "urn:oid:2.16.840.1.113730.3.1.241", - "isRequired": true, - "friendlyName": "displayName" - }, - { - "name": "urn:oid:1.3.6.1.4.1.5923.1.1.1.6", - "isRequired": true, - "friendlyName": "eduPersonPrincipalName" - }, - { - "name": "urn:oid:2.5.4.42", - "isRequired": true, - "friendlyName": "givenName" - }, - { - "name": "urn:oid:0.9.2342.19200300.100.1.3", - "isRequired": true, - "friendlyName": "mail" - }, - { - "name": "urn:oid:2.16.840.1.113730.3.1.39", - "isRequired": true, - "friendlyName": "preferredLanguage" - }, - { - "name": "urn:oid:2.5.4.4", - "isRequired": true, - "friendlyName": "sn" - }, - { - "name": "urn:oid:1.3.6.1.4.1.25178.1.2.14", - "isRequired": false, - "friendlyName": "schacPersonalUniqueCode" - } - ] - }, - "NameIDFormat": "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" - } -} diff --git a/timApp/auth/saml/identity_assurance.py b/timApp/auth/saml/identity_assurance.py new file mode 100644 index 0000000000..ab51d8ade4 --- /dev/null +++ b/timApp/auth/saml/identity_assurance.py @@ -0,0 +1,55 @@ +import functools +from dataclasses import dataclass +from enum import Enum +from typing import Any, Optional, TypeVar + +REFEDS_PREFIX = "https://refeds.org/assurance" +REFEDS_LOCAL_ENTRPRISE_ASSURANCE = f"{REFEDS_PREFIX}/IAP/local-enterprise" + + +@functools.total_ordering +class RefedsIapLevel(Enum): + """Valid Identity Assurance Proofing levels for REFEDS Assurance Framework ver 1.0 based on + https://wiki.refeds.org/display/ASS/REFEDS+Assurance+Framework+ver+1.0 + """ + + low = f"{REFEDS_PREFIX}/IAP/low" + medium = f"{REFEDS_PREFIX}/IAP/medium" + high = f"{REFEDS_PREFIX}/IAP/high" + + @staticmethod + @functools.cache + def as_list() -> list["RefedsIapLevel"]: + return list(RefedsIapLevel) + + def __lt__(self, other: Any) -> bool: + if not isinstance(other, RefedsIapLevel): + return NotImplemented + return RefedsIapLevel.as_list().index(self) < RefedsIapLevel.as_list().index( + other + ) + + @staticmethod + def from_string(s: str) -> Optional["RefedsIapLevel"]: + try: + return RefedsIapLevel(s) + except ValueError: + return None + + +@dataclass(frozen=True) +class IdentityAssuranceProofing: + """Represents user's Identity Assurance Proofing (IAP) level. + IAP describes how the user's identity is assured (e.g. email, government ID, etc.) + + Attributes: + highest_refeds_level: Highest IAP level according to REFEDS Assurance Framework + local_enterprise: If True, user's identity proofing is good enough to access Home Organisations' + administrative systems. + """ + + highest_refeds_level: RefedsIapLevel + local_enterprise: bool + + +_T = TypeVar("_T") diff --git a/timApp/auth/saml/prod/readme.md b/timApp/auth/saml/prod/readme.md index 9e71a1442e..f2668bce46 100644 --- a/timApp/auth/saml/prod/readme.md +++ b/timApp/auth/saml/prod/readme.md @@ -1 +1,3 @@ -For production, add "certs" folder and "settings.json" file to this folder like in "dev" folder. +For production, add "certs" folder and "config.py" file to this folder like in "dev" folder. + +See for more information about config.py file. diff --git a/timApp/auth/saml/routes.py b/timApp/auth/saml/routes.py new file mode 100644 index 0000000000..edb7cff347 --- /dev/null +++ b/timApp/auth/saml/routes.py @@ -0,0 +1,220 @@ +from dataclasses import field + +import requests +from flask import make_response, session, request, url_for, redirect +from saml2 import SAMLError, BINDING_HTTP_POST +from saml2.client import Saml2Client +from saml2.config import Config as Saml2Config +from saml2.mdstore import MetadataStore +from saml2.metadata import create_metadata_string +from saml2.s_utils import SamlException +from werkzeug.sansio.response import Response + +from timApp.auth.accesshelper import AccessDenied +from timApp.auth.login import create_or_update_user, set_user_to_session +from timApp.auth.saml.attributes import SAMLUserAttributes +from timApp.auth.saml.client import ( + get_saml_config, + get_saml_client, +) +from timApp.auth.sessioninfo import logged_in +from timApp.tim_app import app, csrf +from timApp.timdb.sqa import db +from timApp.user.personaluniquecode import SchacPersonalUniqueCode +from timApp.user.user import UserInfo, UserOrigin +from timApp.user.usercontact import ContactOrigin +from timApp.user.usergroup import UserGroup +from timApp.util.error_handlers import report_error +from timApp.util.flask.cache import cache +from timApp.util.flask.requesthelper import RouteException +from timApp.util.flask.responsehelper import json_response +from timApp.util.flask.typedblueprint import TypedBlueprint +from timApp.util.logger import log_warning + +saml = TypedBlueprint("saml", __name__, url_prefix="/saml") + + +@cache.memoize(timeout=3600 * 24) +def _get_idp_metadata_from_url(url: str) -> bytes: + try: + response = requests.get(url) + response.raise_for_status() + return response.content + except requests.exceptions.RequestException as e: + raise RouteException(f"Could not fetch IDP metadata from {url}: {e}") + + +def _get_haka_metadata() -> bytes: + return _get_idp_metadata_from_url(app.config["HAKA_METADATA_URL"]) + + +def _get_saml_client() -> Saml2Client: + return get_saml_client(_get_haka_metadata) + + +def _get_saml_config() -> Saml2Config: + return get_saml_config(_get_haka_metadata) + + +@saml.get("/sso") +def sso( + return_to: str, + entity_id: str = field(metadata={"data_key": "entityID"}), + debug: bool = False, + add_user: bool = field(default=False, metadata={"data_key": "addUser"}), +) -> Response: + if not logged_in() and add_user: + raise AccessDenied("You must be logged in before adding users to session.") + + client = _get_saml_client() + req_id, info = client.prepare_for_authenticate(entity_id, relay_state=return_to) + redirect_url = next(v for k, v in info["headers"] if k == "Location") + + session["entityID"] = entity_id + session["adding_user"] = add_user + session["requestID"] = req_id + session["cameFrom"] = request.base_url + if debug: + session["debugSSO"] = True + else: + session.pop("debugSSO", None) + return redirect(redirect_url) + + +@csrf.exempt +@saml.post("/acs") +def acs() -> Response: + entity_id = session.get("entityID") + came_from = session.get("cameFrom") + request_id = session.get("requestID") + if not entity_id: + raise RouteException("No entityID in session.") + if not came_from: + raise RouteException("No cameFrom in session.") + if not request_id: + raise RouteException("No requestID in session.") + + client = _get_saml_client() + + try: + resp = client.parse_authn_request_response( + request.form["SAMLResponse"], + BINDING_HTTP_POST, + outstanding={request_id: came_from}, + ) + except SamlException as e: + report_error(f"Error parsing SAML response: {e}", with_http_body=True) + raise RouteException( + f"Error parsing SAML response. You can log in using your TIM username and password instead. " + f"Please contact {app.config['HELP_EMAIL']} if the problem persists." + ) + ava = resp.get_identity() + + session.pop("requestID", None) + session.pop("cameFrom", None) + + saml_attributes = SAMLUserAttributes(ava) + org_group = UserGroup.get_organization_group(saml_attributes.org) + parsed_codes = [] + ucs = saml_attributes.unique_codes + if ucs: + for c in ucs: + parsed = SchacPersonalUniqueCode.parse(c) + if not parsed: + log_warning(f"Failed to parse unique code: {c}") + else: + parsed_codes.append(parsed) + elif ucs is None: + log_warning(f"{saml_attributes.derived_username} did not receive unique codes") + else: + log_warning( + f"{saml_attributes.derived_username} received empty unique code list" + ) + # Don't update email here to prevent setting is as primary automatically + user = create_or_update_user( + UserInfo( + username=saml_attributes.derived_username, + full_name=f"{saml_attributes.surname} {saml_attributes.given_name}", + email=saml_attributes.email, + given_name=saml_attributes.given_name, + last_name=saml_attributes.surname, + origin=UserOrigin.Haka, + unique_codes=parsed_codes, + ), + group_to_add=org_group, + update_email=False, # Don't update the email here since we don't want to force the Haka mail as primary + ) + user.set_emails( + [saml_attributes.email], ContactOrigin.Haka, can_update_primary=True + ) + haka = UserGroup.get_haka_group() + if haka not in user.groups: + user.groups.append(haka) + db.session.commit() + set_user_to_session(user) + if session.get("debugSSO"): + return json_response(ava) + rs = request.form.get("RelayState") + if rs: + return redirect(rs) + return redirect(url_for("start_page")) + + +@saml.get("") +def get_metadata() -> Response: + saml_config = _get_saml_config() + try: + resp = make_response(create_metadata_string(None, config=saml_config), 200) + resp.headers["Content-Type"] = "text/xml" + except SAMLError as e: + log_warning(f"Could not create SAML metadata: {e}") + resp = make_response(f"Could not create SAML metadata: {e}", 400) + + return resp + + +@saml.get("/feed") +def get_idps() -> Response: + config = _get_saml_config() + meta: MetadataStore = config.metadata + idps = meta.with_descriptor("idpsso") + feed = [] + for entity_id, idp_info in idps.items(): + sso_desc = idp_info.get("idpsso_descriptor") + if not sso_desc: + log_warning(f"SAML: Could not find SSO info for {entity_id}") + continue + ext_elems = sso_desc[0].get("extensions", {}).get("extension_elements", None) + if ext_elems is None: + log_warning(f"SAML: Could not find extension elements for {entity_id}") + continue + ui_info = next( + (d for d in ext_elems if d["__class__"].endswith("ui&UIInfo")), None + ) + assert isinstance(ui_info, dict) + if ui_info is None: + log_warning(f"SAML: Could not find UIInfo for {entity_id}") + continue + # get display names + display_names = [ + { + "value": name_entry["text"], + "lang": name_entry["lang"], + } + for name_entry in ui_info["display_name"] + ] + # Check for scope extension + scopes = [] + scope_elems = [e for e in ext_elems if e["__class__"].endswith("&Scope")] + for ext_elem in scope_elems: + scopes.append(ext_elem["text"]) + + feed.append( + { + "entityID": entity_id, + "displayNames": display_names, + "scopes": scopes, + } + ) + + return json_response(feed) diff --git a/timApp/auth/saml/test/config.py b/timApp/auth/saml/test/config.py new file mode 100644 index 0000000000..8ea84722ca --- /dev/null +++ b/timApp/auth/saml/test/config.py @@ -0,0 +1,36 @@ +from saml2 import BINDING_HTTP_POST +from saml2.saml import NAMEID_FORMAT_PERSISTENT + +from timApp.util.flask.requesthelper import get_active_host_url + +CONFIG = { + "entityid": "https://timdevs02.it.jyu.fi/saml", + "name": "Jyvaskylan yliopiston TIM (testipalvelin)", + "description": "", + "service": { + "sp": { + "endpoints": { + "assertion_consumer_service": [ + (f"{get_active_host_url()}saml/acs", BINDING_HTTP_POST), + ], + }, + "required_attributes": [ + "cn", + "displayName", + "eduPersonAssurance", + "eduPersonPrincipalName", + "givenName", + "mail", + "preferredLanguage", + "sn", + ], + "optional_attributes": [ + "schacPersonalUniqueCode", + ], + "want_response_signed": False, + "want_assertions_signed": False, + "want_assertions_or_response_signed": True, + "name_id_format": NAMEID_FORMAT_PERSISTENT, + }, + }, +} diff --git a/timApp/auth/saml/test/settings.json b/timApp/auth/saml/test/settings.json deleted file mode 100644 index 253fde2b04..0000000000 --- a/timApp/auth/saml/test/settings.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "strict": true, - "sp": { - "entityId": "https://timdevs02-5.it.jyu.fi/saml", - "assertionConsumerService": { - "url": "https://timdevs02-5.it.jyu.fi/saml/acs", - "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" - }, - "attributeConsumingService": { - "serviceName": "Jyvaskylan yliopiston TIM (testipalvelin)", - "serviceDescription": "", - "requestedAttributes": [ - { - "name": "urn:oid:2.5.4.3", - "isRequired": true, - "friendlyName": "cn" - }, - { - "name": "urn:oid:2.16.840.1.113730.3.1.241", - "isRequired": true, - "friendlyName": "displayName" - }, - { - "name": "urn:oid:1.3.6.1.4.1.5923.1.1.1.6", - "isRequired": true, - "friendlyName": "eduPersonPrincipalName" - }, - { - "name": "urn:oid:2.5.4.42", - "isRequired": true, - "friendlyName": "givenName" - }, - { - "name": "urn:oid:0.9.2342.19200300.100.1.3", - "isRequired": true, - "friendlyName": "mail" - }, - { - "name": "urn:oid:2.16.840.1.113730.3.1.39", - "isRequired": true, - "friendlyName": "preferredLanguage" - }, - { - "name": "urn:oid:2.5.4.4", - "isRequired": true, - "friendlyName": "sn" - }, - { - "name": "urn:oid:1.3.6.1.4.1.25178.1.2.14", - "isRequired": false, - "friendlyName": "schacPersonalUniqueCode" - } - ] - }, - "NameIDFormat": "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" - } -} diff --git a/timApp/defaultconfig.py b/timApp/defaultconfig.py index 07a64a76e1..c57d872e92 100644 --- a/timApp/defaultconfig.py +++ b/timApp/defaultconfig.py @@ -159,21 +159,12 @@ SISU_CERT_PATH = "/service/certs/sisu.pem" SAML_PATH = "/service/timApp/auth/saml/dev" +SAML_VERIFY_METADATA = False HAKA_METADATA_URL = "https://haka.funet.fi/metadata/haka_test_metadata_signed.xml" -HAKA_METADATA_FINGERPRINT = ( - "811dd04e5bde0976be6c7aa6a62e2e633d3de37807642e6c532019674545d019" -) # In production, copy these to prodconfig.py and remove the "_PROD" suffix. SAML_PATH_PROD = "/service/timApp/auth/saml/prod" HAKA_METADATA_URL_PROD = "https://haka.funet.fi/metadata/haka-metadata.xml" -HAKA_METADATA_FINGERPRINT_PROD = ( - "70a9058262190cc23f8b0b14d6f0b7c0c74648e8b979bf4258eb7e23674a52f8" -) -# Fingerprint for the upcoming (1.12.2020) v5 certificate. -HAKA_METADATA_FINGERPRINT_NEW_PROD = ( - "a2c1eff331849cbfbfc920924861e03c8a56414ec003bf919e7f1b1a7dbc3169" -) HOME_ORGANIZATION = "jyu.fi" diff --git a/timApp/tim.py b/timApp/tim.py index 4172d88fe2..747834b4a5 100755 --- a/timApp/tim.py +++ b/timApp/tim.py @@ -22,7 +22,7 @@ from timApp.auth.access.routes import access from timApp.auth.login import login_page from timApp.auth.oauth2.oauth2 import init_oauth -from timApp.auth.saml import saml +from timApp.auth.saml.routes import saml from timApp.auth.session.routes import user_sessions from timApp.auth.sessioninfo import ( get_current_user_object, diff --git a/timApp/util/flask/requesthelper.py b/timApp/util/flask/requesthelper.py index 6a8194496f..da2a7bc11d 100644 --- a/timApp/util/flask/requesthelper.py +++ b/timApp/util/flask/requesthelper.py @@ -9,7 +9,7 @@ from urllib.parse import urlparse import requests -from flask import Request, current_app, g, Response +from flask import Request, current_app, g, Response, has_request_context from flask import request from marshmallow import ValidationError, Schema from webargs.flaskparser import use_args @@ -105,6 +105,13 @@ def is_localhost() -> bool: return current_app.config["TIM_HOST"] in ("http://localhost", "http://caddy") +def get_active_host_url() -> str: + # check if inside request context + if has_request_context(): + return request.host_url + return f"{current_app.config['TIM_HOST']}/" + + def get_consent_opt() -> Consent | None: consent_opt = get_option(request, "consent", "any") if consent_opt == "true": From c2279ce0d36ff147cf4c140bf216dc24f20423b6 Mon Sep 17 00:00:00 2001 From: dezhidki Date: Mon, 7 Nov 2022 14:06:03 +0200 Subject: [PATCH 3/7] error_handlers: Allow appending request body to all error reports --- timApp/util/error_handlers.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/timApp/util/error_handlers.py b/timApp/util/error_handlers.py index 591d7128d2..ad51d228ff 100644 --- a/timApp/util/error_handlers.py +++ b/timApp/util/error_handlers.py @@ -9,7 +9,15 @@ from typing import Callable, Any import filelock -from flask import request, render_template, session, flash, Flask, redirect +from flask import ( + request, + render_template, + session, + flash, + Flask, + redirect, + has_request_context, +) from flask.typing import ResponseReturnValue from markupsafe import Markup # type: ignore from marshmallow import ValidationError @@ -119,12 +127,13 @@ def _set_error_mute_info(error_code: str, info: ErrorMuteInfo) -> None: cache[error_code] = info -def report_error(err_msg: str) -> None: +def report_error(err_msg: str, with_http_body: bool = False) -> None: """ Log an error and send information about it to the error reporting service. A full stack trace and an error database code is included in the message. :param err_msg: Error message to include in the report + :param with_http_body: If true, include HTTP body of the active request """ log_error(err_msg) _, ex, tb_obj = sys.exc_info() @@ -161,6 +170,9 @@ def report_error(err_msg: str) -> None: will_mute_next = True _set_error_mute_info(error_code, mute_info) + if with_http_body and has_request_context(): + err_msg += f"\n\nHTTP Body:\n{get_request_message(include_body=True)}" + message = f""" Exception happened on {get_current_time()} at {request.url} From e460a507934dc6fbdb957e178f25c4d2d5af9e9a Mon Sep 17 00:00:00 2001 From: dezhidki Date: Mon, 7 Nov 2022 15:10:49 +0200 Subject: [PATCH 4/7] Fix SAML tests --- timApp/auth/saml/client.py | 37 +++++---- timApp/auth/saml/routes.py | 28 +++++-- timApp/testconfig.py | 1 + timApp/tests/server/test_signup.py | 118 +++++++---------------------- 4 files changed, 74 insertions(+), 110 deletions(-) diff --git a/timApp/auth/saml/client.py b/timApp/auth/saml/client.py index fe8c2db544..c3628f13c3 100644 --- a/timApp/auth/saml/client.py +++ b/timApp/auth/saml/client.py @@ -58,23 +58,34 @@ def _do_get_saml_config(try_new_cert: bool, try_new_metadata: bool) -> Saml2Conf except KeyError: raise KeyError(f"Could not find CONFIG dict in SAML config file.") - metadata_file_name = "metadata_new.crt" if try_new_metadata else "metadata.crt" - - config_dict["key_file"] = str(saml_path / "certs" / "sp.key") - config_dict["cert_file"] = str(saml_path / "certs" / "sp.crt") - # Encryption keypairs seem to be a different option, but e.g., in HAKA the same keys are used for encrypting - # requests and decrypting responses - config_dict["encryption_keypairs"] = [ - { - "key_file": str(saml_path / "certs" / "sp.key"), - "cert_file": str(saml_path / "certs" / "sp.crt"), - } - ] + metadata_cert_file_name = ( + "metadata_new.crt" if try_new_metadata else "metadata.crt" + ) + metadata_cert_file = saml_path / "certs" / metadata_cert_file_name + + sp_key = saml_path / "certs" / "sp.key" + sp_cert = saml_path / "certs" / "sp.crt" + + if sp_key.exists() and sp_cert.exists(): + sp_key_str = str(sp_key) + sp_cert_str = str(sp_cert) + config_dict["key_file"] = sp_key_str + config_dict["cert_file"] = sp_cert_str + # Encryption keypairs seem to be a different option, but e.g., in HAKA the same keys are used for encrypting + # requests and decrypting responses + config_dict["encryption_keypairs"] = [ + { + "key_file": sp_key_str, + "cert_file": sp_cert_str, + } + ] config_dict["metadata"] = { "loadex": [ { "loader": metadata_loader, - "cert": str(saml_path / "certs" / metadata_file_name), + "cert": str(saml_path / "certs" / metadata_cert_file) + if app.config["SAML_VERIFY_METADATA"] + else None, } ] } diff --git a/timApp/auth/saml/routes.py b/timApp/auth/saml/routes.py index edb7cff347..39c96d86ec 100644 --- a/timApp/auth/saml/routes.py +++ b/timApp/auth/saml/routes.py @@ -1,4 +1,5 @@ from dataclasses import field +from xml.etree.ElementTree import ParseError import requests from flask import make_response, session, request, url_for, redirect @@ -7,6 +8,7 @@ from saml2.config import Config as Saml2Config from saml2.mdstore import MetadataStore from saml2.metadata import create_metadata_string +from saml2.response import AuthnResponse from saml2.s_utils import SamlException from werkzeug.sansio.response import Response @@ -26,7 +28,7 @@ from timApp.user.usergroup import UserGroup from timApp.util.error_handlers import report_error from timApp.util.flask.cache import cache -from timApp.util.flask.requesthelper import RouteException +from timApp.util.flask.requesthelper import RouteException, is_testing from timApp.util.flask.responsehelper import json_response from timApp.util.flask.typedblueprint import TypedBlueprint from timApp.util.logger import log_warning @@ -81,6 +83,19 @@ def sso( return redirect(redirect_url) +def _get_saml_response( + client: Saml2Client, request_id: str, came_from: str +) -> AuthnResponse: + saml_response = request.form.get("SAMLResponse") + if not saml_response: + raise SamlException("SAML Response is missing") + return client.parse_authn_request_response( + saml_response, + BINDING_HTTP_POST, + outstanding={request_id: came_from}, + ) + + @csrf.exempt @saml.post("/acs") def acs() -> Response: @@ -97,17 +112,16 @@ def acs() -> Response: client = _get_saml_client() try: - resp = client.parse_authn_request_response( - request.form["SAMLResponse"], - BINDING_HTTP_POST, - outstanding={request_id: came_from}, - ) - except SamlException as e: + resp = _get_saml_response(client, request_id, came_from) + except (SamlException, ParseError, SAMLError) as e: report_error(f"Error parsing SAML response: {e}", with_http_body=True) + if is_testing(): + raise RouteException(str(e)) raise RouteException( f"Error parsing SAML response. You can log in using your TIM username and password instead. " f"Please contact {app.config['HELP_EMAIL']} if the problem persists." ) + ava = resp.get_identity() session.pop("requestID", None) diff --git a/timApp/testconfig.py b/timApp/testconfig.py index f85c127ada..918d2a6c68 100644 --- a/timApp/testconfig.py +++ b/timApp/testconfig.py @@ -49,6 +49,7 @@ class Schedule(TypedDict): HOME_ORGANIZATION = "jyu.fi" SAML_PATH = "/service/timApp/auth/saml/test" +SAML_VERIFY_METADATA = False SESSION_COOKIE_SECURE = ( False # Test running does not have HTTPS, so secure cookie can't be used. diff --git a/timApp/tests/server/test_signup.py b/timApp/tests/server/test_signup.py index 95b57ece35..70dfb86e26 100644 --- a/timApp/tests/server/test_signup.py +++ b/timApp/tests/server/test_signup.py @@ -63,61 +63,6 @@ """.strip() -class SettingsMock: - def get_sp_data(self): - return { - "attributeConsumingService": { - "requestedAttributes": [ - { - "name": "urn:oid:2.5.4.3", - "isRequired": True, - "friendlyName": "cn", - }, - { - "name": "urn:oid:2.16.840.1.113730.3.1.241", - "isRequired": True, - "friendlyName": "displayName", - }, - { - "name": "urn:oid:1.3.6.1.4.1.5923.1.1.1.6", - "isRequired": True, - "friendlyName": "eduPersonPrincipalName", - }, - { - "name": "urn:oid:1.3.6.1.4.1.5923.1.1.1.11", - "isRequired": True, - "friendlyName": "eduPersonAssurance", - }, - { - "name": "urn:oid:2.5.4.42", - "isRequired": True, - "friendlyName": "givenName", - }, - { - "name": "urn:oid:0.9.2342.19200300.100.1.3", - "isRequired": True, - "friendlyName": "mail", - }, - { - "name": "urn:oid:2.16.840.1.113730.3.1.39", - "isRequired": True, - "friendlyName": "preferredLanguage", - }, - { - "name": "urn:oid:2.5.4.4", - "isRequired": True, - "friendlyName": "sn", - }, - { - "name": "urn:oid:1.3.6.1.4.1.25178.1.2.14", - "isRequired": False, - "friendlyName": "schacPersonalUniqueCode", - }, - ], - }, - } - - LOW_ASSURANCE = "https://refeds.org/assurance/IAP/low" MEDIUM_ASSURANCE = "https://refeds.org/assurance/IAP/medium" HIGH_ASSURANCE = "https://refeds.org/assurance/IAP/high" @@ -125,43 +70,27 @@ def get_sp_data(self): @dataclass -class OneLoginMock: +class SamlSSOResponseMock: info: UserInfo assurance_levels: list[str] mock_missing_uniquecode: bool = False - def process_response(self, request_id): - pass - - def get_errors(self): - return [] - - def is_authenticated(self): - return True - - def get_settings(self): - return SettingsMock() - - def get_attribute(self, name): - values = { - "urn:oid:0.9.2342.19200300.100.1.3": [self.info.email], # mail - "urn:oid:1.3.6.1.4.1.5923.1.1.1.6": [ - self.info.username - ], # eduPersonPrincipalName - "urn:oid:1.3.6.1.4.1.5923.1.1.1.11": [ - self.assurance_levels - ], # eduPersonAssurance - "urn:oid:2.16.840.1.113730.3.1.241": [self.info.full_name], # displayName - "urn:oid:2.16.840.1.113730.3.1.39": ["fi"], # preferredLanguage - "urn:oid:2.5.4.3": [self.info.full_name], # cn - "urn:oid:2.5.4.4": [self.info.last_name], # sn - "urn:oid:2.5.4.42": [self.info.given_name], # givenName + def get_identity(self) -> dict: + res = { + "mail": [self.info.email], + "eduPersonPrincipalName": [self.info.username], + "eduPersonAssurance": self.assurance_levels, + "displayName": [self.info.full_name], + "preferredLanguage": ["fi"], + "cn": [self.info.full_name], + "sn": [self.info.last_name], + "givenName": [self.info.given_name], } if not self.mock_missing_uniquecode: - values["urn:oid:1.3.6.1.4.1.25178.1.2.14"] = [ + res["schacPersonalUniqueCode"] = [ uq.to_urn() for uq in self.info.unique_codes ] - return values.get(name) + return res acs_url = "/saml/acs" @@ -591,7 +520,7 @@ def test_haka_invalid_settings(self): acs_url, {}, expect_status=400, - expect_content="entityID not in session", + expect_content="No entityID in session.", ) self.get( "/saml/sso", @@ -605,7 +534,7 @@ def test_haka_invalid_settings(self): acs_url, data={}, expect_status=400, - expect_content="Error processing SAML response: SAML Response not found, Only supported HTTP_POST Binding", + expect_content="SAML Response is missing", ) self.post( acs_url, @@ -613,7 +542,7 @@ def test_haka_invalid_settings(self): "SAMLResponse": base64.encodebytes(b"x").decode(), }, expect_status=400, - expect_content="Error processing SAML response: Start tag expected, '<' not found, line 1, column 1 (, line 1)", + expect_content="syntax error: line 1, column 0", ) self.post( acs_url, @@ -621,7 +550,7 @@ def test_haka_invalid_settings(self): "SAMLResponse": base64.encodebytes(samltestresp.encode()).decode(), }, expect_status=400, - expect_contains="Error processing SAML response: No private key available to decrypt the assertion, check settings", + expect_contains="Unsolicited response:", ) def test_haka_login(self): @@ -722,6 +651,8 @@ def test_student_id_login_match(self): UserInfo( username="xxxx@jyu.fi", full_name="X Test", + last_name="X", + given_name="Test", email="xxxx@example.com", origin=UserOrigin.Haka, unique_codes=[ @@ -736,6 +667,8 @@ def test_student_id_login_match(self): UserInfo( username="xxxx2@jyu.fi", full_name="X Test", + last_name="X", + given_name="Test", email="xxxx2@example.com", origin=UserOrigin.Haka, unique_codes=[ @@ -765,6 +698,9 @@ def test_haka_login_email_conflict(self): username="sp@jyu.fi", email="somep@example.com", origin=UserOrigin.Haka, + full_name="Person Söme", + last_name="Person", + given_name="Söme", ) ) @@ -773,6 +709,8 @@ def test_missing_uniquecode(self): UserInfo( username="xxxx@jyu.fi", full_name="X Test", + last_name="X", + given_name="Test", email="xxxx@example.com", origin=UserOrigin.Haka, unique_codes=[ @@ -795,8 +733,8 @@ def do_acs_mock( }, expect_status=302, ) - with mock.patch("timApp.auth.saml.OneLogin_Saml2_Auth") as m: - m.return_value = OneLoginMock( + with mock.patch("timApp.auth.saml.routes._get_saml_response") as m: + m.return_value = SamlSSOResponseMock( info=info, mock_missing_uniquecode=missing_uniquecode, assurance_levels=assurance_levels or [LOW_ASSURANCE], From 70bb40d271c97d26b75314310c7eb8e97abc8cc7 Mon Sep 17 00:00:00 2001 From: dezhidki Date: Tue, 8 Nov 2022 10:48:40 +0200 Subject: [PATCH 5/7] saml: Add documentation --- timApp/auth/saml/attributes.py | 92 +++++++++++++++++++++++++++++- timApp/auth/saml/client.py | 13 +++++ timApp/auth/saml/routes.py | 30 ++++++++++ timApp/util/flask/requesthelper.py | 8 ++- 4 files changed, 139 insertions(+), 4 deletions(-) diff --git a/timApp/auth/saml/attributes.py b/timApp/auth/saml/attributes.py index 24f501c78d..9c50e4388e 100644 --- a/timApp/auth/saml/attributes.py +++ b/timApp/auth/saml/attributes.py @@ -14,7 +14,17 @@ @dataclass class SAMLUserAttributes: + """ + User-specific attributes returned by the IdP and used by TIM. + + .. note:: The attribute names are based on the funetEduPersonSchema described in + https://wiki.eduuni.fi/display/CSCHAKA/funetEduPersonSchema2dot4 + """ + response_attribues: dict[str, Any] + """ + Raw attribute data + """ def _get_attribute_safe(self, name: str) -> Optional[_T]: return self.response_attribues.get(name, [None])[0] @@ -27,30 +37,75 @@ def _get_attribute(self, name: str) -> _T: @property def common_name(self) -> str: + """ + Common name of the user. + Generally, the name the individual has indicated as the one (s)he uses + sn + + :return: Common name of the user + """ return self._get_attribute("cn") @property def email(self) -> str: + """ + Email address of the user. + Generally, the preferred address for the "to:" field of email to be sent to this person. + + :return: Email address of the user + """ return self._get_attribute("mail") @property def surname(self) -> str: + """ + Surname of the user. + + :return: Surname of the user + """ return self._get_attribute("sn") @property def given_name(self) -> str: + """ + Given name of the user. + Generally, the preferred given name the person has indicated to be used. + + :return: Given name of the user + """ return self._get_attribute("givenName") @property def display_name(self) -> str: + """ + Preferred name of a person to be used when displaying entries. + Generally, the name the individual has indicated as the one (s)he uses + sn. + + :return: Preferred name of a person to be used when displaying entries + """ return self._get_attribute("displayName") @property def edu_person_principal_name(self) -> str: + """ + A scoped identifier for a person. + Represented in form user@scope where 'user' is a name-based identifier for the person and where the "scope" + is the administrative domain of the identity system where the identifier was created and assigned. + + :return: A scoped identifier for a person + """ return self._get_attribute("eduPersonPrincipalName") @property def edu_person_assurance(self) -> list[str]: + """ + List of identity assurance profiles (IAPs) which are the set of standards that are met by an identity assertion. + In other words, specifies the level of assurance of the user's identity that the IdP is asserting. + + .. note:: Instead, consider using :py:attr:`identity_assurance_proofing` which is a higher-level + abstraction over the value. + + :return: List of identity assurance profiles (IAPs) + """ res = self.response_attribues.get("eduPersonAssurance") if not isinstance(res, list): raise RouteException("eduPersonAssurance is not a list") @@ -58,22 +113,48 @@ def edu_person_assurance(self) -> list[str]: @property def preferred_language(self) -> str: + """ + Preferred written or spoken language for a person. + + :return: Preferred written or spoken language for a person + """ return self._get_attribute("preferredLanguage") @property def eppn_parts(self) -> list[str]: + """ + Parts of the eduPersonPrincipalName attribute split into a list of [username, scope]. + + :return: eduPersonPrincipalName attribute split into a list of [username, scope] + """ return self.edu_person_principal_name.split("@") @property def org(self) -> str: + """ + User's organization. This is essentially the "scope" value of eduPersonPrincipalName. + + :return: User's organization in host format + """ return self.eppn_parts[1] @property def unique_codes(self) -> list[str] | None: + """ + A list of "unique codes" of the user. The values are meant to be unique and allow to identify the user in + the given organization. + + :return: A list of unique codes of the user + """ return self.response_attribues.get("schacPersonalUniqueCode") @property def derived_username(self) -> str: + """ + TIM username derived from the attributes. + + :return: The TIM username of the user + """ uname, org = self.eppn_parts if org == app.config["HOME_ORGANIZATION"]: return uname @@ -81,10 +162,11 @@ def derived_username(self) -> str: @functools.cached_property def identity_assurance_proofing(self) -> IdentityAssuranceProofing: - """Parses and returns the best Identity Assurance Proofing (IAP) level - from eduPersonAssurance based on REFEDS Assurance Framework ver 1.0 spec. + """ + The best Identity Assurance Proofing (IAP) level + that the user has based on the REFEDS Assurance Framework ver 1.0 spec. - :return: Identity assurence proofing level + :return: Identity assurance proofing level """ iap_levels = [ RefedsIapLevel.from_string(level) for level in self.edu_person_assurance @@ -96,6 +178,10 @@ def identity_assurance_proofing(self) -> IdentityAssuranceProofing: ) def to_json(self) -> dict[str, Any]: + """ + Convert the attributes to a JSON-serializable dictionary. + :return: JSON-serializable dictionary of attributes + """ return { "cn": self.common_name, "displayName": self.display_name, diff --git a/timApp/auth/saml/client.py b/timApp/auth/saml/client.py index c3628f13c3..b7d9bfb3b1 100644 --- a/timApp/auth/saml/client.py +++ b/timApp/auth/saml/client.py @@ -38,6 +38,13 @@ def _metadatastore_load(self: MetadataStore, *args: list, **kwargs: dict) -> Non def get_saml_config(metadata_loader: Callable[[], bytes]) -> Saml2Config: + """ + Get the SAML2 configuration for the client. + + :param metadata_loader: Custom loader that provides the XML metadata. + :return: SAML2 configuration + """ + def _do_get_saml_config(try_new_cert: bool, try_new_metadata: bool) -> Saml2Config: saml_path = Path(app.config["SAML_PATH"]) if try_new_cert: @@ -118,5 +125,11 @@ def _do_get_saml_config(try_new_cert: bool, try_new_metadata: bool) -> Saml2Conf def get_saml_client(metadata_loader: Callable[[], bytes]) -> Saml2Client: + """ + Get the SAML2 client used to make SAML requests. + + :param metadata_loader: Custom loader that provides the XML metadata. + :return: SAML2 client + """ saml2_client = Saml2Client(config=get_saml_config(metadata_loader)) return saml2_client diff --git a/timApp/auth/saml/routes.py b/timApp/auth/saml/routes.py index 39c96d86ec..55f4650dae 100644 --- a/timApp/auth/saml/routes.py +++ b/timApp/auth/saml/routes.py @@ -34,6 +34,9 @@ from timApp.util.logger import log_warning saml = TypedBlueprint("saml", __name__, url_prefix="/saml") +""" +Blueprint for SAML routes. +""" @cache.memoize(timeout=3600 * 24) @@ -65,6 +68,16 @@ def sso( debug: bool = False, add_user: bool = field(default=False, metadata={"data_key": "addUser"}), ) -> Response: + """ + Perform single sign-on to TIM via the SAML2 protocol. + This is the starting point of SAML login once the IdP has been selected. + + :param return_to: What URL to return to after successful login? + :param entity_id: IdP entity ID that will provide authentication + :param debug: If true, return debug information instead of redirecting to return_to + :param add_user: Whether to add the logged-in user to the session instead of replacing it + :return: SSO redirect + """ if not logged_in() and add_user: raise AccessDenied("You must be logged in before adding users to session.") @@ -99,6 +112,13 @@ def _get_saml_response( @csrf.exempt @saml.post("/acs") def acs() -> Response: + """ + Handle Assertion Consumer Service (ACS) response from IdP. + Finalise the SSO process and log the user in. + If the user doesn't yet exist in the database, create it. + + :return: Redirect to return_to URL or debug information depending on the information provided during /sso + """ entity_id = session.get("entityID") came_from = session.get("cameFrom") request_id = session.get("requestID") @@ -176,6 +196,11 @@ def acs() -> Response: @saml.get("") def get_metadata() -> Response: + """ + Get the SAML2 metadata for this service in XML format. + + :return: SAML2 metadata of this service provider + """ saml_config = _get_saml_config() try: resp = make_response(create_metadata_string(None, config=saml_config), 200) @@ -189,6 +214,11 @@ def get_metadata() -> Response: @saml.get("/feed") def get_idps() -> Response: + """ + Get a list of IdPs available for login based on active metadata. + + :return: JSON list of IdPs usable for login. + """ config = _get_saml_config() meta: MetadataStore = config.metadata idps = meta.with_descriptor("idpsso") diff --git a/timApp/util/flask/requesthelper.py b/timApp/util/flask/requesthelper.py index da2a7bc11d..bf5a3f05a9 100644 --- a/timApp/util/flask/requesthelper.py +++ b/timApp/util/flask/requesthelper.py @@ -106,7 +106,13 @@ def is_localhost() -> bool: def get_active_host_url() -> str: - # check if inside request context + """ + Returns the URL of the currently active host URL. + If the call is made while handling a request, the host that was used in the request is returned. + Otherwise, the host URL is read from the configuration. + + :return: Active host URL + """ if has_request_context(): return request.host_url return f"{current_app.config['TIM_HOST']}/" From a04f1f4b5bdff46d450b5419da07deb6d37b038c Mon Sep 17 00:00:00 2001 From: dezhidki Date: Tue, 8 Nov 2022 10:51:49 +0200 Subject: [PATCH 6/7] saml: Improve type assertion for /feed --- timApp/auth/saml/routes.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/timApp/auth/saml/routes.py b/timApp/auth/saml/routes.py index 55f4650dae..e93be26002 100644 --- a/timApp/auth/saml/routes.py +++ b/timApp/auth/saml/routes.py @@ -235,10 +235,12 @@ def get_idps() -> Response: ui_info = next( (d for d in ext_elems if d["__class__"].endswith("ui&UIInfo")), None ) - assert isinstance(ui_info, dict) if ui_info is None: log_warning(f"SAML: Could not find UIInfo for {entity_id}") continue + if not isinstance(ui_info, dict): + log_warning(f"SAML: UIInfo is not a dict for {entity_id}") + continue # get display names display_names = [ { From 4c7906f95cdb35d6e3ad69565a9731c808979ae9 Mon Sep 17 00:00:00 2001 From: dezhidki Date: Wed, 9 Nov 2022 15:02:56 +0200 Subject: [PATCH 7/7] saml.client: Emit separate error message when validation fails --- timApp/auth/saml/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/timApp/auth/saml/client.py b/timApp/auth/saml/client.py index b7d9bfb3b1..2368b7dbb2 100644 --- a/timApp/auth/saml/client.py +++ b/timApp/auth/saml/client.py @@ -111,7 +111,7 @@ def _do_get_saml_config(try_new_cert: bool, try_new_metadata: bool) -> Saml2Conf log_warning(err) errors.append(err) except SignatureError as e: - err = f"SAML (new_cert={new_cert}, new_meta={new_meta}): Could not load SAML config: {e}" + err = f"SAML (new_cert={new_cert}, new_meta={new_meta}): Could not validate SAML metadata: {e}" log_warning(err) errors.append(err)