Source code for keycloak.uma_permissions

# -*- coding: utf-8 -*-
#
# The MIT License (MIT)
#
# Copyright (C) 2017 Marcos Pereira <marcospereira.mpj@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# 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.

from keycloak.exceptions import KeycloakPermissionFormatError, PermissionDefinitionError


[docs]class UMAPermission: """A class to conveniently assembly permissions. The class itself is callable, and will return the assembled permission. Usage example: >>> r = Resource("Users") >>> s = Scope("delete") >>> permission = r(s) >>> print(permission) 'Users#delete' """ def __init__(self, permission=None, resource="", scope=""): self.resource = resource self.scope = scope if permission: if not isinstance(permission, UMAPermission): raise PermissionDefinitionError( "can't determine if '{}' is a resource or scope".format(permission) ) if permission.resource: self.resource = str(permission.resource) if permission.scope: self.scope = str(permission.scope)
[docs] def __str__(self): scope = self.scope if scope: scope = "#" + scope return "{}{}".format(self.resource, scope)
[docs] def __eq__(self, __o: object) -> bool: return str(self) == str(__o)
[docs] def __repr__(self) -> str: return self.__str__()
[docs] def __hash__(self) -> int: return hash(str(self))
[docs] def __call__(self, permission=None, resource="", scope="") -> object: result_resource = self.resource result_scope = self.scope if resource: result_resource = str(resource) if scope: result_scope = str(scope) if permission: if not isinstance(permission, UMAPermission): raise PermissionDefinitionError( "can't determine if '{}' is a resource or scope".format(permission) ) if permission.resource: result_resource = str(permission.resource) if permission.scope: result_scope = str(permission.scope) return UMAPermission(resource=result_resource, scope=result_scope)
[docs]class Resource(UMAPermission): """An UMAPermission Resource class to conveniently assembly permissions. The class itself is callable, and will return the assembled permission. """ def __init__(self, resource): super().__init__(resource=resource)
[docs]class Scope(UMAPermission): """An UMAPermission Scope class to conveniently assembly permissions. The class itself is callable, and will return the assembled permission. """ def __init__(self, scope): super().__init__(scope=scope)
[docs]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.""" def __init__(self, is_logged_in, is_authorized, missing_permissions): self.is_logged_in = is_logged_in self.is_authorized = is_authorized self.missing_permissions = missing_permissions
[docs] def __bool__(self): return self.is_authorized
[docs] def __repr__(self): return ( f"AuthStatus(" f"is_authorized={self.is_authorized}, " f"is_logged_in={self.is_logged_in}, " f"missing_permissions={self.missing_permissions})" )
[docs]def build_permission_param(permissions): """ Transform permissions to a set, so they are usable for requests :param permissions: either str (resource#scope), iterable[str] (resource#scope), dict[str,str] (resource: scope), dict[str,iterable[str]] (resource: scopes) :return: result bool """ if permissions is None or permissions == "": return set() if isinstance(permissions, str): return set((permissions,)) if isinstance(permissions, UMAPermission): return set((str(permissions),)) try: # treat as dictionary of permissions result = set() for resource, scopes in permissions.items(): print(f"resource={resource}scopes={scopes}") if scopes is None: result.add(resource) elif isinstance(scopes, str): result.add("{}#{}".format(resource, scopes)) else: try: for scope in scopes: if not isinstance(scope, str): raise KeycloakPermissionFormatError( "misbuilt permission {}".format(permissions) ) result.add("{}#{}".format(resource, scope)) except TypeError: raise KeycloakPermissionFormatError( "misbuilt permission {}".format(permissions) ) return result except AttributeError: pass try: # treat as any other iterable of permissions result = set() for permission in permissions: if not isinstance(permission, (str, UMAPermission)): raise KeycloakPermissionFormatError("misbuilt permission {}".format(permissions)) result.add(str(permission)) return result except TypeError: pass raise KeycloakPermissionFormatError("misbuilt permission {}".format(permissions))