From e59aa5f2fd3d5550619db51d771e653847385f24 Mon Sep 17 00:00:00 2001 From: lhchavez Date: Thu, 20 Oct 2022 20:37:58 +0000 Subject: [PATCH] =?UTF-8?q?[=F0=9F=91=BB]=20Add=20support=20for=20verifyin?= =?UTF-8?q?g=20Ghostwriter=20tokens?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We are going to start using Replit Identity to authenticate against Ghostwriter for better latency. This change is the first implementation, with sufficient support to validate Ghostwriter tokens! --- .flake8 | 2 + .semaphore/semaphore.yml | 2 +- docs/index.rst | 1 + poetry.lock | 384 ++++++++++++++++-- pyproject.toml | 9 +- src/replit/goval/__init__.py | 1 + src/replit/goval/api/__init__.py | 0 src/replit/goval/api/client_pb2.py | 53 +++ src/replit/goval/api/features/__init__.py | 0 src/replit/goval/api/features/features_pb2.py | 30 ++ src/replit/goval/api/repl/__init__.py | 0 src/replit/goval/api/repl/repl_pb2.py | 37 ++ src/replit/goval/api/signing_pb2.py | 40 ++ src/replit/identity/__init__.py | 4 + src/replit/identity/exceptions.py | 7 + src/replit/identity/verify.py | 256 ++++++++++++ tests/test_identity.py | 31 ++ 17 files changed, 832 insertions(+), 25 deletions(-) create mode 100644 src/replit/goval/__init__.py create mode 100644 src/replit/goval/api/__init__.py create mode 100644 src/replit/goval/api/client_pb2.py create mode 100644 src/replit/goval/api/features/__init__.py create mode 100644 src/replit/goval/api/features/features_pb2.py create mode 100644 src/replit/goval/api/repl/__init__.py create mode 100644 src/replit/goval/api/repl/repl_pb2.py create mode 100644 src/replit/goval/api/signing_pb2.py create mode 100644 src/replit/identity/__init__.py create mode 100644 src/replit/identity/exceptions.py create mode 100644 src/replit/identity/verify.py create mode 100644 tests/test_identity.py diff --git a/.flake8 b/.flake8 index 741ed15b..e0c85c52 100644 --- a/.flake8 +++ b/.flake8 @@ -4,6 +4,8 @@ ignore = E203,W503,ANN101,ANN102,S322,ANN206,S201,D105,D107 per-file-ignores = src/replit/__init__.py:F401 src/replit/web/__init__.py:F401 +exclude = + src/replit/goval/api max-line-length = 88 application-import-names = vidgen,tests diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml index 24efc6e4..dde56b1e 100644 --- a/.semaphore/semaphore.yml +++ b/.semaphore/semaphore.yml @@ -18,7 +18,7 @@ blocks: - checkout --use-cache - python -m pip install --upgrade poetry - poetry install - - git diff origin/master | poetry run flake8 --diff + - poetry run flake8 - name: unittest commands: - sem-version python 3.8 diff --git a/docs/index.rst b/docs/index.rst index c0ba3c97..5440758c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -15,6 +15,7 @@ provides: - A fully-featured `Replit DB `_ client and CLI. - A Flaskā€“based application framework for accellerating development on the platform. - A simple audio library that can play tones and audio files! +- A library to authenticate between Repls. Table of Contents ================= diff --git a/poetry.lock b/poetry.lock index 80421bc5..8a5cc322 100644 --- a/poetry.lock +++ b/poetry.lock @@ -16,7 +16,7 @@ multidict = ">=4.5,<7.0" yarl = ">=1.0,<2.0" [package.extras] -speedups = ["aiodns", "brotli", "cchardet"] +speedups = ["Brotli", "aiodns", "cchardet"] [[package]] name = "aiosignal" @@ -45,6 +45,37 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "argon2-cffi" +version = "21.3.0" +description = "The secure Argon2 password hashing algorithm." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +argon2-cffi-bindings = "*" + +[package.extras] +dev = ["cogapp", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "pre-commit", "pytest", "sphinx", "sphinx-notfound-page", "tomli"] +docs = ["furo", "sphinx", "sphinx-notfound-page"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pytest"] + +[[package]] +name = "argon2-cffi-bindings" +version = "21.2.0" +description = "Low-level CFFI bindings for Argon2" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +cffi = ">=1.0.1" + +[package.extras] +dev = ["cogapp", "pre-commit", "pytest", "wheel"] +tests = ["pytest"] + [[package]] name = "async-timeout" version = "4.0.2" @@ -62,10 +93,10 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.extras] -dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] -docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] -tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] -tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"] +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"] [[package]] name = "babel" @@ -95,7 +126,7 @@ stevedore = ">=1.20.0" [package.extras] test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)", "toml"] toml = ["toml"] -yaml = ["pyyaml"] +yaml = ["PyYAML"] [[package]] name = "black" @@ -125,6 +156,17 @@ category = "main" optional = false python-versions = "*" +[[package]] +name = "cffi" +version = "1.15.1" +description = "Foreign Function Interface for Python calling C code." +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +pycparser = "*" + [[package]] name = "charset-normalizer" version = "2.0.12" @@ -134,7 +176,7 @@ optional = false python-versions = ">=3.5.0" [package.extras] -unicode_backport = ["unicodedata2"] +unicode-backport = ["unicodedata2"] [[package]] name = "click" @@ -163,6 +205,25 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" [package.extras] toml = ["toml"] +[[package]] +name = "cryptography" +version = "38.0.1" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +cffi = ">=1.12" + +[package.extras] +docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx-rtd-theme"] +docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"] +pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"] +sdist = ["setuptools-rust (>=0.11.4)"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["hypothesis (>=1.11.4,!=3.79.2)", "iso8601", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-subtests", "pytest-xdist", "pytz"] + [[package]] name = "darglint" version = "1.8.1" @@ -243,7 +304,7 @@ attrs = ">=19.2.0" flake8 = ">=3.0.0" [package.extras] -dev = ["coverage", "black", "hypothesis", "hypothesmith"] +dev = ["black", "coverage", "hypothesis", "hypothesmith"] [[package]] name = "flake8-docstrings" @@ -267,6 +328,7 @@ python-versions = "*" [package.dependencies] pycodestyle = "*" +setuptools = "*" [[package]] name = "flake8-polyfill" @@ -343,6 +405,14 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +[[package]] +name = "iso8601" +version = "1.1.0" +description = "Simple module to parse ISO 8601 dates" +category = "main" +optional = false +python-versions = ">=3.6.2,<4.0" + [[package]] name = "itsdangerous" version = "2.1.0" @@ -436,6 +506,23 @@ python-versions = ">=3.6" [package.dependencies] pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" +[[package]] +name = "passlib" +version = "1.7.4" +description = "comprehensive password hashing framework supporting over 30 schemes" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +argon2-cffi = {version = ">=18.2.0", optional = true, markers = "extra == \"argon2\""} + +[package.extras] +argon2 = ["argon2-cffi (>=18.2.0)"] +bcrypt = ["bcrypt (>=3.1.0)"] +build-docs = ["cloud-sptheme (>=1.10.1)", "sphinx (>=1.6)", "sphinxcontrib-fulltoc (>=1.2.0)"] +totp = ["cryptography"] + [[package]] name = "pathspec" version = "0.9.0" @@ -452,6 +539,14 @@ category = "dev" optional = false python-versions = ">=2.6" +[[package]] +name = "protobuf" +version = "4.21.8" +description = "" +category = "main" +optional = false +python-versions = ">=3.7" + [[package]] name = "pycodestyle" version = "2.7.0" @@ -460,6 +555,22 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +[[package]] +name = "pycparser" +version = "2.21" +description = "C parser in Python" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "pycryptodomex" +version = "3.15.0" +description = "Cryptographic library for Python" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + [[package]] name = "pydocstyle" version = "6.1.1" @@ -501,6 +612,23 @@ python-versions = ">=3.6" [package.extras] diagrams = ["jinja2", "railroad-diagrams"] +[[package]] +name = "pyseto" +version = "1.6.11" +description = "A Python implementation of PASETO/PASERK." +category = "main" +optional = false +python-versions = ">=3.6.2,<4.0.0" + +[package.dependencies] +cryptography = ">=36,<39" +iso8601 = ">=1.0.2,<2.0.0" +passlib = {version = ">=1.7.4,<2.0.0", extras = ["argon2"]} +pycryptodomex = ">=3.12.0,<4.0.0" + +[package.extras] +docs = ["Sphinx[docs] (>=4.3.2,<6.0.0)", "sphinx-autodoc-typehints[docs] (==1.12.0)", "sphinx-rtd-theme[docs] (>=1.0.0,<2.0.0)"] + [[package]] name = "pytz" version = "2021.3" @@ -541,7 +669,20 @@ urllib3 = ">=1.21.1,<1.27" [package.extras] socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] -use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<5)"] + +[[package]] +name = "setuptools" +version = "65.5.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mock", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "six" @@ -585,6 +726,7 @@ Jinja2 = ">=2.3" packaging = "*" Pygments = ">=2.0" requests = ">=2.5.0" +setuptools = "*" snowballstemmer = ">=1.1" sphinxcontrib-applehelp = "*" sphinxcontrib-devhelp = "*" @@ -595,8 +737,8 @@ sphinxcontrib-serializinghtml = "*" [package.extras] docs = ["sphinxcontrib-websupport"] -lint = ["flake8 (>=3.5.0)", "isort", "mypy (>=0.800)", "docutils-stubs"] -test = ["pytest", "pytest-cov", "html5lib", "cython", "typed-ast"] +lint = ["docutils-stubs", "flake8 (>=3.5.0)", "isort", "mypy (>=0.800)"] +test = ["cython", "html5lib", "pytest", "pytest-cov", "typed-ast"] [[package]] name = "sphinx-autobuild" @@ -625,8 +767,8 @@ python-versions = ">=3.6" Sphinx = ">=3.0" [package.extras] -test = ["pytest (>=3.1.0)", "typing-extensions (>=3.5)", "sphobjinv (>=2.0)", "Sphinx (>=3.2.0)", "dataclasses"] -type_comments = ["typed-ast (>=1.4.0)"] +test = ["Sphinx (>=3.2.0)", "dataclasses", "pytest (>=3.1.0)", "sphobjinv (>=2.0)", "typing-extensions (>=3.5)"] +type-comments = ["typed-ast (>=1.4.0)"] [[package]] name = "sphinx-click" @@ -654,7 +796,7 @@ docutils = "<0.17" sphinx = "*" [package.extras] -dev = ["transifex-client", "sphinxcontrib-httpdomain", "bump2version"] +dev = ["bump2version", "sphinxcontrib-httpdomain", "transifex-client"] [[package]] name = "sphinxcontrib-applehelp" @@ -665,7 +807,7 @@ optional = false python-versions = ">=3.5" [package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] +lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] [[package]] @@ -677,7 +819,7 @@ optional = false python-versions = ">=3.5" [package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] +lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] [[package]] @@ -689,8 +831,8 @@ optional = false python-versions = ">=3.6" [package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] -test = ["pytest", "html5lib"] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["html5lib", "pytest"] [[package]] name = "sphinxcontrib-jsmath" @@ -701,7 +843,7 @@ optional = false python-versions = ">=3.5" [package.extras] -test = ["pytest", "flake8", "mypy"] +test = ["flake8", "mypy", "pytest"] [[package]] name = "sphinxcontrib-qthelp" @@ -712,7 +854,7 @@ optional = false python-versions = ">=3.5" [package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] +lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] [[package]] @@ -724,7 +866,7 @@ optional = false python-versions = ">=3.5" [package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] +lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] [[package]] @@ -780,7 +922,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" [package.extras] brotli = ["brotlipy (>=0.6.0)"] -secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] @@ -809,7 +951,7 @@ multidict = ">=4.0" [metadata] lock-version = "1.1" python-versions = "^3.8" -content-hash = "5212591e3a37e3cffa932f30e710feae18ac8a498a2bea724fea7321c3704240" +content-hash = "fd0c1136b52d0b4879a63b94e8d5b1ab841597fb195dc0f868785d05cdc7e9f8" [metadata.files] aiohttp = [ @@ -898,6 +1040,33 @@ appdirs = [ {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, ] +argon2-cffi = [ + {file = "argon2-cffi-21.3.0.tar.gz", hash = "sha256:d384164d944190a7dd7ef22c6aa3ff197da12962bd04b17f64d4e93d934dba5b"}, + {file = "argon2_cffi-21.3.0-py3-none-any.whl", hash = "sha256:8c976986f2c5c0e5000919e6de187906cfd81fb1c72bf9d88c01177e77da7f80"}, +] +argon2-cffi-bindings = [ + {file = "argon2-cffi-bindings-21.2.0.tar.gz", hash = "sha256:bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ccb949252cb2ab3a08c02024acb77cfb179492d5701c7cbdbfd776124d4d2367"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9524464572e12979364b7d600abf96181d3541da11e23ddf565a32e70bd4dc0d"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b746dba803a79238e925d9046a63aa26bf86ab2a2fe74ce6b009a1c3f5c8f2ae"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58ed19212051f49a523abb1dbe954337dc82d947fb6e5a0da60f7c8471a8476c"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:bd46088725ef7f58b5a1ef7ca06647ebaf0eb4baff7d1d0d177c6cc8744abd86"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_i686.whl", hash = "sha256:8cd69c07dd875537a824deec19f978e0f2078fdda07fd5c42ac29668dda5f40f"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f1152ac548bd5b8bcecfb0b0371f082037e47128653df2e8ba6e914d384f3c3e"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win32.whl", hash = "sha256:603ca0aba86b1349b147cab91ae970c63118a0f30444d4bc80355937c950c082"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win_amd64.whl", hash = "sha256:b2ef1c30440dbbcba7a5dc3e319408b59676e2e039e2ae11a8775ecf482b192f"}, + {file = "argon2_cffi_bindings-21.2.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e415e3f62c8d124ee16018e491a009937f8cf7ebf5eb430ffc5de21b900dad93"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3e385d1c39c520c08b53d63300c3ecc28622f076f4c2b0e6d7e796e9f6502194"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3e3cc67fdb7d82c4718f19b4e7a87123caf8a93fde7e23cf66ac0337d3cb3f"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a22ad9800121b71099d0fb0a65323810a15f2e292f2ba450810a7316e128ee5"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9f8b450ed0547e3d473fdc8612083fd08dd2120d6ac8f73828df9b7d45bb351"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:93f9bf70084f97245ba10ee36575f0c3f1e7d7724d67d8e5b08e61787c320ed7"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3b9ef65804859d335dc6b31582cad2c5166f0c3e7975f324d9ffaa34ee7e6583"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4966ef5848d820776f5f562a7d45fdd70c2f330c961d0d745b784034bd9f48d"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ef543a89dee4db46a1a6e206cd015360e5a75822f76df533845c3cbaf72670"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed2937d286e2ad0cc79a7087d3c272832865f779430e0cc2b4f3718d3159b0cb"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5e00316dabdaea0b2dd82d141cc66889ced0cdcbfa599e8b471cf22c620c329a"}, +] async-timeout = [ {file = "async-timeout-4.0.2.tar.gz", hash = "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15"}, {file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"}, @@ -922,6 +1091,72 @@ certifi = [ {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"}, {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"}, ] +cffi = [ + {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, + {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, + {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, + {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, + {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, + {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, + {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, + {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, + {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, + {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, + {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, + {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, + {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, + {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, + {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, + {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, + {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, + {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, + {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, +] charset-normalizer = [ {file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"}, {file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"}, @@ -988,6 +1223,34 @@ coverage = [ {file = "coverage-5.5-pp37-none-any.whl", hash = "sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4"}, {file = "coverage-5.5.tar.gz", hash = "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c"}, ] +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"}, +] darglint = [ {file = "darglint-1.8.1-py3-none-any.whl", hash = "sha256:5ae11c259c17b0701618a20c3da343a3eb98b3bc4b5a83d31cdd94f5ebdced8d"}, {file = "darglint-1.8.1.tar.gz", hash = "sha256:080d5106df149b199822e7ee7deb9c012b49891538f14a11be681044f0bb20da"}, @@ -1108,6 +1371,10 @@ imagesize = [ {file = "imagesize-1.3.0-py2.py3-none-any.whl", hash = "sha256:1db2f82529e53c3e929e8926a1fa9235aa82d0bd0c580359c67ec31b2fddaa8c"}, {file = "imagesize-1.3.0.tar.gz", hash = "sha256:cd1750d452385ca327479d45b64d9c7729ecf0b3969a58148298c77092261f9d"}, ] +iso8601 = [ + {file = "iso8601-1.1.0-py3-none-any.whl", hash = "sha256:8400e90141bf792bce2634df533dc57e3bee19ea120a87bebcd3da89a58ad73f"}, + {file = "iso8601-1.1.0.tar.gz", hash = "sha256:32811e7b81deee2063ea6d2e94f8819a86d1f3811e49d23623a41fa832bef03f"}, +] itsdangerous = [ {file = "itsdangerous-2.1.0-py3-none-any.whl", hash = "sha256:29285842166554469a56d427addc0843914172343784cb909695fdbe90a3e129"}, {file = "itsdangerous-2.1.0.tar.gz", hash = "sha256:d848fcb8bc7d507c4546b448574e8a44fc4ea2ba84ebf8d783290d53e81992f5"}, @@ -1250,6 +1517,10 @@ packaging = [ {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, ] +passlib = [ + {file = "passlib-1.7.4-py2.py3-none-any.whl", hash = "sha256:aa6bca462b8d8bda89c70b382f0c298a20b5560af6cbfa2dce410c0a2fb669f1"}, + {file = "passlib-1.7.4.tar.gz", hash = "sha256:defd50f72b65c5402ab2c573830a6978e5f202ad0d984793c8dde2c4152ebe04"}, +] pathspec = [ {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, @@ -1258,10 +1529,62 @@ pbr = [ {file = "pbr-5.8.1-py2.py3-none-any.whl", hash = "sha256:27108648368782d07bbf1cb468ad2e2eeef29086affd14087a6d04b7de8af4ec"}, {file = "pbr-5.8.1.tar.gz", hash = "sha256:66bc5a34912f408bb3925bf21231cb6f59206267b7f63f3503ef865c1a292e25"}, ] +protobuf = [ + {file = "protobuf-4.21.8-cp310-abi3-win32.whl", hash = "sha256:c252c55ee15175aa1b21b7b9896e6add5162d066d5202e75c39f96136f08cce3"}, + {file = "protobuf-4.21.8-cp310-abi3-win_amd64.whl", hash = "sha256:809ca0b225d3df42655a12f311dd0f4148a943c51f1ad63c38343e457492b689"}, + {file = "protobuf-4.21.8-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bbececaf3cfea9ea65ebb7974e6242d310d2a7772a6f015477e0d79993af4511"}, + {file = "protobuf-4.21.8-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:b02eabb9ebb1a089ed20626a90ad7a69cee6bcd62c227692466054b19c38dd1f"}, + {file = "protobuf-4.21.8-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:4761201b93e024bb70ee3a6a6425d61f3152ca851f403ba946fb0cde88872661"}, + {file = "protobuf-4.21.8-cp37-cp37m-win32.whl", hash = "sha256:f2d55ff22ec300c4d954d3b0d1eeb185681ec8ad4fbecff8a5aee6a1cdd345ba"}, + {file = "protobuf-4.21.8-cp37-cp37m-win_amd64.whl", hash = "sha256:c5f94911dd8feb3cd3786fc90f7565c9aba7ce45d0f254afd625b9628f578c3f"}, + {file = "protobuf-4.21.8-cp38-cp38-win32.whl", hash = "sha256:b37b76efe84d539f16cba55ee0036a11ad91300333abd213849cbbbb284b878e"}, + {file = "protobuf-4.21.8-cp38-cp38-win_amd64.whl", hash = "sha256:2c92a7bfcf4ae76a8ac72e545e99a7407e96ffe52934d690eb29a8809ee44d7b"}, + {file = "protobuf-4.21.8-cp39-cp39-win32.whl", hash = "sha256:89d641be4b5061823fa0e463c50a2607a97833e9f8cfb36c2f91ef5ccfcc3861"}, + {file = "protobuf-4.21.8-cp39-cp39-win_amd64.whl", hash = "sha256:bc471cf70a0f53892fdd62f8cd4215f0af8b3f132eeee002c34302dff9edd9b6"}, + {file = "protobuf-4.21.8-py2.py3-none-any.whl", hash = "sha256:a55545ce9eec4030cf100fcb93e861c622d927ef94070c1a3c01922902464278"}, + {file = "protobuf-4.21.8-py3-none-any.whl", hash = "sha256:0f236ce5016becd989bf39bd20761593e6d8298eccd2d878eda33012645dc369"}, + {file = "protobuf-4.21.8.tar.gz", hash = "sha256:427426593b55ff106c84e4a88cac855175330cb6eb7e889e85aaa7b5652b686d"}, +] pycodestyle = [ {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, ] +pycparser = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] +pycryptodomex = [ + {file = "pycryptodomex-3.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:6f5b6ba8aefd624834bc177a2ac292734996bb030f9d1b388e7504103b6fcddf"}, + {file = "pycryptodomex-3.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:4540904c09704b6f831059c0dfb38584acb82cb97b0125cd52688c1f1e3fffa6"}, + {file = "pycryptodomex-3.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:0fadb9f7fa3150577800eef35f62a8a24b9ddf1563ff060d9bd3af22d3952c8c"}, + {file = "pycryptodomex-3.15.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:fc9bc7a9b79fe5c750fc81a307052f8daabb709bdaabb0fb18fb136b66b653b5"}, + {file = "pycryptodomex-3.15.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:f8be976cec59b11f011f790b88aca67b4ea2bd286578d0bd3e31bcd19afcd3e4"}, + {file = "pycryptodomex-3.15.0-cp27-cp27m-manylinux2014_aarch64.whl", hash = "sha256:78d9621cf0ea35abf2d38fa2ca6d0634eab6c991a78373498ab149953787e5e5"}, + {file = "pycryptodomex-3.15.0-cp27-cp27m-win32.whl", hash = "sha256:b6306403228edde6e289f626a3908a2f7f67c344e712cf7c0a508bab3ad9e381"}, + {file = "pycryptodomex-3.15.0-cp27-cp27m-win_amd64.whl", hash = "sha256:48697790203909fab02a33226fda546604f4e2653f9d47bc5d3eb40879fa7c64"}, + {file = "pycryptodomex-3.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:18e2ab4813883ae63396c0ffe50b13554b32bb69ec56f0afaf052e7a7ae0d55b"}, + {file = "pycryptodomex-3.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:3709f13ca3852b0b07fc04a2c03b379189232b24007c466be0f605dd4723e9d4"}, + {file = "pycryptodomex-3.15.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:191e73bc84a8064ad1874dba0ebadedd7cce4dedee998549518f2c74a003b2e1"}, + {file = "pycryptodomex-3.15.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:e3164a18348bd53c69b4435ebfb4ac8a4076291ffa2a70b54f0c4b80c7834b1d"}, + {file = "pycryptodomex-3.15.0-cp27-cp27mu-manylinux2014_aarch64.whl", hash = "sha256:5676a132169a1c1a3712edf25250722ebc8c9102aa9abd814df063ca8362454f"}, + {file = "pycryptodomex-3.15.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:e2b12968522a0358b8917fc7b28865acac002f02f4c4c6020fcb264d76bfd06d"}, + {file = "pycryptodomex-3.15.0-cp35-abi3-manylinux1_i686.whl", hash = "sha256:e47bf8776a7e15576887f04314f5228c6527b99946e6638cf2f16da56d260cab"}, + {file = "pycryptodomex-3.15.0-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:996e1ba717077ce1e6d4849af7a1426f38b07b3d173b879e27d5e26d2e958beb"}, + {file = "pycryptodomex-3.15.0-cp35-abi3-manylinux2010_i686.whl", hash = "sha256:65204412d0c6a8e3c41e21e93a5e6054a74fea501afa03046a388cf042e3377a"}, + {file = "pycryptodomex-3.15.0-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:dd452a5af7014e866206d41751886c9b4bf379a339fdf2dbfc7dd16c0fb4f8e0"}, + {file = "pycryptodomex-3.15.0-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:b9279adc16e4b0f590ceff581f53a80179b02cba9056010d733eb4196134a870"}, + {file = "pycryptodomex-3.15.0-cp35-abi3-win32.whl", hash = "sha256:46b3f05f2f7ac7841053da4e0f69616929ca3c42f238c405f6c3df7759ad2780"}, + {file = "pycryptodomex-3.15.0-cp35-abi3-win_amd64.whl", hash = "sha256:8eecdf9cdc7343001d047f951b9cc805cd68cb6cd77b20ea46af5bffc5bd3dfb"}, + {file = "pycryptodomex-3.15.0-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:67e1e6a92151023ccdfcfbc0afb3314ad30080793b4c27956ea06ab1fb9bcd8a"}, + {file = "pycryptodomex-3.15.0-pp27-pypy_73-manylinux1_x86_64.whl", hash = "sha256:c4cb9cb492ea7dcdf222a8d19a1d09002798ea516aeae8877245206d27326d86"}, + {file = "pycryptodomex-3.15.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:94c7b60e1f52e1a87715571327baea0733708ab4723346598beca4a3b6879794"}, + {file = "pycryptodomex-3.15.0-pp27-pypy_73-win32.whl", hash = "sha256:04cc393045a8f19dd110c975e30f38ed7ab3faf21ede415ea67afebd95a22380"}, + {file = "pycryptodomex-3.15.0-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0776bfaf2c48154ab54ea45392847c1283d2fcf64e232e85565f858baedfc1fa"}, + {file = "pycryptodomex-3.15.0-pp36-pypy36_pp73-manylinux1_x86_64.whl", hash = "sha256:463119d7d22d0fc04a0f9122e9d3e6121c6648bcb12a052b51bd1eed1b996aa2"}, + {file = "pycryptodomex-3.15.0-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:a07a64709e366c2041cd5cfbca592b43998bf4df88f7b0ca73dca37071ccf1bd"}, + {file = "pycryptodomex-3.15.0-pp36-pypy36_pp73-win32.whl", hash = "sha256:35a8f7afe1867118330e2e0e0bf759c409e28557fb1fc2fbb1c6c937297dbe9a"}, + {file = "pycryptodomex-3.15.0.tar.gz", hash = "sha256:7341f1bb2dadb0d1a0047f34c3a58208a92423cdbd3244d998e4b28df5eac0ed"}, +] pydocstyle = [ {file = "pydocstyle-6.1.1-py3-none-any.whl", hash = "sha256:6987826d6775056839940041beef5c08cc7e3d71d63149b48e36727f70144dc4"}, {file = "pydocstyle-6.1.1.tar.gz", hash = "sha256:1d41b7c459ba0ee6c345f2eb9ae827cab14a7533a88c5c6f7e94923f72df92dc"}, @@ -1278,6 +1601,10 @@ pyparsing = [ {file = "pyparsing-3.0.7-py3-none-any.whl", hash = "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"}, {file = "pyparsing-3.0.7.tar.gz", hash = "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea"}, ] +pyseto = [ + {file = "pyseto-1.6.11-py3-none-any.whl", hash = "sha256:f2bf839ae7c9914999c9d6c1168bf9fbada734f49880a218ccd07d7492a0e003"}, + {file = "pyseto-1.6.11.tar.gz", hash = "sha256:928f7890bacd156480b31b2f6693120634cde09023beee6c24fb8eb9154a2940"}, +] pytz = [ {file = "pytz-2021.3-py2.py3-none-any.whl", hash = "sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c"}, {file = "pytz-2021.3.tar.gz", hash = "sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326"}, @@ -1290,6 +1617,13 @@ pyyaml = [ {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, + {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"}, + {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"}, + {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"}, + {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"}, {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, @@ -1397,6 +1731,10 @@ requests = [ {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"}, {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"}, ] +setuptools = [ + {file = "setuptools-65.5.0-py3-none-any.whl", hash = "sha256:f62ea9da9ed6289bfe868cd6845968a2c854d1427f8548d52cae02a42b4f0356"}, + {file = "setuptools-65.5.0.tar.gz", hash = "sha256:512e5536220e38146176efb833d4a62aa726b7bbff82cfbc8ba9eaa3996e0b17"}, +] six = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, diff --git a/pyproject.toml b/pyproject.toml index d9c95af7..2a0250b6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "replit" -version = "3.2.4" +version = "3.2.5" description = "A library for interacting with features of repl.it" authors = ["Repl.it ", "mat ", "kennethreitz ", "Scoder12 ", "AllAwesome497", ] license = "ISC" @@ -16,6 +16,8 @@ Flask = "^2.0.0" Werkzeug = "^2.0.0" aiohttp = "^3.6.2" requests = "^2.25.1" +pyseto = "^1.6.11" +protobuf = "^4.21.8" [tool.poetry.dev-dependencies] flake8 = "^3.8.3" @@ -41,3 +43,8 @@ build-backend = "poetry.masonry.api" [tool.poetry.scripts] replit = "replit.__main__:cli" + +[tool.mypy] +exclude = [ + "_pb2.py$", # Generated code +] diff --git a/src/replit/goval/__init__.py b/src/replit/goval/__init__.py new file mode 100644 index 00000000..b224abb6 --- /dev/null +++ b/src/replit/goval/__init__.py @@ -0,0 +1 @@ +"""The replit Python package.""" diff --git a/src/replit/goval/api/__init__.py b/src/replit/goval/api/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/replit/goval/api/client_pb2.py b/src/replit/goval/api/client_pb2.py new file mode 100644 index 00000000..cc539ba1 --- /dev/null +++ b/src/replit/goval/api/client_pb2.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: replit/goval/api/client.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 +from replit.goval.api.repl import repl_pb2 as replit_dot_goval_dot_api_dot_repl_dot_repl__pb2 +from replit.goval.api.features import features_pb2 as replit_dot_goval_dot_api_dot_features_dot_features__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1dreplit/goval/api/client.proto\x12\x10replit.goval.api\x1a\x1fgoogle/protobuf/timestamp.proto\x1a replit/goval/api/repl/repl.proto\x1a(replit/goval/api/features/features.proto\"\xd1\x06\n\tReplToken\x12\'\n\x03iat\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\'\n\x03\x65xp\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0c\n\x04salt\x18\x03 \x01(\t\x12\x0f\n\x07\x63luster\x18\x04 \x01(\t\x12\x37\n\x0bpersistence\x18\x06 \x01(\x0e\x32\".replit.goval.api.repl.Persistence\x12+\n\x04repl\x18\x07 \x01(\x0b\x32\x1b.replit.goval.api.repl.ReplH\x00\x12\x30\n\x02id\x18\x08 \x01(\x0b\x32\".replit.goval.api.ReplToken.ReplIDH\x00\x12\x46\n\tclassroom\x18\t \x01(\x0b\x32-.replit.goval.api.ReplToken.ClassroomMetadataB\x02\x18\x01H\x00\x12=\n\x0eresourceLimits\x18\n \x01(\x0b\x32%.replit.goval.api.repl.ResourceLimits\x12\x36\n\x06\x66ormat\x18\x0c \x01(\x0e\x32&.replit.goval.api.ReplToken.WireFormat\x12\x38\n\tpresenced\x18\r \x01(\x0b\x32%.replit.goval.api.ReplToken.Presenced\x12\r\n\x05\x66lags\x18\x0e \x03(\t\x12\x37\n\x0bpermissions\x18\x0f \x01(\x0b\x32\".replit.goval.api.repl.Permissions\x12\x34\n\x08\x66\x65\x61tures\x18\x10 \x03(\x0b\x32\".replit.goval.api.features.Feature\x1a\x31\n\x11\x43lassroomMetadata\x12\n\n\x02id\x18\x01 \x01(\t\x12\x10\n\x08language\x18\x02 \x01(\t\x1a(\n\x06ReplID\x12\n\n\x02id\x18\x01 \x01(\t\x12\x12\n\nsourceRepl\x18\x02 \x01(\t\x1a\x31\n\tPresenced\x12\x10\n\x08\x62\x65\x61rerID\x18\x01 \x01(\r\x12\x12\n\nbearerName\x18\x02 \x01(\t\"(\n\nWireFormat\x12\x0c\n\x08PROTOBUF\x10\x00\x12\x0c\n\x04JSON\x10\x01\x1a\x02\x08\x01\x42\n\n\x08metadata\".\n\x0eTLSCertificate\x12\x0e\n\x06\x64omain\x18\x01 \x01(\t\x12\x0c\n\x04\x63\x65rt\x18\x02 \x01(\x0c\"\x8d\x02\n\x0cReplTransfer\x12)\n\x04repl\x18\x01 \x01(\x0b\x32\x1b.replit.goval.api.repl.Repl\x12\x39\n\nreplLimits\x18\x02 \x01(\x0b\x32%.replit.goval.api.repl.ResourceLimits\x12\x39\n\nuserLimits\x18\x03 \x01(\x0b\x32%.replit.goval.api.repl.ResourceLimits\x12\x15\n\rcustomDomains\x18\x04 \x03(\t\x12\x36\n\x0c\x63\x65rtificates\x18\x05 \x03(\x0b\x32 .replit.goval.api.TLSCertificate\x12\r\n\x05\x66lags\x18\x06 \x03(\t\"H\n\x10\x41llowReplRequest\x12\x34\n\x0creplTransfer\x18\x01 \x01(\x0b\x32\x1e.replit.goval.api.ReplTransfer\"Y\n\x0f\x43lusterMetadata\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\tconmanURL\x18\x02 \x01(\t\x12\x0c\n\x04gurl\x18\x03 \x01(\t\x12\r\n\x05proxy\x18\x05 \x01(\tJ\x04\x08\x04\x10\x05J\x04\x08\x06\x10\x07\"y\n\x10\x45victReplRequest\x12:\n\x0f\x63lusterMetadata\x18\x01 \x01(\x0b\x32!.replit.goval.api.ClusterMetadata\x12\r\n\x05token\x18\x02 \x01(\t\x12\x0c\n\x04user\x18\x03 \x01(\t\x12\x0c\n\x04slug\x18\x04 \x01(\t\"I\n\x11\x45victReplResponse\x12\x34\n\x0creplTransfer\x18\x01 \x01(\x0b\x32\x1e.replit.goval.api.ReplTransferB\x1dZ\x1bgithub.com/replit/goval/apib\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'replit.goval.api.client_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'Z\033github.com/replit/goval/api' + _REPLTOKEN_WIREFORMAT.values_by_name["JSON"]._options = None + _REPLTOKEN_WIREFORMAT.values_by_name["JSON"]._serialized_options = b'\010\001' + _REPLTOKEN.fields_by_name['classroom']._options = None + _REPLTOKEN.fields_by_name['classroom']._serialized_options = b'\030\001' + _REPLTOKEN._serialized_start=161 + _REPLTOKEN._serialized_end=1010 + _REPLTOKEN_CLASSROOMMETADATA._serialized_start=814 + _REPLTOKEN_CLASSROOMMETADATA._serialized_end=863 + _REPLTOKEN_REPLID._serialized_start=865 + _REPLTOKEN_REPLID._serialized_end=905 + _REPLTOKEN_PRESENCED._serialized_start=907 + _REPLTOKEN_PRESENCED._serialized_end=956 + _REPLTOKEN_WIREFORMAT._serialized_start=958 + _REPLTOKEN_WIREFORMAT._serialized_end=998 + _TLSCERTIFICATE._serialized_start=1012 + _TLSCERTIFICATE._serialized_end=1058 + _REPLTRANSFER._serialized_start=1061 + _REPLTRANSFER._serialized_end=1330 + _ALLOWREPLREQUEST._serialized_start=1332 + _ALLOWREPLREQUEST._serialized_end=1404 + _CLUSTERMETADATA._serialized_start=1406 + _CLUSTERMETADATA._serialized_end=1495 + _EVICTREPLREQUEST._serialized_start=1497 + _EVICTREPLREQUEST._serialized_end=1618 + _EVICTREPLRESPONSE._serialized_start=1620 + _EVICTREPLRESPONSE._serialized_end=1693 +# @@protoc_insertion_point(module_scope) diff --git a/src/replit/goval/api/features/__init__.py b/src/replit/goval/api/features/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/replit/goval/api/features/features_pb2.py b/src/replit/goval/api/features/features_pb2.py new file mode 100644 index 00000000..d6070635 --- /dev/null +++ b/src/replit/goval/api/features/features_pb2.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: replit/goval/api/features/features.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n(replit/goval/api/features/features.proto\x12\x19replit.goval.api.features\"\x05\n\x03Gpu\"\t\n\x07\x42oosted\"\x8c\x01\n\x07\x46\x65\x61ture\x12-\n\x03gpu\x18\x01 \x01(\x0b\x32\x1e.replit.goval.api.features.GpuH\x00\x12\x35\n\x07\x62oosted\x18\x02 \x01(\x0b\x32\".replit.goval.api.features.BoostedH\x00\x12\x10\n\x08required\x18\x03 \x01(\x08\x42\t\n\x07\x66\x65\x61tureB&Z$github.com/replit/goval/api/featuresb\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'replit.goval.api.features.features_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'Z$github.com/replit/goval/api/features' + _GPU._serialized_start=71 + _GPU._serialized_end=76 + _BOOSTED._serialized_start=78 + _BOOSTED._serialized_end=87 + _FEATURE._serialized_start=90 + _FEATURE._serialized_end=230 +# @@protoc_insertion_point(module_scope) diff --git a/src/replit/goval/api/repl/__init__.py b/src/replit/goval/api/repl/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/replit/goval/api/repl/repl_pb2.py b/src/replit/goval/api/repl/repl_pb2.py new file mode 100644 index 00000000..a22fcd3d --- /dev/null +++ b/src/replit/goval/api/repl/repl_pb2.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: replit/goval/api/repl/repl.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from replit.goval.api.features import features_pb2 as replit_dot_goval_dot_api_dot_features_dot_features__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n replit/goval/api/repl/repl.proto\x12\x15replit.goval.api.repl\x1a(replit/goval/api/features/features.proto\"v\n\x04Repl\x12\n\n\x02id\x18\x01 \x01(\t\x12\x10\n\x08language\x18\x02 \x01(\t\x12\x0e\n\x06\x62ucket\x18\x03 \x01(\t\x12\x0c\n\x04slug\x18\x04 \x01(\t\x12\x0c\n\x04user\x18\x05 \x01(\t\x12\x12\n\nsourceRepl\x18\x06 \x01(\t\x12\x10\n\x08\x64\x61tabase\x18\x07 \x01(\t\"\xfb\x01\n\x0eResourceLimits\x12\x0b\n\x03net\x18\x01 \x01(\x08\x12\x0e\n\x06memory\x18\x02 \x01(\x03\x12\x0f\n\x07threads\x18\x03 \x01(\x01\x12\x0e\n\x06shares\x18\x04 \x01(\x01\x12\x0c\n\x04\x64isk\x18\x05 \x01(\x03\x12@\n\x05\x63\x61\x63he\x18\x06 \x01(\x0e\x32\x31.replit.goval.api.repl.ResourceLimits.Cachability\x12\x17\n\x0frestrictNetwork\x18\x07 \x01(\x08\x12\x15\n\rpreventWakeup\x18\x08 \x01(\x08\"+\n\x0b\x43\x61\x63hability\x12\x08\n\x04NONE\x10\x00\x12\x08\n\x04USER\x10\x01\x12\x08\n\x04REPL\x10\x02\"%\n\x0bPermissions\x12\x16\n\x0etoggleAlwaysOn\x18\x01 \x01(\x08\"\xab\x02\n\x08Metadata\x12)\n\x04repl\x18\x07 \x01(\x0b\x32\x1b.replit.goval.api.repl.Repl\x12=\n\x0eresourceLimits\x18\n \x01(\x0b\x32%.replit.goval.api.repl.ResourceLimits\x12\x37\n\x0bpersistence\x18\x06 \x01(\x0e\x32\".replit.goval.api.repl.Persistence\x12\r\n\x05\x66lags\x18\x0e \x03(\t\x12\x37\n\x0bpermissions\x18\x0f \x01(\x0b\x32\".replit.goval.api.repl.Permissions\x12\x34\n\x08\x66\x65\x61tures\x18\x10 \x03(\x0b\x32\".replit.goval.api.features.Feature*6\n\x0bPersistence\x12\x0e\n\nPERSISTENT\x10\x00\x12\r\n\tEPHEMERAL\x10\x01\x12\x08\n\x04NONE\x10\x02\x42\"Z github.com/replit/goval/api/replb\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'replit.goval.api.repl.repl_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'Z github.com/replit/goval/api/repl' + _PERSISTENCE._serialized_start=816 + _PERSISTENCE._serialized_end=870 + _REPL._serialized_start=101 + _REPL._serialized_end=219 + _RESOURCELIMITS._serialized_start=222 + _RESOURCELIMITS._serialized_end=473 + _RESOURCELIMITS_CACHABILITY._serialized_start=430 + _RESOURCELIMITS_CACHABILITY._serialized_end=473 + _PERMISSIONS._serialized_start=475 + _PERMISSIONS._serialized_end=512 + _METADATA._serialized_start=515 + _METADATA._serialized_end=814 +# @@protoc_insertion_point(module_scope) diff --git a/src/replit/goval/api/signing_pb2.py b/src/replit/goval/api/signing_pb2.py new file mode 100644 index 00000000..dbbcd01c --- /dev/null +++ b/src/replit/goval/api/signing_pb2.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: replit/goval/api/signing.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 +from replit.goval.api import client_pb2 as replit_dot_goval_dot_api_dot_client__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1ereplit/goval/api/signing.proto\x12\x10replit.goval.api\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x1dreplit/goval/api/client.proto\"\x89\x01\n\x15GovalSigningAuthority\x12\x10\n\x06key_id\x18\x01 \x01(\tH\x00\x12\x15\n\x0bsigned_cert\x18\x02 \x01(\tH\x00\x12/\n\x07version\x18\x03 \x01(\x0e\x32\x1e.replit.goval.api.TokenVersion\x12\x0e\n\x06issuer\x18\x04 \x01(\tB\x06\n\x04\x63\x65rt\"}\n\x10\x43\x65rtificateClaim\x12\x10\n\x06replid\x18\x01 \x01(\tH\x00\x12\x0e\n\x04user\x18\x02 \x01(\tH\x00\x12\x11\n\x07\x63luster\x18\x04 \x01(\tH\x00\x12+\n\x04\x66lag\x18\x03 \x01(\x0e\x32\x1b.replit.goval.api.FlagClaimH\x00\x42\x07\n\x05\x63laim\"\xa4\x01\n\tGovalCert\x12\'\n\x03iat\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\'\n\x03\x65xp\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x32\n\x06\x63laims\x18\x03 \x03(\x0b\x32\".replit.goval.api.CertificateClaim\x12\x11\n\tpublicKey\x18\x04 \x01(\t\"\xe8\x01\n\nGovalToken\x12\'\n\x03iat\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\'\n\x03\x65xp\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0e\n\x06replid\x18\x03 \x01(\t\x12\x31\n\nrepl_token\x18\x04 \x01(\x0b\x32\x1b.replit.goval.api.ReplTokenH\x00\x12<\n\rrepl_identity\x18\x05 \x01(\x0b\x32#.replit.goval.api.GovalReplIdentityH\x00\x42\x07\n\x05Token\"u\n\x11GovalReplIdentity\x12\x0e\n\x06replid\x18\x01 \x01(\t\x12\x0c\n\x04user\x18\x02 \x01(\t\x12\x0c\n\x04slug\x18\x03 \x01(\t\x12\x0b\n\x03\x61ud\x18\x04 \x01(\t\x12\x11\n\tephemeral\x18\x05 \x01(\x08\x12\x14\n\x0coriginReplid\x18\x06 \x01(\t*9\n\x0cTokenVersion\x12\x13\n\x0f\x42\x41RE_REPL_TOKEN\x10\x00\x12\x14\n\x10TYPE_AWARE_TOKEN\x10\x01*\x8b\x01\n\tFlagClaim\x12\x14\n\x10MINT_GOVAL_TOKEN\x10\x00\x12\x1a\n\x16SIGN_INTERMEDIATE_CERT\x10\x01\x12\x0c\n\x08IDENTITY\x10\x05\x12\x0f\n\x0bGHOSTWRITER\x10\x06\x12\x0e\n\nANY_REPLID\x10\x02\x12\x0c\n\x08\x41NY_USER\x10\x03\x12\x0f\n\x0b\x41NY_CLUSTER\x10\x04\x42\x1dZ\x1bgithub.com/replit/goval/apib\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'replit.goval.api.signing_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'Z\033github.com/replit/goval/api' + _TOKENVERSION._serialized_start=904 + _TOKENVERSION._serialized_end=961 + _FLAGCLAIM._serialized_start=964 + _FLAGCLAIM._serialized_end=1103 + _GOVALSIGNINGAUTHORITY._serialized_start=117 + _GOVALSIGNINGAUTHORITY._serialized_end=254 + _CERTIFICATECLAIM._serialized_start=256 + _CERTIFICATECLAIM._serialized_end=381 + _GOVALCERT._serialized_start=384 + _GOVALCERT._serialized_end=548 + _GOVALTOKEN._serialized_start=551 + _GOVALTOKEN._serialized_end=783 + _GOVALREPLIDENTITY._serialized_start=785 + _GOVALREPLIDENTITY._serialized_end=902 +# @@protoc_insertion_point(module_scope) diff --git a/src/replit/identity/__init__.py b/src/replit/identity/__init__.py new file mode 100644 index 00000000..772a8d2e --- /dev/null +++ b/src/replit/identity/__init__.py @@ -0,0 +1,4 @@ +"""Python implementation of Replit identity.""" + +from .exceptions import * # noqa: F403,F401 +from .verify import * # noqa: F403,F401 diff --git a/src/replit/identity/exceptions.py b/src/replit/identity/exceptions.py new file mode 100644 index 00000000..e9ffcd60 --- /dev/null +++ b/src/replit/identity/exceptions.py @@ -0,0 +1,7 @@ +"""Exceptions that can be thrown by identity.""" + + +class VerifyError(Exception): + """An Exception occurred when a verification process failed.""" + + pass diff --git a/src/replit/identity/verify.py b/src/replit/identity/verify.py new file mode 100644 index 00000000..c2588a40 --- /dev/null +++ b/src/replit/identity/verify.py @@ -0,0 +1,256 @@ +"""Identity verification.""" + +import base64 +import dataclasses +import datetime +import json +import os +from typing import Callable, cast, Dict, Optional, Set, Tuple + +import pyseto + +from .exceptions import VerifyError +from ..goval.api import signing_pb2 + +PubKeySource = Callable[[str, str], pyseto.KeyInterface] + + +@dataclasses.dataclass +class _MessageClaims: + """Claims from a signing_pb2.GovalCert.""" + + repls: Set[str] = dataclasses.field(default_factory=set) + users: Set[str] = dataclasses.field(default_factory=set) + clusters: Set[str] = dataclasses.field(default_factory=set) + flags: Set[int] = dataclasses.field(default_factory=set) + + +def _parse_claims(cert: signing_pb2.GovalCert) -> _MessageClaims: + """Parses claims from a signing_pb2.GovalCert. + + Args: + cert: The certificate + + Returns: + The parsed _MessageClaims. + """ + claims = _MessageClaims() + + for claim in cert.claims: + if claim.WhichOneof("claim") == "replid": + claims.repls.insert(claim.replid) + elif claim.WhichOneof("claim") == "user": + claims.users.insert(claim.user) + elif claim.WhichOneof("claim") == "cluster": + claims.clusters.insert(claim.cluster) + elif claim.WhichOneof("claim") == "flag": + claims.flags.insert(claim.flag) + + return claims + + +def _get_signing_authority(token: str) -> signing_pb2.GovalSigningAuthority: + # The library does not allow just grabbing the footer to know what key to + # use, so we need to manually extract that. + token_parts = token.split(".") + if len(token_parts) != 4: + raise VerifyError("token is not correctly PASETO-encoded") + version, purpose, raw_payload, raw_footer = token_parts + if version != "v2": + raise VerifyError(f"only v2 is supported: {version}") + if purpose != "public": + raise VerifyError(f'only "public" purpose is supported: {purpose}') + + return signing_pb2.GovalSigningAuthority.FromString( + base64.b64decode(base64.urlsafe_b64decode(raw_footer + "==")) + ) + + +def _verify_raw_claims( + replid: Optional[str] = None, + user: Optional[str] = None, + cluster: Optional[str] = None, + claims: Optional[_MessageClaims] = None, + any_replid: bool = False, + any_user: bool = False, + any_cluster: bool = False, +) -> None: + if claims is None: + return + + if not any_replid and replid is not None and replid not in claims.repls: + raise VerifyError("not authorized (replid)") + if not any_user and user is not None and user not in claims.users: + raise VerifyError("not authorized (user)") + if not any_cluster and cluster is not None and cluster not in claims.clusters: + raise VerifyError("not authorized (cluster)") + + +def _verify_claims( + iat: datetime.datetime, + exp: datetime.datetime, + replid: Optional[str] = None, + user: Optional[str] = None, + cluster: Optional[str] = None, + claims: Optional[_MessageClaims] = None, +) -> None: + now = datetime.datetime.utcnow() + if iat > now: + raise VerifyError(f"not valid for {iat - now}") + if exp < now: + raise VerifyError(f"expired {now - exp} ago") + + _verify_raw_claims(replid=replid, user=user, cluster=cluster, claims=claims) + + +class Verifier: + """Provides verification of tokens.""" + + def __init__(self) -> None: + pass + + def verify_chain( + self, token: str, pubkey_source: PubKeySource, + ) -> Tuple[bytes, Optional[signing_pb2.GovalCert]]: + """Verifies that the token and its signing chain are valid.""" + gsa = _get_signing_authority(token) + + if gsa.key_id != "": + # If it's signed directly with a root key, grab the pubkey and + # verify it. + return ( + self.verify_token_with_keyid( + token, gsa.key_id, gsa.issuer, pubkey_source + ), + None, + ) + + if gsa.signed_cert != "": + # If it's signed by another token, verify the other token first. + signing_bytes, skip_level_cert = self.verify_chain( + gsa.signed_cert, pubkey_source + ) + + # Make sure the two parent certs agree. + signing_cert = self.verify_cert(signing_bytes, skip_level_cert) + + # Now verify this token using the parent cert. + return self.verify_token_with_cert(token, signing_cert), signing_cert + + raise VerifyError(f"Invalid signing authority: {gsa}") + + def verify_token_with_keyid( + self, token: str, key_id: str, issuer: str, pubkey_source: PubKeySource, + ) -> bytes: + """Verifies that the token is valid and signed by the keyid.""" + pubkey = pubkey_source(key_id, issuer) + return self.verify_token(token, pubkey) + + def verify_token_with_cert(self, token: str, cert: signing_pb2.GovalCert,) -> bytes: + """Verifies that the token is valid and signed by the cert.""" + pubkey = pyseto.Key.from_paserk(cert.publicKey) + return self.verify_token(token, pubkey) + + def verify_cert( + self, encoded_cert: bytes, signing_cert: Optional[signing_pb2.GovalCert] + ) -> signing_pb2.GovalCert: + """Verifies that the certificate is valid.""" + cert = signing_pb2.GovalCert.FromString(encoded_cert) + + # Verify that the cert is valid. + _verify_claims(iat=cert.iat.ToDatetime(), exp=cert.exp.ToDatetime()) + + # If the parent is the root cert, there's nothing else to verify. + if signing_cert: + claims = _parse_claims(signing_cert) + if signing_pb2.FlagClaim.SIGN_INTERMEDIATE_CERT not in claims.flags: + raise VerifyError( + "signing cert does not have authority to sign intermediate certs" + ) + + # Verify the cert claims agrees with its signer. + authorized_claims: Set[str] = set() + any_replid = False + any_user = False + any_cluster = False + for claim in signing_cert.claims: + authorized_claims.insert(str(claim)) + if claim.WhichOneof("claim") == "flag": + if claim.flag == signing_pb2.FlagClaim.ANY_REPLID: + any_replid = True + elif claim.flag == signing_pb2.FlagClaim.ANY_USER: + any_user = True + elif claim.flag == signing_pb2.FlagClaim.ANY_CLUSTER: + any_cluster = True + + for claim in signing_cert.claims: + if claim.WhichOneof("claim") == "replid" and any_replid: + continue + if claim.WhichOneof("claim") == "user" and any_user: + continue + if claim.WhichOneof("claim") == "cluster" and any_cluster: + continue + if str(claim) not in authorized_claims: + raise VerifyError(f"signing cert does not authorize claim {claim}") + + return cert + + def verify_token(self, token: str, pubkey: pyseto.KeyInterface,) -> bytes: + """Verifies that the token is valid.""" + decoded = pyseto.decode(pubkey, token) + return base64.b64decode(decoded.payload) + + +def read_public_key_from_env(keyid: str, issuer: str) -> pyseto.KeyInterface: + """Provides a [PubKeySource] that reads public keys from the environment. + + Args: + keyid: The ID of the public key used to sign a token. + issuer: The name of the issuer of the certificate. + + Returns: + The public key corresponding to the key id. + """ + pubkeys = cast(Dict[str, str], json.loads(os.getenv("REPL_PUBKEYS"))) + key = base64.b64decode(pubkeys[keyid]) + return pyseto.Key.from_asymmetric_key_params(version=2, x=key) + + +def verify_ghostwriter( + ghostwriter_token: str, pubkey_source: PubKeySource = read_public_key_from_env, +) -> signing_pb2.GovalToken: + """Verifies a Ghostwriter token. + + Args: + ghostwriter_token: The Ghostwriter token. + pubkey_source: The PubKeySource to get the public key. + + Returns: + The parsed and verified signing_pb2.GovalToken. + + Raises: + VerifyError: If there's any problem verifying the token. + """ + v = Verifier() + raw_goval_token, goval_cert = v.verify_chain(ghostwriter_token, pubkey_source) + # TODO: We may need to use a different type. + goval_token = signing_pb2.GovalToken.FromString(raw_goval_token) + + # Verify that the cert is valid. + if goval_cert: + _verify_claims( + iat=goval_cert.iat.ToDatetime(), + exp=goval_cert.exp.ToDatetime(), + replid=goval_token.replid or None, + ) + # Ensure that the claims include ghostwriter. + has_ghostwriter_claim = False + for claim in goval_cert.claims: + if claim.WhichOneof("claim") != "flag": + continue + if claim.flag == signing_pb2.FlagClaim.GHOSTWRITER: + has_ghostwriter_claim = True + break + if not has_ghostwriter_claim: + raise VerifyError("not authorized (ghostwriter)") + return goval_token diff --git a/tests/test_identity.py b/tests/test_identity.py new file mode 100644 index 00000000..1a2f11fb --- /dev/null +++ b/tests/test_identity.py @@ -0,0 +1,31 @@ +"""Tests for replit.identity.""" + +import json +import os +import unittest + +import pyseto +import replit.identity + +PUBLIC_KEY = "on0FkSmEC+ce40V9Vc4QABXSx6TXo+lhp99b6Ka0gro=" + + +class TestIdentity(unittest.TestCase): + """Tests for replit.identity.""" + + def setUp(self) -> None: + """Set up the public keys.""" + if "REPL_PUBKEYS" not in os.environ: + os.environ["REPL_PUBKEYS"] = json.dumps({"dev:1": PUBLIC_KEY}) + + def test_read_public_key_from_env(self) -> None: + """Test read_public_key_from_env.""" + pubkey = replit.identity.read_public_key_from_env("dev:1", "goval") + self.assertIsInstance(pubkey, pyseto.versions.v2.V2Public) + + def test_verify_ghostwriter(self) -> None: + """Test verify_ghostwriter.""" + # This token should be valid for 10y. + replit.identity.verify_ghostwriter( + "v2.public.Q2dzSTc4L0htZ1lRbXB1VmNoSUxDTDZDekpvR0VOQ2RsWEk9S1AmYjDdBvVZjmHFgcYVDD57VvJFKbXF9q-fFYQ0XYu12xG8cMV0EK12qFcD3ihtCdqkNoCbNmFpNByDooKSDA.R0FFaUMyZG9iM04wZDNKcGRHVnlFcndDZGpJdWNIVmliR2xqTGxFeVpIcFRWR00wVERCb2RGb3hiRkpNTURWRVVrZE9iMU5WZUVSVVJGcEVaV3R3ZGxJd1ZrNVpiR3h1VFRCc2FGRlhhRzVTTUdSdVUxWnNRbUZIT1VSU01FWk9XVlZHYjFvd1ZrcGhiRnA1VkZkck1XUXlVbGhUYms1b1ZqQXhNVnBXV2tObGJGcHlZVWhPVTJWck5ERmFSelZyWkZVNVJtVkdWbXROUkdneldsY3hjMU5IU2xobFJFcHJVakJzTUZsVVFrdFZSMFpYV25wQ1lWSkdTbHBhUnpGSFpFVXdlR0ZGY0dGa2VqQTVUbXRKWWpaM1NscG9NRGcyYnpRMGIwaEhVRGhCZG13MWNWaHRaMkUyZFd4R1JGcHhObmRSWTFGU2MyRnVjV2hXVEhsamJsQTRXbTFEUVhOV2JWOVplbmQxZUVreGIwaEVZbmQzYmw4d2QxbzRaakp4UTJjdVVqQkdSbUZWVFhsYVJ6bHBUVEEwZDFwRVRrdGpSMUpJVm01c1JGb3hXbkpYYkdoYVRtc3hVbEJVTUE9PQ" # noqa: E501,B950 # line too long + )