Browse Source

style: start of more checks

pull/373/head
Richard Nemeth 2 years ago
parent
commit
2bf150f7c1
No known key found for this signature in database GPG Key ID: 21C39470DF3DEC39
  1. 1
      docs/source/conf.py
  2. 38
      poetry.lock
  3. 5
      pyproject.toml
  4. 8
      src/keycloak/authorization/__init__.py
  5. 69
      src/keycloak/authorization/permission.py
  6. 78
      src/keycloak/authorization/policy.py
  7. 27
      src/keycloak/authorization/role.py
  8. 115
      src/keycloak/connection.py
  9. 2
      src/keycloak/exceptions.py
  10. 192
      src/keycloak/keycloak_admin.py
  11. 2
      tests/test_keycloak_admin.py
  12. 5
      tox.ini

1
docs/source/conf.py

@ -86,6 +86,7 @@ language = "en"
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
suppress_warnings = ["ref.python"]
add_function_parentheses = False
add_module_names = True

38
poetry.lock

@ -140,6 +140,18 @@ python-versions = ">=3.7"
colorama = {version = "*", markers = "platform_system == \"Windows\""}
importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
[[package]]
name = "codespell"
version = "2.1.0"
description = "Codespell"
category = "dev"
optional = false
python-versions = ">=3.5"
[package.extras]
dev = ["check-manifest", "flake8", "pytest", "pytest-cov", "pytest-dependency"]
hard-encoding-detection = ["chardet"]
[[package]]
name = "colorama"
version = "0.4.5"
@ -212,6 +224,14 @@ sdist = ["setuptools_rust (>=0.11.4)"]
ssh = ["bcrypt (>=3.1.5)"]
test = ["pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"]
[[package]]
name = "darglint"
version = "1.8.1"
description = "A utility for ensuring Google-style docstrings stay up to date with the source code."
category = "dev"
optional = false
python-versions = ">=3.6,<4.0"
[[package]]
name = "decli"
version = "0.5.2"
@ -291,7 +311,7 @@ pydocstyle = ">=2.1"
[[package]]
name = "identify"
version = "2.5.1"
version = "2.5.2"
description = "File identification library for Python"
category = "dev"
optional = false
@ -731,7 +751,7 @@ requests = ">=2.0.1,<3.0.0"
[[package]]
name = "rsa"
version = "4.8"
version = "4.9"
description = "Pure-Python RSA implementation"
category = "main"
optional = false
@ -1039,7 +1059,7 @@ docs = ["mock", "alabaster", "commonmark", "recommonmark", "Sphinx", "sphinx-rtd
[metadata]
lock-version = "1.1"
python-versions = "^3.7"
content-hash = "b6851001fb6b3e331a39e6bab1fa2ed99fdc555e9683137c20c9c593c0e1c040"
content-hash = "5d740e81a3604cb20ca85945c2fc134f6373f599315992afb50284f1f993c1d0"
[metadata.files]
alabaster = [
@ -1075,6 +1095,7 @@ click = [
{file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
{file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
]
codespell = []
colorama = [
{file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"},
{file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"},
@ -1086,6 +1107,7 @@ commonmark = [
]
coverage = []
cryptography = []
darglint = []
decli = [
{file = "decli-0.5.2-py3-none-any.whl", hash = "sha256:d3207bc02d0169bf6ed74ccca09ce62edca0eb25b0ebf8bf4ae3fb8333e15ca0"},
{file = "decli-0.5.2.tar.gz", hash = "sha256:f2cde55034a75c819c630c7655a844c612f2598c42c21299160465df6ad463ad"},
@ -1105,10 +1127,7 @@ flake8 = [
{file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"},
]
flake8-docstrings = []
identify = [
{file = "identify-2.5.1-py2.py3-none-any.whl", hash = "sha256:0dca2ea3e4381c435ef9c33ba100a78a9b40c0bab11189c7cf121f75815efeaa"},
{file = "identify-2.5.1.tar.gz", hash = "sha256:3d11b16f3fe19f52039fb7e39c9c884b21cb1b586988114fbe42671f03de3e82"},
]
identify = []
idna = [
{file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"},
{file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"},
@ -1355,10 +1374,7 @@ recommonmark = [
]
requests = []
requests-toolbelt = []
rsa = [
{file = "rsa-4.8-py3-none-any.whl", hash = "sha256:95c5d300c4e879ee69708c428ba566c59478fd653cc3a22243eeb8ed846950bb"},
{file = "rsa-4.8.tar.gz", hash = "sha256:5c6bd9dc7a543b7fe4304a631f8a8a3b674e2bbfc49c2ae96200cdbe55df6b17"},
]
rsa = []
six = [
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},

5
pyproject.toml

@ -56,6 +56,8 @@ flake8 = "^3.5.0"
flake8-docstrings = "^1.6.0"
commitizen = "^2.28.0"
cryptography = "^37.0.4"
codespell = "^2.1.0"
darglint = "^1.8.1"
[tool.poetry.extras]
docs = [
@ -80,3 +82,6 @@ line-length = 99
[tool.isort]
line_length = 99
profile = "black"
[tool.darglint]
enable = "DAR104"

8
src/keycloak/authorization/__init__.py

@ -44,7 +44,11 @@ class Authorization:
@property
def policies(self):
"""Get policies."""
"""Get policies.
:returns: Policies
:rtype: dict
"""
return self._policies
@policies.setter
@ -55,7 +59,7 @@ class Authorization:
"""Load policies, roles and permissions (scope/resources).
:param data: keycloak authorization data (dict)
:returns: None
:type data: dict
"""
for pol in data["policies"]:
if pol["type"] == "role":

69
src/keycloak/authorization/permission.py

@ -45,10 +45,29 @@ class Permission:
https://keycloak.gitbooks.io/documentation/authorization_services/topics/permission/overview.html
:param name: Name
:type name: str
:param type: Type
:type type: str
:param logic: Logic
:type logic: str
:param decision_strategy: Decision strategy
:type decision_strategy: str
"""
def __init__(self, name, type, logic, decision_strategy):
"""Init method."""
"""Init method.
:param name: Name
:type name: str
:param type: Type
:type type: str
:param logic: Logic
:type logic: str
:param decision_strategy: Decision strategy
:type decision_strategy: str
"""
self.name = name
self.type = type
self.logic = logic
@ -57,16 +76,28 @@ class Permission:
self.scopes = []
def __repr__(self):
"""Repr method."""
"""Repr method.
:returns: Class representation
:rtype: str
"""
return "<Permission: %s (%s)>" % (self.name, self.type)
def __str__(self):
"""Str method."""
"""Str method.
:returns: Class string representation
:rtype: str
"""
return "Permission: %s (%s)" % (self.name, self.type)
@property
def name(self):
"""Get name."""
"""Get name.
:returns: name
:rtype: str
"""
return self._name
@name.setter
@ -75,7 +106,11 @@ class Permission:
@property
def type(self):
"""Get type."""
"""Get type.
:returns: type
:rtype: str
"""
return self._type
@type.setter
@ -84,7 +119,11 @@ class Permission:
@property
def logic(self):
"""Get logic."""
"""Get logic.
:returns: Logic
:rtype: str
"""
return self._logic
@logic.setter
@ -93,7 +132,11 @@ class Permission:
@property
def decision_strategy(self):
"""Get decision strategy."""
"""Get decision strategy.
:returns: Decision strategy
:rtype: str
"""
return self._decision_strategy
@decision_strategy.setter
@ -102,7 +145,11 @@ class Permission:
@property
def resources(self):
"""Get resources."""
"""Get resources.
:returns: Resources
:rtype: list
"""
return self._resources
@resources.setter
@ -111,7 +158,11 @@ class Permission:
@property
def scopes(self):
"""Get scopes."""
"""Get scopes.
:returns: Scopes
:rtype: list
"""
return self._scopes
@scopes.setter

78
src/keycloak/authorization/policy.py

@ -39,10 +39,29 @@ class Policy:
https://keycloak.gitbooks.io/documentation/authorization_services/topics/policy/overview.html
:param name: Name
:type name: str
:param type: Type
:type type: str
:param logic: Logic
:type logic: str
:param decision_strategy: Decision strategy
:type decision_strategy: str
"""
def __init__(self, name, type, logic, decision_strategy):
"""Init method."""
"""Init method.
:param name: Name
:type name: str
:param type: Type
:type type: str
:param logic: Logic
:type logic: str
:param decision_strategy: Decision strategy
:type decision_strategy: str
"""
self.name = name
self.type = type
self.logic = logic
@ -51,16 +70,28 @@ class Policy:
self.permissions = []
def __repr__(self):
"""Repr method."""
"""Repr method.
:returns: Class representation
:rtype: str
"""
return "<Policy: %s (%s)>" % (self.name, self.type)
def __str__(self):
"""Str method."""
"""Str method.
:returns: Class string representation
:rtype: str
"""
return "Policy: %s (%s)" % (self.name, self.type)
@property
def name(self):
"""Get name."""
"""Get name.
:returns: Name
:rtype: str
"""
return self._name
@name.setter
@ -69,7 +100,11 @@ class Policy:
@property
def type(self):
"""Get type."""
"""Get type.
:returns: Type
:rtype: str
"""
return self._type
@type.setter
@ -78,7 +113,11 @@ class Policy:
@property
def logic(self):
"""Get logic."""
"""Get logic.
:returns: Logic
:rtype: str
"""
return self._logic
@logic.setter
@ -87,7 +126,11 @@ class Policy:
@property
def decision_strategy(self):
"""Get decision strategy."""
"""Get decision strategy.
:returns: Decision strategy
:rtype: str
"""
return self._decision_strategy
@decision_strategy.setter
@ -96,7 +139,11 @@ class Policy:
@property
def roles(self):
"""Get roles."""
"""Get roles.
:returns: Roles
:rtype: list
"""
return self._roles
@roles.setter
@ -105,7 +152,11 @@ class Policy:
@property
def permissions(self):
"""Get permissions."""
"""Get permissions.
:returns: Permissions
:rtype: list
"""
return self._permissions
@permissions.setter
@ -115,8 +166,9 @@ class Policy:
def add_role(self, role):
"""Add keycloak role in policy.
:param role: keycloak role.
:return:
:param role: Keycloak role
:type role: keycloak.authorization.Role
:raises KeycloakAuthorizationConfigError: In case of misconfigured policy type
"""
if self.type != "role":
raise KeycloakAuthorizationConfigError(
@ -127,7 +179,7 @@ class Policy:
def add_permission(self, permission):
"""Add keycloak permission in policy.
:param permission: keycloak permission.
:return:
:param permission: Keycloak permission
:type permission: keycloak.authorization.Permission
"""
self._permissions.append(permission)

27
src/keycloak/authorization/role.py

@ -31,19 +31,40 @@ class Role:
manager, and employee are all typical roles that may exist in an organization.
https://keycloak.gitbooks.io/documentation/server_admin/topics/roles.html
:param name: Name
:type name: str
:param required: Required role indicator
:type required: bool
"""
def __init__(self, name, required=False):
"""Init method."""
"""Init method.
:param name: Name
:type name: str
:param required: Required role indicator
:type required: bool
"""
self.name = name
self.required = required
def get_name(self):
"""Get name."""
"""Get name.
:returns: Name
:rtype: str
"""
return self.name
def __eq__(self, other):
"""Eq method."""
"""Eq method.
:param other: The other object
:type other: str
:returns: Equality bool
:rtype: bool | NotImplemented
"""
if isinstance(other, str):
return self.name == other
return NotImplemented

115
src/keycloak/connection.py

@ -37,15 +37,32 @@ from .exceptions import KeycloakConnectionError
class ConnectionManager(object):
"""Represents a simple server connection.
:param base_url: (str) The server URL.
:param headers: (dict) The header parameters of the requests to the server.
:param timeout: (int) Timeout to use for requests to the server.
:param verify: (bool) Verify server SSL.
:param proxies: (dict) The proxies servers requests is sent by.
:param base_url: The server URL.
:type base_url: str
:param headers: The header parameters of the requests to the server.
:type headers: dict
:param timeout: Timeout to use for requests to the server.
:type timeout: int
:param verify: Verify server SSL.
:type verify: bool
:param proxies: The proxies servers requests is sent by.
:type proxies: dict
"""
def __init__(self, base_url, headers={}, timeout=60, verify=True, proxies=None):
"""Init method."""
"""Init method.
:param base_url: The server URL.
:type base_url: str
:param headers: The header parameters of the requests to the server.
:type headers: dict
:param timeout: Timeout to use for requests to the server.
:type timeout: int
:param verify: Verify server SSL.
:type verify: bool
:param proxies: The proxies servers requests is sent by.
:type proxies: dict
"""
self.base_url = base_url
self.headers = headers
self.timeout = timeout
@ -73,7 +90,11 @@ class ConnectionManager(object):
@property
def base_url(self):
"""Return base url in use for requests to the server."""
"""Return base url in use for requests to the server.
:returns: Base URL
:rtype: str
"""
return self._base_url
@base_url.setter
@ -82,7 +103,11 @@ class ConnectionManager(object):
@property
def timeout(self):
"""Return timeout in use for request to the server."""
"""Return timeout in use for request to the server.
:returns: Timeout
:rtype: int
"""
return self._timeout
@timeout.setter
@ -91,7 +116,11 @@ class ConnectionManager(object):
@property
def verify(self):
"""Return verify in use for request to the server."""
"""Return verify in use for request to the server.
:returns: Verify indicator
:rtype: bool
"""
return self._verify
@verify.setter
@ -100,7 +129,11 @@ class ConnectionManager(object):
@property
def headers(self):
"""Return header request to the server."""
"""Return header request to the server.
:returns: Request headers
:rtype: dict
"""
return self._headers
@headers.setter
@ -110,8 +143,10 @@ class ConnectionManager(object):
def param_headers(self, key):
"""Return a specific header parameter.
:param key: (str) Header parameters key.
:param key: Header parameters key.
:type key: str
:returns: If the header parameters exist, return its value.
:rtype: str
"""
return self.headers.get(key)
@ -122,32 +157,41 @@ class ConnectionManager(object):
def exist_param_headers(self, key):
"""Check if the parameter exists in the header.
:param key: (str) Header parameters key.
:param key: Header parameters key.
:type key: str
:returns: If the header parameters exist, return True.
:rtype: bool
"""
return self.param_headers(key) is not None
def add_param_headers(self, key, value):
"""Add a single parameter inside the header.
:param key: (str) Header parameters key.
:param value: (str) Value to be added.
:param key: Header parameters key.
:type key: str
:param value: Value to be added.
:type value: str
"""
self.headers[key] = value
def del_param_headers(self, key):
"""Remove a specific parameter.
:param key: (str) Key of the header parameters.
:param key: Key of the header parameters.
:type key: str
"""
self.headers.pop(key, None)
def raw_get(self, path, **kwargs):
"""Submit get request to the path.
:param path: (str) Path for request.
:param path: Path for request.
:type path: str
:param kwargs: Additional arguments
:type kwargs: dict
:returns: Response the request.
:raises: HttpError Can't connect to server.
:rtype: Response
:raises KeycloakConnectionError: HttpError Can't connect to server.
"""
try:
return self._s.get(
@ -163,10 +207,15 @@ class ConnectionManager(object):
def raw_post(self, path, data, **kwargs):
"""Submit post request to the path.
:param path: (str) Path for request.
:param data: (dict) Payload for request.
:param path: Path for request.
:type path: str
:param data: Payload for request.
:type data: dict
:param kwargs: Additional arguments
:type kwargs: dict
:returns: Response the request.
:raises: HttpError Can't connect to server.
:rtype: Response
:raises KeycloakConnectionError: HttpError Can't connect to server.
"""
try:
return self._s.post(
@ -183,10 +232,15 @@ class ConnectionManager(object):
def raw_put(self, path, data, **kwargs):
"""Submit put request to the path.
:param path: (str) Path for request.
:param data: (dict) Payload for request.
:param path: Path for request.
:type path: str
:param data: Payload for request.
:type data: dict
:param kwargs: Additional arguments
:type kwargs: dict
:returns: Response the request.
:raises: HttpError Can't connect to server.
:rtype: Response
:raises KeycloakConnectionError: HttpError Can't connect to server.
"""
try:
return self._s.put(
@ -200,19 +254,24 @@ class ConnectionManager(object):
except Exception as e:
raise KeycloakConnectionError("Can't connect to server (%s)" % e)
def raw_delete(self, path, data={}, **kwargs):
def raw_delete(self, path, data=None, **kwargs):
"""Submit delete request to the path.
:param path: (str) Path for request.
:param data: (dict) Payload for request.
:param path: Path for request.
:type path: str
:param data: Payload for request.
:type data: dict | None
:param kwargs: Additional arguments
:type kwargs: dict
:returns: Response the request.
:raises: HttpError Can't connect to server.
:rtype: Response
:raises KeycloakConnectionError: HttpError Can't connect to server.
"""
try:
return self._s.delete(
urljoin(self.base_url, path),
params=kwargs,
data=data,
data=data or dict(),
headers=self.headers,
timeout=self.timeout,
verify=self.verify,

2
src/keycloak/exceptions.py

@ -21,7 +21,7 @@
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""Keycloak custom exeptions module."""
"""Keycloak custom exceptions module."""
import requests

192
src/keycloak/keycloak_admin.py

@ -49,19 +49,31 @@ class KeycloakAdmin:
"""Keycloak Admin client.
:param server_url: Keycloak server url
:type server_url: str
:param username: admin username
:type username: str
:param password: admin password
:type password: str
:param totp: Time based OTP
:type totp: str
:param realm_name: realm name
:type realm_name: str
:param client_id: client id
:type client_id: str
:param verify: True if want check connection SSL
:type verify: bool
:param client_secret_key: client secret key
(optional, required only for access type confidential)
:type client_secret_key: str
:param custom_headers: dict of custom header to pass to each HTML request
:type custom_headers: dict
:param user_realm_name: The realm name of the user, if different from realm_name
:type user_realm_name: str
:param auto_refresh_token: list of methods that allows automatic token refresh.
Ex: ['get', 'put', 'post', 'delete']
:type auto_refresh_token: list
:param timeout: connection timeout in seconds
:type timeout: int
"""
PAGE_SIZE = 100
@ -95,7 +107,35 @@ class KeycloakAdmin:
auto_refresh_token=None,
timeout=60,
):
"""Init method."""
"""Init method.
:param server_url: Keycloak server url
:type server_url: str
:param username: admin username
:type username: str
:param password: admin password
:type password: str
:param totp: Time based OTP
:type totp: str
:param realm_name: realm name
:type realm_name: str
:param client_id: client id
:type client_id: str
:param verify: True if want check connection SSL
:type verify: bool
:param client_secret_key: client secret key
(optional, required only for access type confidential)
:type client_secret_key: str
:param custom_headers: dict of custom header to pass to each HTML request
:type custom_headers: dict
:param user_realm_name: The realm name of the user, if different from realm_name
:type user_realm_name: str
:param auto_refresh_token: list of methods that allows automatic token refresh.
Ex: ['get', 'put', 'post', 'delete']
:type auto_refresh_token: list
:param timeout: connection timeout in seconds
:type timeout: int
"""
self.server_url = server_url
self.username = username
self.password = password
@ -114,7 +154,11 @@ class KeycloakAdmin:
@property
def server_url(self):
"""Get server url."""
"""Get server url.
:returns: Keycloak server url
:rtype: str
"""
return self._server_url
@server_url.setter
@ -123,7 +167,11 @@ class KeycloakAdmin:
@property
def realm_name(self):
"""Get realm name."""
"""Get realm name.
:returns: Realm name
:rtype: str
"""
return self._realm_name
@realm_name.setter
@ -132,7 +180,11 @@ class KeycloakAdmin:
@property
def connection(self):
"""Get connection."""
"""Get connection.
:returns: Connection manager
:rtype: ConnectionManager
"""
return self._connection
@connection.setter
@ -141,7 +193,11 @@ class KeycloakAdmin:
@property
def client_id(self):
"""Get client id."""
"""Get client id.
:returns: Client id
:rtype: str
"""
return self._client_id
@client_id.setter
@ -150,7 +206,11 @@ class KeycloakAdmin:
@property
def client_secret_key(self):
"""Get client secret key."""
"""Get client secret key.
:returns: Client secret key
:rtype: str
"""
return self._client_secret_key
@client_secret_key.setter
@ -159,7 +219,11 @@ class KeycloakAdmin:
@property
def verify(self):
"""Get verify."""
"""Get verify.
:returns: Verify indicator
:rtype: bool
"""
return self._verify
@verify.setter
@ -168,7 +232,11 @@ class KeycloakAdmin:
@property
def username(self):
"""Get username."""
"""Get username.
:returns: Admin username
:rtype: str
"""
return self._username
@username.setter
@ -177,7 +245,11 @@ class KeycloakAdmin:
@property
def password(self):
"""Get password."""
"""Get password.
:returns: Admin password
:rtype: str
"""
return self._password
@password.setter
@ -186,7 +258,11 @@ class KeycloakAdmin:
@property
def totp(self):
"""Get totp."""
"""Get totp.
:returns: TOTP
:rtype: str
"""
return self._totp
@totp.setter
@ -195,7 +271,11 @@ class KeycloakAdmin:
@property
def token(self):
"""Get token."""
"""Get token.
:returns: Access and refresh token
:rtype: dict
"""
return self._token
@token.setter
@ -204,12 +284,20 @@ class KeycloakAdmin:
@property
def auto_refresh_token(self):
"""Get auto refresh token."""
"""Get auto refresh token.
:returns: List of methods for automatic token refresh
:rtype: list
"""
return self._auto_refresh_token
@property
def user_realm_name(self):
"""Get user realm name."""
"""Get user realm name.
:returns: User realm name
:rtype: str
"""
return self._user_realm_name
@user_realm_name.setter
@ -218,7 +306,11 @@ class KeycloakAdmin:
@property
def custom_headers(self):
"""Get custom headers."""
"""Get custom headers.
:returns: Custom headers
:rtype: dict
"""
return self._custom_headers
@custom_headers.setter
@ -247,13 +339,16 @@ class KeycloakAdmin:
Wrapper function to paginate GET requests.
:param url: The url on which the query is executed
:type url: str
:param query: Existing query parameters (optional)
:type query: dict
:return: Combined results of paginated queries
:rtype: list
"""
results = []
# initalize query if it was called with None
# initialize query if it was called with None
if not query:
query = {}
page = 0
@ -274,8 +369,16 @@ class KeycloakAdmin:
return results
def __fetch_paginated(self, url, query=None):
query = query or {}
"""Make a specific paginated request.
:param url: The url on which the query is executed
:type url: str
:param query: Pagination settings
:type query: dict
:returns: Response
:rtype: dict
"""
query = query or {}
return raise_error_from_response(self.raw_get(url, **query), KeycloakGetError)
def import_realm(self, payload):
@ -287,8 +390,9 @@ class KeycloakAdmin:
https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_realmrepresentation
:param payload: RealmRepresentation
:type payload: dict
:return: RealmRepresentation
:rtype: dict
"""
data_raw = self.raw_post(urls_patterns.URL_ADMIN_REALMS, data=json.dumps(payload))
return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
@ -299,10 +403,13 @@ class KeycloakAdmin:
RealmRepresentation
https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_partialexport
:param export-clients: Skip if not want to export realm clients
:param export-groups-and-roles: Skip if not want to export realm groups and roles
:param export_clients: Skip if not want to export realm clients
:type export_clients: bool
:param export_groups_and_role: Skip if not want to export realm groups and roles
:type export_groups_and_role: bool
:return: realm configurations JSON
:rtype: dict
"""
params_path = {
"realm-name": self.realm_name,
@ -318,6 +425,7 @@ class KeycloakAdmin:
"""List all realms in Keycloak deployment.
:return: realms list
:rtype: list
"""
data_raw = self.raw_get(urls_patterns.URL_ADMIN_REALMS)
return raise_error_from_response(data_raw, KeycloakGetError)
@ -329,7 +437,9 @@ class KeycloakAdmin:
https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_realmrepresentation
:param realm_name: Realm name (not the realm id)
:type realm_name: str
:return: RealmRepresentation
:rtype: dict
"""
params_path = {"realm-name": realm_name}
data_raw = self.raw_get(urls_patterns.URL_ADMIN_REALM.format(**params_path))
@ -342,8 +452,11 @@ class KeycloakAdmin:
https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_realmrepresentation
:param payload: RealmRepresentation
:type payload: dict
:param skip_exists: Skip if Realm already exist.
:type skip_exists: bool
:return: Keycloak server response (RealmRepresentation)
:rtype: dict
"""
data_raw = self.raw_post(urls_patterns.URL_ADMIN_REALMS, data=json.dumps(payload))
return raise_error_from_response(
@ -353,15 +466,18 @@ class KeycloakAdmin:
def update_realm(self, realm_name, payload):
"""Update a realm.
This wil only update top level attributes and will ignore any user,
This will only update top level attributes and will ignore any user,
role, or client information in the payload.
RealmRepresentation:
https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_realmrepresentation
:param realm_name: Realm name (not the realm id)
:type realm_name: str
:param payload: RealmRepresentation
:type payload: dict
:return: Http response
:rtype: dict
"""
params_path = {"realm-name": realm_name}
data_raw = self.raw_put(
@ -373,7 +489,9 @@ class KeycloakAdmin:
"""Delete a realm.
:param realm_name: Realm name (not the realm id)
:type realm_name: str
:return: Http response
:rtype: dict
"""
params_path = {"realm-name": realm_name}
data_raw = self.raw_delete(urls_patterns.URL_ADMIN_REALM.format(**params_path))
@ -388,7 +506,9 @@ class KeycloakAdmin:
https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_userrepresentation
:param query: Query parameters (optional)
:type query: dict
:return: users list
:rtype: list
"""
query = query or {}
params_path = {"realm-name": self.realm_name}
@ -406,6 +526,9 @@ class KeycloakAdmin:
https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_identityproviderrepresentation
:param: payload: IdentityProviderRepresentation
:type payload: dict
:returns: Keycloak server response
:rtype: dict
"""
params_path = {"realm-name": self.realm_name}
data_raw = self.raw_post(
@ -419,8 +542,12 @@ class KeycloakAdmin:
IdentityProviderRepresentation
https://www.keycloak.org/docs-api/15.0/rest-api/index.html#_identity_providers_resource
:param: alias: alias for IdP to update
:param: idp_alias: alias for IdP to update
:type idp_alias: str
:param: payload: The IdentityProviderRepresentation
:type payload: dict
:returns: Keycloak server response
:rtype: dict
"""
params_path = {"realm-name": self.realm_name, "alias": idp_alias}
data_raw = self.raw_put(
@ -435,7 +562,11 @@ class KeycloakAdmin:
https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_identityprovidermapperrepresentation
:param: idp_alias: alias for Idp to add mapper in
:type idp_alias: str
:param: payload: IdentityProviderMapperRepresentation
:type payload: dict
:returns: Keycloak server response
:rtype: dict
"""
params_path = {"realm-name": self.realm_name, "idp-alias": idp_alias}
data_raw = self.raw_post(
@ -450,9 +581,13 @@ class KeycloakAdmin:
https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_update
:param: idp_alias: alias for Idp to fetch mappers
:type idp_alias: str
:param: mapper_id: Mapper Id to update
:type mapper_id: str
:param: payload: IdentityProviderMapperRepresentation
:type payload: dict
:return: Http response
:rtype: dict
"""
params_path = {
"realm-name": self.realm_name,
@ -476,7 +611,9 @@ class KeycloakAdmin:
https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_getmappers
:param: idp_alias: alias for Idp to fetch mappers
:type idp_alias: str
:return: array IdentityProviderMapperRepresentation
:rtype: list
"""
params_path = {"realm-name": self.realm_name, "idp-alias": idp_alias}
data_raw = self.raw_get(urls_patterns.URL_ADMIN_IDP_MAPPERS.format(**params_path))
@ -491,6 +628,7 @@ class KeycloakAdmin:
https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_identityproviderrepresentation
:return: array IdentityProviderRepresentation
:rtype: list
"""
params_path = {"realm-name": self.realm_name}
data_raw = self.raw_get(urls_patterns.URL_ADMIN_IDPS.format(**params_path))
@ -500,6 +638,9 @@ class KeycloakAdmin:
"""Delete an ID Provider.
:param: idp_alias: idp alias name
:type idp_alias: str
:returns: Keycloak server response
:rtype: dict
"""
params_path = {"realm-name": self.realm_name, "alias": idp_alias}
data_raw = self.raw_delete(urls_patterns.URL_ADMIN_IDP.format(**params_path))
@ -514,10 +655,13 @@ class KeycloakAdmin:
https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_userrepresentation
:param payload: UserRepresentation
:type payload: dict
:param exist_ok: If False, raise KeycloakGetError if username already exists.
Otherwise, return existing user ID.
:type exist_ok: bool
:return: UserRepresentation
:rtype: dict
"""
params_path = {"realm-name": self.realm_name}
@ -540,8 +684,10 @@ class KeycloakAdmin:
https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_users_resource
:param query: (dict) Query parameters for users count
:type query: dict
:return: counter
:rtype: int
"""
query = query or dict()
params_path = {"realm-name": self.realm_name}
@ -557,8 +703,10 @@ class KeycloakAdmin:
https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_userrepresentation
:param username: id in UserRepresentation
:type username: str
:return: user_id
:rtype: str
"""
lower_user_name = username.lower()
users = self.get_users(query={"search": lower_user_name})
@ -2870,7 +3018,7 @@ class KeycloakAdmin:
def get_required_action_by_alias(self, action_alias):
"""Get a required action by its alias.
:param action_alias: the alias of the requried action.
:param action_alias: the alias of the required action.
:type action_alias: str
:return: the required action (RequiredActionProviderRepresentation).
:rtype: dict

2
tests/test_keycloak_admin.py

@ -1820,7 +1820,7 @@ def test_auto_refresh(admin: KeycloakAdmin, realm: str):
def test_get_required_actions(admin: KeycloakAdmin, realm: str):
"""Test requried actions."""
"""Test required actions."""
admin.realm_name = realm
ractions = admin.get_required_actions()
assert isinstance(ractions, list)

5
tox.ini

@ -13,6 +13,7 @@ commands =
black --check --diff src/keycloak tests docs
isort -c --df src/keycloak tests docs
flake8 src/keycloak tests docs
codespell src tests docs
[testenv:apply-check]
commands =
@ -50,3 +51,7 @@ commands =
max-line-length = 99
docstring-convention = all
ignore = D203, D213, W503
docstring_style = sphinx
[darglint]
enable = DAR104
Loading…
Cancel
Save