diff --git a/poetry.lock b/poetry.lock index c747f94..06cdcc1 100644 --- a/poetry.lock +++ b/poetry.lock @@ -22,7 +22,7 @@ test = ["coverage", "flake8", "pexpect", "wheel"] [[package]] name = "astroid" -version = "2.11.6" +version = "2.11.7" description = "An abstract syntax tree for Python with inference support." category = "main" optional = true @@ -36,7 +36,7 @@ wrapt = ">=1.11,<2" [[package]] name = "atomicwrites" -version = "1.4.0" +version = "1.4.1" description = "Atomic file writes." category = "dev" optional = false @@ -208,7 +208,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "ecdsa" -version = "0.17.0" +version = "0.18.0" description = "ECDSA cryptographic signature library (pure python)" category = "main" optional = false @@ -460,7 +460,7 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "pre-commit" -version = "2.19.0" +version = "2.20.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." category = "dev" optional = false @@ -871,7 +871,7 @@ python-versions = ">=3.7" [[package]] name = "tomlkit" -version = "0.11.0" +version = "0.11.1" description = "Style preserving TOML library" category = "dev" optional = false @@ -926,11 +926,11 @@ python-versions = ">=3.5" [[package]] name = "urllib3" -version = "1.26.9" +version = "1.26.10" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4" [package.extras] brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"] @@ -1001,14 +1001,8 @@ argcomplete = [ {file = "argcomplete-1.12.3-py2.py3-none-any.whl", hash = "sha256:291f0beca7fd49ce285d2f10e4c1c77e9460cf823eef2de54df0c0fec88b0d81"}, {file = "argcomplete-1.12.3.tar.gz", hash = "sha256:2c7dbffd8c045ea534921e63b0be6fe65e88599990d8dc408ac8c542b72a5445"}, ] -astroid = [ - {file = "astroid-2.11.6-py3-none-any.whl", hash = "sha256:ba33a82a9a9c06a5ceed98180c5aab16e29c285b828d94696bf32d6015ea82a9"}, - {file = "astroid-2.11.6.tar.gz", hash = "sha256:4f933d0bf5e408b03a6feb5d23793740c27e07340605f236496cd6ce552043d6"}, -] -atomicwrites = [ - {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, - {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, -] +astroid = [] +atomicwrites = [] attrs = [ {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, @@ -1125,10 +1119,7 @@ docutils = [ {file = "docutils-0.17.1-py2.py3-none-any.whl", hash = "sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"}, {file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"}, ] -ecdsa = [ - {file = "ecdsa-0.17.0-py2.py3-none-any.whl", hash = "sha256:5cf31d5b33743abe0dfc28999036c849a69d548f994b535e527ee3cb7f3ef676"}, - {file = "ecdsa-0.17.0.tar.gz", hash = "sha256:b9f500bb439e4153d0330610f5d26baaf18d17b8ced1bc54410d189385ea68aa"}, -] +ecdsa = [] filelock = [ {file = "filelock-3.7.1-py3-none-any.whl", hash = "sha256:37def7b658813cda163b56fc564cdc75e86d338246458c4c28ae84cabefa2404"}, {file = "filelock-3.7.1.tar.gz", hash = "sha256:3a0fd85166ad9dbab54c9aec96737b744106dc5f15c0b09a6744a445299fcf04"}, @@ -1290,10 +1281,7 @@ pluggy = [ {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, ] -pre-commit = [ - {file = "pre_commit-2.19.0-py2.py3-none-any.whl", hash = "sha256:10c62741aa5704faea2ad69cb550ca78082efe5697d6f04e5710c3c229afdd10"}, - {file = "pre_commit-2.19.0.tar.gz", hash = "sha256:4233a1e38621c87d9dda9808c6606d7e7ba0e087cd56d3fe03202a01d2919615"}, -] +pre-commit = [] prompt-toolkit = [ {file = "prompt_toolkit-3.0.30-py3-none-any.whl", hash = "sha256:d8916d3f62a7b67ab353a952ce4ced6a1d2587dfe9ef8ebc30dd7c386751f289"}, {file = "prompt_toolkit-3.0.30.tar.gz", hash = "sha256:859b283c50bde45f5f97829f77a4674d1c1fcd88539364f1b28a37805cfd89c0"}, @@ -1463,10 +1451,7 @@ tomli = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] -tomlkit = [ - {file = "tomlkit-0.11.0-py3-none-any.whl", hash = "sha256:0f4050db66fd445b885778900ce4dd9aea8c90c4721141fde0d6ade893820ef1"}, - {file = "tomlkit-0.11.0.tar.gz", hash = "sha256:71ceb10c0eefd8b8f11fe34e8a51ad07812cb1dc3de23247425fbc9ddc47b9dd"}, -] +tomlkit = [] tox = [ {file = "tox-3.25.1-py2.py3-none-any.whl", hash = "sha256:c38e15f4733683a9cc0129fba078633e07eb0961f550a010ada879e95fb32632"}, {file = "tox-3.25.1.tar.gz", hash = "sha256:c138327815f53bc6da4fe56baec5f25f00622ae69ef3fe4e1e385720e22486f9"}, @@ -1505,10 +1490,7 @@ unidecode = [ {file = "Unidecode-1.3.4-py3-none-any.whl", hash = "sha256:afa04efcdd818a93237574791be9b2817d7077c25a068b00f8cff7baa4e59257"}, {file = "Unidecode-1.3.4.tar.gz", hash = "sha256:8e4352fb93d5a735c788110d2e7ac8e8031eb06ccbfe8d324ab71735015f9342"}, ] -urllib3 = [ - {file = "urllib3-1.26.9-py2.py3-none-any.whl", hash = "sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14"}, - {file = "urllib3-1.26.9.tar.gz", hash = "sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e"}, -] +urllib3 = [] virtualenv = [ {file = "virtualenv-20.15.1-py2.py3-none-any.whl", hash = "sha256:b30aefac647e86af6d82bfc944c556f8f1a9c90427b2fb4e3bfbf338cb82becf"}, {file = "virtualenv-20.15.1.tar.gz", hash = "sha256:288171134a2ff3bfb1a2f54f119e77cd1b81c29fc1265a2356f3e8d14c7d58c4"}, diff --git a/src/keycloak/__init__.py b/src/keycloak/__init__.py index 2c7f70f..694e53d 100644 --- a/src/keycloak/__init__.py +++ b/src/keycloak/__init__.py @@ -21,6 +21,8 @@ # 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. +"""Python-Keycloak library.""" + from ._version import __version__ from .connection import ConnectionManager from .exceptions import ( diff --git a/src/keycloak/authorization/__init__.py b/src/keycloak/authorization/__init__.py index 789656d..fddd551 100644 --- a/src/keycloak/authorization/__init__.py +++ b/src/keycloak/authorization/__init__.py @@ -21,6 +21,8 @@ # 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. +"""Authorization module.""" + import ast import json @@ -30,18 +32,19 @@ from .role import Role class Authorization: - """ - Keycloak Authorization (policies, roles, scopes and resources). + """Keycloak Authorization (policies, roles, scopes and resources). https://keycloak.gitbooks.io/documentation/authorization_services/index.html """ def __init__(self): + """Init method.""" self.policies = {} @property def policies(self): + """Get policies.""" return self._policies @policies.setter @@ -49,8 +52,7 @@ class Authorization: self._policies = value def load_config(self, data): - """ - Load policies, roles and permissions (scope/resources). + """Load policies, roles and permissions (scope/resources). :param data: keycloak authorization data (dict) :returns: None diff --git a/src/keycloak/authorization/permission.py b/src/keycloak/authorization/permission.py index a200afe..a444f83 100644 --- a/src/keycloak/authorization/permission.py +++ b/src/keycloak/authorization/permission.py @@ -21,9 +21,12 @@ # 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 authorization Permission module.""" + class Permission: - """ + """Base permission class. + Consider this simple and very common permission: A permission associates the object being protected with the policies that must be evaluated to @@ -45,6 +48,7 @@ class Permission: """ def __init__(self, name, type, logic, decision_strategy): + """Init method.""" self._name = name self._type = type self._logic = logic @@ -53,13 +57,16 @@ class Permission: self._scopes = [] def __repr__(self): + """Repr method.""" return "" % (self.name, self.type) def __str__(self): + """Str method.""" return "Permission: %s (%s)" % (self.name, self.type) @property def name(self): + """Get name.""" return self._name @name.setter @@ -68,6 +75,7 @@ class Permission: @property def type(self): + """Get type.""" return self._type @type.setter @@ -76,6 +84,7 @@ class Permission: @property def logic(self): + """Get logic.""" return self._logic @logic.setter @@ -84,6 +93,7 @@ class Permission: @property def decision_strategy(self): + """Get decision strategy.""" return self._decision_strategy @decision_strategy.setter @@ -92,6 +102,7 @@ class Permission: @property def resources(self): + """Get resources.""" return self._resources @resources.setter @@ -100,6 +111,7 @@ class Permission: @property def scopes(self): + """Get scopes.""" return self._scopes @scopes.setter diff --git a/src/keycloak/authorization/policy.py b/src/keycloak/authorization/policy.py index 4014b7a..6b558d8 100644 --- a/src/keycloak/authorization/policy.py +++ b/src/keycloak/authorization/policy.py @@ -21,11 +21,14 @@ # 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 authorization Policy module.""" + from ..exceptions import KeycloakAuthorizationConfigError class Policy: - """ + """Base policy class. + A policy defines the conditions that must be satisfied to grant access to an object. Unlike permissions, you do not specify the object being protected but rather the conditions that must be satisfied for access to a given object (for example, resource, scope, or both). @@ -39,6 +42,7 @@ class Policy: """ def __init__(self, name, type, logic, decision_strategy): + """Init method.""" self._name = name self._type = type self._logic = logic @@ -47,13 +51,16 @@ class Policy: self._permissions = [] def __repr__(self): + """Repr method.""" return "" % (self.name, self.type) def __str__(self): + """Str method.""" return "Policy: %s (%s)" % (self.name, self.type) @property def name(self): + """Get name.""" return self._name @name.setter @@ -62,6 +69,7 @@ class Policy: @property def type(self): + """Get type.""" return self._type @type.setter @@ -70,6 +78,7 @@ class Policy: @property def logic(self): + """Get logic.""" return self._logic @logic.setter @@ -78,6 +87,7 @@ class Policy: @property def decision_strategy(self): + """Get decision strategy.""" return self._decision_strategy @decision_strategy.setter @@ -86,15 +96,16 @@ class Policy: @property def roles(self): + """Get roles.""" return self._roles @property def permissions(self): + """Get permissions.""" return self._permissions def add_role(self, role): - """ - Add keycloak role in policy. + """Add keycloak role in policy. :param role: keycloak role. :return: @@ -106,8 +117,7 @@ class Policy: self._roles.append(role) def add_permission(self, permission): - """ - Add keycloak permission in policy. + """Add keycloak permission in policy. :param permission: keycloak permission. :return: diff --git a/src/keycloak/authorization/role.py b/src/keycloak/authorization/role.py index 3ff06dd..05da243 100644 --- a/src/keycloak/authorization/role.py +++ b/src/keycloak/authorization/role.py @@ -21,25 +21,30 @@ # 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. +"""The authorization Role module.""" + class Role: - """ + """Authorization Role base class. + Roles identify a type or category of user. Admin, user, manager, and employee are all typical roles that may exist in an organization. https://keycloak.gitbooks.io/documentation/server_admin/topics/roles.html - """ def __init__(self, name, required=False): + """Init method.""" self.name = name self.required = required @property def get_name(self): + """Get name.""" return self.name def __eq__(self, other): + """Eq method.""" if isinstance(other, str): return self.name == other return NotImplemented diff --git a/src/keycloak/connection.py b/src/keycloak/connection.py index 0757377..361d95d 100644 --- a/src/keycloak/connection.py +++ b/src/keycloak/connection.py @@ -21,6 +21,8 @@ # 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. +"""Connection manager module.""" + try: from urllib.parse import urljoin except ImportError: @@ -33,8 +35,7 @@ from .exceptions import KeycloakConnectionError class ConnectionManager(object): - """ - Represents a simple server connection. + """Represents a simple server connection. :param base_url: (str) The server URL. :param headers: (dict) The header parameters of the requests to the server. @@ -44,6 +45,7 @@ class ConnectionManager(object): """ def __init__(self, base_url, headers={}, timeout=60, verify=True, proxies=None): + """Init method.""" self._base_url = base_url self._headers = headers self._timeout = timeout @@ -66,6 +68,7 @@ class ConnectionManager(object): self._s.proxies.update(proxies) def __del__(self): + """Del method.""" self._s.close() @property @@ -75,7 +78,6 @@ class ConnectionManager(object): @base_url.setter def base_url(self, value): - """ """ self._base_url = value @property @@ -85,7 +87,6 @@ class ConnectionManager(object): @timeout.setter def timeout(self, value): - """ """ self._timeout = value @property @@ -95,7 +96,6 @@ class ConnectionManager(object): @verify.setter def verify(self, value): - """ """ self._verify = value @property @@ -105,12 +105,10 @@ class ConnectionManager(object): @headers.setter def headers(self, value): - """ """ self._headers = value def param_headers(self, key): - """ - Return a specific header parameter. + """Return a specific header parameter. :param key: (str) Header parameters key. :returns: If the header parameters exist, return its value. @@ -151,7 +149,6 @@ class ConnectionManager(object): :returns: Response the request. :raises: HttpError Can't connect to server. """ - try: return self._s.get( urljoin(self.base_url, path), diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index b2c1de4..0825730 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -24,6 +24,8 @@ # Unless otherwise stated in the comments, "id", in e.g. user_id, refers to the # internal Keycloak server ID, usually a uuid string +"""The keycloak admin module.""" + import json from builtins import isinstance from typing import Iterable @@ -41,8 +43,7 @@ from .keycloak_openid import KeycloakOpenID class KeycloakAdmin: - """ - Keycloak Admin client. + """Keycloak Admin client. :param server_url: Keycloak server url :param username: admin username @@ -90,6 +91,7 @@ class KeycloakAdmin: auto_refresh_token=None, timeout=60, ): + """Init method.""" self.server_url = server_url self.username = username self.password = password @@ -108,6 +110,7 @@ class KeycloakAdmin: @property def server_url(self): + """Get server url.""" return self._server_url @server_url.setter @@ -116,6 +119,7 @@ class KeycloakAdmin: @property def realm_name(self): + """Get realm name.""" return self._realm_name @realm_name.setter @@ -124,6 +128,7 @@ class KeycloakAdmin: @property def connection(self): + """Get connection.""" return self._connection @connection.setter @@ -132,6 +137,7 @@ class KeycloakAdmin: @property def client_id(self): + """Get client id.""" return self._client_id @client_id.setter @@ -140,6 +146,7 @@ class KeycloakAdmin: @property def client_secret_key(self): + """Get client secret key.""" return self._client_secret_key @client_secret_key.setter @@ -148,6 +155,7 @@ class KeycloakAdmin: @property def verify(self): + """Get verify.""" return self._verify @verify.setter @@ -156,6 +164,7 @@ class KeycloakAdmin: @property def username(self): + """Get username.""" return self._username @username.setter @@ -164,6 +173,7 @@ class KeycloakAdmin: @property def password(self): + """Get password.""" return self._password @password.setter @@ -172,6 +182,7 @@ class KeycloakAdmin: @property def totp(self): + """Get totp.""" return self._totp @totp.setter @@ -180,6 +191,7 @@ class KeycloakAdmin: @property def token(self): + """Get token.""" return self._token @token.setter @@ -188,10 +200,12 @@ class KeycloakAdmin: @property def auto_refresh_token(self): + """Get auto refresh token.""" return self._auto_refresh_token @property def user_realm_name(self): + """Get user realm name.""" return self._user_realm_name @user_realm_name.setter @@ -200,6 +214,7 @@ class KeycloakAdmin: @property def custom_headers(self): + """Get custom headers.""" return self._custom_headers @custom_headers.setter @@ -223,7 +238,9 @@ class KeycloakAdmin: self._auto_refresh_token = value def __fetch_all(self, url, query=None): - """Wrapper function to paginate GET requests + """Paginate over get requests. + + Wrapper function to paginate GET requests. :param url: The url on which the query is executed :param query: Existing query parameters (optional) @@ -258,8 +275,9 @@ class KeycloakAdmin: return raise_error_from_response(self.raw_get(url, **query), KeycloakGetError) def import_realm(self, payload): - """ - Import a new realm from a RealmRepresentation. Realm name must be unique. + """Import a new realm from a RealmRepresentation. + + Realm name must be unique. RealmRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_realmrepresentation @@ -268,13 +286,11 @@ class KeycloakAdmin: :return: RealmRepresentation """ - 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]) def export_realm(self, export_clients=False, export_groups_and_role=False): - """ - Export the realm configurations in the json format + """Export the realm configurations in the json format. RealmRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_partialexport @@ -295,8 +311,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError) def get_realms(self): - """ - Lists all realms in Keycloak deployment + """List all realms in Keycloak deployment. :return: realms list """ @@ -304,8 +319,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_realm(self, realm_name): - """ - Get a specific realm. + """Get a specific realm. RealmRepresentation: https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_realmrepresentation @@ -318,8 +332,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200]) def create_realm(self, payload, skip_exists=False): - """ - Create a realm + """Create a realm. RealmRepresentation: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_realmrepresentation @@ -328,15 +341,15 @@ class KeycloakAdmin: :param skip_exists: Skip if Realm already exist. :return: Keycloak server response (RealmRepresentation) """ - 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], skip_exists=skip_exists ) def update_realm(self, realm_name, payload): - """ - Update a realm. This wil only update top level attributes and will ignore any user, + """Update a realm. + + This wil only update top level attributes and will ignore any user, role, or client information in the payload. RealmRepresentation: @@ -346,7 +359,6 @@ class KeycloakAdmin: :param payload: RealmRepresentation :return: Http response """ - params_path = {"realm-name": realm_name} data_raw = self.raw_put( urls_patterns.URL_ADMIN_REALM.format(**params_path), data=json.dumps(payload) @@ -354,19 +366,18 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def delete_realm(self, realm_name): - """ - Delete a realm + """Delete a realm. :param realm_name: Realm name (not the realm id) :return: Http response """ - params_path = {"realm-name": realm_name} data_raw = self.raw_delete(urls_patterns.URL_ADMIN_REALM.format(**params_path)) return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def get_users(self, query=None): - """ + """Get all users. + Return a list of users, filtered according to query parameters UserRepresentation @@ -385,8 +396,7 @@ class KeycloakAdmin: return self.__fetch_all(url, query) def create_idp(self, payload): - """ - Create an ID Provider, + """Create an ID Provider. IdentityProviderRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_identityproviderrepresentation @@ -400,8 +410,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) def update_idp(self, idp_alias, payload): - """ - Update an ID Provider + """Update an ID Provider. IdentityProviderRepresentation https://www.keycloak.org/docs-api/15.0/rest-api/index.html#_identity_providers_resource @@ -416,8 +425,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def add_mapper_to_idp(self, idp_alias, payload): - """ - Create an ID Provider, + """Create an ID Provider. IdentityProviderRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_identityprovidermapperrepresentation @@ -432,8 +440,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) def update_mapper_in_idp(self, idp_alias, mapper_id, payload): - """ - Update an IdP mapper + """Update an IdP mapper. IdentityProviderMapperRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_update @@ -457,7 +464,8 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def get_idp_mappers(self, idp_alias): - """ + """Get IDP mappers. + Returns a list of ID Providers mappers IdentityProviderMapperRepresentation @@ -471,7 +479,8 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_idps(self): - """ + """Get IDPs. + Returns a list of ID Providers, IdentityProviderRepresentation @@ -484,8 +493,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def delete_idp(self, idp_alias): - """ - Deletes ID Provider, + """Delete an ID Provider. :param: idp_alias: idp alias name """ @@ -494,8 +502,9 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def create_user(self, payload, exist_ok=False): - """ - Create a new user. Username must be unique + """Create a new user. + + Username must be unique UserRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_userrepresentation @@ -522,8 +531,7 @@ class KeycloakAdmin: return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203 def users_count(self, query=None): - """ - User counter + """Count users. https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_users_resource @@ -537,8 +545,8 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_user_id(self, username): - """ - Get internal keycloak user id from username + """Get internal keycloak user id from username. + This is required for further actions against this user. UserRepresentation @@ -553,8 +561,7 @@ class KeycloakAdmin: return next((user["id"] for user in users if user["username"] == lower_user_name), None) def get_user(self, user_id): - """ - Get representation of the user + """Get representation of the user. :param user_id: User id @@ -568,7 +575,8 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_user_groups(self, user_id): - """ + """Get user groups. + Returns a list of groups of which the user is a member :param user_id: User id @@ -580,8 +588,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def update_user(self, user_id, payload): - """ - Update the user + """Update the user. :param user_id: User id :param payload: UserRepresentation @@ -595,8 +602,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def delete_user(self, user_id): - """ - Delete the user + """Delete the user. :param user_id: User id @@ -607,8 +613,9 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def set_user_password(self, user_id, password, temporary=True): - """ - Set up a password for the user. If temporary is True, the user will have to reset + """Set up a password for the user. + + If temporary is True, the user will have to reset the temporary password next time they log in. https://www.keycloak.org/docs-api/18.0/rest-api/#_users_resource @@ -628,7 +635,8 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def get_credentials(self, user_id): - """ + """Get user credentials. + Returns a list of credential belonging to the user. CredentialRepresentation @@ -642,8 +650,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def delete_credential(self, user_id, credential_id): - """ - Delete credential of the user. + """Delete credential of the user. CredentialRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_credentialrepresentation @@ -661,8 +668,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakDeleteError) def user_logout(self, user_id): - """ - Logs out user. + """Log out the user. https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_logout @@ -676,8 +682,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204]) def user_consents(self, user_id): - """ - Get consents granted by the user + """Get consents granted by the user. UserConsentRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_userconsentrepresentation @@ -690,7 +695,8 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_user_social_logins(self, user_id): - """ + """Get user social logins. + Returns a list of federated identities/social logins of which the user has been associated with :param user_id: User id @@ -703,9 +709,8 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def add_user_social_login(self, user_id, provider_id, provider_userid, provider_username): + """Add a federated identity / social login provider to the user. - """ - Add a federated identity / social login provider to the user :param user_id: User id :param provider_id: Social login provider id :param provider_userid: userid specified by the provider @@ -725,9 +730,8 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201, 204]) def delete_user_social_login(self, user_id, provider_id): + """Delete a federated identity / social login provider from the user. - """ - Delete a federated identity / social login provider from the user :param user_id: User id :param provider_id: Social login provider id :return: @@ -741,9 +745,9 @@ class KeycloakAdmin: def send_update_account( self, user_id, payload, client_id=None, lifespan=None, redirect_uri=None ): - """ - Send an update account email to the user. An email contains a - link the user can click to perform a set of required actions. + """Send an update account email to the user. + + An email contains a link the user can click to perform a set of required actions. :param user_id: User id :param payload: A list of actions for the user to complete @@ -763,9 +767,9 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError) def send_verify_email(self, user_id, client_id=None, redirect_uri=None): - """ - Send a update account email to the user An email contains a - link the user can click to perform a set of required actions. + """Send a update account email to the user. + + An email contains a link the user can click to perform a set of required actions. :param user_id: User id :param client_id: Client id (optional) @@ -783,8 +787,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError) def get_sessions(self, user_id): - """ - Get sessions associated with the user + """Get sessions associated with the user. :param user_id: id of user @@ -798,8 +801,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_server_info(self): - """ - Get themes, social providers, auth providers, and event listeners available on this server + """Get themes, social providers, auth providers, and event listeners available on this server. ServerInfoRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_serverinforepresentation @@ -810,7 +812,8 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_groups(self, query=None): - """ + """Get groups. + Returns a list of groups belonging to the realm GroupRepresentation @@ -828,8 +831,9 @@ class KeycloakAdmin: return self.__fetch_all(url, query) def get_group(self, group_id): - """ - Get group by id. Returns full group details + """Get group by id. + + Returns full group details GroupRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/#_grouprepresentation @@ -842,7 +846,8 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_subgroups(self, group, path): - """ + """Get subgroups. + Utility function to iterate through nested group structures GroupRepresentation @@ -853,7 +858,6 @@ class KeycloakAdmin: :return: Keycloak server response (GroupRepresentation) """ - for subgroup in group["subGroups"]: if subgroup["path"] == path: return subgroup @@ -866,8 +870,9 @@ class KeycloakAdmin: return None def get_group_members(self, group_id, query=None): - """ - Get members by group id. Returns group members + """Get members by group id. + + Returns group members GroupRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/#_userrepresentation @@ -887,8 +892,8 @@ class KeycloakAdmin: return self.__fetch_all(url, query) def get_group_by_path(self, path, search_in_subgroups=False): - """ - Get group id based on name or path. + """Get group id based on name or path. + A straight name or path match with a top-level group will return first. Subgroups are traversed, the first to match path (or name with path) is returned. @@ -899,7 +904,6 @@ class KeycloakAdmin: :param search_in_subgroups: True if want search in the subgroups :return: Keycloak server response (GroupRepresentation) """ - groups = self.get_groups() # TODO: Review this code is necessary @@ -916,8 +920,7 @@ class KeycloakAdmin: return None def create_group(self, payload, parent=None, skip_exists=False): - """ - Creates a group in the Realm + """Create a group in the Realm. :param payload: GroupRepresentation :param parent: parent group's id. Required to create a sub-group. @@ -928,7 +931,6 @@ class KeycloakAdmin: :return: Group id for newly created group or None for an existing group """ - if parent is None: params_path = {"realm-name": self.realm_name} data_raw = self.raw_post( @@ -950,8 +952,7 @@ class KeycloakAdmin: return def update_group(self, group_id, payload): - """ - Update group, ignores subgroups. + """Update group, ignores subgroups. :param group_id: id of group :param payload: GroupRepresentation with updated information. @@ -961,7 +962,6 @@ class KeycloakAdmin: :return: Http response """ - params_path = {"realm-name": self.realm_name, "id": group_id} data_raw = self.raw_put( urls_patterns.URL_ADMIN_GROUP.format(**params_path), data=json.dumps(payload) @@ -969,14 +969,14 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def group_set_permissions(self, group_id, enabled=True): - """ - Enable/Disable permissions for a group. Cannot delete group if disabled + """Enable/Disable permissions for a group. + + Cannot delete group if disabled :param group_id: id of group :param enabled: boolean :return: Keycloak server response """ - params_path = {"realm-name": self.realm_name, "id": group_id} data_raw = self.raw_put( urls_patterns.URL_ADMIN_GROUP_PERMISSIONS.format(**params_path), @@ -985,14 +985,12 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError) def group_user_add(self, user_id, group_id): - """ - Add user to group (user_id and group_id) + """Add user to group (user_id and group_id). :param user_id: id of user :param group_id: id of group to add to :return: Keycloak server response """ - params_path = {"realm-name": self.realm_name, "id": user_id, "group-id": group_id} data_raw = self.raw_put( urls_patterns.URL_ADMIN_USER_GROUP.format(**params_path), data=None @@ -1000,32 +998,29 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def group_user_remove(self, user_id, group_id): - """ - Remove user from group (user_id and group_id) + """Remove user from group (user_id and group_id). :param user_id: id of user :param group_id: id of group to remove from :return: Keycloak server response """ - 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)) return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def delete_group(self, group_id): - """ - Deletes a group in the Realm + """Delete a group in the Realm. :param group_id: id of group to delete :return: Keycloak server response """ - params_path = {"realm-name": self.realm_name, "id": group_id} data_raw = self.raw_delete(urls_patterns.URL_ADMIN_GROUP.format(**params_path)) return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def get_clients(self): - """ + """Get clients. + Returns a list of clients belonging to the realm ClientRepresentation @@ -1033,14 +1028,12 @@ class KeycloakAdmin: :return: Keycloak server response (ClientRepresentation) """ - params_path = {"realm-name": self.realm_name} data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENTS.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def get_client(self, client_id): - """ - Get representation of the client + """Get representation of the client. ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation @@ -1048,21 +1041,19 @@ class KeycloakAdmin: :param client_id: id of client (not client-id) :return: Keycloak server response (ClientRepresentation) """ - params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def get_client_id(self, client_name): - """ - Get internal keycloak client id from client-id. + """Get internal keycloak client id from client-id. + This is required for further actions against this client. :param client_name: name in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation :return: client_id (uuid as string) """ - clients = self.get_clients() for client in clients: @@ -1072,14 +1063,12 @@ class KeycloakAdmin: return None def get_client_authz_settings(self, client_id): - """ - Get authorization json from client. + """Get authorization json from client. :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation :return: Keycloak server response """ - params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get( urls_patterns.URL_ADMIN_CLIENT_AUTHZ_SETTINGS.format(**params_path) @@ -1087,8 +1076,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def create_client_authz_resource(self, client_id, payload, skip_exists=False): - """ - Create resources of client. + """Create resources of client. :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation @@ -1097,7 +1085,6 @@ class KeycloakAdmin: :return: Keycloak server response """ - params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_post( @@ -1109,14 +1096,12 @@ class KeycloakAdmin: ) def get_client_authz_resources(self, client_id): - """ - Get resources from client. + """Get resources from client. :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation :return: Keycloak server response """ - params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get( urls_patterns.URL_ADMIN_CLIENT_AUTHZ_RESOURCES.format(**params_path) @@ -1124,8 +1109,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def create_client_authz_role_based_policy(self, client_id, payload, skip_exists=False): - """ - Create role-based policy of client. + """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 @@ -1147,7 +1131,6 @@ class KeycloakAdmin: } """ - params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_post( @@ -1159,8 +1142,7 @@ class KeycloakAdmin: ) def create_client_authz_resource_based_permission(self, client_id, payload, skip_exists=False): - """ - Create resource-based permission of client. + """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 @@ -1183,7 +1165,6 @@ class KeycloakAdmin: ] """ - params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_post( @@ -1195,27 +1176,23 @@ class KeycloakAdmin: ) def get_client_authz_scopes(self, client_id): - """ - Get scopes from client. + """Get scopes from client. :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation :return: Keycloak server response """ - 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)) return raise_error_from_response(data_raw, KeycloakGetError) def get_client_authz_permissions(self, client_id): - """ - Get permissions from client. + """Get permissions from client. :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation :return: Keycloak server response """ - params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get( urls_patterns.URL_ADMIN_CLIENT_AUTHZ_PERMISSIONS.format(**params_path) @@ -1223,14 +1200,12 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_client_authz_policies(self, client_id): - """ - Get policies from client. + """Get policies from client. :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation :return: Keycloak server response """ - params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get( urls_patterns.URL_ADMIN_CLIENT_AUTHZ_POLICIES.format(**params_path) @@ -1238,14 +1213,12 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_client_service_account_user(self, client_id): - """ - Get service account user from client. + """Get service account user from client. :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation :return: UserRepresentation """ - params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get( urls_patterns.URL_ADMIN_CLIENT_SERVICE_ACCOUNT_USER.format(**params_path) @@ -1253,8 +1226,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def create_client(self, payload, skip_exists=False): - """ - Create a client + """Create a client. ClientRepresentation: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation @@ -1263,7 +1235,6 @@ class KeycloakAdmin: :param payload: ClientRepresentation :return: Client ID """ - if skip_exists: client_id = self.get_client_id(client_name=payload["name"]) @@ -1281,8 +1252,7 @@ class KeycloakAdmin: return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203 def update_client(self, client_id, payload): - """ - Update a client + """Update a client. :param client_id: Client id :param payload: ClientRepresentation @@ -1296,8 +1266,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def delete_client(self, client_id): - """ - Get representation of the client + """Get representation of the client. ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation @@ -1305,14 +1274,12 @@ class KeycloakAdmin: :param client_id: keycloak client id (not oauth client-id) :return: Keycloak server response (ClientRepresentation) """ - params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_delete(urls_patterns.URL_ADMIN_CLIENT.format(**params_path)) return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def get_client_installation_provider(self, client_id, provider_id): - """ - Get content for given installation provider + """Get content for given installation provider. Related documentation: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clients_resource @@ -1323,7 +1290,6 @@ class KeycloakAdmin: :param client_id: Client id :param provider_id: provider id to specify response format """ - params_path = {"realm-name": self.realm_name, "id": client_id, "provider-id": provider_id} data_raw = self.raw_get( urls_patterns.URL_ADMIN_CLIENT_INSTALLATION_PROVIDER.format(**params_path) @@ -1331,22 +1297,20 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200]) def get_realm_roles(self): - """ - Get all roles for the realm or client + """Get all roles for the realm or client. RoleRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation :return: Keycloak server response (RoleRepresentation) """ - params_path = {"realm-name": self.realm_name} data_raw = self.raw_get(urls_patterns.URL_ADMIN_REALM_ROLES.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def get_realm_role_members(self, role_name, query=None): - """ - Get role members of realm by role name. + """Get role members of realm by role name. + :param role_name: Name of the role. :param query: Additional Query parameters (see https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_roles_resource) @@ -1359,8 +1323,7 @@ class KeycloakAdmin: ) def get_client_roles(self, client_id): - """ - Get all roles for the client + """Get all roles for the client. :param client_id: id of client (not client-id) @@ -1369,14 +1332,13 @@ class KeycloakAdmin: :return: Keycloak server response (RoleRepresentation) """ - params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_ROLES.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def get_client_role(self, client_id, role_name): - """ - Get client role id by name + """Get client role id by name. + This is required for further actions with this role. :param client_id: id of client (not client-id) @@ -1392,10 +1354,8 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_client_role_id(self, client_id, role_name): - """ - Warning: Deprecated + """Get client role id by name. - Get client role id by name This is required for further actions with this role. :param client_id: id of client (not client-id) @@ -1410,8 +1370,7 @@ class KeycloakAdmin: return role.get("id") def create_client_role(self, client_role_id, payload, skip_exists=False): - """ - Create a client role + """Create a client role. RoleRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation @@ -1421,7 +1380,6 @@ class KeycloakAdmin: :param skip_exists: If true then do not raise an error if client role already exists :return: Client role name """ - if skip_exists: try: res = self.get_client_role(client_id=client_role_id, role_name=payload["name"]) @@ -1440,15 +1398,13 @@ class KeycloakAdmin: return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203 def add_composite_client_roles_to_role(self, client_role_id, role_name, roles): - """ - Add composite roles to client role + """Add composite roles to client role. :param client_role_id: id of client (not client-id) :param role_name: The name of the role :param roles: roles list or role (use RoleRepresentation) to be updated :return: Keycloak server response """ - payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": client_role_id, "role-name": role_name} data_raw = self.raw_post( @@ -1458,8 +1414,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204]) def update_client_role(self, client_role_id, role_name, payload): - """ - Update a client role + """Update a client role. RoleRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation @@ -1475,8 +1430,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def delete_client_role(self, client_role_id, role_name): - """ - Delete a client role + """Delete a client role. RoleRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_rolerepresentation @@ -1489,15 +1443,13 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def assign_client_role(self, user_id, client_id, roles): - """ - Assign a client role to a user + """Assign a client role to a user. :param user_id: id of user :param client_id: id of client (not client-id) :param roles: roles list or role (use RoleRepresentation) :return: Keycloak server response """ - payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": user_id, "client-id": client_id} data_raw = self.raw_post( @@ -1507,8 +1459,8 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204]) def get_client_role_members(self, client_id, role_name, **query): - """ - Get members by client role . + """Get members by client role. + :param client_id: The client id :param role_name: the name of role to be queried. :param query: Additional query parameters @@ -1521,8 +1473,8 @@ class KeycloakAdmin: ) def get_client_role_groups(self, client_id, role_name, **query): - """ - Get group members by client role . + """Get group members by client role. + :param client_id: The client id :param role_name: the name of role to be queried. :param query: Additional query parameters @@ -1535,14 +1487,12 @@ class KeycloakAdmin: ) def create_realm_role(self, payload, skip_exists=False): - """ - Create a new role for the realm or client + """Create a new role for the realm or client. :param payload: The role (use RoleRepresentation) :param skip_exists: If true then do not raise an error if realm role already exists :return: Realm role name """ - if skip_exists: try: role = self.get_realm_role(role_name=payload["name"]) @@ -1561,8 +1511,8 @@ class KeycloakAdmin: return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203 def get_realm_role(self, role_name): - """ - Get realm role by role name + """Get realm role by role name. + :param role_name: role's name, not id! RoleRepresentation @@ -1576,13 +1526,12 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def update_realm_role(self, role_name, payload): - """ - Update a role for the realm by name + """Update a role for the realm by name. + :param role_name: The name of the role to be updated :param payload: The role (use RoleRepresentation) :return Keycloak server response """ - params_path = {"realm-name": self.realm_name, "role-name": role_name} data_raw = self.raw_put( urls_patterns.URL_ADMIN_REALM_ROLES_ROLE_BY_NAME.format(**params_path), @@ -1591,12 +1540,11 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def delete_realm_role(self, role_name): - """ - Delete a role for the realm by name + """Delete a role for the realm by name. + :param payload: The role name {'role-name':'name-of-the-role'} :return Keycloak server response """ - params_path = {"realm-name": self.realm_name, "role-name": role_name} data_raw = self.raw_delete( urls_patterns.URL_ADMIN_REALM_ROLES_ROLE_BY_NAME.format(**params_path) @@ -1604,14 +1552,12 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def add_composite_realm_roles_to_role(self, role_name, roles): - """ - Add composite roles to the role + """Add composite roles to the role. :param role_name: The name of the role :param roles: roles list or role (use RoleRepresentation) to be updated :return: Keycloak server response """ - payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "role-name": role_name} data_raw = self.raw_post( @@ -1621,14 +1567,12 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204]) def remove_composite_realm_roles_to_role(self, role_name, roles): - """ - Remove composite roles from the role + """Remove composite roles from the role. :param role_name: The name of the role :param roles: roles list or role (use RoleRepresentation) to be removed :return: Keycloak server response """ - payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "role-name": role_name} data_raw = self.raw_delete( @@ -1638,13 +1582,11 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def get_composite_realm_roles_of_role(self, role_name): - """ - Get composite roles of the role + """Get composite roles of the role. :param role_name: The name of the role :return: Keycloak server response (array RoleRepresentation) """ - params_path = {"realm-name": self.realm_name, "role-name": role_name} data_raw = self.raw_get( urls_patterns.URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE.format(**params_path) @@ -1652,14 +1594,12 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def assign_realm_roles(self, user_id, roles): - """ - Assign realm roles to a user + """Assign realm roles to a user. :param user_id: id of user :param roles: roles list or role (use RoleRepresentation) :return: Keycloak server response """ - payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": user_id} data_raw = self.raw_post( @@ -1669,14 +1609,12 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204]) def delete_realm_roles_of_user(self, user_id, roles): - """ - Deletes realm roles of a user + """Delete realm roles of a user. :param user_id: id of user :param roles: roles list or role (use RoleRepresentation) :return: Keycloak server response """ - payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": user_id} data_raw = self.raw_delete( @@ -1686,20 +1624,18 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def get_realm_roles_of_user(self, user_id): - """ - Get all realm roles for a user. + """Get all realm roles for a user. :param user_id: id of user :return: Keycloak server response (array RoleRepresentation) """ - 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)) return raise_error_from_response(data_raw, KeycloakGetError) def get_available_realm_roles_of_user(self, user_id): - """ - Get all available (i.e. unassigned) realm roles for a user. + """Get all available (i.e. unassigned) realm roles for a user. + :param user_id: id of user :return: Keycloak server response (array RoleRepresentation) """ @@ -1710,8 +1646,8 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_composite_realm_roles_of_user(self, user_id): - """ - Get all composite (i.e. implicit) realm roles for a user. + """Get all composite (i.e. implicit) realm roles for a user. + :param user_id: id of user :return: Keycloak server response (array RoleRepresentation) """ @@ -1722,14 +1658,12 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def assign_group_realm_roles(self, group_id, roles): - """ - Assign realm roles to a group + """Assign realm roles to a group. :param group_id: id of groupp :param roles: roles list or role (use GroupRoleRepresentation) :return: Keycloak server response """ - payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": group_id} data_raw = self.raw_post( @@ -1739,14 +1673,12 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204]) def delete_group_realm_roles(self, group_id, roles): - """ - Delete realm roles of a group + """Delete realm roles of a group. :param group_id: id of group :param roles: roles list or role (use GroupRoleRepresentation) :return: Keycloak server response """ - payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": group_id} data_raw = self.raw_delete( @@ -1756,8 +1688,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def get_group_realm_roles(self, group_id): - """ - Get all realm roles for a group. + """Get all realm roles for a group. :param user_id: id of the group :return: Keycloak server response (array RoleRepresentation) @@ -1767,15 +1698,13 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def assign_group_client_roles(self, group_id, client_id, roles): - """ - Assign client roles to a group + """Assign client roles to a group. :param group_id: id of group :param client_id: id of client (not client-id) :param roles: roles list or role (use GroupRoleRepresentation) :return: Keycloak server response """ - payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": group_id, "client-id": client_id} data_raw = self.raw_post( @@ -1785,28 +1714,24 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204]) def get_group_client_roles(self, group_id, client_id): - """ - Get client roles of a group + """Get client roles of a group. :param group_id: id of group :param client_id: id of client (not client-id) :return: Keycloak server response """ - 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)) return raise_error_from_response(data_raw, KeycloakGetError) def delete_group_client_roles(self, group_id, client_id, roles): - """ - Delete client roles of a group + """Delete client roles of a group. :param group_id: id of group :param client_id: id of client (not client-id) :param roles: roles list or role (use GroupRoleRepresentation) :return: Keycloak server response (array RoleRepresentation) """ - payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": group_id, "client-id": client_id} data_raw = self.raw_delete( @@ -1816,8 +1741,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def get_client_roles_of_user(self, user_id, client_id): - """ - Get all client roles for a user. + """Get all client roles for a user. :param user_id: id of user :param client_id: id of client (not client-id) @@ -1828,8 +1752,7 @@ class KeycloakAdmin: ) def get_available_client_roles_of_user(self, user_id, client_id): - """ - Get available client role-mappings for a user. + """Get available client role-mappings for a user. :param user_id: id of user :param client_id: id of client (not client-id) @@ -1840,8 +1763,7 @@ class KeycloakAdmin: ) def get_composite_client_roles_of_user(self, user_id, client_id): - """ - Get composite client role-mappings for a user. + """Get composite client role-mappings for a user. :param user_id: id of user :param client_id: id of client (not client-id) @@ -1857,8 +1779,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def delete_client_roles_of_user(self, user_id, client_id, roles): - """ - Delete client roles from a user. + """Delete client roles from a user. :param user_id: id of user :param client_id: id of client containing role (not client-id) @@ -1874,8 +1795,9 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def get_authentication_flows(self): - """ - Get authentication flows. Returns all flow details + """Get authentication flows. + + Returns all flow details AuthenticationFlowRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationflowrepresentation @@ -1887,8 +1809,9 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_authentication_flow_for_id(self, flow_id): - """ - Get one authentication flow by it's id. Returns all flow details + """Get one authentication flow by it's id. + + Returns all flow details AuthenticationFlowRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationflowrepresentation @@ -1901,8 +1824,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def create_authentication_flow(self, payload, skip_exists=False): - """ - Create a new authentication flow + """Create a new authentication flow. AuthenticationFlowRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationflowrepresentation @@ -1911,7 +1833,6 @@ class KeycloakAdmin: :param skip_exists: Do not raise an error if authentication flow already exists :return: Keycloak server response (RoleRepresentation) """ - params_path = {"realm-name": self.realm_name} data_raw = self.raw_post( urls_patterns.URL_ADMIN_FLOWS.format(**params_path), data=json.dumps(payload) @@ -1921,15 +1842,14 @@ class KeycloakAdmin: ) def copy_authentication_flow(self, payload, flow_alias): - """ - Copy existing authentication flow under a new name. The new name is given as 'newName' - attribute of the passed payload. + """Copy existing authentication flow under a new name. + + The new name is given as 'newName' attribute of the passed payload. :param payload: JSON containing 'newName' attribute :param flow_alias: the flow alias :return: Keycloak server response (RoleRepresentation) """ - params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias} data_raw = self.raw_post( urls_patterns.URL_ADMIN_FLOWS_COPY.format(**params_path), data=json.dumps(payload) @@ -1937,8 +1857,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) def delete_authentication_flow(self, flow_id): - """ - Delete authentication flow + """Delete authentication flow. AuthenticationInfoRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationinforepresentation @@ -1951,8 +1870,9 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def get_authentication_flow_executions(self, flow_alias): - """ - Get authentication flow executions. Returns all execution steps + """Get authentication flow executions. + + Returns all execution steps :param flow_alias: the flow alias :return: Response(json) @@ -1962,8 +1882,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def update_authentication_flow_executions(self, payload, flow_alias): - """ - Update an authentication flow execution + """Update an authentication flow execution. AuthenticationExecutionInfoRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationexecutioninforepresentation @@ -1972,7 +1891,6 @@ class KeycloakAdmin: :param flow_alias: The flow alias :return: Keycloak server response """ - params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias} data_raw = self.raw_put( urls_patterns.URL_ADMIN_FLOWS_EXECUTIONS.format(**params_path), @@ -1981,8 +1899,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[202, 204]) def get_authentication_flow_execution(self, execution_id): - """ - Get authentication flow execution. + """Get authentication flow execution. AuthenticationExecutionInfoRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationexecutioninforepresentation @@ -1995,8 +1912,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def create_authentication_flow_execution(self, payload, flow_alias): - """ - Create an authentication flow execution + """Create an authentication flow execution. AuthenticationExecutionInfoRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationexecutioninforepresentation @@ -2005,7 +1921,6 @@ class KeycloakAdmin: :param flow_alias: The flow alias :return: Keycloak server response """ - params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias} data_raw = self.raw_post( urls_patterns.URL_ADMIN_FLOWS_EXECUTIONS_EXECUTION.format(**params_path), @@ -2014,8 +1929,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) def delete_authentication_flow_execution(self, execution_id): - """ - Delete authentication flow execution + """Delete authentication flow execution. AuthenticationExecutionInfoRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationexecutioninforepresentation @@ -2028,8 +1942,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def create_authentication_flow_subflow(self, payload, flow_alias, skip_exists=False): - """ - Create a new sub authentication flow for a given authentication flow + """Create a new sub authentication flow for a given authentication flow. AuthenticationFlowRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticationflowrepresentation @@ -2039,7 +1952,6 @@ class KeycloakAdmin: :param skip_exists: Do not raise an error if authentication flow already exists :return: Keycloak server response (RoleRepresentation) """ - params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias} data_raw = self.raw_post( urls_patterns.URL_ADMIN_FLOWS_EXECUTIONS_FLOW.format(**params_path), @@ -2050,8 +1962,7 @@ class KeycloakAdmin: ) def get_authenticator_providers(self): - """ - Get authenticator providers list. + """Get authenticator providers list. :return: Response(json) """ @@ -2062,8 +1973,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_authenticator_provider_config_description(self, provider_id): - """ - Get authenticator's provider configuration description. + """Get authenticator's provider configuration description. AuthenticatorConfigInfoRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticatorconfiginforepresentation @@ -2078,8 +1988,9 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_authenticator_config(self, config_id): - """ - Get authenticator configuration. Returns all configuration details. + """Get authenticator configuration. + + Returns all configuration details. :param config_id: Authenticator config id :return: Response(json) @@ -2089,8 +2000,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def update_authenticator_config(self, payload, config_id): - """ - Update an authenticator configuration. + """Update an authenticator configuration. AuthenticatorConfigRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authenticatorconfigrepresentation @@ -2107,14 +2017,13 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def delete_authenticator_config(self, config_id): - """ - Delete a authenticator configuration. + """Delete a authenticator configuration. + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_authentication_management_resource :param config_id: Authenticator config id :return: Keycloak server Response """ - params_path = {"realm-name": self.realm_name, "id": config_id} data_raw = self.raw_delete( urls_patterns.URL_ADMIN_AUTHENTICATOR_CONFIG.format(**params_path) @@ -2122,8 +2031,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def sync_users(self, storage_id, action): - """ - Function to trigger user sync from provider + """Trigger user sync from provider. :param storage_id: The id of the user storage provider :param action: Action can be "triggerFullSync" or "triggerChangedUsersSync" @@ -2141,39 +2049,39 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError) def get_client_scopes(self): - """ + """Get client scopes. + Get representation of the client scopes for the realm where we are connected to https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_getclientscopes :return: Keycloak server response Array of (ClientScopeRepresentation) """ - params_path = {"realm-name": self.realm_name} data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_SCOPES.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def get_client_scope(self, client_scope_id): - """ + """Get client scope. + Get representation of the client scopes for the realm where we are connected to https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_getclientscopes :param client_scope_id: The id of the client scope :return: Keycloak server response (ClientScopeRepresentation) """ - 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)) return raise_error_from_response(data_raw, KeycloakGetError) def get_client_scope_by_name(self, client_scope_name): - """ + """Get client scope by name. + Get representation of the client scope identified by the client scope name. https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_getclientscopes :param client_scope_name: (str) Name of the client scope :returns: ClientScopeRepresentation or None """ - client_scopes = self.get_client_scopes() for client_scope in client_scopes: if client_scope["name"] == client_scope_name: @@ -2182,8 +2090,7 @@ class KeycloakAdmin: return None def create_client_scope(self, payload, skip_exists=False): - """ - Create a client scope + """Create a client scope. ClientScopeRepresentation: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_getclientscopes @@ -2192,7 +2099,6 @@ class KeycloakAdmin: :param skip_exists: If true then do not raise an error if client scope already exists :return: Client scope id """ - if skip_exists: exists = self.get_client_scope_by_name(client_scope_name=payload["name"]) @@ -2210,8 +2116,7 @@ class KeycloakAdmin: return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203 def update_client_scope(self, client_scope_id, payload): - """ - Update a client scope + """Update a client scope. ClientScopeRepresentation: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_client_scopes_resource @@ -2220,7 +2125,6 @@ class KeycloakAdmin: :param payload: ClientScopeRepresentation :return: Keycloak server response (ClientScopeRepresentation) """ - params_path = {"realm-name": self.realm_name, "scope-id": client_scope_id} data_raw = self.raw_put( urls_patterns.URL_ADMIN_CLIENT_SCOPE.format(**params_path), data=json.dumps(payload) @@ -2228,8 +2132,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def delete_client_scope(self, client_scope_id): - """ - Delete existing client scope. + """Delete existing client scope. ClientScopeRepresentation: https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_client_scopes_resource @@ -2242,8 +2145,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def get_mappers_from_client_scope(self, client_scope_id): - """ - Get a list of all mappers connected to the client scope + """Get a list of all mappers connected to the client scope. https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_protocol_mappers_resource :param client_scope_id: Client scope id @@ -2256,15 +2158,14 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200]) def add_mapper_to_client_scope(self, client_scope_id, payload): - """ - Add a mapper to a client scope + """Add a mapper to a client scope. + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_create_mapper :param client_scope_id: The id of the client scope :param payload: ProtocolMapperRepresentation :return: Keycloak server Response """ - params_path = {"realm-name": self.realm_name, "scope-id": client_scope_id} data_raw = self.raw_post( @@ -2275,15 +2176,14 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) def delete_mapper_from_client_scope(self, client_scope_id, protocol_mapper_id): - """ - Delete a mapper from a client scope + """Delete a mapper from a client scope. + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_delete_mapper :param client_scope_id: The id of the client scope :param protocol_mapper_id: Protocol mapper id :return: Keycloak server Response """ - params_path = { "realm-name": self.realm_name, "scope-id": client_scope_id, @@ -2296,8 +2196,8 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def update_mapper_in_client_scope(self, client_scope_id, protocol_mapper_id, payload): - """ - Update an existing protocol mapper in a client scope + """Update an existing protocol mapper in a client scope. + 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 @@ -2306,7 +2206,6 @@ class KeycloakAdmin: :param payload: ProtocolMapperRepresentation :return: Keycloak server Response """ - params_path = { "realm-name": self.realm_name, "scope-id": client_scope_id, @@ -2321,7 +2220,8 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def get_default_default_client_scopes(self): - """ + """Get default default client scopes. + Return list of default default client scopes :return: Keycloak server response @@ -2333,8 +2233,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def delete_default_default_client_scope(self, scope_id): - """ - Delete default default client scope + """Delete default default client scope. :param scope_id: default default client scope id :return: Keycloak server response @@ -2346,8 +2245,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def add_default_default_client_scope(self, scope_id): - """ - Add default default client scope + """Add default default client scope. :param scope_id: default default client scope id :return: Keycloak server response @@ -2361,7 +2259,8 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def get_default_optional_client_scopes(self): - """ + """Get default optional client scopes. + Return list of default optional client scopes :return: Keycloak server response @@ -2373,8 +2272,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def delete_default_optional_client_scope(self, scope_id): - """ - Delete default optional client scope + """Delete default optional client scope. :param scope_id: default optional client scope id :return: Keycloak server response @@ -2386,8 +2284,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def add_default_optional_client_scope(self, scope_id): - """ - Add default optional client scope + """Add default optional client scope. :param scope_id: default optional client scope id :return: Keycloak server response @@ -2401,8 +2298,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def get_mappers_from_client(self, client_id): - """ - List of all client mappers. + """List of all client mappers. https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_protocolmapperrepresentation @@ -2418,15 +2314,14 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[200]) def add_mapper_to_client(self, client_id, payload): - """ - Add a mapper to a client + """Add a mapper to a client. + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_create_mapper :param client_id: The id of the client :param payload: ProtocolMapperRepresentation :return: Keycloak server Response """ - params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_post( @@ -2437,14 +2332,13 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) def update_client_mapper(self, client_id, mapper_id, payload): - """ - Update client mapper + """Update client mapper. + :param client_id: The id of the client :param client_mapper_id: The id of the mapper to be deleted :param payload: ProtocolMapperRepresentation :return: Keycloak server response """ - params_path = { "realm-name": self.realm_name, "id": client_id, @@ -2459,14 +2353,13 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def remove_client_mapper(self, client_id, client_mapper_id): - """ - Removes a mapper from the client + """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 :param client_mapper_id: The id of the mapper to be deleted :return: Keycloak server response """ - params_path = { "realm-name": self.realm_name, "id": client_id, @@ -2479,15 +2372,13 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def generate_client_secrets(self, client_id): - """ + """Generate a new secret for the client. - Generate a new secret for the client https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_regeneratesecret :param client_id: id of client (not client-id) :return: Keycloak server response (ClientRepresentation) """ - params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_post( urls_patterns.URL_ADMIN_CLIENT_SECRETS.format(**params_path), data=None @@ -2495,21 +2386,20 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPostError) def get_client_secrets(self, client_id): - """ + """Get representation of the client secrets. - Get representation of the client secrets https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_getclientsecret :param client_id: id of client (not client-id) :return: Keycloak server response (ClientRepresentation) """ - params_path = {"realm-name": self.realm_name, "id": client_id} data_raw = self.raw_get(urls_patterns.URL_ADMIN_CLIENT_SECRETS.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def get_components(self, query=None): - """ + """Get components. + Return a list of components, filtered according to query parameters ComponentRepresentation @@ -2526,8 +2416,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def create_component(self, payload): - """ - Create a new component. + """Create a new component. ComponentRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_componentrepresentation @@ -2544,8 +2433,7 @@ class KeycloakAdmin: return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203 def get_component(self, component_id): - """ - Get representation of the component + """Get representation of the component. :param component_id: Component id @@ -2559,8 +2447,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def update_component(self, component_id, payload): - """ - Update the component + """Update the component. :param component_id: Component id :param payload: ComponentRepresentation @@ -2575,8 +2462,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def delete_component(self, component_id): - """ - Delete the component + """Delete the component. :param component_id: Component id @@ -2587,7 +2473,8 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204]) def get_keys(self): - """ + """Get keys. + Return a list of keys, filtered according to query parameters KeysMetadataRepresentation @@ -2600,7 +2487,8 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_events(self, query=None): - """ + """Get events. + Return a list of events, filtered according to query parameters EventRepresentation array @@ -2616,8 +2504,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def set_events(self, payload): - """ - Set realm events configuration + """Set realm events configuration. RealmEventsConfigRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_realmeventsconfigrepresentation @@ -2631,8 +2518,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204]) def raw_get(self, *args, **kwargs): - """ - Calls connection.raw_get. + """Call connection.raw_get. If auto_refresh is set for *get* and *access_token* is expired, it will refresh the token and try *get* once more. @@ -2644,8 +2530,7 @@ class KeycloakAdmin: return r def raw_post(self, *args, **kwargs): - """ - Calls connection.raw_post. + """Call connection.raw_post. If auto_refresh is set for *post* and *access_token* is expired, it will refresh the token and try *post* once more. @@ -2657,8 +2542,7 @@ class KeycloakAdmin: return r def raw_put(self, *args, **kwargs): - """ - Calls connection.raw_put. + """Call connection.raw_put. If auto_refresh is set for *put* and *access_token* is expired, it will refresh the token and try *put* once more. @@ -2670,8 +2554,7 @@ class KeycloakAdmin: return r def raw_delete(self, *args, **kwargs): - """ - Calls connection.raw_delete. + """Call connection.raw_delete. If auto_refresh is set for *delete* and *access_token* is expired, it will refresh the token and try *delete* once more. @@ -2683,6 +2566,7 @@ class KeycloakAdmin: return r def get_token(self): + """Get admin token.""" if self.user_realm_name: token_realm_name = self.user_realm_name elif self.realm_name: @@ -2730,6 +2614,7 @@ class KeycloakAdmin: ) def refresh_token(self): + """Refresh the token.""" refresh_token = self.token.get("refresh_token", None) if refresh_token is None: self.get_token() @@ -2752,8 +2637,7 @@ class KeycloakAdmin: ) def get_client_all_sessions(self, client_id): - """ - Get sessions associated with the client + """Get sessions associated with the client. :param client_id: id of client @@ -2767,8 +2651,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_client_sessions_stats(self): - """ - Get current session count for all clients with active sessions + """Get current session count for all clients with active sessions. https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_getclientsessionstats @@ -2779,8 +2662,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_client_management_permissions(self, client_id): - """ - Get management permissions for a client. + """Get management permissions for a client. :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation @@ -2793,8 +2675,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def update_client_management_permissions(self, payload, client_id): - """ - Update management permissions for a client. + """Update management permissions for a client. ManagementPermissionReference https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_managementpermissionreference @@ -2819,8 +2700,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[200]) def get_client_authz_policy_scopes(self, client_id, policy_id): - """ - Get scopes for a given policy. + """Get scopes for a given policy. :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation @@ -2834,8 +2714,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_client_authz_policy_resources(self, client_id, policy_id): - """ - Get resources for a given policy. + """Get resources for a given policy. :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation @@ -2849,8 +2728,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def get_client_authz_scope_permission(self, client_id, scope_id): - """ - Get permissions for a given scope. + """Get permissions for a given scope. :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation @@ -2864,8 +2742,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def update_client_authz_scope_permission(self, payload, client_id, scope_id): - """ - Update permissions for a given scope. + """Update permissions for a given scope. :param payload: No Document :param client_id: id in ClientRepresentation @@ -2895,8 +2772,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[201]) def get_client_authz_client_policies(self, client_id): - """ - Get policies for a given client. + """Get policies for a given client. :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation @@ -2909,8 +2785,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200]) def create_client_authz_client_policy(self, payload, client_id): - """ - Create a new policy for a given client. + """Create a new policy for a given client. :param payload: No Document :param client_id: id in ClientRepresentation diff --git a/src/keycloak/keycloak_openid.py b/src/keycloak/keycloak_openid.py index fa04e4d..83575b2 100644 --- a/src/keycloak/keycloak_openid.py +++ b/src/keycloak/keycloak_openid.py @@ -21,6 +21,12 @@ # 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 OpenID module. + +The module contains mainly the implementation of KeycloakOpenID class, the main +class to handle authentication and token manipulation. +""" + import json from jose import jwt @@ -52,8 +58,7 @@ from .urls_patterns import ( class KeycloakOpenID: - """ - Keycloak OpenID client. + """Keycloak OpenID client. :param server_url: Keycloak server url :param client_id: client id @@ -75,6 +80,7 @@ class KeycloakOpenID: proxies=None, timeout=60, ): + """Init method.""" self.client_id = client_id self.client_secret_key = client_secret_key self.realm_name = realm_name @@ -87,6 +93,7 @@ class KeycloakOpenID: @property def client_id(self): + """Get client id.""" return self._client_id @client_id.setter @@ -95,6 +102,7 @@ class KeycloakOpenID: @property def client_secret_key(self): + """Get the client secret key.""" return self._client_secret_key @client_secret_key.setter @@ -103,6 +111,7 @@ class KeycloakOpenID: @property def realm_name(self): + """Get the realm name.""" return self._realm_name @realm_name.setter @@ -111,6 +120,7 @@ class KeycloakOpenID: @property def connection(self): + """Get connection.""" return self._connection @connection.setter @@ -119,6 +129,7 @@ class KeycloakOpenID: @property def authorization(self): + """Get authorization.""" return self._authorization @authorization.setter @@ -126,8 +137,7 @@ class KeycloakOpenID: self._authorization = value def _add_secret_key(self, payload): - """ - Add secret key if exist. + """Add secret key if exists. :param payload: :return: @@ -138,7 +148,7 @@ class KeycloakOpenID: return payload def _build_name_role(self, role): - """ + """Build name of a role. :param role: :return: @@ -146,7 +156,7 @@ class KeycloakOpenID: 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: @@ -161,19 +171,20 @@ class KeycloakOpenID: return token_info def well_known(self): - """The most important endpoint to understand is the well-known configuration + """Get the well_known object. + + The most important endpoint to understand is the well-known configuration 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. """ - params_path = {"realm-name": self.realm_name} data_raw = self.connection.raw_get(URL_WELL_KNOWN.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) def auth_url(self, redirect_uri): - """ + """Get the authentication URL endpoint. http://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint @@ -196,7 +207,8 @@ class KeycloakOpenID: totp=None, **extra ): - """ + """Retrieve user token. + The token endpoint is used to obtain tokens. Tokens can either be obtained by exchanging an authorization code or by supplying credentials directly depending on what flow is used. The token endpoint is also used to obtain new access tokens @@ -232,7 +244,8 @@ class KeycloakOpenID: return raise_error_from_response(data_raw, KeycloakPostError) def refresh_token(self, refresh_token, grant_type=["refresh_token"]): - """ + """Refresh the user token. + The token endpoint is used to obtain tokens. Tokens can either be obtained by exchanging an authorization code or by supplying credentials directly depending on what flow is used. The token endpoint is also used to obtain new access tokens @@ -255,7 +268,8 @@ class KeycloakOpenID: return raise_error_from_response(data_raw, KeycloakPostError) def exchange_token(self, token: str, client_id: str, audience: str, subject: str) -> dict: - """ + """Exchange user token. + Use a token to obtain an entirely different token. See https://www.keycloak.org/docs/latest/securing_apps/index.html#_token-exchange @@ -279,7 +293,8 @@ class KeycloakOpenID: return raise_error_from_response(data_raw, KeycloakPostError) def userinfo(self, token): - """ + """Get the user info object. + The userinfo endpoint returns standard claims about the authenticated user, and is protected by a bearer token. @@ -294,8 +309,8 @@ class KeycloakOpenID: return raise_error_from_response(data_raw, KeycloakGetError) def logout(self, refresh_token): - """ - The logout endpoint logs out the authenticated user. + """Log out the authenticated user. + :param refresh_token: :return: """ @@ -306,7 +321,8 @@ class KeycloakOpenID: return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204]) def certs(self): - """ + """Get certificates. + The certificate endpoint returns the public keys enabled by the realm, encoded as a JSON Web Key (JWK). Depending on the realm settings there can be one or more keys enabled for verifying tokens. @@ -320,7 +336,8 @@ class KeycloakOpenID: return raise_error_from_response(data_raw, KeycloakGetError) def public_key(self): - """ + """Retrieve the public key. + The public key is exposed by the realm page directly. :return: @@ -330,7 +347,8 @@ class KeycloakOpenID: return raise_error_from_response(data_raw, KeycloakGetError)["public_key"] def entitlement(self, token, resource_server_id): - """ + """Get entitlements from the token. + Client applications can use a specific endpoint to obtain a special security token called a requesting party token (RPT). This token consists of all the entitlements (or permissions) for a user as a result of the evaluation of the permissions and @@ -349,7 +367,8 @@ class KeycloakOpenID: return raise_error_from_response(data_raw, KeycloakGetError) # pragma: no cover def introspect(self, token, rpt=None, token_type_hint=None): - """ + """Introspect the user token. + The introspection endpoint is used to retrieve the active state of a token. It is can only be invoked by confidential clients. @@ -377,7 +396,8 @@ class KeycloakOpenID: return raise_error_from_response(data_raw, KeycloakPostError) def decode_token(self, token, key, algorithms=["RS256"], **kwargs): - """ + """Decode user token. + A JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key. This specification also defines a JWK Set JSON data structure that represents a set of @@ -395,8 +415,7 @@ class KeycloakOpenID: return jwt.decode(token, key, algorithms=algorithms, audience=self.client_id, **kwargs) def load_authorization_config(self, path): - """ - Load Keycloak settings (authorization) + """Load Keycloak settings (authorization). :param path: settings file (json) :return: @@ -407,8 +426,7 @@ class KeycloakOpenID: self.authorization.load_config(authorization_json) def get_policies(self, token, method_token_info="introspect", **kwargs): - """ - Get policies by user token + """Get policies by user token. :param token: user token :return: policies list @@ -438,8 +456,7 @@ class KeycloakOpenID: return list(set(policies)) def get_permissions(self, token, method_token_info="introspect", **kwargs): - """ - Get permission by user token + """Get permission by user token. :param token: user token :param method_token_info: Decode token method @@ -471,8 +488,7 @@ class KeycloakOpenID: return list(set(permissions)) def uma_permissions(self, token, permissions=""): - """ - Get UMA permissions by user token with requested permissions + """Get UMA permissions by user token with requested permissions. The token endpoint is used to retrieve UMA permissions from Keycloak. It can only be invoked by confidential clients. @@ -499,8 +515,7 @@ class KeycloakOpenID: return raise_error_from_response(data_raw, KeycloakPostError) def has_uma_access(self, token, permissions): - """ - Determine whether user has uma permissions with specified user token + """Determine whether user has uma permissions with specified user token. :param token: user token :param permissions: list of uma permissions (resource:scope) diff --git a/src/keycloak/uma_permissions.py b/src/keycloak/uma_permissions.py index 5653c76..1bf2136 100644 --- a/src/keycloak/uma_permissions.py +++ b/src/keycloak/uma_permissions.py @@ -21,11 +21,14 @@ # 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. +"""User-managed access permissions module.""" + from keycloak.exceptions import KeycloakPermissionFormatError, PermissionDefinitionError class UMAPermission: """A class to conveniently assembly permissions. + The class itself is callable, and will return the assembled permission. Usage example: @@ -36,9 +39,16 @@ class UMAPermission: >>> print(permission) 'Users#delete' + :param permission: Permission + :type permission: UMAPermission + :param resource: Resource + :type resource: str + :param scope: Scope + :type scope: str """ def __init__(self, permission=None, resource="", scope=""): + """Init method.""" self.resource = resource self.scope = scope @@ -53,21 +63,26 @@ class UMAPermission: self.scope = str(permission.scope) def __str__(self): + """Str method.""" scope = self.scope if scope: scope = "#" + scope return "{}{}".format(self.resource, scope) def __eq__(self, __o: object) -> bool: + """Eq method.""" return str(self) == str(__o) def __repr__(self) -> str: + """Repr method.""" return self.__str__() def __hash__(self) -> int: + """Hash method.""" return hash(str(self)) def __call__(self, permission=None, resource="", scope="") -> object: + """Call method.""" result_resource = self.resource result_scope = self.scope @@ -91,36 +106,58 @@ class UMAPermission: class Resource(UMAPermission): """An UMAPermission Resource class to conveniently assembly permissions. + The class itself is callable, and will return the assembled permission. + + :param resource: Resource + :type resource: str """ def __init__(self, resource): + """Init method.""" super().__init__(resource=resource) class Scope(UMAPermission): """An UMAPermission Scope class to conveniently assembly permissions. + The class itself is callable, and will return the assembled permission. + + :param scope: Scope + :type scope: str """ def __init__(self, scope): + """Init method.""" super().__init__(scope=scope) class AuthStatus: """A class that represents the authorization/login status of a user associated with a token. + This has to evaluate to True if and only if the user is properly authorized - for the requested resource.""" + for the requested resource. + + :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 + """ def __init__(self, is_logged_in, is_authorized, missing_permissions): + """Init method.""" self.is_logged_in = is_logged_in self.is_authorized = is_authorized self.missing_permissions = missing_permissions def __bool__(self): + """Bool method.""" return self.is_authorized def __repr__(self): + """Repr method.""" return ( f"AuthStatus(" f"is_authorized={self.is_authorized}, " @@ -130,8 +167,7 @@ class AuthStatus: def build_permission_param(permissions): - """ - Transform permissions to a set, so they are usable for requests + """Transform permissions to a set, so they are usable for requests. :param permissions: either str (resource#scope), iterable[str] (resource#scope), diff --git a/src/keycloak/urls_patterns.py b/src/keycloak/urls_patterns.py index 3ec134c..b836692 100644 --- a/src/keycloak/urls_patterns.py +++ b/src/keycloak/urls_patterns.py @@ -21,6 +21,8 @@ # 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 URL patterns.""" + # OPENID URLS URL_REALM = "realms/{realm-name}" URL_WELL_KNOWN = "realms/{realm-name}/.well-known/openid-configuration" diff --git a/tests/__init__.py b/tests/__init__.py index e69de29..f1b390f 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -0,0 +1 @@ +"""Tests module.""" diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index e62bdda..069bb61 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -1,3 +1,5 @@ +"""Test the keycloak admin object.""" + import pytest import keycloak @@ -13,10 +15,12 @@ from keycloak.exceptions import ( def test_keycloak_version(): + """Test version.""" assert keycloak.__version__, keycloak.__version__ def test_keycloak_admin_bad_init(env): + """Test keycloak admin bad init.""" with pytest.raises(TypeError) as err: KeycloakAdmin( server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", @@ -37,6 +41,7 @@ def test_keycloak_admin_bad_init(env): def test_keycloak_admin_init(env): + """Test keycloak admin init.""" admin = KeycloakAdmin( server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", username=env.KEYCLOAK_ADMIN, @@ -111,6 +116,7 @@ def test_keycloak_admin_init(env): def test_realms(admin: KeycloakAdmin): + """Test realms.""" # Get realms realms = admin.get_realms() assert len(realms) == 1, realms @@ -175,6 +181,7 @@ def test_realms(admin: KeycloakAdmin): def test_import_export_realms(admin: KeycloakAdmin, realm: str): + """Test import and export of realms.""" admin.realm_name = realm realm_export = admin.export_realm(export_clients=True, export_groups_and_role=True) @@ -192,6 +199,7 @@ def test_import_export_realms(admin: KeycloakAdmin, realm: str): def test_users(admin: KeycloakAdmin, realm: str): + """Test users.""" admin.realm_name = realm # Check no users present @@ -283,6 +291,7 @@ def test_users(admin: KeycloakAdmin, realm: str): def test_users_pagination(admin: KeycloakAdmin, realm: str): + """Test user pagination.""" admin.realm_name = realm for ind in range(admin.PAGE_SIZE + 50): @@ -300,6 +309,7 @@ def test_users_pagination(admin: KeycloakAdmin, realm: str): def test_idps(admin: KeycloakAdmin, realm: str): + """Test IDPs.""" admin.realm_name = realm # Create IDP @@ -371,6 +381,7 @@ def test_idps(admin: KeycloakAdmin, realm: str): def test_user_credentials(admin: KeycloakAdmin, user: str): + """Test user credentials.""" res = admin.set_user_password(user_id=user, password="booya", temporary=True) assert res == dict(), res @@ -398,6 +409,7 @@ def test_user_credentials(admin: KeycloakAdmin, user: str): def test_social_logins(admin: KeycloakAdmin, user: str): + """Test social logins.""" res = admin.add_user_social_login( user_id=user, provider_id="gitlab", provider_userid="test", provider_username="test" ) @@ -437,6 +449,7 @@ def test_social_logins(admin: KeycloakAdmin, user: str): def test_server_info(admin: KeycloakAdmin): + """Test server info.""" info = admin.get_server_info() assert set(info.keys()) == { "systemInfo", @@ -456,6 +469,7 @@ def test_server_info(admin: KeycloakAdmin): def test_groups(admin: KeycloakAdmin, user: str): + """Test groups.""" # Test get groups groups = admin.get_groups() assert len(groups) == 0 @@ -599,6 +613,7 @@ def test_groups(admin: KeycloakAdmin, user: str): def test_clients(admin: KeycloakAdmin, realm: str): + """Test clients.""" admin.realm_name = realm # Test get clients @@ -860,6 +875,7 @@ def test_clients(admin: KeycloakAdmin, realm: str): def test_realm_roles(admin: KeycloakAdmin, realm: str): + """Test realm roles.""" admin.realm_name = realm # Test get realm roles @@ -1015,6 +1031,7 @@ def test_realm_roles(admin: KeycloakAdmin, realm: str): def test_client_roles(admin: KeycloakAdmin, client: str): + """Test client roles.""" # Test get client roles res = admin.get_client_roles(client_id=client) assert len(res) == 0 @@ -1177,6 +1194,7 @@ def test_client_roles(admin: KeycloakAdmin, client: str): def test_enable_token_exchange(admin: KeycloakAdmin, realm: str): + """Test enable token exchange.""" # Test enabling token exchange between two confidential clients admin.realm_name = realm @@ -1265,6 +1283,7 @@ def test_enable_token_exchange(admin: KeycloakAdmin, realm: str): def test_email(admin: KeycloakAdmin, user: str): + """Test email.""" # 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()) @@ -1277,6 +1296,7 @@ def test_email(admin: KeycloakAdmin, user: str): def test_get_sessions(admin: KeycloakAdmin): + """Test get sessions.""" sessions = admin.get_sessions(user_id=admin.get_user_id(username=admin.username)) assert len(sessions) >= 1 with pytest.raises(KeycloakGetError) as err: @@ -1285,6 +1305,7 @@ def test_get_sessions(admin: KeycloakAdmin): def test_get_client_installation_provider(admin: KeycloakAdmin, client: str): + """Test get client installation provider.""" 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"}\'') @@ -1303,6 +1324,7 @@ def test_get_client_installation_provider(admin: KeycloakAdmin, client: str): def test_auth_flows(admin: KeycloakAdmin, realm: str): + """Test auth flows.""" admin.realm_name = realm res = admin.get_authentication_flows() @@ -1449,6 +1471,7 @@ def test_auth_flows(admin: KeycloakAdmin, realm: str): def test_authentication_configs(admin: KeycloakAdmin, realm: str): + """Test authentication configs.""" admin.realm_name = realm # Test list of auth providers @@ -1480,6 +1503,7 @@ def test_authentication_configs(admin: KeycloakAdmin, realm: str): def test_sync_users(admin: KeycloakAdmin, realm: str): + """Test sync users.""" admin.realm_name = realm # Only testing the error message @@ -1489,6 +1513,7 @@ def test_sync_users(admin: KeycloakAdmin, realm: str): def test_client_scopes(admin: KeycloakAdmin, realm: str): + """Test client scopes.""" admin.realm_name = realm # Test get client scopes @@ -1626,6 +1651,7 @@ def test_client_scopes(admin: KeycloakAdmin, realm: str): def test_components(admin: KeycloakAdmin, realm: str): + """Test components.""" admin.realm_name = realm # Test get components @@ -1676,6 +1702,7 @@ def test_components(admin: KeycloakAdmin, realm: str): def test_keys(admin: KeycloakAdmin, realm: str): + """Test keys.""" 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"]} == { @@ -1687,6 +1714,7 @@ def test_keys(admin: KeycloakAdmin, realm: str): def test_events(admin: KeycloakAdmin, realm: str): + """Test events.""" admin.realm_name = realm events = admin.get_events() @@ -1706,6 +1734,7 @@ def test_events(admin: KeycloakAdmin, realm: str): def test_auto_refresh(admin: KeycloakAdmin, realm: str): + """Test auto refresh token.""" # Test get refresh admin.auto_refresh_token = list() admin.connection = ConnectionManager( diff --git a/tests/test_uma_permissions.py b/tests/test_uma_permissions.py index 09d7147..581faf4 100644 --- a/tests/test_uma_permissions.py +++ b/tests/test_uma_permissions.py @@ -14,6 +14,9 @@ # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . + +"""Test uma permissions.""" + import re import pytest @@ -23,30 +26,35 @@ from keycloak.uma_permissions import Resource, Scope, build_permission_param def test_resource_with_scope_obj(): + """Test resource with scope.""" r = Resource("Resource1") s = Scope("Scope1") assert r(s) == "Resource1#Scope1" def test_scope_with_resource_obj(): + """Test scope with resource.""" r = Resource("Resource1") s = Scope("Scope1") assert s(r) == "Resource1#Scope1" def test_resource_scope_str(): + """Test resource scope as string.""" r = Resource("Resource1") s = "Scope1" assert r(scope=s) == "Resource1#Scope1" def test_scope_resource_str(): + """Test scope resource as string.""" r = "Resource1" s = Scope("Scope1") assert s(resource=r) == "Resource1#Scope1" def test_resource_scope_list(): + """Test resource scope as list.""" r = Resource("Resource1") s = ["Scope1"] with pytest.raises(PermissionDefinitionError) as err: @@ -55,94 +63,114 @@ def test_resource_scope_list(): def test_build_permission_none(): + """Test build permission param with None.""" assert build_permission_param(None) == set() def test_build_permission_empty_str(): + """Test build permission param with an empty string.""" assert build_permission_param("") == set() def test_build_permission_empty_list(): + """Test build permission param with an empty list.""" assert build_permission_param([]) == set() def test_build_permission_empty_tuple(): + """Test build permission param with an empty tuple.""" assert build_permission_param(()) == set() def test_build_permission_empty_set(): + """Test build permission param with an empty set.""" assert build_permission_param(set()) == set() def test_build_permission_empty_dict(): + """Test build permission param with an empty dict.""" assert build_permission_param({}) == set() def test_build_permission_str(): + """Test build permission param as string.""" assert build_permission_param("resource1") == {"resource1"} def test_build_permission_list_str(): + """Test build permission param with list of strings.""" assert build_permission_param(["res1#scope1", "res1#scope2"]) == {"res1#scope1", "res1#scope2"} def test_build_permission_tuple_str(): + """Test build permission param with tuple of strings.""" assert build_permission_param(("res1#scope1", "res1#scope2")) == {"res1#scope1", "res1#scope2"} def test_build_permission_set_str(): + """Test build permission param with set of strings.""" assert build_permission_param({"res1#scope1", "res1#scope2"}) == {"res1#scope1", "res1#scope2"} def test_build_permission_tuple_dict_str_str(): + """Test build permission param with dictionary.""" assert build_permission_param({"res1": "scope1"}) == {"res1#scope1"} def test_build_permission_tuple_dict_str_list_str(): + """Test build permission param with dictionary of list.""" assert build_permission_param({"res1": ["scope1", "scope2"]}) == {"res1#scope1", "res1#scope2"} def test_build_permission_tuple_dict_str_list_str2(): + """Test build permission param with mutliple-keyed dictionary.""" assert build_permission_param( {"res1": ["scope1", "scope2"], "res2": ["scope2", "scope3"]} ) == {"res1#scope1", "res1#scope2", "res2#scope2", "res2#scope3"} def test_build_permission_uma(): + """Test build permission param with UMA.""" assert build_permission_param(Resource("res1")(Scope("scope1"))) == {"res1#scope1"} def test_build_permission_uma_list(): + """Test build permission param with list of UMAs.""" assert build_permission_param( [Resource("res1")(Scope("scope1")), Resource("res1")(Scope("scope2"))] ) == {"res1#scope1", "res1#scope2"} def test_build_permission_misbuilt_dict_str_list_list_str(): + """Test bad build of permission param from dictionary.""" with pytest.raises(KeycloakPermissionFormatError) as err: build_permission_param({"res1": [["scope1", "scope2"]]}) assert err.match(re.escape("misbuilt permission {'res1': [['scope1', 'scope2']]}")) def test_build_permission_misbuilt_list_list_str(): + """Test bad build of permission param from list.""" with pytest.raises(KeycloakPermissionFormatError) as err: print(build_permission_param([["scope1", "scope2"]])) assert err.match(re.escape("misbuilt permission [['scope1', 'scope2']]")) def test_build_permission_misbuilt_list_set_str(): + """Test bad build of permission param from set.""" with pytest.raises(KeycloakPermissionFormatError) as err: build_permission_param([{"scope1", "scope2"}]) assert err.match("misbuilt permission.*") def test_build_permission_misbuilt_set_set_str(): + """Test bad build of permission param from list of set.""" with pytest.raises(KeycloakPermissionFormatError) as err: build_permission_param([{"scope1"}]) assert err.match(re.escape("misbuilt permission [{'scope1'}]")) def test_build_permission_misbuilt_dict_non_iterable(): + """Test bad build of permission param from non-iterable.""" with pytest.raises(KeycloakPermissionFormatError) as err: build_permission_param({"res1": 5}) assert err.match(re.escape("misbuilt permission {'res1': 5}")) diff --git a/tests/test_urls_patterns.py b/tests/test_urls_patterns.py index 6fa5a87..5fae847 100644 --- a/tests/test_urls_patterns.py +++ b/tests/test_urls_patterns.py @@ -1,9 +1,10 @@ +"""Test URL patterns.""" + from keycloak import urls_patterns def test_correctness_of_patterns(): """Test that there are no duplicate url patterns.""" - # Test that the patterns are present urls = [x for x in dir(urls_patterns) if not x.startswith("__")] assert len(urls) >= 0