diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 102c113..ab37940 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -1,2 +1 @@ .. mdinclude:: ../../CHANGELOG.md - \ No newline at end of file diff --git a/docs/source/conf.py b/docs/source/conf.py index 9a5d438..c6bcb60 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -86,6 +86,7 @@ language = "en" # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] +suppress_warnings = ["ref.python"] add_function_parentheses = False add_module_names = True diff --git a/poetry.lock b/poetry.lock index 8af9cc0..c94a679 100644 --- a/poetry.lock +++ b/poetry.lock @@ -117,17 +117,9 @@ category = "dev" optional = false python-versions = ">=3.6.1" -[[package]] -name = "chardet" -version = "5.0.0" -description = "Universal encoding detector for Python 3" -category = "dev" -optional = false -python-versions = ">=3.6" - [[package]] name = "charset-normalizer" -version = "2.1.0" +version = "2.1.1" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." category = "main" optional = false @@ -148,6 +140,18 @@ python-versions = ">=3.7" colorama = {version = "*", markers = "platform_system == \"Windows\""} importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} +[[package]] +name = "codespell" +version = "2.2.1" +description = "Codespell" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +dev = ["check-manifest", "flake8", "pytest", "pytest-cov", "pytest-dependency"] +hard-encoding-detection = ["chardet"] + [[package]] name = "colorama" version = "0.4.5" @@ -158,7 +162,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "commitizen" -version = "2.29.2" +version = "2.32.2" description = "Python commitizen client tool" category = "dev" optional = false @@ -166,7 +170,7 @@ python-versions = ">=3.6.2,<4.0.0" [package.dependencies] argcomplete = ">=1.12.1,<2.0.0" -chardet = ">=5.0.0,<6.0.0" +charset-normalizer = ">=2.1.0,<3.0.0" colorama = ">=0.4.1,<0.5.0" decli = ">=0.5.2,<0.6.0" jinja2 = ">=2.10.3" @@ -186,11 +190,11 @@ optional = true python-versions = "*" [package.extras] -test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"] +test = ["hypothesis (==3.55.3)", "flake8 (==3.7.8)"] [[package]] name = "coverage" -version = "6.4.2" +version = "6.4.4" description = "Code coverage measurement for Python" category = "dev" optional = false @@ -221,6 +225,14 @@ sdist = ["setuptools_rust (>=0.11.4)"] ssh = ["bcrypt (>=3.1.5)"] test = ["pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"] +[[package]] +name = "darglint" +version = "1.8.1" +description = "A utility for ensuring Google-style docstrings stay up to date with the source code." +category = "dev" +optional = false +python-versions = ">=3.6,<4.0" + [[package]] name = "decli" version = "0.5.2" @@ -231,7 +243,7 @@ python-versions = ">=3.6" [[package]] name = "distlib" -version = "0.3.5" +version = "0.3.6" description = "Distribution utilities" category = "dev" optional = false @@ -262,15 +274,15 @@ gmpy2 = ["gmpy2"] [[package]] name = "filelock" -version = "3.7.1" +version = "3.8.0" description = "A platform independent file lock." category = "dev" optional = false python-versions = ">=3.7" [package.extras] -docs = ["furo (>=2021.8.17b43)", "sphinx (>=4.1)", "sphinx-autodoc-typehints (>=1.12)"] -testing = ["covdefaults (>=1.2.0)", "coverage (>=4)", "pytest (>=4)", "pytest-cov", "pytest-timeout (>=1.4.2)"] +docs = ["furo (>=2022.6.21)", "sphinx (>=5.1.1)", "sphinx-autodoc-typehints (>=1.19.1)"] +testing = ["covdefaults (>=2.2)", "coverage (>=6.4.2)", "pytest (>=7.1.2)", "pytest-cov (>=3)", "pytest-timeout (>=2.1)"] [[package]] name = "flake8" @@ -300,7 +312,7 @@ pydocstyle = ">=2.1" [[package]] name = "identify" -version = "2.5.2" +version = "2.5.3" description = "File identification library for Python" category = "dev" optional = false @@ -494,8 +506,8 @@ python-versions = ">=3.6" importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} [package.extras] -dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] +testing = ["pytest-benchmark", "pytest"] +dev = ["tox", "pre-commit"] [[package]] name = "pre-commit" @@ -581,12 +593,15 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pygments" -version = "2.12.0" +version = "2.13.0" description = "Pygments is a syntax highlighting package written in Python." category = "main" optional = true python-versions = ">=3.6" +[package.extras] +plugins = ["importlib-metadata"] + [[package]] name = "pyparsing" version = "3.0.9" @@ -633,7 +648,7 @@ coverage = {version = ">=5.2.1", extras = ["toml"]} pytest = ">=4.6" [package.extras] -testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtualenv"] +testing = ["virtualenv", "pytest-xdist", "six", "process-tests", "hunter", "fields"] [[package]] name = "python-jose" @@ -655,7 +670,7 @@ pycryptodome = ["pycryptodome (>=3.3.1,<4.0.0)", "pyasn1"] [[package]] name = "pytz" -version = "2022.1" +version = "2022.2.1" description = "World timezone definitions, modern and historical" category = "main" optional = true @@ -830,7 +845,7 @@ docutils = "<0.18" sphinx = ">=1.6" [package.extras] -dev = ["transifex-client", "sphinxcontrib-httpdomain", "bump2version"] +dev = ["bump2version", "sphinxcontrib-httpdomain", "transifex-client"] [[package]] name = "sphinxcontrib-applehelp" @@ -841,8 +856,8 @@ optional = true python-versions = ">=3.5" [package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] test = ["pytest"] +lint = ["docutils-stubs", "mypy", "flake8"] [[package]] name = "sphinxcontrib-devhelp" @@ -853,8 +868,8 @@ optional = true python-versions = ">=3.5" [package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] test = ["pytest"] +lint = ["docutils-stubs", "mypy", "flake8"] [[package]] name = "sphinxcontrib-htmlhelp" @@ -865,8 +880,8 @@ optional = true python-versions = ">=3.6" [package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] -test = ["pytest", "html5lib"] +test = ["html5lib", "pytest"] +lint = ["docutils-stubs", "mypy", "flake8"] [[package]] name = "sphinxcontrib-jsmath" @@ -877,7 +892,7 @@ optional = true python-versions = ">=3.5" [package.extras] -test = ["pytest", "flake8", "mypy"] +test = ["mypy", "flake8", "pytest"] [[package]] name = "sphinxcontrib-qthelp" @@ -888,8 +903,8 @@ optional = true python-versions = ">=3.5" [package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] test = ["pytest"] +lint = ["docutils-stubs", "mypy", "flake8"] [[package]] name = "sphinxcontrib-serializinghtml" @@ -900,8 +915,8 @@ optional = true python-versions = ">=3.5" [package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] test = ["pytest"] +lint = ["docutils-stubs", "mypy", "flake8"] [[package]] name = "termcolor" @@ -929,7 +944,7 @@ python-versions = ">=3.7" [[package]] name = "tomlkit" -version = "0.11.1" +version = "0.11.4" description = "Style preserving TOML library" category = "dev" optional = false @@ -984,7 +999,7 @@ python-versions = ">=3.5" [[package]] name = "urllib3" -version = "1.26.11" +version = "1.26.12" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false @@ -992,26 +1007,26 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, [package.extras] brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"] -secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] +secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "urllib3-secure-extra", "ipaddress"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] name = "virtualenv" -version = "20.16.2" +version = "20.16.3" description = "Virtual Python Environment builder" category = "dev" optional = false python-versions = ">=3.6" [package.dependencies] -distlib = ">=0.3.1,<1" -filelock = ">=3.2,<4" -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} -platformdirs = ">=2,<3" +distlib = ">=0.3.5,<1" +filelock = ">=3.4.1,<4" +importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.8\""} +platformdirs = ">=2.4,<3" [package.extras] -docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=21.3)"] -testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "packaging (>=20.0)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)"] +docs = ["proselint (>=0.13)", "sphinx (>=5.1.1)", "sphinx-argparse (>=0.3.1)", "sphinx-rtd-theme (>=1)", "towncrier (>=21.9)"] +testing = ["coverage (>=6.2)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=21.3)", "pytest (>=7.0.1)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.6.1)", "pytest-randomly (>=3.10.3)", "pytest-timeout (>=2.1)"] [[package]] name = "wcwidth" @@ -1047,470 +1062,96 @@ docs = ["mock", "alabaster", "commonmark", "recommonmark", "Sphinx", "sphinx-rtd [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "b6851001fb6b3e331a39e6bab1fa2ed99fdc555e9683137c20c9c593c0e1c040" +content-hash = "5d740e81a3604cb20ca85945c2fc134f6373f599315992afb50284f1f993c1d0" [metadata.files] -alabaster = [ - {file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"}, - {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"}, -] -argcomplete = [ - {file = "argcomplete-1.12.3-py2.py3-none-any.whl", hash = "sha256:291f0beca7fd49ce285d2f10e4c1c77e9460cf823eef2de54df0c0fec88b0d81"}, - {file = "argcomplete-1.12.3.tar.gz", hash = "sha256:2c7dbffd8c045ea534921e63b0be6fe65e88599990d8dc408ac8c542b72a5445"}, -] +alabaster = [] +argcomplete = [] astroid = [] atomicwrites = [] attrs = [] -babel = [ - {file = "Babel-2.10.3-py3-none-any.whl", hash = "sha256:ff56f4892c1c4bf0d814575ea23471c230d544203c7748e8c68f0089478d48eb"}, - {file = "Babel-2.10.3.tar.gz", hash = "sha256:7614553711ee97490f732126dc077f8d0ae084ebc6a96e23db1482afabdb2c51"}, -] +babel = [] black = [] -certifi = [ - {file = "certifi-2022.6.15-py3-none-any.whl", hash = "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"}, - {file = "certifi-2022.6.15.tar.gz", hash = "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d"}, -] +certifi = [] cffi = [] -cfgv = [ - {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, - {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, -] -chardet = [] +cfgv = [] charset-normalizer = [] -click = [ - {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, - {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"}, -] +click = [] +codespell = [] +colorama = [] commitizen = [] -commonmark = [ - {file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"}, - {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, -] +commonmark = [] coverage = [] cryptography = [] -decli = [ - {file = "decli-0.5.2-py3-none-any.whl", hash = "sha256:d3207bc02d0169bf6ed74ccca09ce62edca0eb25b0ebf8bf4ae3fb8333e15ca0"}, - {file = "decli-0.5.2.tar.gz", hash = "sha256:f2cde55034a75c819c630c7655a844c612f2598c42c21299160465df6ad463ad"}, -] +darglint = [] +decli = [] distlib = [] -docutils = [ - {file = "docutils-0.17.1-py2.py3-none-any.whl", hash = "sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"}, - {file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"}, -] +docutils = [] ecdsa = [] -filelock = [ - {file = "filelock-3.7.1-py3-none-any.whl", hash = "sha256:37def7b658813cda163b56fc564cdc75e86d338246458c4c28ae84cabefa2404"}, - {file = "filelock-3.7.1.tar.gz", hash = "sha256:3a0fd85166ad9dbab54c9aec96737b744106dc5f15c0b09a6744a445299fcf04"}, -] -flake8 = [ - {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, - {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, -] +filelock = [] +flake8 = [] flake8-docstrings = [] identify = [] -idna = [ - {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, - {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, -] +idna = [] imagesize = [] -importlib-metadata = [ - {file = "importlib_metadata-4.12.0-py3-none-any.whl", hash = "sha256:7401a975809ea1fdc658c3aa4f78cc2195a0e019c5cbc4c06122884e9ae80c23"}, - {file = "importlib_metadata-4.12.0.tar.gz", hash = "sha256:637245b8bab2b6502fcbc752cc4b7a6f6243bb02b31c5c26156ad103d3d45670"}, -] -iniconfig = [ - {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, - {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, -] -isort = [ - {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, - {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, -] -jinja2 = [ - {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, - {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, -] -lazy-object-proxy = [ - {file = "lazy-object-proxy-1.7.1.tar.gz", hash = "sha256:d609c75b986def706743cdebe5e47553f4a5a1da9c5ff66d76013ef396b5a8a4"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb8c5fd1684d60a9902c60ebe276da1f2281a318ca16c1d0a96db28f62e9166b"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a57d51ed2997e97f3b8e3500c984db50a554bb5db56c50b5dab1b41339b37e36"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd45683c3caddf83abbb1249b653a266e7069a09f486daa8863fb0e7496a9fdb"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8561da8b3dd22d696244d6d0d5330618c993a215070f473b699e00cf1f3f6443"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fccdf7c2c5821a8cbd0a9440a456f5050492f2270bd54e94360cac663398739b"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-win32.whl", hash = "sha256:898322f8d078f2654d275124a8dd19b079080ae977033b713f677afcfc88e2b9"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:85b232e791f2229a4f55840ed54706110c80c0a210d076eee093f2b2e33e1bfd"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:46ff647e76f106bb444b4533bb4153c7370cdf52efc62ccfc1a28bdb3cc95442"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12f3bb77efe1367b2515f8cb4790a11cffae889148ad33adad07b9b55e0ab22c"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c19814163728941bb871240d45c4c30d33b8a2e85972c44d4e63dd7107faba44"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:e40f2013d96d30217a51eeb1db28c9ac41e9d0ee915ef9d00da639c5b63f01a1"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:2052837718516a94940867e16b1bb10edb069ab475c3ad84fd1e1a6dd2c0fcfc"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-win32.whl", hash = "sha256:6a24357267aa976abab660b1d47a34aaf07259a0c3859a34e536f1ee6e76b5bb"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-win_amd64.whl", hash = "sha256:6aff3fe5de0831867092e017cf67e2750c6a1c7d88d84d2481bd84a2e019ec35"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6a6e94c7b02641d1311228a102607ecd576f70734dc3d5e22610111aeacba8a0"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ce15276a1a14549d7e81c243b887293904ad2d94ad767f42df91e75fd7b5b6"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e368b7f7eac182a59ff1f81d5f3802161932a41dc1b1cc45c1f757dc876b5d2c"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6ecbb350991d6434e1388bee761ece3260e5228952b1f0c46ffc800eb313ff42"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:553b0f0d8dbf21890dd66edd771f9b1b5f51bd912fa5f26de4449bfc5af5e029"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-win32.whl", hash = "sha256:c7a683c37a8a24f6428c28c561c80d5f4fd316ddcf0c7cab999b15ab3f5c5c69"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-win_amd64.whl", hash = "sha256:df2631f9d67259dc9620d831384ed7732a198eb434eadf69aea95ad18c587a28"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:07fa44286cda977bd4803b656ffc1c9b7e3bc7dff7d34263446aec8f8c96f88a"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4dca6244e4121c74cc20542c2ca39e5c4a5027c81d112bfb893cf0790f96f57e"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91ba172fc5b03978764d1df5144b4ba4ab13290d7bab7a50f12d8117f8630c38"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:043651b6cb706eee4f91854da4a089816a6606c1428fd391573ef8cb642ae4f7"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b9e89b87c707dd769c4ea91f7a31538888aad05c116a59820f28d59b3ebfe25a"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-win32.whl", hash = "sha256:9d166602b525bf54ac994cf833c385bfcc341b364e3ee71e3bf5a1336e677b55"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-win_amd64.whl", hash = "sha256:8f3953eb575b45480db6568306893f0bd9d8dfeeebd46812aa09ca9579595148"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dd7ed7429dbb6c494aa9bc4e09d94b778a3579be699f9d67da7e6804c422d3de"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70ed0c2b380eb6248abdef3cd425fc52f0abd92d2b07ce26359fcbc399f636ad"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7096a5e0c1115ec82641afbdd70451a144558ea5cf564a896294e346eb611be1"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f769457a639403073968d118bc70110e7dce294688009f5c24ab78800ae56dc8"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:39b0e26725c5023757fc1ab2a89ef9d7ab23b84f9251e28f9cc114d5b59c1b09"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-win32.whl", hash = "sha256:2130db8ed69a48a3440103d4a520b89d8a9405f1b06e2cc81640509e8bf6548f"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:677ea950bef409b47e51e733283544ac3d660b709cfce7b187f5ace137960d61"}, - {file = "lazy_object_proxy-1.7.1-pp37.pp38-none-any.whl", hash = "sha256:d66906d5785da8e0be7360912e99c9188b70f52c422f9fc18223347235691a84"}, -] -m2r2 = [ - {file = "m2r2-0.3.2-py3-none-any.whl", hash = "sha256:d3684086b61b4bebe2307f15189495360f05a123c9bda2a66462649b7ca236aa"}, - {file = "m2r2-0.3.2.tar.gz", hash = "sha256:ccd95b052dcd1ac7442ecb3111262b2001c10e4119b459c34c93ac7a5c2c7868"}, -] -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"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"}, - {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"}, -] -mccabe = [ - {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, - {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, -] -mistune = [ - {file = "mistune-0.8.4-py2.py3-none-any.whl", hash = "sha256:88a1051873018da288eee8538d476dffe1262495144b33ecb586c4ab266bb8d4"}, - {file = "mistune-0.8.4.tar.gz", hash = "sha256:59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e"}, -] -mock = [ - {file = "mock-4.0.3-py3-none-any.whl", hash = "sha256:122fcb64ee37cfad5b3f48d7a7d51875d7031aaf3d8be7c42e2bee25044eee62"}, - {file = "mock-4.0.3.tar.gz", hash = "sha256:7d3fbbde18228f4ff2f1f119a45cdffa458b4c0dee32eb4d2bb2f82554bac7bc"}, -] -mypy-extensions = [ - {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, - {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, -] -nodeenv = [ - {file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"}, - {file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"}, -] -packaging = [ - {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, - {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, -] -pathspec = [ - {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, - {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, -] -platformdirs = [ - {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, - {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, -] -pluggy = [ - {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, - {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, -] +importlib-metadata = [] +iniconfig = [] +isort = [] +jinja2 = [] +lazy-object-proxy = [] +m2r2 = [] +markupsafe = [] +mccabe = [] +mistune = [] +mock = [] +mypy-extensions = [] +nodeenv = [] +packaging = [] +pathspec = [] +platformdirs = [] +pluggy = [] pre-commit = [] prompt-toolkit = [] -py = [ - {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, - {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, -] -pyasn1 = [ - {file = "pyasn1-0.4.8-py2.4.egg", hash = "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"}, - {file = "pyasn1-0.4.8-py2.5.egg", hash = "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf"}, - {file = "pyasn1-0.4.8-py2.6.egg", hash = "sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00"}, - {file = "pyasn1-0.4.8-py2.7.egg", hash = "sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8"}, - {file = "pyasn1-0.4.8-py2.py3-none-any.whl", hash = "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d"}, - {file = "pyasn1-0.4.8-py3.1.egg", hash = "sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86"}, - {file = "pyasn1-0.4.8-py3.2.egg", hash = "sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7"}, - {file = "pyasn1-0.4.8-py3.3.egg", hash = "sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576"}, - {file = "pyasn1-0.4.8-py3.4.egg", hash = "sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12"}, - {file = "pyasn1-0.4.8-py3.5.egg", hash = "sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2"}, - {file = "pyasn1-0.4.8-py3.6.egg", hash = "sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359"}, - {file = "pyasn1-0.4.8-py3.7.egg", hash = "sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776"}, - {file = "pyasn1-0.4.8.tar.gz", hash = "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"}, -] -pycodestyle = [ - {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, - {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, -] +py = [] +pyasn1 = [] +pycodestyle = [] pycparser = [] pydocstyle = [] -pyflakes = [ - {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, - {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, -] -pygments = [ - {file = "Pygments-2.12.0-py3-none-any.whl", hash = "sha256:dc9c10fb40944260f6ed4c688ece0cd2048414940f1cea51b8b226318411c519"}, - {file = "Pygments-2.12.0.tar.gz", hash = "sha256:5eb116118f9612ff1ee89ac96437bb6b49e8f04d8a13b514ba26f620208e26eb"}, -] -pyparsing = [ - {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, - {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, -] -pytest = [ - {file = "pytest-7.1.2-py3-none-any.whl", hash = "sha256:13d0e3ccfc2b6e26be000cb6568c832ba67ba32e719443bfe725814d3c42433c"}, - {file = "pytest-7.1.2.tar.gz", hash = "sha256:a06a0425453864a270bc45e71f783330a7428defb4230fb5e6a731fde06ecd45"}, -] -pytest-cov = [ - {file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"}, - {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"}, -] -python-jose = [ - {file = "python-jose-3.3.0.tar.gz", hash = "sha256:55779b5e6ad599c6336191246e95eb2293a9ddebd555f796a65f838f07e5d78a"}, - {file = "python_jose-3.3.0-py2.py3-none-any.whl", hash = "sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a"}, -] -pytz = [ - {file = "pytz-2022.1-py2.py3-none-any.whl", hash = "sha256:e68985985296d9a66a881eb3193b0906246245294a881e7c8afe623866ac6a5c"}, - {file = "pytz-2022.1.tar.gz", hash = "sha256:1e760e2fe6a8163bc0b3d9a19c4f84342afa0a2affebfaa84b01b978a02ecaa7"}, -] -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"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, - {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-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"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, - {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, - {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, - {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, - {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, - {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, - {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, - {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, - {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, - {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, - {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, - {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, -] -questionary = [ - {file = "questionary-1.10.0-py3-none-any.whl", hash = "sha256:fecfcc8cca110fda9d561cb83f1e97ecbb93c613ff857f655818839dac74ce90"}, - {file = "questionary-1.10.0.tar.gz", hash = "sha256:600d3aefecce26d48d97eee936fdb66e4bc27f934c3ab6dd1e292c4f43946d90"}, -] -readthedocs-sphinx-ext = [ - {file = "readthedocs-sphinx-ext-2.1.8.tar.gz", hash = "sha256:a57e3713daf77bf91d1ba19e4b9888a47c0abfeb63ecf02e3ac77fcfd99bfe69"}, - {file = "readthedocs_sphinx_ext-2.1.8-py2.py3-none-any.whl", hash = "sha256:5ab5875993191e5e526ca196a1082b73116b0cefd79073ab25367ba0458fffe9"}, -] -recommonmark = [ - {file = "recommonmark-0.7.1-py2.py3-none-any.whl", hash = "sha256:1b1db69af0231efce3fa21b94ff627ea33dee7079a01dd0a7f8482c3da148b3f"}, - {file = "recommonmark-0.7.1.tar.gz", hash = "sha256:bdb4db649f2222dcd8d2d844f0006b958d627f732415d399791ee436a3686d67"}, -] +pyflakes = [] +pygments = [] +pyparsing = [] +pytest = [] +pytest-cov = [] +python-jose = [] +pytz = [] +pyyaml = [] +questionary = [] +readthedocs-sphinx-ext = [] +recommonmark = [] requests = [] requests-toolbelt = [] rsa = [] -six = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] -snowballstemmer = [ - {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, - {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, -] +six = [] +snowballstemmer = [] sphinx = [] sphinx-autoapi = [] -sphinx-rtd-theme = [ - {file = "sphinx_rtd_theme-1.0.0-py2.py3-none-any.whl", hash = "sha256:4d35a56f4508cfee4c4fb604373ede6feae2a306731d533f409ef5c3496fdbd8"}, - {file = "sphinx_rtd_theme-1.0.0.tar.gz", hash = "sha256:eec6d497e4c2195fa0e8b2016b337532b8a699a68bcb22a512870e16925c6a5c"}, -] -sphinxcontrib-applehelp = [ - {file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"}, - {file = "sphinxcontrib_applehelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a"}, -] -sphinxcontrib-devhelp = [ - {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, - {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, -] -sphinxcontrib-htmlhelp = [ - {file = "sphinxcontrib-htmlhelp-2.0.0.tar.gz", hash = "sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"}, - {file = "sphinxcontrib_htmlhelp-2.0.0-py2.py3-none-any.whl", hash = "sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07"}, -] -sphinxcontrib-jsmath = [ - {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, - {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, -] -sphinxcontrib-qthelp = [ - {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, - {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, -] -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"}, -] -termcolor = [ - {file = "termcolor-1.1.0.tar.gz", hash = "sha256:1d6d69ce66211143803fbc56652b41d73b4a400a2891d7bf7a1cdf4c02de613b"}, -] -toml = [ - {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, - {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, -] -tomli = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] +sphinx-rtd-theme = [] +sphinxcontrib-applehelp = [] +sphinxcontrib-devhelp = [] +sphinxcontrib-htmlhelp = [] +sphinxcontrib-jsmath = [] +sphinxcontrib-qthelp = [] +sphinxcontrib-serializinghtml = [] +termcolor = [] +toml = [] +tomli = [] tomlkit = [] tox = [] -typed-ast = [ - {file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"}, - {file = "typed_ast-1.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62"}, - {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:267e3f78697a6c00c689c03db4876dd1efdfea2f251a5ad6555e82a26847b4ac"}, - {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c542eeda69212fa10a7ada75e668876fdec5f856cd3d06829e6aa64ad17c8dfe"}, - {file = "typed_ast-1.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:a9916d2bb8865f973824fb47436fa45e1ebf2efd920f2b9f99342cb7fab93f72"}, - {file = "typed_ast-1.5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:79b1e0869db7c830ba6a981d58711c88b6677506e648496b1f64ac7d15633aec"}, - {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a94d55d142c9265f4ea46fab70977a1944ecae359ae867397757d836ea5a3f47"}, - {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:183afdf0ec5b1b211724dfef3d2cad2d767cbefac291f24d69b00546c1837fb6"}, - {file = "typed_ast-1.5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:639c5f0b21776605dd6c9dbe592d5228f021404dafd377e2b7ac046b0349b1a1"}, - {file = "typed_ast-1.5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf4afcfac006ece570e32d6fa90ab74a17245b83dfd6655a6f68568098345ff6"}, - {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66"}, - {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6778e1b2f81dfc7bc58e4b259363b83d2e509a65198e85d5700dfae4c6c8ff1c"}, - {file = "typed_ast-1.5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:0261195c2062caf107831e92a76764c81227dae162c4f75192c0d489faf751a2"}, - {file = "typed_ast-1.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2efae9db7a8c05ad5547d522e7dbe62c83d838d3906a3716d1478b6c1d61388d"}, - {file = "typed_ast-1.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7d5d014b7daa8b0bf2eaef684295acae12b036d79f54178b92a2b6a56f92278f"}, - {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:370788a63915e82fd6f212865a596a0fefcbb7d408bbbb13dea723d971ed8bdc"}, - {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e964b4ff86550a7a7d56345c7864b18f403f5bd7380edf44a3c1fb4ee7ac6c6"}, - {file = "typed_ast-1.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:683407d92dc953c8a7347119596f0b0e6c55eb98ebebd9b23437501b28dcbb8e"}, - {file = "typed_ast-1.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4879da6c9b73443f97e731b617184a596ac1235fe91f98d279a7af36c796da35"}, - {file = "typed_ast-1.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3e123d878ba170397916557d31c8f589951e353cc95fb7f24f6bb69adc1a8a97"}, - {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3"}, - {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98f80dee3c03455e92796b58b98ff6ca0b2a6f652120c263efdba4d6c5e58f72"}, - {file = "typed_ast-1.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1"}, - {file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"}, -] +typed-ast = [] typing-extensions = [] -unidecode = [ - {file = "Unidecode-1.3.4-py3-none-any.whl", hash = "sha256:afa04efcdd818a93237574791be9b2817d7077c25a068b00f8cff7baa4e59257"}, - {file = "Unidecode-1.3.4.tar.gz", hash = "sha256:8e4352fb93d5a735c788110d2e7ac8e8031eb06ccbfe8d324ab71735015f9342"}, -] +unidecode = [] urllib3 = [] virtualenv = [] -wcwidth = [ - {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, - {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, -] -wrapt = [ - {file = "wrapt-1.14.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3"}, - {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef"}, - {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28"}, - {file = "wrapt-1.14.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ddaea91abf8b0d13443f6dac52e89051a5063c7d014710dcb4d4abb2ff811a59"}, - {file = "wrapt-1.14.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:36f582d0c6bc99d5f39cd3ac2a9062e57f3cf606ade29a0a0d6b323462f4dd87"}, - {file = "wrapt-1.14.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:7ef58fb89674095bfc57c4069e95d7a31cfdc0939e2a579882ac7d55aadfd2a1"}, - {file = "wrapt-1.14.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:e2f83e18fe2f4c9e7db597e988f72712c0c3676d337d8b101f6758107c42425b"}, - {file = "wrapt-1.14.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:ee2b1b1769f6707a8a445162ea16dddf74285c3964f605877a20e38545c3c462"}, - {file = "wrapt-1.14.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:833b58d5d0b7e5b9832869f039203389ac7cbf01765639c7309fd50ef619e0b1"}, - {file = "wrapt-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:80bb5c256f1415f747011dc3604b59bc1f91c6e7150bd7db03b19170ee06b320"}, - {file = "wrapt-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07f7a7d0f388028b2df1d916e94bbb40624c59b48ecc6cbc232546706fac74c2"}, - {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02b41b633c6261feff8ddd8d11c711df6842aba629fdd3da10249a53211a72c4"}, - {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2fe803deacd09a233e4762a1adcea5db5d31e6be577a43352936179d14d90069"}, - {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:257fd78c513e0fb5cdbe058c27a0624c9884e735bbd131935fd49e9fe719d310"}, - {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4fcc4649dc762cddacd193e6b55bc02edca674067f5f98166d7713b193932b7f"}, - {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:11871514607b15cfeb87c547a49bca19fde402f32e2b1c24a632506c0a756656"}, - {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8ad85f7f4e20964db4daadcab70b47ab05c7c1cf2a7c1e51087bfaa83831854c"}, - {file = "wrapt-1.14.1-cp310-cp310-win32.whl", hash = "sha256:a9a52172be0b5aae932bef82a79ec0a0ce87288c7d132946d645eba03f0ad8a8"}, - {file = "wrapt-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:6d323e1554b3d22cfc03cd3243b5bb815a51f5249fdcbb86fda4bf62bab9e164"}, - {file = "wrapt-1.14.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:43ca3bbbe97af00f49efb06e352eae40434ca9d915906f77def219b88e85d907"}, - {file = "wrapt-1.14.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:6b1a564e6cb69922c7fe3a678b9f9a3c54e72b469875aa8018f18b4d1dd1adf3"}, - {file = "wrapt-1.14.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3"}, - {file = "wrapt-1.14.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:a85d2b46be66a71bedde836d9e41859879cc54a2a04fad1191eb50c2066f6e9d"}, - {file = "wrapt-1.14.1-cp35-cp35m-win32.whl", hash = "sha256:dbcda74c67263139358f4d188ae5faae95c30929281bc6866d00573783c422b7"}, - {file = "wrapt-1.14.1-cp35-cp35m-win_amd64.whl", hash = "sha256:b21bb4c09ffabfa0e85e3a6b623e19b80e7acd709b9f91452b8297ace2a8ab00"}, - {file = "wrapt-1.14.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:9e0fd32e0148dd5dea6af5fee42beb949098564cc23211a88d799e434255a1f4"}, - {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9736af4641846491aedb3c3f56b9bc5568d92b0692303b5a305301a95dfd38b1"}, - {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b02d65b9ccf0ef6c34cba6cf5bf2aab1bb2f49c6090bafeecc9cd81ad4ea1c1"}, - {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21ac0156c4b089b330b7666db40feee30a5d52634cc4560e1905d6529a3897ff"}, - {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:9f3e6f9e05148ff90002b884fbc2a86bd303ae847e472f44ecc06c2cd2fcdb2d"}, - {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:6e743de5e9c3d1b7185870f480587b75b1cb604832e380d64f9504a0535912d1"}, - {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:d79d7d5dc8a32b7093e81e97dad755127ff77bcc899e845f41bf71747af0c569"}, - {file = "wrapt-1.14.1-cp36-cp36m-win32.whl", hash = "sha256:81b19725065dcb43df02b37e03278c011a09e49757287dca60c5aecdd5a0b8ed"}, - {file = "wrapt-1.14.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b014c23646a467558be7da3d6b9fa409b2c567d2110599b7cf9a0c5992b3b471"}, - {file = "wrapt-1.14.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:88bd7b6bd70a5b6803c1abf6bca012f7ed963e58c68d76ee20b9d751c74a3248"}, - {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5901a312f4d14c59918c221323068fad0540e34324925c8475263841dbdfe68"}, - {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d77c85fedff92cf788face9bfa3ebaa364448ebb1d765302e9af11bf449ca36d"}, - {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d649d616e5c6a678b26d15ece345354f7c2286acd6db868e65fcc5ff7c24a77"}, - {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7d2872609603cb35ca513d7404a94d6d608fc13211563571117046c9d2bcc3d7"}, - {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:ee6acae74a2b91865910eef5e7de37dc6895ad96fa23603d1d27ea69df545015"}, - {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2b39d38039a1fdad98c87279b48bc5dce2c0ca0d73483b12cb72aa9609278e8a"}, - {file = "wrapt-1.14.1-cp37-cp37m-win32.whl", hash = "sha256:60db23fa423575eeb65ea430cee741acb7c26a1365d103f7b0f6ec412b893853"}, - {file = "wrapt-1.14.1-cp37-cp37m-win_amd64.whl", hash = "sha256:709fe01086a55cf79d20f741f39325018f4df051ef39fe921b1ebe780a66184c"}, - {file = "wrapt-1.14.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8c0ce1e99116d5ab21355d8ebe53d9460366704ea38ae4d9f6933188f327b456"}, - {file = "wrapt-1.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e3fb1677c720409d5f671e39bac6c9e0e422584e5f518bfd50aa4cbbea02433f"}, - {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:642c2e7a804fcf18c222e1060df25fc210b9c58db7c91416fb055897fc27e8cc"}, - {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b7c050ae976e286906dd3f26009e117eb000fb2cf3533398c5ad9ccc86867b1"}, - {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef3f72c9666bba2bab70d2a8b79f2c6d2c1a42a7f7e2b0ec83bb2f9e383950af"}, - {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:01c205616a89d09827986bc4e859bcabd64f5a0662a7fe95e0d359424e0e071b"}, - {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5a0f54ce2c092aaf439813735584b9537cad479575a09892b8352fea5e988dc0"}, - {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2cf71233a0ed05ccdabe209c606fe0bac7379fdcf687f39b944420d2a09fdb57"}, - {file = "wrapt-1.14.1-cp38-cp38-win32.whl", hash = "sha256:aa31fdcc33fef9eb2552cbcbfee7773d5a6792c137b359e82879c101e98584c5"}, - {file = "wrapt-1.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:d1967f46ea8f2db647c786e78d8cc7e4313dbd1b0aca360592d8027b8508e24d"}, - {file = "wrapt-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3232822c7d98d23895ccc443bbdf57c7412c5a65996c30442ebe6ed3df335383"}, - {file = "wrapt-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:988635d122aaf2bdcef9e795435662bcd65b02f4f4c1ae37fbee7401c440b3a7"}, - {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cca3c2cdadb362116235fdbd411735de4328c61425b0aa9f872fd76d02c4e86"}, - {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d52a25136894c63de15a35bc0bdc5adb4b0e173b9c0d07a2be9d3ca64a332735"}, - {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40e7bc81c9e2b2734ea4bc1aceb8a8f0ceaac7c5299bc5d69e37c44d9081d43b"}, - {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b9b7a708dd92306328117d8c4b62e2194d00c365f18eff11a9b53c6f923b01e3"}, - {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6a9a25751acb379b466ff6be78a315e2b439d4c94c1e99cb7266d40a537995d3"}, - {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:34aa51c45f28ba7f12accd624225e2b1e5a3a45206aa191f6f9aac931d9d56fe"}, - {file = "wrapt-1.14.1-cp39-cp39-win32.whl", hash = "sha256:dee0ce50c6a2dd9056c20db781e9c1cfd33e77d2d569f5d1d9321c641bb903d5"}, - {file = "wrapt-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb"}, - {file = "wrapt-1.14.1.tar.gz", hash = "sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d"}, -] +wcwidth = [] +wrapt = [] zipp = [] diff --git a/pyproject.toml b/pyproject.toml index 8adfa58..a115c32 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -56,6 +56,8 @@ flake8 = "^3.5.0" flake8-docstrings = "^1.6.0" commitizen = "^2.28.0" cryptography = "^37.0.4" +codespell = "^2.1.0" +darglint = "^1.8.1" [tool.poetry.extras] docs = [ @@ -80,3 +82,6 @@ line-length = 99 [tool.isort] line_length = 99 profile = "black" + +[tool.darglint] +enable = "DAR104" diff --git a/src/keycloak/authorization/__init__.py b/src/keycloak/authorization/__init__.py index fddd551..feebe0b 100644 --- a/src/keycloak/authorization/__init__.py +++ b/src/keycloak/authorization/__init__.py @@ -44,7 +44,11 @@ class Authorization: @property def policies(self): - """Get policies.""" + """Get policies. + + :returns: Policies + :rtype: dict + """ return self._policies @policies.setter @@ -55,7 +59,7 @@ class Authorization: """Load policies, roles and permissions (scope/resources). :param data: keycloak authorization data (dict) - :returns: None + :type data: dict """ for pol in data["policies"]: if pol["type"] == "role": diff --git a/src/keycloak/authorization/permission.py b/src/keycloak/authorization/permission.py index 667f8c3..d1b606f 100644 --- a/src/keycloak/authorization/permission.py +++ b/src/keycloak/authorization/permission.py @@ -45,10 +45,29 @@ class Permission: https://keycloak.gitbooks.io/documentation/authorization_services/topics/permission/overview.html + :param name: Name + :type name: str + :param type: Type + :type type: str + :param logic: Logic + :type logic: str + :param decision_strategy: Decision strategy + :type decision_strategy: str + """ def __init__(self, name, type, logic, decision_strategy): - """Init method.""" + """Init method. + + :param name: Name + :type name: str + :param type: Type + :type type: str + :param logic: Logic + :type logic: str + :param decision_strategy: Decision strategy + :type decision_strategy: str + """ self.name = name self.type = type self.logic = logic @@ -57,16 +76,28 @@ class Permission: self.scopes = [] def __repr__(self): - """Repr method.""" + """Repr method. + + :returns: Class representation + :rtype: str + """ return "" % (self.name, self.type) def __str__(self): - """Str method.""" + """Str method. + + :returns: Class string representation + :rtype: str + """ return "Permission: %s (%s)" % (self.name, self.type) @property def name(self): - """Get name.""" + """Get name. + + :returns: name + :rtype: str + """ return self._name @name.setter @@ -75,7 +106,11 @@ class Permission: @property def type(self): - """Get type.""" + """Get type. + + :returns: type + :rtype: str + """ return self._type @type.setter @@ -84,7 +119,11 @@ class Permission: @property def logic(self): - """Get logic.""" + """Get logic. + + :returns: Logic + :rtype: str + """ return self._logic @logic.setter @@ -93,7 +132,11 @@ class Permission: @property def decision_strategy(self): - """Get decision strategy.""" + """Get decision strategy. + + :returns: Decision strategy + :rtype: str + """ return self._decision_strategy @decision_strategy.setter @@ -102,7 +145,11 @@ class Permission: @property def resources(self): - """Get resources.""" + """Get resources. + + :returns: Resources + :rtype: list + """ return self._resources @resources.setter @@ -111,7 +158,11 @@ class Permission: @property def scopes(self): - """Get scopes.""" + """Get scopes. + + :returns: Scopes + :rtype: list + """ return self._scopes @scopes.setter diff --git a/src/keycloak/authorization/policy.py b/src/keycloak/authorization/policy.py index 7e03db0..fdf482d 100644 --- a/src/keycloak/authorization/policy.py +++ b/src/keycloak/authorization/policy.py @@ -39,10 +39,29 @@ class Policy: https://keycloak.gitbooks.io/documentation/authorization_services/topics/policy/overview.html + :param name: Name + :type name: str + :param type: Type + :type type: str + :param logic: Logic + :type logic: str + :param decision_strategy: Decision strategy + :type decision_strategy: str + """ def __init__(self, name, type, logic, decision_strategy): - """Init method.""" + """Init method. + + :param name: Name + :type name: str + :param type: Type + :type type: str + :param logic: Logic + :type logic: str + :param decision_strategy: Decision strategy + :type decision_strategy: str + """ self.name = name self.type = type self.logic = logic @@ -51,16 +70,28 @@ class Policy: self.permissions = [] def __repr__(self): - """Repr method.""" + """Repr method. + + :returns: Class representation + :rtype: str + """ return "" % (self.name, self.type) def __str__(self): - """Str method.""" + """Str method. + + :returns: Class string representation + :rtype: str + """ return "Policy: %s (%s)" % (self.name, self.type) @property def name(self): - """Get name.""" + """Get name. + + :returns: Name + :rtype: str + """ return self._name @name.setter @@ -69,7 +100,11 @@ class Policy: @property def type(self): - """Get type.""" + """Get type. + + :returns: Type + :rtype: str + """ return self._type @type.setter @@ -78,7 +113,11 @@ class Policy: @property def logic(self): - """Get logic.""" + """Get logic. + + :returns: Logic + :rtype: str + """ return self._logic @logic.setter @@ -87,7 +126,11 @@ class Policy: @property def decision_strategy(self): - """Get decision strategy.""" + """Get decision strategy. + + :returns: Decision strategy + :rtype: str + """ return self._decision_strategy @decision_strategy.setter @@ -96,7 +139,11 @@ class Policy: @property def roles(self): - """Get roles.""" + """Get roles. + + :returns: Roles + :rtype: list + """ return self._roles @roles.setter @@ -105,7 +152,11 @@ class Policy: @property def permissions(self): - """Get permissions.""" + """Get permissions. + + :returns: Permissions + :rtype: list + """ return self._permissions @permissions.setter @@ -115,8 +166,9 @@ class Policy: def add_role(self, role): """Add keycloak role in policy. - :param role: keycloak role. - :return: + :param role: Keycloak role + :type role: keycloak.authorization.Role + :raises KeycloakAuthorizationConfigError: In case of misconfigured policy type """ if self.type != "role": raise KeycloakAuthorizationConfigError( @@ -127,7 +179,7 @@ class Policy: def add_permission(self, permission): """Add keycloak permission in policy. - :param permission: keycloak permission. - :return: + :param permission: Keycloak permission + :type permission: keycloak.authorization.Permission """ self._permissions.append(permission) diff --git a/src/keycloak/authorization/role.py b/src/keycloak/authorization/role.py index 4ee6ec9..3d4c000 100644 --- a/src/keycloak/authorization/role.py +++ b/src/keycloak/authorization/role.py @@ -31,19 +31,40 @@ class Role: manager, and employee are all typical roles that may exist in an organization. https://keycloak.gitbooks.io/documentation/server_admin/topics/roles.html + + :param name: Name + :type name: str + :param required: Required role indicator + :type required: bool """ def __init__(self, name, required=False): - """Init method.""" + """Init method. + + :param name: Name + :type name: str + :param required: Required role indicator + :type required: bool + """ self.name = name self.required = required def get_name(self): - """Get name.""" + """Get name. + + :returns: Name + :rtype: str + """ return self.name def __eq__(self, other): - """Eq method.""" + """Eq method. + + :param other: The other object + :type other: str + :returns: Equality bool + :rtype: bool | NotImplemented + """ if isinstance(other, str): return self.name == other return NotImplemented diff --git a/src/keycloak/connection.py b/src/keycloak/connection.py index 44978e5..fcdffb4 100644 --- a/src/keycloak/connection.py +++ b/src/keycloak/connection.py @@ -37,15 +37,32 @@ from .exceptions import KeycloakConnectionError class ConnectionManager(object): """Represents a simple server connection. - :param base_url: (str) The server URL. - :param headers: (dict) The header parameters of the requests to the server. - :param timeout: (int) Timeout to use for requests to the server. - :param verify: (bool) Verify server SSL. - :param proxies: (dict) The proxies servers requests is sent by. + :param base_url: The server URL. + :type base_url: str + :param headers: The header parameters of the requests to the server. + :type headers: dict + :param timeout: Timeout to use for requests to the server. + :type timeout: int + :param verify: Verify server SSL. + :type verify: bool + :param proxies: The proxies servers requests is sent by. + :type proxies: dict """ def __init__(self, base_url, headers={}, timeout=60, verify=True, proxies=None): - """Init method.""" + """Init method. + + :param base_url: The server URL. + :type base_url: str + :param headers: The header parameters of the requests to the server. + :type headers: dict + :param timeout: Timeout to use for requests to the server. + :type timeout: int + :param verify: Verify server SSL. + :type verify: bool + :param proxies: The proxies servers requests is sent by. + :type proxies: dict + """ self.base_url = base_url self.headers = headers self.timeout = timeout @@ -73,7 +90,11 @@ class ConnectionManager(object): @property def base_url(self): - """Return base url in use for requests to the server.""" + """Return base url in use for requests to the server. + + :returns: Base URL + :rtype: str + """ return self._base_url @base_url.setter @@ -82,7 +103,11 @@ class ConnectionManager(object): @property def timeout(self): - """Return timeout in use for request to the server.""" + """Return timeout in use for request to the server. + + :returns: Timeout + :rtype: int + """ return self._timeout @timeout.setter @@ -91,7 +116,11 @@ class ConnectionManager(object): @property def verify(self): - """Return verify in use for request to the server.""" + """Return verify in use for request to the server. + + :returns: Verify indicator + :rtype: bool + """ return self._verify @verify.setter @@ -100,7 +129,11 @@ class ConnectionManager(object): @property def headers(self): - """Return header request to the server.""" + """Return header request to the server. + + :returns: Request headers + :rtype: dict + """ return self._headers @headers.setter @@ -110,8 +143,10 @@ class ConnectionManager(object): def param_headers(self, key): """Return a specific header parameter. - :param key: (str) Header parameters key. + :param key: Header parameters key. + :type key: str :returns: If the header parameters exist, return its value. + :rtype: str """ return self.headers.get(key) @@ -122,32 +157,41 @@ class ConnectionManager(object): def exist_param_headers(self, key): """Check if the parameter exists in the header. - :param key: (str) Header parameters key. + :param key: Header parameters key. + :type key: str :returns: If the header parameters exist, return True. + :rtype: bool """ return self.param_headers(key) is not None def add_param_headers(self, key, value): """Add a single parameter inside the header. - :param key: (str) Header parameters key. - :param value: (str) Value to be added. + :param key: Header parameters key. + :type key: str + :param value: Value to be added. + :type value: str """ self.headers[key] = value def del_param_headers(self, key): """Remove a specific parameter. - :param key: (str) Key of the header parameters. + :param key: Key of the header parameters. + :type key: str """ self.headers.pop(key, None) def raw_get(self, path, **kwargs): """Submit get request to the path. - :param path: (str) Path for request. + :param path: Path for request. + :type path: str + :param kwargs: Additional arguments + :type kwargs: dict :returns: Response the request. - :raises: HttpError Can't connect to server. + :rtype: Response + :raises KeycloakConnectionError: HttpError Can't connect to server. """ try: return self._s.get( @@ -163,10 +207,15 @@ class ConnectionManager(object): def raw_post(self, path, data, **kwargs): """Submit post request to the path. - :param path: (str) Path for request. - :param data: (dict) Payload for request. + :param path: Path for request. + :type path: str + :param data: Payload for request. + :type data: dict + :param kwargs: Additional arguments + :type kwargs: dict :returns: Response the request. - :raises: HttpError Can't connect to server. + :rtype: Response + :raises KeycloakConnectionError: HttpError Can't connect to server. """ try: return self._s.post( @@ -183,10 +232,15 @@ class ConnectionManager(object): def raw_put(self, path, data, **kwargs): """Submit put request to the path. - :param path: (str) Path for request. - :param data: (dict) Payload for request. + :param path: Path for request. + :type path: str + :param data: Payload for request. + :type data: dict + :param kwargs: Additional arguments + :type kwargs: dict :returns: Response the request. - :raises: HttpError Can't connect to server. + :rtype: Response + :raises KeycloakConnectionError: HttpError Can't connect to server. """ try: return self._s.put( @@ -200,19 +254,24 @@ class ConnectionManager(object): except Exception as e: raise KeycloakConnectionError("Can't connect to server (%s)" % e) - def raw_delete(self, path, data={}, **kwargs): + def raw_delete(self, path, data=None, **kwargs): """Submit delete request to the path. - :param path: (str) Path for request. - :param data: (dict) Payload for request. + :param path: Path for request. + :type path: str + :param data: Payload for request. + :type data: dict | None + :param kwargs: Additional arguments + :type kwargs: dict :returns: Response the request. - :raises: HttpError Can't connect to server. + :rtype: Response + :raises KeycloakConnectionError: HttpError Can't connect to server. """ try: return self._s.delete( urljoin(self.base_url, path), params=kwargs, - data=data, + data=data or dict(), headers=self.headers, timeout=self.timeout, verify=self.verify, diff --git a/src/keycloak/exceptions.py b/src/keycloak/exceptions.py index 8eb69bf..fe46bf4 100644 --- a/src/keycloak/exceptions.py +++ b/src/keycloak/exceptions.py @@ -21,7 +21,7 @@ # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -"""Keycloak custom exeptions module.""" +"""Keycloak custom exceptions module.""" import requests @@ -36,7 +36,15 @@ class KeycloakError(Exception): """ def __init__(self, error_message="", response_code=None, response_body=None): - """Init method.""" + """Init method. + + :param error_message: The error message + :type error_message: str + :param response_code: The code of the response + :type response_code: int + :param response_body: Body of the response + :type response_body: bytes + """ Exception.__init__(self, error_message) self.response_code = response_code @@ -44,7 +52,11 @@ class KeycloakError(Exception): self.error_message = error_message def __str__(self): - """Str method.""" + """Str method. + + :returns: String representation of the object + :rtype: str + """ if self.response_code is not None: return "{0}: {1}".format(self.response_code, self.error_message) else: @@ -136,7 +148,21 @@ class PermissionDefinitionError(Exception): def raise_error_from_response(response, error, expected_codes=None, skip_exists=False): - """Raise an exception for the response.""" + """Raise an exception for the response. + + :param response: The response object + :type response: Response + :param error: Error object to raise + :type error: dict or Exception + :param expected_codes: Set of expected codes, which should not raise the exception + :type expected_codes: Sequence[int] + :param skip_exists: Indicates whether the response on already existing object should be ignored + :type skip_exists: bool + + :returns: Content of the response message + :type: bytes or dict + :raises KeycloakError: In case of unexpected status codes + """ # noqa: DAR401,DAR402 if expected_codes is None: expected_codes = [200, 201, 204] diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index 90c8eef..798ba72 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -49,19 +49,31 @@ class KeycloakAdmin: """Keycloak Admin client. :param server_url: Keycloak server url + :type server_url: str :param username: admin username + :type username: str :param password: admin password + :type password: str :param totp: Time based OTP + :type totp: str :param realm_name: realm name + :type realm_name: str :param client_id: client id + :type client_id: str :param verify: True if want check connection SSL + :type verify: bool :param client_secret_key: client secret key (optional, required only for access type confidential) + :type client_secret_key: str :param custom_headers: dict of custom header to pass to each HTML request + :type custom_headers: dict :param user_realm_name: The realm name of the user, if different from realm_name + :type user_realm_name: str :param auto_refresh_token: list of methods that allows automatic token refresh. Ex: ['get', 'put', 'post', 'delete'] + :type auto_refresh_token: list :param timeout: connection timeout in seconds + :type timeout: int """ PAGE_SIZE = 100 @@ -95,7 +107,35 @@ class KeycloakAdmin: auto_refresh_token=None, timeout=60, ): - """Init method.""" + """Init method. + + :param server_url: Keycloak server url + :type server_url: str + :param username: admin username + :type username: str + :param password: admin password + :type password: str + :param totp: Time based OTP + :type totp: str + :param realm_name: realm name + :type realm_name: str + :param client_id: client id + :type client_id: str + :param verify: True if want check connection SSL + :type verify: bool + :param client_secret_key: client secret key + (optional, required only for access type confidential) + :type client_secret_key: str + :param custom_headers: dict of custom header to pass to each HTML request + :type custom_headers: dict + :param user_realm_name: The realm name of the user, if different from realm_name + :type user_realm_name: str + :param auto_refresh_token: list of methods that allows automatic token refresh. + Ex: ['get', 'put', 'post', 'delete'] + :type auto_refresh_token: list + :param timeout: connection timeout in seconds + :type timeout: int + """ self.server_url = server_url self.username = username self.password = password @@ -114,7 +154,11 @@ class KeycloakAdmin: @property def server_url(self): - """Get server url.""" + """Get server url. + + :returns: Keycloak server url + :rtype: str + """ return self._server_url @server_url.setter @@ -123,7 +167,11 @@ class KeycloakAdmin: @property def realm_name(self): - """Get realm name.""" + """Get realm name. + + :returns: Realm name + :rtype: str + """ return self._realm_name @realm_name.setter @@ -132,7 +180,11 @@ class KeycloakAdmin: @property def connection(self): - """Get connection.""" + """Get connection. + + :returns: Connection manager + :rtype: ConnectionManager + """ return self._connection @connection.setter @@ -141,7 +193,11 @@ class KeycloakAdmin: @property def client_id(self): - """Get client id.""" + """Get client id. + + :returns: Client id + :rtype: str + """ return self._client_id @client_id.setter @@ -150,7 +206,11 @@ class KeycloakAdmin: @property def client_secret_key(self): - """Get client secret key.""" + """Get client secret key. + + :returns: Client secret key + :rtype: str + """ return self._client_secret_key @client_secret_key.setter @@ -159,7 +219,11 @@ class KeycloakAdmin: @property def verify(self): - """Get verify.""" + """Get verify. + + :returns: Verify indicator + :rtype: bool + """ return self._verify @verify.setter @@ -168,7 +232,11 @@ class KeycloakAdmin: @property def username(self): - """Get username.""" + """Get username. + + :returns: Admin username + :rtype: str + """ return self._username @username.setter @@ -177,7 +245,11 @@ class KeycloakAdmin: @property def password(self): - """Get password.""" + """Get password. + + :returns: Admin password + :rtype: str + """ return self._password @password.setter @@ -186,7 +258,11 @@ class KeycloakAdmin: @property def totp(self): - """Get totp.""" + """Get totp. + + :returns: TOTP + :rtype: str + """ return self._totp @totp.setter @@ -195,7 +271,11 @@ class KeycloakAdmin: @property def token(self): - """Get token.""" + """Get token. + + :returns: Access and refresh token + :rtype: dict + """ return self._token @token.setter @@ -204,12 +284,20 @@ class KeycloakAdmin: @property def auto_refresh_token(self): - """Get auto refresh token.""" + """Get auto refresh token. + + :returns: List of methods for automatic token refresh + :rtype: list + """ return self._auto_refresh_token @property def user_realm_name(self): - """Get user realm name.""" + """Get user realm name. + + :returns: User realm name + :rtype: str + """ return self._user_realm_name @user_realm_name.setter @@ -218,7 +306,11 @@ class KeycloakAdmin: @property def custom_headers(self): - """Get custom headers.""" + """Get custom headers. + + :returns: Custom headers + :rtype: dict + """ return self._custom_headers @custom_headers.setter @@ -247,13 +339,16 @@ class KeycloakAdmin: Wrapper function to paginate GET requests. :param url: The url on which the query is executed + :type url: str :param query: Existing query parameters (optional) + :type query: dict :return: Combined results of paginated queries + :rtype: list """ results = [] - # initalize query if it was called with None + # initialize query if it was called with None if not query: query = {} page = 0 @@ -274,8 +369,16 @@ class KeycloakAdmin: return results def __fetch_paginated(self, url, query=None): - query = query or {} + """Make a specific paginated request. + :param url: The url on which the query is executed + :type url: str + :param query: Pagination settings + :type query: dict + :returns: Response + :rtype: dict + """ + query = query or {} return raise_error_from_response(self.raw_get(url, **query), KeycloakGetError) def import_realm(self, payload): @@ -287,8 +390,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_realmrepresentation :param payload: RealmRepresentation - + :type payload: dict :return: RealmRepresentation + :rtype: dict """ data_raw = self.raw_post(urls_patterns.URL_ADMIN_REALMS, data=json.dumps(payload)) return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) @@ -299,10 +403,13 @@ class KeycloakAdmin: RealmRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_partialexport - :param export-clients: Skip if not want to export realm clients - :param export-groups-and-roles: Skip if not want to export realm groups and roles + :param export_clients: Skip if not want to export realm clients + :type export_clients: bool + :param export_groups_and_role: Skip if not want to export realm groups and roles + :type export_groups_and_role: bool :return: realm configurations JSON + :rtype: dict """ params_path = { "realm-name": self.realm_name, @@ -318,6 +425,7 @@ class KeycloakAdmin: """List all realms in Keycloak deployment. :return: realms list + :rtype: list """ data_raw = self.raw_get(urls_patterns.URL_ADMIN_REALMS) return raise_error_from_response(data_raw, KeycloakGetError) @@ -329,7 +437,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_realmrepresentation :param realm_name: Realm name (not the realm id) + :type realm_name: str :return: RealmRepresentation + :rtype: dict """ params_path = {"realm-name": realm_name} data_raw = self.raw_get(urls_patterns.URL_ADMIN_REALM.format(**params_path)) @@ -342,8 +452,11 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_realmrepresentation :param payload: RealmRepresentation + :type payload: dict :param skip_exists: Skip if Realm already exist. - :return: Keycloak server response (RealmRepresentation) + :type skip_exists: bool + :return: Keycloak server response (RealmRepresentation) + :rtype: dict """ data_raw = self.raw_post(urls_patterns.URL_ADMIN_REALMS, data=json.dumps(payload)) return raise_error_from_response( @@ -353,15 +466,18 @@ class KeycloakAdmin: def update_realm(self, realm_name, payload): """Update a realm. - This wil only update top level attributes and will ignore any user, + This will only update top level attributes and will ignore any user, role, or client information in the payload. RealmRepresentation: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_realmrepresentation :param realm_name: Realm name (not the realm id) + :type realm_name: str :param payload: RealmRepresentation + :type payload: dict :return: Http response + :rtype: dict """ params_path = {"realm-name": realm_name} data_raw = self.raw_put( @@ -373,7 +489,9 @@ class KeycloakAdmin: """Delete a realm. :param realm_name: Realm name (not the realm id) + :type realm_name: str :return: Http response + :rtype: dict """ params_path = {"realm-name": realm_name} data_raw = self.raw_delete(urls_patterns.URL_ADMIN_REALM.format(**params_path)) @@ -388,7 +506,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_userrepresentation :param query: Query parameters (optional) + :type query: dict :return: users list + :rtype: list """ query = query or {} params_path = {"realm-name": self.realm_name} @@ -406,6 +526,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_identityproviderrepresentation :param: payload: IdentityProviderRepresentation + :type payload: dict + :returns: Keycloak server response + :rtype: dict """ params_path = {"realm-name": self.realm_name} data_raw = self.raw_post( @@ -419,8 +542,12 @@ class KeycloakAdmin: IdentityProviderRepresentation https://www.keycloak.org/docs-api/15.0/rest-api/index.html#_identity_providers_resource - :param: alias: alias for IdP to update + :param: idp_alias: alias for IdP to update + :type idp_alias: str :param: payload: The IdentityProviderRepresentation + :type payload: dict + :returns: Keycloak server response + :rtype: dict """ params_path = {"realm-name": self.realm_name, "alias": idp_alias} data_raw = self.raw_put( @@ -435,7 +562,11 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_identityprovidermapperrepresentation :param: idp_alias: alias for Idp to add mapper in + :type idp_alias: str :param: payload: IdentityProviderMapperRepresentation + :type payload: dict + :returns: Keycloak server response + :rtype: dict """ params_path = {"realm-name": self.realm_name, "idp-alias": idp_alias} data_raw = self.raw_post( @@ -450,9 +581,13 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_update :param: idp_alias: alias for Idp to fetch mappers + :type idp_alias: str :param: mapper_id: Mapper Id to update + :type mapper_id: str :param: payload: IdentityProviderMapperRepresentation + :type payload: dict :return: Http response + :rtype: dict """ params_path = { "realm-name": self.realm_name, @@ -476,7 +611,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_getmappers :param: idp_alias: alias for Idp to fetch mappers + :type idp_alias: str :return: array IdentityProviderMapperRepresentation + :rtype: list """ params_path = {"realm-name": self.realm_name, "idp-alias": idp_alias} data_raw = self.raw_get(urls_patterns.URL_ADMIN_IDP_MAPPERS.format(**params_path)) @@ -491,6 +628,7 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_identityproviderrepresentation :return: array IdentityProviderRepresentation + :rtype: list """ params_path = {"realm-name": self.realm_name} data_raw = self.raw_get(urls_patterns.URL_ADMIN_IDPS.format(**params_path)) @@ -500,6 +638,9 @@ class KeycloakAdmin: """Delete an ID Provider. :param: idp_alias: idp alias name + :type idp_alias: str + :returns: Keycloak server response + :rtype: dict """ params_path = {"realm-name": self.realm_name, "alias": idp_alias} data_raw = self.raw_delete(urls_patterns.URL_ADMIN_IDP.format(**params_path)) @@ -514,10 +655,13 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_userrepresentation :param payload: UserRepresentation + :type payload: dict :param exist_ok: If False, raise KeycloakGetError if username already exists. Otherwise, return existing user ID. + :type exist_ok: bool :return: UserRepresentation + :rtype: dict """ params_path = {"realm-name": self.realm_name} @@ -540,8 +684,10 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_users_resource :param query: (dict) Query parameters for users count + :type query: dict :return: counter + :rtype: int """ query = query or dict() params_path = {"realm-name": self.realm_name} @@ -557,8 +703,10 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_userrepresentation :param username: id in UserRepresentation + :type username: str :return: user_id + :rtype: str """ lower_user_name = username.lower() users = self.get_users(query={"search": lower_user_name}) @@ -567,11 +715,11 @@ class KeycloakAdmin: def get_user(self, user_id): """Get representation of the user. - :param user_id: User id - UserRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_userrepresentation + :param user_id: User id + :type user_id: str :return: UserRepresentation """ params_path = {"realm-name": self.realm_name, "id": user_id} @@ -584,9 +732,11 @@ class KeycloakAdmin: Returns a list of groups of which the user is a member :param user_id: User id + :type user_id: str :param brief_representation: whether to omit attributes in the response - + :type brief_representation: bool :return: user groups list + :rtype: list """ params = {"briefRepresentation": brief_representation} params_path = {"realm-name": self.realm_name, "id": user_id} @@ -599,9 +749,12 @@ class KeycloakAdmin: """Update the user. :param user_id: User id + :type user_id: str :param payload: UserRepresentation + :type payload: dict :return: Http response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": user_id} data_raw = self.raw_put( @@ -613,8 +766,9 @@ class KeycloakAdmin: """Delete the user. :param user_id: User id - + :type user_id: str :return: Http response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": user_id} data_raw = self.raw_delete(urls_patterns.URL_ADMIN_USER.format(**params_path)) @@ -630,10 +784,13 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/#_credentialrepresentation :param user_id: User id + :type user_id: str :param password: New password + :type password: str :param temporary: True if password is temporary - - :return: + :type temporary: bool + :returns: Response + :rtype: dict """ payload = {"type": "password", "temporary": temporary, "value": password} params_path = {"realm-name": self.realm_name, "id": user_id} @@ -651,7 +808,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_credentialrepresentation :param: user_id: user id - :return: Keycloak server response (CredentialRepresentation) + :type user_id: str + :returns: Keycloak server response (CredentialRepresentation) + :rtype: dict """ params_path = {"realm-name": self.realm_name, "id": user_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_USER_CREDENTIALS.format(**params_path)) @@ -664,8 +823,11 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_credentialrepresentation :param: user_id: user id + :type user_id: str :param: credential_id: credential id + :type credential_id: str :return: Keycloak server response (ClientRepresentation) + :rtype: bytes """ params_path = { "realm-name": self.realm_name, @@ -681,7 +843,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_logout :param user_id: User id - :return: + :type user_id: str + :returns: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": user_id} data_raw = self.raw_post( @@ -696,7 +860,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_userconsentrepresentation :param user_id: User id - :return: List of UserConsentRepresentations + :type user_id: str + :returns: List of UserConsentRepresentations + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": user_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_USER_CONSENTS.format(**params_path)) @@ -708,7 +874,9 @@ class KeycloakAdmin: Returns a list of federated identities/social logins of which the user has been associated with :param user_id: User id - :return: federated identities list + :type user_id: str + :returns: Federated identities list + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": user_id} data_raw = self.raw_get( @@ -720,10 +888,15 @@ class KeycloakAdmin: """Add a federated identity / social login provider to the user. :param user_id: User id + :type user_id: str :param provider_id: Social login provider id + :type provider_id: str :param provider_userid: userid specified by the provider + :type provider_userid: str :param provider_username: username specified by the provider - :return: + :type provider_username: str + :returns: Keycloak server response + :rtype: bytes """ payload = { "identityProvider": provider_id, @@ -741,8 +914,11 @@ class KeycloakAdmin: """Delete a federated identity / social login provider from the user. :param user_id: User id + :type user_id: str :param provider_id: Social login provider id - :return: + :type provider_id: str + :returns: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": user_id, "provider": provider_id} data_raw = self.raw_delete( @@ -758,12 +934,18 @@ class KeycloakAdmin: An email contains a link the user can click to perform a set of required actions. :param user_id: User id + :type user_id: str :param payload: A list of actions for the user to complete + :type payload: list :param client_id: Client id (optional) + :type client_id: str :param lifespan: Number of seconds after which the generated token expires (optional) + :type lifespan: int :param redirect_uri: The redirect uri (optional) + :type redirect_uri: str - :return: + :returns: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": user_id} params_query = {"client_id": client_id, "lifespan": lifespan, "redirect_uri": redirect_uri} @@ -780,10 +962,14 @@ class KeycloakAdmin: An email contains a link the user can click to perform a set of required actions. :param user_id: User id + :type user_id: str :param client_id: Client id (optional) + :type client_id: str :param redirect_uri: Redirect uri (optional) + :type redirect_uri: str - :return: + :returns: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": user_id} params_query = {"client_id": client_id, "redirect_uri": redirect_uri} @@ -797,12 +983,13 @@ class KeycloakAdmin: def get_sessions(self, user_id): """Get sessions associated with the user. - :param user_id: id of user - UserSessionRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_usersessionrepresentation + :param user_id: Id of user + :type user_id: str :return: UserSessionRepresentation + :rtype: dict """ params_path = {"realm-name": self.realm_name, "id": user_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_GET_SESSIONS.format(**params_path)) @@ -815,6 +1002,7 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_serverinforepresentation :return: ServerInfoRepresentation + :rtype: dict """ data_raw = self.raw_get(urls_patterns.URL_ADMIN_SERVER_INFO) return raise_error_from_response(data_raw, KeycloakGetError) @@ -827,7 +1015,10 @@ class KeycloakAdmin: GroupRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/#_grouprepresentation + :param query: Additional query options + :type query: dict :return: array GroupRepresentation + :rtype: list """ query = query or {} params_path = {"realm-name": self.realm_name} @@ -847,7 +1038,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/#_grouprepresentation :param group_id: The group id + :type group_id: str :return: Keycloak server response (GroupRepresentation) + :rtype: dict """ params_path = {"realm-name": self.realm_name, "id": group_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_GROUP.format(**params_path)) @@ -861,10 +1054,12 @@ class KeycloakAdmin: GroupRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/#_grouprepresentation - :param name: group (GroupRepresentation) + :param group: group (GroupRepresentation) + :type group: dict :param path: group path (string) - + :type path: str :return: Keycloak server response (GroupRepresentation) + :rtype: dict """ for subgroup in group["subGroups"]: if subgroup["path"] == path: @@ -886,9 +1081,12 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/#_userrepresentation :param group_id: The group id + :type group_id: str :param query: Additional query parameters (see https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_getmembers) + :type query: dict :return: Keycloak server response (UserRepresentation) + :rtype: list """ query = query or {} params_path = {"realm-name": self.realm_name, "id": group_id} @@ -909,8 +1107,11 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/#_grouprepresentation :param path: group path + :type path: str :param search_in_subgroups: True if want search in the subgroups + :type search_in_subgroups: bool :return: Keycloak server response (GroupRepresentation) + :rtype: dict """ groups = self.get_groups() @@ -930,14 +1131,18 @@ class KeycloakAdmin: def create_group(self, payload, parent=None, skip_exists=False): """Create a group in the Realm. + GroupRepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/#_grouprepresentation + :param payload: GroupRepresentation + :type payload: dict :param parent: parent group's id. Required to create a sub-group. + :type parent: str :param skip_exists: If true then do not raise an error if it already exists - - GroupRepresentation - https://www.keycloak.org/docs-api/18.0/rest-api/#_grouprepresentation + :type skip_exists: bool :return: Group id for newly created group or None for an existing group + :rtype: str """ if parent is None: params_path = {"realm-name": self.realm_name} @@ -962,13 +1167,16 @@ class KeycloakAdmin: def update_group(self, group_id, payload): """Update group, ignores subgroups. - :param group_id: id of group - :param payload: GroupRepresentation with updated information. - GroupRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/#_grouprepresentation + :param group_id: id of group + :type group_id: str + :param payload: GroupRepresentation with updated information. + :type payload: dict + :return: Http response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": group_id} data_raw = self.raw_put( @@ -982,8 +1190,11 @@ class KeycloakAdmin: Cannot delete group if disabled :param group_id: id of group - :param enabled: boolean + :type group_id: str + :param enabled: Enabled flag + :type enabled: bool :return: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": group_id} data_raw = self.raw_put( @@ -996,8 +1207,11 @@ class KeycloakAdmin: """Add user to group (user_id and group_id). :param user_id: id of user + :type user_id: str :param group_id: id of group to add to + :type group_id: str :return: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": user_id, "group-id": group_id} data_raw = self.raw_put( @@ -1009,8 +1223,11 @@ class KeycloakAdmin: """Remove user from group (user_id and group_id). :param user_id: id of user + :type user_id: str :param group_id: id of group to remove from + :type group_id: str :return: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": user_id, "group-id": group_id} data_raw = self.raw_delete(urls_patterns.URL_ADMIN_USER_GROUP.format(**params_path)) @@ -1020,7 +1237,9 @@ class KeycloakAdmin: """Delete a group in the Realm. :param group_id: id of group to delete + :type group_id: str :return: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": group_id} data_raw = self.raw_delete(urls_patterns.URL_ADMIN_GROUP.format(**params_path)) @@ -1035,6 +1254,7 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation :return: Keycloak server response (ClientRepresentation) + :rtype: list """ params_path = {"realm-name": self.realm_name} data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENTS.format(**params_path)) @@ -1047,7 +1267,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation :param client_id: id of client (not client-id) + :type client_id: str :return: Keycloak server response (ClientRepresentation) + :rtype: dict """ params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT.format(**params_path)) @@ -1060,7 +1282,9 @@ class KeycloakAdmin: :param client_id: clientId in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :type client_id: str :return: client_id (uuid as string) + :rtype: str """ clients = self.get_clients() @@ -1075,7 +1299,9 @@ class KeycloakAdmin: :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :type client_id: str :return: Keycloak server response + :rtype: dict """ params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get( @@ -1088,10 +1314,15 @@ class KeycloakAdmin: :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :type client_id: str :param payload: ResourceRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_resourcerepresentation + :type payload: dict + :param skip_exists: Skip the creation in case the resource exists + :type skip_exists: bool :return: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": client_id} @@ -1108,7 +1339,9 @@ class KeycloakAdmin: :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :type client_id: str :return: Keycloak server response + :rtype: dict """ params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get( @@ -1119,11 +1352,6 @@ class KeycloakAdmin: def create_client_authz_role_based_policy(self, client_id, payload, skip_exists=False): """Create role-based policy of client. - :param client_id: id in ClientRepresentation - https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation - :param payload: No Document - :return: Keycloak server response - Payload example:: payload={ @@ -1138,6 +1366,16 @@ class KeycloakAdmin: ] } + :param client_id: id in ClientRepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :type client_id: str + :param payload: No Document + :type payload: dict + :param skip_exists: Skip creation in case the object exists + :type skip_exists: bool + :return: Keycloak server response + :rtype: bytes + """ params_path = {"realm-name": self.realm_name, "id": client_id} @@ -1152,12 +1390,6 @@ class KeycloakAdmin: def create_client_authz_resource_based_permission(self, client_id, payload, skip_exists=False): """Create resource-based permission of client. - :param client_id: id in ClientRepresentation - https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation - :param payload: PolicyRepresentation - https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_policyrepresentation - :return: Keycloak server response - Payload example:: payload={ @@ -1172,6 +1404,17 @@ class KeycloakAdmin: policy_id ] + :param client_id: id in ClientRepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :type client_id: str + :param payload: PolicyRepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_policyrepresentation + :type payload: dict + :param skip_exists: Skip creation in case the object already exists + :type skip_exists: bool + :return: Keycloak server response + :rtype: bytes + """ params_path = {"realm-name": self.realm_name, "id": client_id} @@ -1188,7 +1431,9 @@ class KeycloakAdmin: :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :type client_id: str :return: Keycloak server response + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_AUTHZ_SCOPES.format(**params_path)) @@ -1199,7 +1444,9 @@ class KeycloakAdmin: :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :type client_id: str :return: Keycloak server response + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get( @@ -1212,7 +1459,9 @@ class KeycloakAdmin: :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :type client_id: str :return: Keycloak server response + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get( @@ -1225,7 +1474,9 @@ class KeycloakAdmin: :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :type client_id: str :return: UserRepresentation + :rtype: dict """ params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get( @@ -1240,8 +1491,11 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation :param skip_exists: If true then do not raise an error if client already exists + :type skip_exists: bool :param payload: ClientRepresentation + :type payload: dict :return: Client ID + :rtype: str """ if skip_exists: client_id = self.get_client_id(client_id=payload["clientId"]) @@ -1263,9 +1517,12 @@ class KeycloakAdmin: """Update a client. :param client_id: Client id + :type client_id: str :param payload: ClientRepresentation + :type payload: dict :return: Http response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_put( @@ -1280,7 +1537,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation :param client_id: keycloak client id (not oauth client-id) + :type client_id: str :return: Keycloak server response (ClientRepresentation) + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_delete(urls_patterns.URL_ADMIN_CLIENT.format(**params_path)) @@ -1296,7 +1555,11 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_serverinforepresentation :param client_id: Client id + :type client_id: str :param provider_id: provider id to specify response format + :type provider_id: str + :returns: Installation providers + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": client_id, "provider-id": provider_id} data_raw = self.raw_get( @@ -1311,7 +1574,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation :param brief_representation: whether to omit role attributes in the response - :return: Keycloak server response (array RoleRepresentation) + :type brief_representation: bool + :return: Keycloak server response (RoleRepresentation) + :rtype: list """ params_path = {"realm-name": self.realm_name} params = {"briefRepresentation": brief_representation} @@ -1324,9 +1589,12 @@ class KeycloakAdmin: """Get role members of realm by role name. :param role_name: Name of the role. + :type role_name: str :param query: Additional Query parameters (see https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_roles_resource) + :type query: dict :return: Keycloak Server Response (UserRepresentation) + :rtype: list """ query = query or dict() params_path = {"realm-name": self.realm_name, "role-name": role_name} @@ -1341,8 +1609,11 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation :param client_id: id of client (not client-id) + :type client_id: str :param brief_representation: whether to omit role attributes in the response - :return: Keycloak server response (array RoleRepresentation) + :type brief_representation: bool + :return: Keycloak server response (RoleRepresentation) + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": client_id} params = {"briefRepresentation": brief_representation} @@ -1360,8 +1631,11 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation :param client_id: id of client (not client-id) - :param role_name: role’s name (not id!) - :return: Keycloak server response (RoleRepresentation) + :type client_id: str + :param role_name: role's name (not id!) + :type role_name: str + :return: role_id + :rtype: str """ params_path = {"realm-name": self.realm_name, "id": client_id, "role-name": role_name} data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_ROLE.format(**params_path)) @@ -1372,13 +1646,15 @@ class KeycloakAdmin: This is required for further actions with this role. - :param client_id: id of client (not client-id) - :param role_name: role’s name (not id!) - RoleRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation + :param client_id: id of client (not client-id) + :type client_id: str + :param role_name: role's name (not id!) + :type role_name: str :return: role_id + :rtype: str """ role = self.get_client_role(client_id, role_name) return role.get("id") @@ -1390,9 +1666,13 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation :param client_role_id: id of client (not client-id) + :type client_role_id: str :param payload: RoleRepresentation + :type payload: dict :param skip_exists: If true then do not raise an error if client role already exists + :type skip_exists: bool :return: Client role name + :rtype: str """ if skip_exists: try: @@ -1415,9 +1695,13 @@ class KeycloakAdmin: """Add composite roles to client role. :param client_role_id: id of client (not client-id) + :type client_role_id: str :param role_name: The name of the role + :type role_name: str :param roles: roles list or role (use RoleRepresentation) to be updated + :type roles: list :return: Keycloak server response + :rtype: bytes """ payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": client_role_id, "role-name": role_name} @@ -1434,8 +1718,13 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation :param client_role_id: id of client (not client-id) + :type client_role_id: str :param role_name: role's name (not id!) + :type role_name: str :param payload: RoleRepresentation + :type payload: dict + :returns: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": client_role_id, "role-name": role_name} data_raw = self.raw_put( @@ -1450,7 +1739,11 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation :param client_role_id: id of client (not client-id) + :type client_role_id: str :param role_name: role's name (not id!) + :type role_name: str + :returns: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": client_role_id, "role-name": role_name} data_raw = self.raw_delete(urls_patterns.URL_ADMIN_CLIENT_ROLE.format(**params_path)) @@ -1460,9 +1753,13 @@ class KeycloakAdmin: """Assign a client role to a user. :param user_id: id of user + :type user_id: str :param client_id: id of client (not client-id) + :type client_id: str :param roles: roles list or role (use RoleRepresentation) + :type roles: list :return: Keycloak server response + :rtype: bytes """ payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": user_id, "client-id": client_id} @@ -1476,10 +1773,14 @@ class KeycloakAdmin: """Get members by client role. :param client_id: The client id + :type client_id: str :param role_name: the name of role to be queried. + :type role_name: str :param query: Additional query parameters (see https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clients_resource) + :type query: dict :return: Keycloak server response (UserRepresentation) + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": client_id, "role-name": role_name} return self.__fetch_all( @@ -1490,10 +1791,14 @@ class KeycloakAdmin: """Get group members by client role. :param client_id: The client id + :type client_id: str :param role_name: the name of role to be queried. + :type role_name: str :param query: Additional query parameters (see https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clients_resource) + :type query: dict :return: Keycloak server response + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": client_id, "role-name": role_name} return self.__fetch_all( @@ -1504,8 +1809,11 @@ class KeycloakAdmin: """Create a new role for the realm or client. :param payload: The role (use RoleRepresentation) + :type payload: dict :param skip_exists: If true then do not raise an error if realm role already exists + :type skip_exists: bool :return: Realm role name + :rtype: str """ if skip_exists: try: @@ -1531,7 +1839,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation :param role_name: role's name, not id! - :return: Keycloak server response (RoleRepresentation) + :type role_name: str + :return: role + :rtype: dict """ params_path = {"realm-name": self.realm_name, "role-name": role_name} data_raw = self.raw_get( @@ -1543,8 +1853,11 @@ class KeycloakAdmin: """Update a role for the realm by name. :param role_name: The name of the role to be updated + :type role_name: str :param payload: The role (use RoleRepresentation) + :type payload: dict :return: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "role-name": role_name} data_raw = self.raw_put( @@ -1556,8 +1869,10 @@ class KeycloakAdmin: def delete_realm_role(self, role_name): """Delete a role for the realm by name. - :param payload: The role name {'role-name':'name-of-the-role'} + :param role_name: The role name + :type role_name: str :return: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "role-name": role_name} data_raw = self.raw_delete( @@ -1569,8 +1884,11 @@ class KeycloakAdmin: """Add composite roles to the role. :param role_name: The name of the role + :type role_name: str :param roles: roles list or role (use RoleRepresentation) to be updated + :type roles: list :return: Keycloak server response + :rtype: bytes """ payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "role-name": role_name} @@ -1584,8 +1902,11 @@ class KeycloakAdmin: """Remove composite roles from the role. :param role_name: The name of the role + :type role_name: str :param roles: roles list or role (use RoleRepresentation) to be removed + :type roles: list :return: Keycloak server response + :rtype: bytes """ payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "role-name": role_name} @@ -1599,7 +1920,9 @@ class KeycloakAdmin: """Get composite roles of the role. :param role_name: The name of the role + :type role_name: str :return: Keycloak server response (array RoleRepresentation) + :rtype: list """ params_path = {"realm-name": self.realm_name, "role-name": role_name} data_raw = self.raw_get( @@ -1611,8 +1934,11 @@ class KeycloakAdmin: """Assign realm roles to a client's scope. :param client_id: id of client (not client-id) + :type client_id: str :param roles: roles list or role (use RoleRepresentation) + :type roles: list :return: Keycloak server response + :rtype: dict """ payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": client_id} @@ -1626,8 +1952,11 @@ class KeycloakAdmin: """Delete realm roles of a client's scope. :param client_id: id of client (not client-id) + :type client_id: str :param roles: roles list or role (use RoleRepresentation) + :type roles: list :return: Keycloak server response + :rtype: dict """ payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": client_id} @@ -1641,7 +1970,9 @@ class KeycloakAdmin: """Get all realm roles for a client's scope. :param client_id: id of client (not client-id) + :type client_id: str :return: Keycloak server response (array RoleRepresentation) + :rtype: dict """ params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get( @@ -1653,9 +1984,13 @@ class KeycloakAdmin: """Assign client roles to a client's scope. :param client_id: id of client (not client-id) who is assigned the roles + :type client_id: str :param client_roles_owner_id: id of client (not client-id) who has the roles + :type client_roles_owner_id: str :param roles: roles list or role (use RoleRepresentation) + :type roles: list :return: Keycloak server response + :rtype: dict """ payload = roles if isinstance(roles, list) else [roles] params_path = { @@ -1673,9 +2008,13 @@ class KeycloakAdmin: """Delete client roles of a client's scope. :param client_id: id of client (not client-id) who is assigned the roles + :type client_id: str :param client_roles_owner_id: id of client (not client-id) who has the roles + :type client_roles_owner_id: str :param roles: roles list or role (use RoleRepresentation) + :type roles: list :return: Keycloak server response + :rtype: dict """ payload = roles if isinstance(roles, list) else [roles] params_path = { @@ -1693,8 +2032,11 @@ class KeycloakAdmin: """Get all client roles for a client's scope. :param client_id: id of client (not client-id) + :type client_id: str :param client_roles_owner_id: id of client (not client-id) who has the roles + :type client_roles_owner_id: str :return: Keycloak server response (array RoleRepresentation) + :rtype: dict """ params_path = { "realm-name": self.realm_name, @@ -1710,8 +2052,11 @@ class KeycloakAdmin: """Assign realm roles to a user. :param user_id: id of user + :type user_id: str :param roles: roles list or role (use RoleRepresentation) + :type roles: list :return: Keycloak server response + :rtype: bytes """ payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": user_id} @@ -1725,8 +2070,11 @@ class KeycloakAdmin: """Delete realm roles of a user. :param user_id: id of user + :type user_id: str :param roles: roles list or role (use RoleRepresentation) + :type roles: list :return: Keycloak server response + :rtype: bytes """ payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": user_id} @@ -1740,7 +2088,9 @@ class KeycloakAdmin: """Get all realm roles for a user. :param user_id: id of user + :type user_id: str :return: Keycloak server response (array RoleRepresentation) + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": user_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_USER_REALM_ROLES.format(**params_path)) @@ -1750,7 +2100,9 @@ class KeycloakAdmin: """Get all available (i.e. unassigned) realm roles for a user. :param user_id: id of user + :type user_id: str :return: Keycloak server response (array RoleRepresentation) + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": user_id} data_raw = self.raw_get( @@ -1762,8 +2114,11 @@ class KeycloakAdmin: """Get all composite (i.e. implicit) realm roles for a user. :param user_id: id of user + :type user_id: str :param brief_representation: whether to omit role attributes in the response + :type brief_representation: bool :return: Keycloak server response (array RoleRepresentation) + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": user_id} params = {"briefRepresentation": brief_representation} @@ -1775,9 +2130,12 @@ class KeycloakAdmin: def assign_group_realm_roles(self, group_id, roles): """Assign realm roles to a group. - :param group_id: id of groupp + :param group_id: id of group + :type group_id: str :param roles: roles list or role (use GroupRoleRepresentation) + :type roles: list :return: Keycloak server response + :rtype: bytes """ payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": group_id} @@ -1791,8 +2149,11 @@ class KeycloakAdmin: """Delete realm roles of a group. :param group_id: id of group + :type group_id: str :param roles: roles list or role (use GroupRoleRepresentation) + :type roles: list :return: Keycloak server response + :rtype: bytes """ payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": group_id} @@ -1805,9 +2166,12 @@ class KeycloakAdmin: def get_group_realm_roles(self, group_id, brief_representation=True): """Get all realm roles for a group. - :param user_id: id of the group + :param group_id: id of the group + :type group_id: str :param brief_representation: whether to omit role attributes in the response + :type brief_representation: bool :return: Keycloak server response (array RoleRepresentation) + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": group_id} params = {"briefRepresentation": brief_representation} @@ -1820,9 +2184,13 @@ class KeycloakAdmin: """Assign client roles to a group. :param group_id: id of group + :type group_id: str :param client_id: id of client (not client-id) + :type client_id: str :param roles: roles list or role (use GroupRoleRepresentation) + :type roles: list :return: Keycloak server response + :rtype: bytes """ payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": group_id, "client-id": client_id} @@ -1836,8 +2204,11 @@ class KeycloakAdmin: """Get client roles of a group. :param group_id: id of group + :type group_id: str :param client_id: id of client (not client-id) + :type client_id: str :return: Keycloak server response + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": group_id, "client-id": client_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_GROUPS_CLIENT_ROLES.format(**params_path)) @@ -1847,9 +2218,13 @@ class KeycloakAdmin: """Delete client roles of a group. :param group_id: id of group + :type group_id: str :param client_id: id of client (not client-id) + :type client_id: str :param roles: roles list or role (use GroupRoleRepresentation) + :type roles: list :return: Keycloak server response (array RoleRepresentation) + :rtype: bytes """ payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": group_id, "client-id": client_id} @@ -1863,8 +2238,11 @@ class KeycloakAdmin: """Get all client roles for a user. :param user_id: id of user + :type user_id: str :param client_id: id of client (not client-id) + :type client_id: str :return: Keycloak server response (array RoleRepresentation) + :rtype: list """ return self._get_client_roles_of_user( urls_patterns.URL_ADMIN_USER_CLIENT_ROLES, user_id, client_id @@ -1874,8 +2252,11 @@ class KeycloakAdmin: """Get available client role-mappings for a user. :param user_id: id of user + :type user_id: str :param client_id: id of client (not client-id) + :type client_id: str :return: Keycloak server response (array RoleRepresentation) + :rtype: list """ return self._get_client_roles_of_user( urls_patterns.URL_ADMIN_USER_CLIENT_ROLES_AVAILABLE, user_id, client_id @@ -1885,9 +2266,13 @@ class KeycloakAdmin: """Get composite client role-mappings for a user. :param user_id: id of user + :type user_id: str :param client_id: id of client (not client-id) + :type client_id: str :param brief_representation: whether to omit attributes in the response + :type brief_representation: bool :return: Keycloak server response (array RoleRepresentation) + :rtype: list """ params = {"briefRepresentation": brief_representation} return self._get_client_roles_of_user( @@ -1897,6 +2282,19 @@ class KeycloakAdmin: def _get_client_roles_of_user( self, client_level_role_mapping_url, user_id, client_id, **params ): + """Get client roles of a single user helper. + + :param client_level_role_mapping_url: Url for the client role mapping + :type client_level_role_mapping_url: str + :param user_id: User id + :type user_id: str + :param client_id: Client id + :type client_id: str + :param params: Additional parameters + :type params: dict + :returns: Client roles of a user + :rtype: list + """ params_path = {"realm-name": self.realm_name, "id": user_id, "client-id": client_id} data_raw = self.raw_get(client_level_role_mapping_url.format(**params_path), **params) return raise_error_from_response(data_raw, KeycloakGetError) @@ -1905,9 +2303,13 @@ class KeycloakAdmin: """Delete client roles from a user. :param user_id: id of user + :type user_id: str :param client_id: id of client containing role (not client-id) + :type client_id: str :param roles: roles list or role to delete (use RoleRepresentation) + :type roles: list :return: Keycloak server response + :rtype: bytes """ payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": user_id, "client-id": client_id} @@ -1926,6 +2328,7 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationflowrepresentation :return: Keycloak server response (AuthenticationFlowRepresentation) + :rtype: list """ params_path = {"realm-name": self.realm_name} data_raw = self.raw_get(urls_patterns.URL_ADMIN_FLOWS.format(**params_path)) @@ -1940,7 +2343,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationflowrepresentation :param flow_id: the id of a flow NOT it's alias + :type flow_id: str :return: Keycloak server response (AuthenticationFlowRepresentation) + :rtype: dict """ params_path = {"realm-name": self.realm_name, "flow-id": flow_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_FLOWS_ALIAS.format(**params_path)) @@ -1953,8 +2358,11 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationflowrepresentation :param payload: AuthenticationFlowRepresentation + :type payload: dict :param skip_exists: Do not raise an error if authentication flow already exists + :type skip_exists: bool :return: Keycloak server response (RoleRepresentation) + :rtype: bytes """ params_path = {"realm-name": self.realm_name} data_raw = self.raw_post( @@ -1970,8 +2378,11 @@ class KeycloakAdmin: The new name is given as 'newName' attribute of the passed payload. :param payload: JSON containing 'newName' attribute + :type payload: dict :param flow_alias: the flow alias + :type flow_alias: str :return: Keycloak server response (RoleRepresentation) + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias} data_raw = self.raw_post( @@ -1986,7 +2397,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationinforepresentation :param flow_id: authentication flow id + :type flow_id: str :return: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": flow_id} data_raw = self.raw_delete(urls_patterns.URL_ADMIN_FLOW.format(**params_path)) @@ -1998,7 +2411,9 @@ class KeycloakAdmin: Returns all execution steps :param flow_alias: the flow alias + :type flow_alias: str :return: Response(json) + :rtype: list """ params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias} data_raw = self.raw_get(urls_patterns.URL_ADMIN_FLOWS_EXECUTIONS.format(**params_path)) @@ -2011,8 +2426,11 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationexecutioninforepresentation :param payload: AuthenticationExecutionInfoRepresentation + :type payload: dict :param flow_alias: The flow alias + :type flow_alias: str :return: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias} data_raw = self.raw_put( @@ -2028,7 +2446,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationexecutioninforepresentation :param execution_id: the execution ID + :type execution_id: str :return: Response(json) + :rtype: dict """ params_path = {"realm-name": self.realm_name, "id": execution_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_FLOWS_EXECUTION.format(**params_path)) @@ -2041,8 +2461,11 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationexecutioninforepresentation :param payload: AuthenticationExecutionInfoRepresentation + :type payload: dict :param flow_alias: The flow alias + :type flow_alias: str :return: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias} data_raw = self.raw_post( @@ -2058,7 +2481,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationexecutioninforepresentation :param execution_id: keycloak client id (not oauth client-id) + :type execution_id: str :return: Keycloak server response (json) + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": execution_id} data_raw = self.raw_delete(urls_patterns.URL_ADMIN_FLOWS_EXECUTION.format(**params_path)) @@ -2071,9 +2496,13 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationflowrepresentation :param payload: AuthenticationFlowRepresentation + :type payload: dict :param flow_alias: The flow alias + :type flow_alias: str :param skip_exists: Do not raise an error if authentication flow already exists + :type skip_exists: bool :return: Keycloak server response (RoleRepresentation) + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias} data_raw = self.raw_post( @@ -2087,7 +2516,8 @@ class KeycloakAdmin: def get_authenticator_providers(self): """Get authenticator providers list. - :return: Response(json) + :return: Authenticator providers + :rtype: list """ params_path = {"realm-name": self.realm_name} data_raw = self.raw_get( @@ -2102,7 +2532,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticatorconfiginforepresentation :param provider_id: Provider Id + :type provider_id: str :return: AuthenticatorConfigInfoRepresentation + :rtype: dict """ params_path = {"realm-name": self.realm_name, "provider-id": provider_id} data_raw = self.raw_get( @@ -2116,7 +2548,9 @@ class KeycloakAdmin: Returns all configuration details. :param config_id: Authenticator config id + :type config_id: str :return: Response(json) + :rtype: dict """ params_path = {"realm-name": self.realm_name, "id": config_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_AUTHENTICATOR_CONFIG.format(**params_path)) @@ -2129,8 +2563,11 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticatorconfigrepresentation :param payload: AuthenticatorConfigRepresentation + :type payload: dict :param config_id: Authenticator config id + :type config_id: str :return: Response(json) + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": config_id} data_raw = self.raw_put( @@ -2145,7 +2582,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authentication_management_resource :param config_id: Authenticator config id + :type config_id: str :return: Keycloak server Response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": config_id} data_raw = self.raw_delete( @@ -2157,8 +2596,11 @@ class KeycloakAdmin: """Trigger user sync from provider. :param storage_id: The id of the user storage provider + :type storage_id: str :param action: Action can be "triggerFullSync" or "triggerChangedUsersSync" - :return: + :type action: str + :return: Keycloak server response + :rtype: bytes """ data = {"action": action} params_query = {"action": action} @@ -2178,6 +2620,7 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_getclientscopes :return: Keycloak server response Array of (ClientScopeRepresentation) + :rtype: list """ params_path = {"realm-name": self.realm_name} data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_SCOPES.format(**params_path)) @@ -2190,7 +2633,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_getclientscopes :param client_scope_id: The id of the client scope + :type client_scope_id: str :return: Keycloak server response (ClientScopeRepresentation) + :rtype: dict """ params_path = {"realm-name": self.realm_name, "scope-id": client_scope_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_SCOPE.format(**params_path)) @@ -2203,7 +2648,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_getclientscopes :param client_scope_name: (str) Name of the client scope + :type client_scope_name: str :returns: ClientScopeRepresentation or None + :rtype: dict """ client_scopes = self.get_client_scopes() for client_scope in client_scopes: @@ -2219,8 +2666,11 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_getclientscopes :param payload: ClientScopeRepresentation + :type payload: dict :param skip_exists: If true then do not raise an error if client scope already exists + :type skip_exists: bool :return: Client scope id + :rtype: str """ if skip_exists: exists = self.get_client_scope_by_name(client_scope_name=payload["name"]) @@ -2245,8 +2695,11 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_client_scopes_resource :param client_scope_id: The id of the client scope + :type client_scope_id: str :param payload: ClientScopeRepresentation - :return: Keycloak server response (ClientScopeRepresentation) + :type payload: dict + :return: Keycloak server response (ClientScopeRepresentation) + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "scope-id": client_scope_id} data_raw = self.raw_put( @@ -2261,7 +2714,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_client_scopes_resource :param client_scope_id: The id of the client scope - :return: Keycloak server response + :type client_scope_id: str + :return: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "scope-id": client_scope_id} data_raw = self.raw_delete(urls_patterns.URL_ADMIN_CLIENT_SCOPE.format(**params_path)) @@ -2272,7 +2727,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_protocol_mappers_resource :param client_scope_id: Client scope id + :type client_scope_id: str :returns: Keycloak server response (ProtocolMapperRepresentation) + :rtype: list """ params_path = {"realm-name": self.realm_name, "scope-id": client_scope_id} data_raw = self.raw_get( @@ -2286,8 +2743,11 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_create_mapper :param client_scope_id: The id of the client scope + :type client_scope_id: str :param payload: ProtocolMapperRepresentation + :type payload: dict :return: Keycloak server Response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "scope-id": client_scope_id} @@ -2304,8 +2764,11 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_delete_mapper :param client_scope_id: The id of the client scope + :type client_scope_id: str :param protocol_mapper_id: Protocol mapper id + :type protocol_mapper_id: str :return: Keycloak server Response + :rtype: bytes """ params_path = { "realm-name": self.realm_name, @@ -2324,10 +2787,14 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_protocol_mappers_resource :param client_scope_id: The id of the client scope + :type client_scope_id: str :param protocol_mapper_id: The id of the protocol mapper which exists in the client scope and should to be updated + :type protocol_mapper_id: str :param payload: ProtocolMapperRepresentation + :type payload: dict :return: Keycloak server Response + :rtype: bytes """ params_path = { "realm-name": self.realm_name, @@ -2348,6 +2815,7 @@ class KeycloakAdmin: Return list of default default client scopes :return: Keycloak server response + :rtype: list """ params_path = {"realm-name": self.realm_name} data_raw = self.raw_get( @@ -2359,7 +2827,9 @@ class KeycloakAdmin: """Delete default default client scope. :param scope_id: default default client scope id + :type scope_id: str :return: Keycloak server response + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": scope_id} data_raw = self.raw_delete( @@ -2371,7 +2841,9 @@ class KeycloakAdmin: """Add default default client scope. :param scope_id: default default client scope id + :type scope_id: str :return: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": scope_id} payload = {"realm": self.realm_name, "clientScopeId": scope_id} @@ -2387,6 +2859,7 @@ class KeycloakAdmin: Return list of default optional client scopes :return: Keycloak server response + :rtype: list """ params_path = {"realm-name": self.realm_name} data_raw = self.raw_get( @@ -2398,7 +2871,9 @@ class KeycloakAdmin: """Delete default optional client scope. :param scope_id: default optional client scope id + :type scope_id: str :return: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": scope_id} data_raw = self.raw_delete( @@ -2410,7 +2885,9 @@ class KeycloakAdmin: """Add default optional client scope. :param scope_id: default optional client scope id + :type scope_id: str :return: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": scope_id} payload = {"realm": self.realm_name, "clientScopeId": scope_id} @@ -2426,7 +2903,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_protocolmapperrepresentation :param client_id: Client id + :type client_id: str :returns: KeycloakServerResponse (list of ProtocolMapperRepresentation) + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": client_id} @@ -2442,8 +2921,11 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_create_mapper :param client_id: The id of the client + :type client_id: str :param payload: ProtocolMapperRepresentation + :type payload: dict :return: Keycloak server Response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": client_id} @@ -2458,9 +2940,13 @@ class KeycloakAdmin: """Update client mapper. :param client_id: The id of the client - :param client_mapper_id: The id of the mapper to be deleted + :type client_id: str + :param mapper_id: The id of the mapper to be deleted + :type mapper_id: str :param payload: ProtocolMapperRepresentation + :type payload: dict :return: Keycloak server response + :rtype: bytes """ params_path = { "realm-name": self.realm_name, @@ -2479,9 +2965,13 @@ class KeycloakAdmin: """Remove a mapper from the client. https://www.keycloak.org/docs-api/15.0/rest-api/index.html#_protocol_mappers_resource + :param client_id: The id of the client + :type client_id: str :param client_mapper_id: The id of the mapper to be deleted + :type client_mapper_id: str :return: Keycloak server response + :rtype: bytes """ params_path = { "realm-name": self.realm_name, @@ -2500,7 +2990,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_regeneratesecret :param client_id: id of client (not client-id) + :type client_id: str :return: Keycloak server response (ClientRepresentation) + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_post( @@ -2514,7 +3006,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_getclientsecret :param client_id: id of client (not client-id) + :type client_id: str :return: Keycloak server response (ClientRepresentation) + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_SECRETS.format(**params_path)) @@ -2529,7 +3023,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_componentrepresentation :param query: Query parameters (optional) + :type query: dict :return: components list + :rtype: list """ query = query or dict() params_path = {"realm-name": self.realm_name} @@ -2545,7 +3041,9 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_componentrepresentation :param payload: ComponentRepresentation + :type payload: dict :return: Component id + :rtype: str """ params_path = {"realm-name": self.realm_name} data_raw = self.raw_post( @@ -2563,7 +3061,10 @@ class KeycloakAdmin: ComponentRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_componentrepresentation + :param component_id: Id of the component + :type component_id: str :return: ComponentRepresentation + :rtype: dict """ params_path = {"realm-name": self.realm_name, "component-id": component_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_COMPONENT.format(**params_path)) @@ -2573,10 +3074,12 @@ class KeycloakAdmin: """Update the component. :param component_id: Component id + :type component_id: str :param payload: ComponentRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_componentrepresentation - + :type payload: dict :return: Http response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "component-id": component_id} data_raw = self.raw_put( @@ -2588,8 +3091,9 @@ class KeycloakAdmin: """Delete the component. :param component_id: Component id - + :type component_id: str :return: Http response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "component-id": component_id} data_raw = self.raw_delete(urls_patterns.URL_ADMIN_COMPONENT.format(**params_path)) @@ -2604,6 +3108,7 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_key_resource :return: keys list + :rtype: list """ params_path = {"realm-name": self.realm_name} data_raw = self.raw_get(urls_patterns.URL_ADMIN_KEYS.format(**params_path), data=None) @@ -2617,7 +3122,10 @@ class KeycloakAdmin: EventRepresentation array https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_eventrepresentation + :param query: Additional query parameters + :type query: dict :return: events list + :rtype: list """ query = query or dict() params_path = {"realm-name": self.realm_name} @@ -2632,7 +3140,10 @@ class KeycloakAdmin: RealmEventsConfigRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_realmeventsconfigrepresentation + :param payload: Payload object for the events configuration + :type payload: dict :return: Http response + :rtype: bytes """ params_path = {"realm-name": self.realm_name} data_raw = self.raw_put( @@ -2645,6 +3156,13 @@ class KeycloakAdmin: If auto_refresh is set for *get* and *access_token* is expired, it will refresh the token and try *get* once more. + + :param args: Additional arguments + :type args: tuple + :param kwargs: Additional keyword arguments + :type kwargs: dict + :returns: Response + :rtype: Response """ r = self.connection.raw_get(*args, **kwargs) if "get" in self.auto_refresh_token and r.status_code == 401: @@ -2657,6 +3175,13 @@ class KeycloakAdmin: If auto_refresh is set for *post* and *access_token* is expired, it will refresh the token and try *post* once more. + + :param args: Additional arguments + :type args: tuple + :param kwargs: Additional keyword arguments + :type kwargs: dict + :returns: Response + :rtype: Response """ r = self.connection.raw_post(*args, **kwargs) if "post" in self.auto_refresh_token and r.status_code == 401: @@ -2669,6 +3194,13 @@ class KeycloakAdmin: If auto_refresh is set for *put* and *access_token* is expired, it will refresh the token and try *put* once more. + + :param args: Additional arguments + :type args: tuple + :param kwargs: Additional keyword arguments + :type kwargs: dict + :returns: Response + :rtype: Response """ r = self.connection.raw_put(*args, **kwargs) if "put" in self.auto_refresh_token and r.status_code == 401: @@ -2681,6 +3213,13 @@ class KeycloakAdmin: If auto_refresh is set for *delete* and *access_token* is expired, it will refresh the token and try *delete* once more. + + :param args: Additional arguments + :type args: tuple + :param kwargs: Additional keyword arguments + :type kwargs: dict + :returns: Response + :rtype: Response """ r = self.connection.raw_delete(*args, **kwargs) if "delete" in self.auto_refresh_token and r.status_code == 401: @@ -2689,7 +3228,10 @@ class KeycloakAdmin: return r def get_token(self): - """Get admin token.""" + """Get admin token. + + The admin token is then set in the `token` attribute. + """ if self.user_realm_name: token_realm_name = self.user_realm_name elif self.realm_name: @@ -2737,7 +3279,10 @@ class KeycloakAdmin: ) def refresh_token(self): - """Refresh the token.""" + """Refresh the token. + + :raises KeycloakPostError: In case the refresh token request failed. + """ refresh_token = self.token.get("refresh_token", None) if refresh_token is None: self.get_token() @@ -2762,12 +3307,13 @@ class KeycloakAdmin: def get_client_all_sessions(self, client_id): """Get sessions associated with the client. - :param client_id: id of client - UserSessionRepresentation http://www.keycloak.org/docs-api/18.0/rest-api/index.html#_usersessionrepresentation + :param client_id: id of client + :type client_id: str :return: UserSessionRepresentation + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_ALL_SESSIONS.format(**params_path)) @@ -2779,6 +3325,7 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_getclientsessionstats :return: Dict of clients and session count + :rtype: dict """ params_path = {"realm-name": self.realm_name} data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_SESSION_STATS.format(**params_path)) @@ -2789,7 +3336,9 @@ class KeycloakAdmin: :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :type client_id: str :return: Keycloak server response + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get( @@ -2803,17 +3352,19 @@ class KeycloakAdmin: ManagementPermissionReference https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_managementpermissionreference - :param payload: ManagementPermissionReference - :param client_id: id in ClientRepresentation - https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation - :return: Keycloak server response - - Payload example:: payload={ "enabled": true } + + :param payload: ManagementPermissionReference + :type payload: dict + :param client_id: id in ClientRepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :type client_id: str + :return: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_put( @@ -2827,8 +3378,11 @@ class KeycloakAdmin: :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :type client_id: str :param policy_id: No Document + :type policy_id: str :return: Keycloak server response + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": client_id, "policy-id": policy_id} data_raw = self.raw_get( @@ -2841,8 +3395,11 @@ class KeycloakAdmin: :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :type client_id: str :param policy_id: No Document + :type policy_id: str :return: Keycloak server response + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": client_id, "policy-id": policy_id} data_raw = self.raw_get( @@ -2855,8 +3412,11 @@ class KeycloakAdmin: :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :type client_id: str :param scope_id: No Document + :type scope_id: str :return: Keycloak server response + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": client_id, "scope-id": scope_id} data_raw = self.raw_get( @@ -2867,13 +3427,6 @@ class KeycloakAdmin: def update_client_authz_scope_permission(self, payload, client_id, scope_id): """Update permissions for a given scope. - :param payload: No Document - :param client_id: id in ClientRepresentation - https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation - :param scope_id: No Document - :return: Keycloak server response - - Payload example:: payload={ @@ -2886,6 +3439,16 @@ class KeycloakAdmin: "scopes": [some_scope_id], "policies": [some_policy_id], } + + :param payload: No Document + :type payload: dict + :param client_id: id in ClientRepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :type client_id: str + :param scope_id: No Document + :type scope_id: str + :return: Keycloak server response + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": client_id, "scope-id": scope_id} data_raw = self.raw_put( @@ -2899,7 +3462,9 @@ class KeycloakAdmin: :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :type client_id: str :return: Keycloak server response (RoleRepresentation) + :rtype: list """ params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get( @@ -2910,12 +3475,6 @@ class KeycloakAdmin: def create_client_authz_client_policy(self, payload, client_id): """Create a new policy for a given client. - :param payload: No Document - :param client_id: id in ClientRepresentation - https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation - :return: Keycloak server response (RoleRepresentation) - - Payload example:: payload={ @@ -2925,6 +3484,14 @@ class KeycloakAdmin: "name": "My Policy", "clients": [other_client_id], } + + :param payload: No Document + :type payload: dict + :param client_id: id in ClientRepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :type client_id: str + :return: Keycloak server response (RoleRepresentation) + :rtype: bytes """ params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_post( @@ -2992,7 +3559,7 @@ class KeycloakAdmin: def get_required_action_by_alias(self, action_alias): """Get a required action by its alias. - :param action_alias: the alias of the requried action. + :param action_alias: the alias of the required action. :type action_alias: str :return: the required action (RequiredActionProviderRepresentation). :rtype: dict diff --git a/src/keycloak/keycloak_openid.py b/src/keycloak/keycloak_openid.py index 055085d..9a79474 100644 --- a/src/keycloak/keycloak_openid.py +++ b/src/keycloak/keycloak_openid.py @@ -81,7 +81,25 @@ class KeycloakOpenID: proxies=None, timeout=60, ): - """Init method.""" + """Init method. + + :param server_url: Keycloak server url + :type server_url: str + :param client_id: client id + :type client_id: str + :param realm_name: realm name + :type realm_name: str + :param client_secret_key: client secret key + :type client_secret_key: str + :param verify: True if want check connection SSL + :type verify: bool + :param custom_headers: dict of custom header to pass to each HTML request + :type custom_headers: dict + :param proxies: dict of proxies to sent the request by. + :type proxies: dict + :param timeout: connection timeout in seconds + :type timeout: int + """ self.client_id = client_id self.client_secret_key = client_secret_key self.realm_name = realm_name @@ -94,7 +112,11 @@ class KeycloakOpenID: @property def client_id(self): - """Get client id.""" + """Get client id. + + :returns: Client id + :rtype: str + """ return self._client_id @client_id.setter @@ -103,7 +125,11 @@ class KeycloakOpenID: @property def client_secret_key(self): - """Get the client secret key.""" + """Get the client secret key. + + :returns: Client secret key + :rtype: str + """ return self._client_secret_key @client_secret_key.setter @@ -112,7 +138,11 @@ class KeycloakOpenID: @property def realm_name(self): - """Get the realm name.""" + """Get the realm name. + + :returns: Realm name + :rtype: str + """ return self._realm_name @realm_name.setter @@ -121,7 +151,11 @@ class KeycloakOpenID: @property def connection(self): - """Get connection.""" + """Get connection. + + :returns: Connection manager object + :rtype: ConnectionManager + """ return self._connection @connection.setter @@ -130,7 +164,11 @@ class KeycloakOpenID: @property def authorization(self): - """Get authorization.""" + """Get authorization. + + :returns: The authorization manager + :rtype: Authorization + """ return self._authorization @authorization.setter @@ -140,8 +178,10 @@ class KeycloakOpenID: def _add_secret_key(self, payload): """Add secret key if exists. - :param payload: - :return: + :param payload: Payload + :type payload: dict + :returns: Payload with the secret key + :rtype: dict """ if self.client_secret_key: payload.update({"client_secret": self.client_secret_key}) @@ -151,18 +191,24 @@ class KeycloakOpenID: def _build_name_role(self, role): """Build name of a role. - :param role: - :return: + :param role: Role name + :type role: str + :returns: Role path + :rtype: str """ return self.client_id + "/" + role def _token_info(self, token, method_token_info, **kwargs): """Getter for the token data. - :param token: - :param method_token_info: - :param kwargs: - :return: + :param token: Token + :type token: str + :param method_token_info: Token info method to use + :type method_token_info: str + :param kwargs: Additional keyword arguments + :type kwargs: dict + :returns: Token info + :rtype: dict """ if method_token_info == "introspect": token_info = self.introspect(token) @@ -178,7 +224,8 @@ class KeycloakOpenID: endpoint. It lists endpoints and other configuration options relevant to the OpenID Connect implementation in Keycloak. - :return It lists endpoints and other configuration options relevant. + :returns: It lists endpoints and other configuration options relevant + :rtype: dict """ params_path = {"realm-name": self.realm_name} data_raw = self.connection.raw_get(URL_WELL_KNOWN.format(**params_path)) @@ -190,9 +237,9 @@ class KeycloakOpenID: :param redirect_uri: Redirect url to receive oauth code :type redirect_uri: str :param scope: Scope of authorization request, split with the blank space - :type: scope: str + :type scope: str :param state: State will be returned to the redirect_uri - :type: str + :type state: str :returns: Authorization URL Full Build :rtype: str """ @@ -224,13 +271,22 @@ class KeycloakOpenID: http://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint - :param username: - :param password: - :param grant_type: - :param code: - :param redirect_uri: - :param totp: - :return: + :param username: Username + :type username: str + :param password: Password + :type password: str + :param grant_type: Grant type + :type grant_type: str + :param code: Code + :type code: str + :param redirect_uri: Redirect URI + :type redirect_uri: str + :param totp: Time-based one-time password + :type totp: int + :param extra: Additional extra arguments + :type extra: dict + :returns: Keycloak token + :rtype: dict """ params_path = {"realm-name": self.realm_name} payload = { @@ -261,9 +317,12 @@ class KeycloakOpenID: http://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint - :param refresh_token: - :param grant_type: - :return: + :param refresh_token: Refresh token from Keycloak + :type refresh_token: str + :param grant_type: Grant type + :type grant_type: str + :returns: New token + :rtype: dict """ params_path = {"realm-name": self.realm_name} payload = { @@ -289,13 +348,20 @@ class KeycloakOpenID: Use a token to obtain an entirely different token. See https://www.keycloak.org/docs/latest/securing_apps/index.html#_token-exchange - :param token: - :param client_id: - :param audience: - :param subject: - :param requested_token_type: - :param scope: - :return: + :param token: Access token + :type token: str + :param client_id: Client id + :type client_id: str + :param audience: Audience + :type audience: str + :param subject: Subject + :type subject: str + :param requested_token_type: Token type specification + :type requested_token_type: str + :param scope: Scope + :type scope: str + :returns: Exchanged token + :rtype: dict """ params_path = {"realm-name": self.realm_name} payload = { @@ -319,8 +385,10 @@ class KeycloakOpenID: http://openid.net/specs/openid-connect-core-1_0.html#UserInfo - :param token: - :return: + :param token: Access token + :type token: str + :returns: Userinfo object + :rtype: dict """ self.connection.add_param_headers("Authorization", "Bearer " + token) params_path = {"realm-name": self.realm_name} @@ -330,8 +398,10 @@ class KeycloakOpenID: def logout(self, refresh_token): """Log out the authenticated user. - :param refresh_token: - :return: + :param refresh_token: Refresh token from Keycloak + :type refresh_token: str + :returns: Keycloak server response + :rtype: dict """ params_path = {"realm-name": self.realm_name} payload = {"client_id": self.client_id, "refresh_token": refresh_token} @@ -348,7 +418,8 @@ class KeycloakOpenID: https://tools.ietf.org/html/rfc7517 - :return: + :returns: Certificates + :rtype: dict """ params_path = {"realm-name": self.realm_name} data_raw = self.connection.raw_get(URL_CERTS.format(**params_path)) @@ -359,7 +430,8 @@ class KeycloakOpenID: The public key is exposed by the realm page directly. - :return: + :returns: The public key + :rtype: str """ params_path = {"realm-name": self.realm_name} data_raw = self.connection.raw_get(URL_REALM.format(**params_path)) @@ -374,7 +446,12 @@ class KeycloakOpenID: authorization policies associated with the resources being requested. With an RPT, client applications can gain access to protected resources at the resource server. - :return: + :param token: Access token + :type token: str + :param resource_server_id: Resource server ID + :type resource_server_id: str + :returns: Entitlements + :rtype: dict """ self.connection.add_param_headers("Authorization", "Bearer " + token) params_path = {"realm-name": self.realm_name, "resource-server-id": resource_server_id} @@ -393,11 +470,16 @@ class KeycloakOpenID: https://tools.ietf.org/html/rfc7662 - :param token: - :param rpt: - :param token_type_hint: + :param token: Access token + :type token: str + :param rpt: Requesting party token + :type rpt: str + :param token_type_hint: Token type hint + :type token_type_hint: str - :return: + :returns: Token info + :rtype: dict + :raises KeycloakRPTNotFound: In case of RPT not specified """ params_path = {"realm-name": self.realm_name} payload = {"client_id": self.client_id, "token": token} @@ -426,10 +508,16 @@ class KeycloakOpenID: https://tools.ietf.org/html/rfc7517 - :param token: - :param key: - :param algorithms: - :return: + :param token: Keycloak token + :type token: str + :param key: Decode key + :type key: str + :param algorithms: Algorithms to use for decoding + :type algorithms: list[str] + :param kwargs: Keyword arguments + :type kwargs: dict + :returns: Decoded token + :rtype: dict """ return jwt.decode(token, key, algorithms=algorithms, audience=self.client_id, **kwargs) @@ -437,7 +525,7 @@ class KeycloakOpenID: """Load Keycloak settings (authorization). :param path: settings file (json) - :return: + :type path: str """ with open(path, "r") as fp: authorization_json = json.load(fp) @@ -447,8 +535,16 @@ class KeycloakOpenID: def get_policies(self, token, method_token_info="introspect", **kwargs): """Get policies by user token. - :param token: user token - :return: policies list + :param token: User token + :type token: str + :param method_token_info: Method for token info decoding + :type method_token_info: str + :param kwargs: Additional keyword arguments + :type kwargs: dict + :return: Policies + :rtype: dict + :raises KeycloakAuthorizationConfigError: In case of bad authorization configuration + :raises KeycloakInvalidTokenError: In case of bad token """ if not self.authorization.policies: raise KeycloakAuthorizationConfigError( @@ -478,9 +574,15 @@ class KeycloakOpenID: """Get permission by user token. :param token: user token + :type token: str :param method_token_info: Decode token method + :type method_token_info: str :param kwargs: parameters for decode - :return: permissions list + :type kwargs: dict + :returns: permissions list + :rtype: list + :raises KeycloakAuthorizationConfigError: In case of bad authorization configuration + :raises KeycloakInvalidTokenError: In case of bad token """ if not self.authorization.policies: raise KeycloakAuthorizationConfigError( @@ -515,8 +617,11 @@ class KeycloakOpenID: http://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint :param token: user token + :type token: str :param permissions: list of uma permissions list(resource:scope) requested by the user - :return: permissions list + :type permissions: str + :returns: Keycloak server response + :rtype: dict """ permission = build_permission_param(permissions) @@ -536,8 +641,13 @@ class KeycloakOpenID: """Determine whether user has uma permissions with specified user token. :param token: user token + :type token: str :param permissions: list of uma permissions (resource:scope) - :return: auth status + :type permissions: str + :return: Authentication status + :rtype: AuthStatus + :raises KeycloakAuthenticationError: In case of failed authentication + :raises KeycloakPostError: In case of failed request to Keycloak """ needed = build_permission_param(permissions) try: diff --git a/src/keycloak/uma_permissions.py b/src/keycloak/uma_permissions.py index 94779f1..eadcd72 100644 --- a/src/keycloak/uma_permissions.py +++ b/src/keycloak/uma_permissions.py @@ -48,7 +48,16 @@ class UMAPermission: """ def __init__(self, permission=None, resource="", scope=""): - """Init method.""" + """Init method. + + :param permission: Permission + :type permission: UMAPermission + :param resource: Resource + :type resource: str + :param scope: Scope + :type scope: str + :raises PermissionDefinitionError: In case bad permission definition + """ self.resource = resource self.scope = scope @@ -63,26 +72,55 @@ class UMAPermission: self.scope = str(permission.scope) def __str__(self): - """Str method.""" + """Str method. + + :returns: String representation + :rtype: str + """ scope = self.scope if scope: scope = "#" + scope return "{}{}".format(self.resource, scope) def __eq__(self, __o: object) -> bool: - """Eq method.""" + """Eq method. + + :param __o: The other object + :type __o: object + :returns: Equality boolean + :rtype: bool + """ return str(self) == str(__o) def __repr__(self) -> str: - """Repr method.""" + """Repr method. + + :returns: The object representation + :rtype: str + """ return self.__str__() def __hash__(self) -> int: - """Hash method.""" + """Hash method. + + :returns: Hash of the object + :rtype: int + """ return hash(str(self)) - def __call__(self, permission=None, resource="", scope="") -> object: - """Call method.""" + def __call__(self, permission=None, resource="", scope="") -> "UMAPermission": + """Call method. + + :param permission: Permission + :type permission: UMAPermission + :param resource: Resource + :type resource: str + :param scope: Scope + :type scope: str + :returns: The combined UMA permission + :rtype: UMAPermission + :raises PermissionDefinitionError: In case bad permission definition + """ result_resource = self.resource result_scope = self.scope @@ -114,7 +152,11 @@ class Resource(UMAPermission): """ def __init__(self, resource): - """Init method.""" + """Init method. + + :param resource: Resource + :type resource: str + """ super().__init__(resource=resource) @@ -128,7 +170,11 @@ class Scope(UMAPermission): """ def __init__(self, scope): - """Init method.""" + """Init method. + + :param scope: Scope + :type scope: str + """ super().__init__(scope=scope) @@ -147,17 +193,33 @@ class AuthStatus: """ def __init__(self, is_logged_in, is_authorized, missing_permissions): - """Init method.""" + """Init method. + + :param is_logged_in: Is logged in indicator + :type is_logged_in: bool + :param is_authorized: Is authorized indicator + :type is_authorized: bool + :param missing_permissions: Missing permissions + :type missing_permissions: set + """ self.is_logged_in = is_logged_in self.is_authorized = is_authorized self.missing_permissions = missing_permissions def __bool__(self): - """Bool method.""" + """Bool method. + + :returns: Boolean representation + :rtype: bool + """ return self.is_authorized def __repr__(self): - """Repr method.""" + """Repr method. + + :returns: The object representation + :rtype: str + """ return ( f"AuthStatus(" f"is_authorized={self.is_authorized}, " @@ -169,11 +231,11 @@ class AuthStatus: def build_permission_param(permissions): """Transform permissions to a set, so they are usable for requests. - :param permissions: either str (resource#scope), - iterable[str] (resource#scope), - dict[str,str] (resource: scope), - dict[str,iterable[str]] (resource: scopes) - :return: result bool + :param permissions: Permissions + :type permissions: str | Iterable[str] | dict[str, str] | dict[str, Iterabble[str]] + :returns: Permission parameters + :rtype: set + :raises KeycloakPermissionFormatError: In case of bad permission format """ if permissions is None or permissions == "": return set() diff --git a/tests/conftest.py b/tests/conftest.py index 39bd584..e0d93ea 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -35,7 +35,17 @@ class KeycloakTestEnv(object): username: str = os.environ["KEYCLOAK_ADMIN"], password: str = os.environ["KEYCLOAK_ADMIN_PASSWORD"], ): - """Init method.""" + """Init method. + + :param host: Hostname + :type host: str + :param port: Port + :type port: str + :param username: Admin username + :type username: str + :param password: Admin password + :type password: str + """ self.KEYCLOAK_HOST = host self.KEYCLOAK_PORT = port self.KEYCLOAK_ADMIN = username @@ -43,54 +53,96 @@ class KeycloakTestEnv(object): @property def KEYCLOAK_HOST(self): - """Hostname getter.""" + """Hostname getter. + + :returns: Keycloak host + :rtype: str + """ return self._KEYCLOAK_HOST @KEYCLOAK_HOST.setter def KEYCLOAK_HOST(self, value: str): - """Hostname setter.""" + """Hostname setter. + + :param value: Keycloak host + :type value: str + """ self._KEYCLOAK_HOST = value @property def KEYCLOAK_PORT(self): - """Port getter.""" + """Port getter. + + :returns: Keycloak port + :rtype: str + """ return self._KEYCLOAK_PORT @KEYCLOAK_PORT.setter def KEYCLOAK_PORT(self, value: str): - """Port setter.""" + """Port setter. + + :param value: Keycloak port + :type value: str + """ self._KEYCLOAK_PORT = value @property def KEYCLOAK_ADMIN(self): - """Admin username getter.""" + """Admin username getter. + + :returns: Admin username + :rtype: str + """ return self._KEYCLOAK_ADMIN @KEYCLOAK_ADMIN.setter def KEYCLOAK_ADMIN(self, value: str): - """Admin username setter.""" + """Admin username setter. + + :param value: Admin username + :type value: str + """ self._KEYCLOAK_ADMIN = value @property def KEYCLOAK_ADMIN_PASSWORD(self): - """Admin password getter.""" + """Admin password getter. + + :returns: Admin password + :rtype: str + """ return self._KEYCLOAK_ADMIN_PASSWORD @KEYCLOAK_ADMIN_PASSWORD.setter def KEYCLOAK_ADMIN_PASSWORD(self, value: str): - """Admin password setter.""" + """Admin password setter. + + :param value: Admin password + :type value: str + """ self._KEYCLOAK_ADMIN_PASSWORD = value @pytest.fixture def env(): - """Fixture for getting the test environment configuration object.""" + """Fixture for getting the test environment configuration object. + + :returns: Keycloak test environment object + :rtype: KeycloakTestEnv + """ return KeycloakTestEnv() @pytest.fixture def admin(env: KeycloakTestEnv): - """Fixture for initialized KeycloakAdmin class.""" + """Fixture for initialized KeycloakAdmin class. + + :param env: Keycloak test environment + :type env: KeycloakTestEnv + :returns: Keycloak admin + :rtype: KeycloakAdmin + """ return KeycloakAdmin( server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", username=env.KEYCLOAK_ADMIN, @@ -100,7 +152,17 @@ def admin(env: KeycloakTestEnv): @pytest.fixture def oid(env: KeycloakTestEnv, realm: str, admin: KeycloakAdmin): - """Fixture for initialized KeycloakOpenID class.""" + """Fixture for initialized KeycloakOpenID class. + + :param env: Keycloak test environment + :type env: KeycloakTestEnv + :param realm: Keycloak realm + :type realm: str + :param admin: Keycloak admin + :type admin: KeycloakAdmin + :yields: Keycloak OpenID client + :rtype: KeycloakOpenID + """ # Set the realm admin.realm_name = realm # Create client @@ -126,7 +188,17 @@ def oid(env: KeycloakTestEnv, realm: str, admin: KeycloakAdmin): @pytest.fixture def oid_with_credentials(env: KeycloakTestEnv, realm: str, admin: KeycloakAdmin): - """Fixture for an initialized KeycloakOpenID class and a random user credentials.""" + """Fixture for an initialized KeycloakOpenID class and a random user credentials. + + :param env: Keycloak test environment + :type env: KeycloakTestEnv + :param realm: Keycloak realm + :type realm: str + :param admin: Keycloak admin + :type admin: KeycloakAdmin + :yields: Keycloak OpenID client with user credentials + :rtype: Tuple[KeycloakOpenID, str, str] + """ # Set the realm admin.realm_name = realm # Create client @@ -173,7 +245,17 @@ def oid_with_credentials(env: KeycloakTestEnv, realm: str, admin: KeycloakAdmin) @pytest.fixture def oid_with_credentials_authz(env: KeycloakTestEnv, realm: str, admin: KeycloakAdmin): - """Fixture for an initialized KeycloakOpenID class and a random user credentials.""" + """Fixture for an initialized KeycloakOpenID class and a random user credentials. + + :param env: Keycloak test environment + :type env: KeycloakTestEnv + :param realm: Keycloak realm + :type realm: str + :param admin: Keycloak admin + :type admin: KeycloakAdmin + :yields: Keycloak OpenID client configured as an authorization server with client credentials + :rtype: Tuple[KeycloakOpenID, str, str] + """ # Set the realm admin.realm_name = realm # Create client @@ -229,7 +311,13 @@ def oid_with_credentials_authz(env: KeycloakTestEnv, realm: str, admin: Keycloak @pytest.fixture def realm(admin: KeycloakAdmin) -> str: - """Fixture for a new random realm.""" + """Fixture for a new random realm. + + :param admin: Keycloak admin + :type admin: KeycloakAdmin + :yields: Keycloak realm + :rtype: str + """ realm_name = str(uuid.uuid4()) admin.create_realm(payload={"realm": realm_name, "enabled": True}) yield realm_name @@ -238,7 +326,15 @@ def realm(admin: KeycloakAdmin) -> str: @pytest.fixture def user(admin: KeycloakAdmin, realm: str) -> str: - """Fixture for a new random user.""" + """Fixture for a new random user. + + :param admin: Keycloak admin + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + :yields: Keycloak user + :rtype: str + """ admin.realm_name = realm username = str(uuid.uuid4()) user_id = admin.create_user(payload={"username": username, "email": f"{username}@test.test"}) @@ -248,7 +344,15 @@ def user(admin: KeycloakAdmin, realm: str) -> str: @pytest.fixture def group(admin: KeycloakAdmin, realm: str) -> str: - """Fixture for a new random group.""" + """Fixture for a new random group. + + :param admin: Keycloak admin + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + :yields: Keycloak group + :rtype: str + """ admin.realm_name = realm group_name = str(uuid.uuid4()) group_id = admin.create_group(payload={"name": group_name}) @@ -258,7 +362,15 @@ def group(admin: KeycloakAdmin, realm: str) -> str: @pytest.fixture def client(admin: KeycloakAdmin, realm: str) -> str: - """Fixture for a new random client.""" + """Fixture for a new random client. + + :param admin: Keycloak admin + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + :yields: Keycloak client id + :rtype: str + """ admin.realm_name = realm client = str(uuid.uuid4()) client_id = admin.create_client(payload={"name": client, "clientId": client}) @@ -268,7 +380,17 @@ def client(admin: KeycloakAdmin, realm: str) -> str: @pytest.fixture def client_role(admin: KeycloakAdmin, realm: str, client: str) -> str: - """Fixture for a new random client role.""" + """Fixture for a new random client role. + + :param admin: Keycloak admin + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + :param client: Keycloak client + :type client: str + :yields: Keycloak client role + :rtype: str + """ admin.realm_name = realm role = str(uuid.uuid4()) admin.create_client_role(client, {"name": role, "composite": False}) @@ -278,7 +400,19 @@ def client_role(admin: KeycloakAdmin, realm: str, client: str) -> str: @pytest.fixture def composite_client_role(admin: KeycloakAdmin, realm: str, client: str, client_role: str) -> str: - """Fixture for a new random composite client role.""" + """Fixture for a new random composite client role. + + :param admin: Keycloak admin + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + :param client: Keycloak client + :type client: str + :param client_role: Keycloak client role + :type client_role: str + :yields: Composite client role + :rtype: str + """ admin.realm_name = realm role = str(uuid.uuid4()) admin.create_client_role(client, {"name": role, "composite": True}) @@ -290,7 +424,11 @@ def composite_client_role(admin: KeycloakAdmin, realm: str, client: str, client_ @pytest.fixture def selfsigned_cert(): - """Generate self signed certificate for a hostname, and optional IP addresses.""" + """Generate self signed certificate for a hostname, and optional IP addresses. + + :returns: Selfsigned certificate + :rtype: Tuple[str, str] + """ hostname = "testcert" ip_addresses = None key = None diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index 327ea39..b3ad951 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -22,7 +22,11 @@ def test_keycloak_version(): def test_keycloak_admin_bad_init(env): - """Test keycloak admin bad init.""" + """Test keycloak admin bad init. + + :param env: Environment fixture + :type env: KeycloakTestEnv + """ with pytest.raises(TypeError) as err: KeycloakAdmin( server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", @@ -43,7 +47,11 @@ def test_keycloak_admin_bad_init(env): def test_keycloak_admin_init(env): - """Test keycloak admin init.""" + """Test keycloak admin init. + + :param env: Environment fixture + :type env: KeycloakTestEnv + """ admin = KeycloakAdmin( server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", username=env.KEYCLOAK_ADMIN, @@ -118,7 +126,11 @@ def test_keycloak_admin_init(env): def test_realms(admin: KeycloakAdmin): - """Test realms.""" + """Test realms. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + """ # Get realms realms = admin.get_realms() assert len(realms) == 1, realms @@ -183,7 +195,13 @@ def test_realms(admin: KeycloakAdmin): def test_import_export_realms(admin: KeycloakAdmin, realm: str): - """Test import and export of realms.""" + """Test import and export of realms. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + """ admin.realm_name = realm realm_export = admin.export_realm(export_clients=True, export_groups_and_role=True) @@ -201,7 +219,13 @@ def test_import_export_realms(admin: KeycloakAdmin, realm: str): def test_users(admin: KeycloakAdmin, realm: str): - """Test users.""" + """Test users. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + """ admin.realm_name = realm # Check no users present @@ -293,7 +317,13 @@ def test_users(admin: KeycloakAdmin, realm: str): def test_users_pagination(admin: KeycloakAdmin, realm: str): - """Test user pagination.""" + """Test user pagination. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + """ admin.realm_name = realm for ind in range(admin.PAGE_SIZE + 50): @@ -311,7 +341,13 @@ def test_users_pagination(admin: KeycloakAdmin, realm: str): def test_idps(admin: KeycloakAdmin, realm: str): - """Test IDPs.""" + """Test IDPs. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + """ admin.realm_name = realm # Create IDP @@ -383,7 +419,13 @@ def test_idps(admin: KeycloakAdmin, realm: str): def test_user_credentials(admin: KeycloakAdmin, user: str): - """Test user credentials.""" + """Test user credentials. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param user: Keycloak user + :type user: str + """ res = admin.set_user_password(user_id=user, password="booya", temporary=True) assert res == dict(), res @@ -411,7 +453,13 @@ def test_user_credentials(admin: KeycloakAdmin, user: str): def test_social_logins(admin: KeycloakAdmin, user: str): - """Test social logins.""" + """Test social logins. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param user: Keycloak user + :type user: str + """ res = admin.add_user_social_login( user_id=user, provider_id="gitlab", provider_userid="test", provider_username="test" ) @@ -451,7 +499,11 @@ def test_social_logins(admin: KeycloakAdmin, user: str): def test_server_info(admin: KeycloakAdmin): - """Test server info.""" + """Test server info. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + """ info = admin.get_server_info() assert set(info.keys()) == { "systemInfo", @@ -471,7 +523,13 @@ def test_server_info(admin: KeycloakAdmin): def test_groups(admin: KeycloakAdmin, user: str): - """Test groups.""" + """Test groups. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param user: Keycloak user + :type user: str + """ # Test get groups groups = admin.get_groups() assert len(groups) == 0 @@ -615,7 +673,13 @@ def test_groups(admin: KeycloakAdmin, user: str): def test_clients(admin: KeycloakAdmin, realm: str): - """Test clients.""" + """Test clients. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + """ admin.realm_name = realm # Test get clients @@ -875,7 +939,13 @@ def test_clients(admin: KeycloakAdmin, realm: str): def test_realm_roles(admin: KeycloakAdmin, realm: str): - """Test realm roles.""" + """Test realm roles. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + """ admin.realm_name = realm # Test get realm roles @@ -1046,7 +1116,21 @@ def test_role_attributes( includes_attributes: bool, testcase: str, ): - """Test getting role attributes for bulk calls.""" + """Test getting role attributes for bulk calls. + + :param admin: Keycloak admin + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + :param client: Keycloak client + :type client: str + :param arg_brief_repr: Brief representation + :type arg_brief_repr: dict + :param includes_attributes: Indicator whether to include attributes + :type includes_attributes: bool + :param testcase: Test case + :type testcase: str + """ # setup attribute_role = "test-realm-role-w-attr" test_attrs = {"attr1": ["val1"], "attr2": ["val2-1", "val2-2"]} @@ -1088,7 +1172,13 @@ def test_role_attributes( def test_client_scope_realm_roles(admin: KeycloakAdmin, realm: str): - """Test client realm roles.""" + """Test client realm roles. + + :param admin: Keycloak admin + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + """ admin.realm_name = realm # Test get realm roles @@ -1146,7 +1236,15 @@ def test_client_scope_realm_roles(admin: KeycloakAdmin, realm: str): def test_client_scope_client_roles(admin: KeycloakAdmin, realm: str, client: str): - """Test client assignment of other client roles.""" + """Test client assignment of other client roles. + + :param admin: Keycloak admin + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + :param client: Keycloak client + :type client: str + """ admin.realm_name = realm client_id = admin.create_client( @@ -1202,7 +1300,13 @@ def test_client_scope_client_roles(admin: KeycloakAdmin, realm: str, client: str def test_client_roles(admin: KeycloakAdmin, client: str): - """Test client roles.""" + """Test client roles. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param client: Keycloak client + :type client: str + """ # Test get client roles res = admin.get_client_roles(client_id=client) assert len(res) == 0 @@ -1365,7 +1469,14 @@ def test_client_roles(admin: KeycloakAdmin, client: str): def test_enable_token_exchange(admin: KeycloakAdmin, realm: str): - """Test enable token exchange.""" + """Test enable token exchange. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + :raises AssertionError: In case of bad configuration + """ # Test enabling token exchange between two confidential clients admin.realm_name = realm @@ -1454,7 +1565,13 @@ def test_enable_token_exchange(admin: KeycloakAdmin, realm: str): def test_email(admin: KeycloakAdmin, user: str): - """Test email.""" + """Test email. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param user: Keycloak user + :type user: str + """ # Emails will fail as we don't have SMTP test setup with pytest.raises(KeycloakPutError) as err: admin.send_update_account(user_id=user, payload=dict()) @@ -1467,7 +1584,11 @@ def test_email(admin: KeycloakAdmin, user: str): def test_get_sessions(admin: KeycloakAdmin): - """Test get sessions.""" + """Test get sessions. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + """ sessions = admin.get_sessions(user_id=admin.get_user_id(username=admin.username)) assert len(sessions) >= 1 with pytest.raises(KeycloakGetError) as err: @@ -1476,7 +1597,13 @@ def test_get_sessions(admin: KeycloakAdmin): def test_get_client_installation_provider(admin: KeycloakAdmin, client: str): - """Test get client installation provider.""" + """Test get client installation provider. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param client: Keycloak client + :type client: str + """ with pytest.raises(KeycloakGetError) as err: admin.get_client_installation_provider(client_id=client, provider_id="bad") assert err.match('404: b\'{"error":"Unknown Provider"}\'') @@ -1495,7 +1622,13 @@ def test_get_client_installation_provider(admin: KeycloakAdmin, client: str): def test_auth_flows(admin: KeycloakAdmin, realm: str): - """Test auth flows.""" + """Test auth flows. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + """ admin.realm_name = realm res = admin.get_authentication_flows() @@ -1642,7 +1775,13 @@ def test_auth_flows(admin: KeycloakAdmin, realm: str): def test_authentication_configs(admin: KeycloakAdmin, realm: str): - """Test authentication configs.""" + """Test authentication configs. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + """ admin.realm_name = realm # Test list of auth providers @@ -1674,7 +1813,13 @@ def test_authentication_configs(admin: KeycloakAdmin, realm: str): def test_sync_users(admin: KeycloakAdmin, realm: str): - """Test sync users.""" + """Test sync users. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + """ admin.realm_name = realm # Only testing the error message @@ -1684,7 +1829,13 @@ def test_sync_users(admin: KeycloakAdmin, realm: str): def test_client_scopes(admin: KeycloakAdmin, realm: str): - """Test client scopes.""" + """Test client scopes. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + """ admin.realm_name = realm # Test get client scopes @@ -1822,7 +1973,13 @@ def test_client_scopes(admin: KeycloakAdmin, realm: str): def test_components(admin: KeycloakAdmin, realm: str): - """Test components.""" + """Test components. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + """ admin.realm_name = realm # Test get components @@ -1873,7 +2030,13 @@ def test_components(admin: KeycloakAdmin, realm: str): def test_keys(admin: KeycloakAdmin, realm: str): - """Test keys.""" + """Test keys. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + """ admin.realm_name = realm assert set(admin.get_keys()["active"].keys()) == {"AES", "HS256", "RS256", "RSA-OAEP"} assert {k["algorithm"] for k in admin.get_keys()["keys"]} == { @@ -1885,7 +2048,13 @@ def test_keys(admin: KeycloakAdmin, realm: str): def test_events(admin: KeycloakAdmin, realm: str): - """Test events.""" + """Test events. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + """ admin.realm_name = realm events = admin.get_events() @@ -1905,7 +2074,13 @@ def test_events(admin: KeycloakAdmin, realm: str): def test_auto_refresh(admin: KeycloakAdmin, realm: str): - """Test auto refresh token.""" + """Test auto refresh token. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + """ # Test get refresh admin.auto_refresh_token = list() admin.connection = ConnectionManager( @@ -1989,7 +2164,13 @@ def test_auto_refresh(admin: KeycloakAdmin, realm: str): def test_get_required_actions(admin: KeycloakAdmin, realm: str): - """Test requried actions.""" + """Test required actions. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + """ admin.realm_name = realm ractions = admin.get_required_actions() assert isinstance(ractions, list) @@ -2007,7 +2188,13 @@ def test_get_required_actions(admin: KeycloakAdmin, realm: str): def test_get_required_action_by_alias(admin: KeycloakAdmin, realm: str): - """Test get required action by alias.""" + """Test get required action by alias. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + """ admin.realm_name = realm ractions = admin.get_required_actions() ra = admin.get_required_action_by_alias("UPDATE_PASSWORD") @@ -2017,7 +2204,13 @@ def test_get_required_action_by_alias(admin: KeycloakAdmin, realm: str): def test_update_required_action(admin: KeycloakAdmin, realm: str): - """Test update required action.""" + """Test update required action. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + """ admin.realm_name = realm ra = admin.get_required_action_by_alias("UPDATE_PASSWORD") old = copy.deepcopy(ra) @@ -2031,7 +2224,19 @@ def test_update_required_action(admin: KeycloakAdmin, realm: str): def test_get_composite_client_roles_of_group( admin: KeycloakAdmin, realm: str, client: str, group: str, composite_client_role: str ): - """Test get composite client roles of group.""" + """Test get composite client roles of group. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + :param client: Keycloak client + :type client: str + :param group: Keycloak group + :type group: str + :param composite_client_role: Composite client role + :type composite_client_role: str + """ admin.realm_name = realm role = admin.get_client_role(client, composite_client_role) admin.assign_group_client_roles(group_id=group, client_id=client, roles=[role]) @@ -2042,7 +2247,19 @@ def test_get_composite_client_roles_of_group( def test_get_role_client_level_children( admin: KeycloakAdmin, realm: str, client: str, composite_client_role: str, client_role: str ): - """Test get children of composite client role.""" + """Test get children of composite client role. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + :param client: Keycloak client + :type client: str + :param composite_client_role: Composite client role + :type composite_client_role: str + :param client_role: Client role + :type client_role: str + """ admin.realm_name = realm child = admin.get_client_role(client, client_role) parent = admin.get_client_role(client, composite_client_role) @@ -2051,7 +2268,17 @@ def test_get_role_client_level_children( def test_upload_certificate(admin: KeycloakAdmin, realm: str, client: str, selfsigned_cert: tuple): - """Test upload certificate.""" + """Test upload certificate. + + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + :param realm: Keycloak realm + :type realm: str + :param client: Keycloak client + :type client: str + :param selfsigned_cert: Selfsigned certificates + :type selfsigned_cert: tuple + """ admin.realm_name = realm cert, _ = selfsigned_cert cert = cert.decode("utf-8").strip() diff --git a/tests/test_keycloak_openid.py b/tests/test_keycloak_openid.py index 6bee648..5a6a2ed 100644 --- a/tests/test_keycloak_openid.py +++ b/tests/test_keycloak_openid.py @@ -22,7 +22,11 @@ from keycloak.keycloak_openid import KeycloakOpenID def test_keycloak_openid_init(env): - """Test KeycloakOpenId's init method.""" + """Test KeycloakOpenId's init method. + + :param env: Environment fixture + :type env: KeycloakTestEnv + """ oid = KeycloakOpenID( server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", realm_name="master", @@ -37,7 +41,11 @@ def test_keycloak_openid_init(env): def test_well_known(oid: KeycloakOpenID): - """Test the well_known method.""" + """Test the well_known method. + + :param oid: Keycloak OpenID client + :type oid: KeycloakOpenID + """ res = oid.well_known() assert res is not None assert res != dict() @@ -100,7 +108,13 @@ def test_well_known(oid: KeycloakOpenID): def test_auth_url(env, oid: KeycloakOpenID): - """Test the auth_url method.""" + """Test the auth_url method. + + :param env: Environment fixture + :type env: KeycloakTestEnv + :param oid: Keycloak OpenID client + :type oid: KeycloakOpenID + """ res = oid.auth_url(redirect_uri="http://test.test/*") assert ( res @@ -111,7 +125,11 @@ def test_auth_url(env, oid: KeycloakOpenID): def test_token(oid_with_credentials: Tuple[KeycloakOpenID, str, str]): - """Test the token method.""" + """Test the token method. + + :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials + :type oid_with_credentials: Tuple[KeycloakOpenID, str, str] + """ oid, username, password = oid_with_credentials token = oid.token(username=username, password=password) assert token == { @@ -155,7 +173,13 @@ def test_token(oid_with_credentials: Tuple[KeycloakOpenID, str, str]): def test_exchange_token( oid_with_credentials: Tuple[KeycloakOpenID, str, str], admin: KeycloakAdmin ): - """Test the exchange token method.""" + """Test the exchange token method. + + :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials + :type oid_with_credentials: Tuple[KeycloakOpenID, str, str] + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + """ # Verify existing user oid, username, password = oid_with_credentials @@ -197,7 +221,11 @@ def test_exchange_token( def test_logout(oid_with_credentials): - """Test logout.""" + """Test logout. + + :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials + :type oid_with_credentials: Tuple[KeycloakOpenID, str, str] + """ oid, username, password = oid_with_credentials token = oid.token(username=username, password=password) @@ -209,19 +237,34 @@ def test_logout(oid_with_credentials): def test_certs(oid: KeycloakOpenID): - """Test certificates.""" + """Test certificates. + + :param oid: Keycloak OpenID client + :type oid: KeycloakOpenID + """ assert len(oid.certs()["keys"]) == 2 def test_public_key(oid: KeycloakOpenID): - """Test public key.""" + """Test public key. + + :param oid: Keycloak OpenID client + :type oid: KeycloakOpenID + """ assert oid.public_key() is not None def test_entitlement( oid_with_credentials_authz: Tuple[KeycloakOpenID, str, str], admin: KeycloakAdmin ): - """Test entitlement.""" + """Test entitlement. + + :param oid_with_credentials_authz: Keycloak OpenID client configured as an authorization + server with client credentials + :type oid_with_credentials_authz: Tuple[KeycloakOpenID, str, str] + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + """ oid, username, password = oid_with_credentials_authz token = oid.token(username=username, password=password) resource_server_id = admin.get_client_authz_resources( @@ -233,7 +276,11 @@ def test_entitlement( def test_introspect(oid_with_credentials: Tuple[KeycloakOpenID, str, str]): - """Test introspect.""" + """Test introspect. + + :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials + :type oid_with_credentials: Tuple[KeycloakOpenID, str, str] + """ oid, username, password = oid_with_credentials token = oid.token(username=username, password=password) @@ -247,7 +294,11 @@ def test_introspect(oid_with_credentials: Tuple[KeycloakOpenID, str, str]): def test_decode_token(oid_with_credentials: Tuple[KeycloakOpenID, str, str]): - """Test decode token.""" + """Test decode token. + + :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials + :type oid_with_credentials: Tuple[KeycloakOpenID, str, str] + """ oid, username, password = oid_with_credentials token = oid.token(username=username, password=password) @@ -262,7 +313,12 @@ def test_decode_token(oid_with_credentials: Tuple[KeycloakOpenID, str, str]): def test_load_authorization_config(oid_with_credentials_authz: Tuple[KeycloakOpenID, str, str]): - """Test load authorization config.""" + """Test load authorization config. + + :param oid_with_credentials_authz: Keycloak OpenID client configured as an authorization + server with client credentials + :type oid_with_credentials_authz: Tuple[KeycloakOpenID, str, str] + """ oid, username, password = oid_with_credentials_authz oid.load_authorization_config(path="tests/data/authz_settings.json") @@ -277,7 +333,12 @@ def test_load_authorization_config(oid_with_credentials_authz: Tuple[KeycloakOpe def test_get_policies(oid_with_credentials_authz: Tuple[KeycloakOpenID, str, str]): - """Test get policies.""" + """Test get policies. + + :param oid_with_credentials_authz: Keycloak OpenID client configured as an authorization + server with client credentials + :type oid_with_credentials_authz: Tuple[KeycloakOpenID, str, str] + """ oid, username, password = oid_with_credentials_authz token = oid.token(username=username, password=password) @@ -310,7 +371,12 @@ def test_get_policies(oid_with_credentials_authz: Tuple[KeycloakOpenID, str, str def test_get_permissions(oid_with_credentials_authz: Tuple[KeycloakOpenID, str, str]): - """Test get policies.""" + """Test get policies. + + :param oid_with_credentials_authz: Keycloak OpenID client configured as an authorization + server with client credentials + :type oid_with_credentials_authz: Tuple[KeycloakOpenID, str, str] + """ oid, username, password = oid_with_credentials_authz token = oid.token(username=username, password=password) @@ -354,7 +420,12 @@ def test_get_permissions(oid_with_credentials_authz: Tuple[KeycloakOpenID, str, def test_uma_permissions(oid_with_credentials_authz: Tuple[KeycloakOpenID, str, str]): - """Test UMA permissions.""" + """Test UMA permissions. + + :param oid_with_credentials_authz: Keycloak OpenID client configured as an authorization + server with client credentials + :type oid_with_credentials_authz: Tuple[KeycloakOpenID, str, str] + """ oid, username, password = oid_with_credentials_authz token = oid.token(username=username, password=password) @@ -365,7 +436,14 @@ def test_uma_permissions(oid_with_credentials_authz: Tuple[KeycloakOpenID, str, def test_has_uma_access( oid_with_credentials_authz: Tuple[KeycloakOpenID, str, str], admin: KeycloakAdmin ): - """Test has UMA access.""" + """Test has UMA access. + + :param oid_with_credentials_authz: Keycloak OpenID client configured as an authorization + server with client credentials + :type oid_with_credentials_authz: Tuple[KeycloakOpenID, str, str] + :param admin: Keycloak Admin client + :type admin: KeycloakAdmin + """ oid, username, password = oid_with_credentials_authz token = oid.token(username=username, password=password) diff --git a/tox.ini b/tox.ini index 16768bb..2b3db36 100644 --- a/tox.ini +++ b/tox.ini @@ -13,6 +13,7 @@ commands = black --check --diff src/keycloak tests docs isort -c --df src/keycloak tests docs flake8 src/keycloak tests docs + codespell src tests docs [testenv:apply-check] commands = @@ -44,9 +45,13 @@ commands = setenv = file|tox.env passenv = CONTAINER_HOST commands = - cz changelog --incremental + cz changelog [flake8] max-line-length = 99 docstring-convention = all ignore = D203, D213, W503 +docstring_style = sphinx + +[darglint] +enable = DAR104