magpie.api.management.group.group_utils ======================================= .. py:module:: magpie.api.management.group.group_utils Attributes ---------- .. autoapisummary:: magpie.api.management.group.group_utils.SERVICE_TYPE_DICT Classes ------- .. autoapisummary:: magpie.api.management.group.group_utils.WebhookAction magpie.api.management.group.group_utils.PermissionSet magpie.api.management.group.group_utils.PermissionType Functions --------- .. autoapisummary:: magpie.api.management.group.group_utils.format_group magpie.api.management.group.group_utils.format_resource magpie.api.management.group.group_utils.check_valid_service_or_resource_permission magpie.api.management.group.group_utils.get_permission_update_params magpie.api.management.group.group_utils.process_webhook_requests magpie.api.management.group.group_utils.format_permissions magpie.api.management.group.group_utils.get_all_group_names magpie.api.management.group.group_utils.get_group_resources magpie.api.management.group.group_utils.create_group magpie.api.management.group.group_utils.get_similar_group_resource_permission magpie.api.management.group.group_utils.create_group_resource_permission_response magpie.api.management.group.group_utils.get_group_resources_permissions_dict magpie.api.management.group.group_utils.get_group_resource_permissions_response magpie.api.management.group.group_utils.delete_group_resource_permission_response magpie.api.management.group.group_utils.get_group_services magpie.api.management.group.group_utils.get_group_services_response magpie.api.management.group.group_utils.get_group_service_permissions magpie.api.management.group.group_utils.get_group_service_permissions_response magpie.api.management.group.group_utils.get_group_service_resources_permissions_dict magpie.api.management.group.group_utils.get_group_service_resources_response Module Contents --------------- .. py:function:: format_group(group: magpie.models.Group, basic_info: bool = False, public_info: bool = False, dotted: bool = False, db_session: Optional[sqlalchemy.orm.session.Session] = None) -> magpie.typedefs.JSON Obtains the JSON formatted :term:`Group` definition according to field selection flags. :param group: Group for which to provide details. :param basic_info: If ``True``, return only sufficient details to identify the group (useful for routes that refer to a group, but that are not requesting it specifically), or return full details (for routes that specifically request its information, e.g.: ``GET /groups/{grp}``). :param public_info: Indicate if the returned details are intended for public information (``True``) or admin-only (``False``). Only higher level users should be provided additional details to avoid leaking potentially sensitive parameters. :param dotted: Employ a dot (``.``) instead of underscore (``_``) to separate :term:`Group` from its basic information. :param db_session: Database connection to retrieve additional details (required when ``public_info=False``). .. py:function:: format_resource(resource: magpie.models.Resource, permissions: Optional[Collection[magpie.typedefs.AnyPermissionType]] = None, permission_type: Optional[magpie.permissions.PermissionType] = None, basic_info: bool = False, dotted: bool = False) -> magpie.typedefs.JSON Formats a :term:`Resource` information into JSON. :param resource: :term:`Resource` to be formatted. :param permissions: Permissions to list along with the :paramref:`resource`. By default, these are the applicable permissions for that corresponding resource type. :param permission_type: Override indication of provenance to apply to :paramref:`permissions`. Only applicable when they are provided. :param basic_info: If ``True``, return only sufficient details to identify the resource, without any additional :paramref:`permissions` detail, nor hierarchical :paramref:`resource` information is returned. :param dotted: Employ a dot (``.``) instead of underscore (``_``) to separate :term:`Resource` from its basic information. .. seealso:: :func:`magpie.api.management.service.service_formats.format_service` .. py:function:: check_valid_service_or_resource_permission(permission_name: Union[magpie.typedefs.Str, magpie.permissions.Permission], service_or_resource: magpie.typedefs.ServiceOrResourceType, db_session: sqlalchemy.orm.session.Session) -> Optional[magpie.permissions.Permission] Checks if a permission is valid to be applied to a specific `service` or a `resource` under a root service. :param permission_name: permission name to be validated :param service_or_resource: resource item corresponding to either a Service or a Resource :param db_session: db connection :return: valid Permission if allowed by the service/resource :raises HTTPBadRequest: if the permission is not valid for the targeted service/resource .. py:class:: WebhookAction Bases: :py:obj:`magpie.utils.ExtendedEnum` Supported :term:`Webhook` actions. .. py:attribute:: CREATE_USER :value: 'create_user' Triggered when a new :term:`User` gets successfully created. .. seealso:: :ref:`webhook_user_create` .. py:attribute:: DELETE_USER :value: 'delete_user' Triggered when an existing :term:`User` gets successfully deleted. .. seealso:: :ref:`webhook_user_delete` .. py:attribute:: UPDATE_USER_STATUS :value: 'update_user_status' Triggered when an existing :term:`User` status gets successfully updated. .. seealso:: :ref:`webhook_user_update_status` .. py:attribute:: CREATE_USER_PERMISSION :value: 'create_user_permission' Triggered when a :term:`Permission` onto a :term:`Service` or :term:`Resource` gets created for a :term:`User`. .. seealso:: :ref:`webhook_permission_updates` .. py:attribute:: DELETE_USER_PERMISSION :value: 'delete_user_permission' Triggered when a :term:`Permission` onto a :term:`Service` or :term:`Resource` gets deleted for a :term:`User`. .. seealso:: :ref:`webhook_permission_updates` .. py:attribute:: CREATE_GROUP_PERMISSION :value: 'create_group_permission' Triggered when a :term:`Permission` onto a :term:`Service` or :term:`Resource` gets created for a :term:`Group`. .. seealso:: :ref:`webhook_permission_updates` .. py:attribute:: DELETE_GROUP_PERMISSION :value: 'delete_group_permission' Triggered when a :term:`Permission` onto a :term:`Service` or :term:`Resource` gets deleted for a :term:`Group`. .. seealso:: :ref:`webhook_permission_updates` .. py:function:: get_permission_update_params(target: Union[magpie.models.User, magpie.models.Group], resource: magpie.typedefs.ServiceOrResourceType, permission: magpie.permissions.PermissionSet, db_session: sqlalchemy.orm.session.Session) -> magpie.typedefs.WebhookTemplateParameters Generates the :term:`Webhook` parameters based on provided references. .. py:function:: process_webhook_requests(action: WebhookAction, params: magpie.typedefs.WebhookTemplateParameters, update_user_status_on_error: bool = False, settings: Optional[magpie.typedefs.AnySettingsContainer] = None) -> None Checks the config for any webhooks that correspond to the input action, and prepares corresponding requests. :param action: tag identifying which webhooks to use in the config :param params: Dictionary containing the required parameters and associated values for the request following the event action. Parameters will replace *templates* found in the ``payload`` definition of the webhook. :param update_user_status_on_error: update the user status or not in case of a webhook error. :param settings: application settings where webhooks configuration can be retrieved. .. py:class:: PermissionSet(permission: magpie.typedefs.AnyPermissionType, access: Optional[Union[Access, magpie.typedefs.Str]] = None, scope: Optional[Union[Scope, magpie.typedefs.Str]] = None, typ: Optional[PermissionType] = None, reason: Optional[magpie.typedefs.Str] = None) Bases: :py:obj:`object` Explicit definition of a :class:`Permission` with applicable :class:`Access` and :class:`Scope` to resolve it. The :class:`Permission` is the *name* of the applicable permission on the :class:`magpie.models.Resource`. The :class:`Scope` defines how the :class:`Permission` should impact the resolution of the perceived :term:`Effective Permissions ` over a :class:`magpie.models.Resource` tree hierarchy. The :class:`Access` defines how the :class:`Permission` access should be interpreted (granted or denied). Optionally, a :class:`PermissionType` can be provided to specifically indicate which kind of permission this set represents. This type is only for informative purposes, and is not saved to database nor displayed by the explicit string representation. It is returned within JSON representation and can be employed by :term:`Effective Permissions ` resolution to be more verbose about returned results. On missing :class:`Access` or :class:`Scope` specifications, they default to :attr:`Access.ALLOW` and :attr:`Scope.RECURSIVE` to handle backward compatible naming convention of plain ``permission_name``. Initializes the permission definition, possibly using required conversion from other implementations. :param permission: Name of the permission, or any other implementation from which the name can be inferred. :param access: Effective behaviour of the permissions. Generally, grant or deny the specified permission. :param scope: Scope for which the permission affects hierarchical resources. Important for effective resolution. :param typ: Type of permission being represented. Informative only, does not impact behavior if omitted. :param reason: Slightly more indicative information on why the current permission-type has this value. Value should be either explicitly provided or will be inferred if converted from input PermissionTuple. .. seealso:: :meth:`PermissionSet._convert` .. py:attribute:: __slots__ :value: ['_name', '_access', '_scope', '_tuple', '_type', '_reason'] .. py:method:: __eq__(other: Any) -> bool Return self==value. .. py:method:: __ne__(other: Any) -> bool Return self!=value. .. py:method:: __lt__(other: Any) -> bool Ascending sort of permission according to their name, access and scope modifiers. First sort by permission name alphabetically, followed by increasing *restrictive access* and increasing *range of scoped resources*. Using this sorting methodology, similar permissions by name are grouped together first, and permissions of same name with modifiers are then ordered, the first having less priority when selecting a single item to display with conflicting possibilities. Respecting :attr:`Access.DENY` is more important than :attr:`Access.ALLOW` (to protect the :term:`Resource`), and :attr:`Scope.MATCH` is *closer* to the actual :term:`Resource` than :attr:`Scope.RECURSIVE` permission received from a *farther* parent in the hierarchy. Sorted explicit string representation becomes:: [name1]-[allow]-[match] [name1]-[allow]-[recursive] [name1]-[deny]-[match] [name1]-[deny]-[recursive] [name2]-[allow]-[match] [name2]-[allow]-[recursive] [name2]-[deny]-[match] [name2]-[deny]-[recursive] ... We then obtain two **crucial** ordering results: 1. We can easily pick the last sorted item with highest resolution priority to find the final result of corresponding permissions. (note: final result for same user or group, their direct/inherited resolution is not considered here). 2. Picking the first element with lowest priority also displays the permission that impacts the widest range of resources. For instance in Magpie UI, indicating that a permission as :attr:`Scope.RECURSIVE` is more verbose as it tell other resources under it are also receive the specified :class:`Access` modifier rather than only the punctual resource. .. warning:: Alphabetically sorting permissions by string representation (implicit/explicit) is not equivalent to sorting them according to :term:`Permission` priority according to how modifiers are resolved. To obtain the prioritized sorting as strings, a list of :class:`PermissionSet` (with the strings as input) should be used to convert and correctly interpreted the raw strings, and then be converted back after sorting. .. code-block:: python # valid priority-sorted strings [str(perm) for perm in sorted(PermissionSet(p) for p in permission_strings)] # not equivalent to raw sorting list(sorted(permission_strings)) .. py:method:: __hash__() -> int Return hash(self). .. py:method:: __str__() -> magpie.typedefs.Str Obtains the compound literal representation of the :class:`PermissionSet`. Employed for database storage supporting ``ziggurat`` format. .. py:method:: __repr__() -> magpie.typedefs.Str Obtains the visual representation of the :class:`PermissionSet`. .. py:method:: like(other: Any) -> bool Evaluates if one permission is *similar* to another permission definition regardless of *modifiers*. This is different than ``==`` operator which will evaluate *exactly* equal permission definitions. .. py:method:: json() -> magpie.typedefs.PermissionDict Obtains the JSON representation of this :class:`PermissionSet`. .. py:method:: webhook_params() -> magpie.typedefs.JSON Obtain JSON representation employed for :term:`Webhook` reference. .. py:method:: ace(user_or_group: Optional[Union[magpie.models.User, magpie.models.Group]]) -> magpie.typedefs.AccessControlEntryType Converts the :class:`PermissionSet` into an :term:`ACE` that :mod:`pyramid` can understand. .. py:property:: reason :type: Optional[magpie.typedefs.Str] Indicative reason of the returned value defined by :meth:`type` or inferred by the :class:`PermissionTuple`. .. seealso:: :meth:`combine` :returns: Single string that describes the reason (source) of the permission, or multiple strings if updated by combination of multiple permissions. .. py:method:: resolve(permission1: magpie.typedefs.ResolvablePermissionType, permission2: magpie.typedefs.ResolvablePermissionType, context: PermissionType = PermissionType.INHERITED, multiple_choice: Optional[magpie.typedefs.Str] = None) -> magpie.typedefs.ResolvablePermissionType :classmethod: Resolves provided permissions into a single one considering various modifiers and groups for a resource. Permissions **MUST** have the same :term:`Permission` name. The associated :term:`Resource` on which the two compared permissions are applied on should also be the same This method **SHOULD NOT** be used by itself to obtain for :term:`Effective Permission` since it does not handle multi-level :term:`Resource` resolution. Resolution is accomplished in this case only for a given level in the tree hierarchy. The comparison considers both the :class:`Access` and :class:`Scope` of every :term:`Inherited Permission` of the :term:`User`, as well as its :term:`Group` memberships sorted by their priority. .. seealso:: - :meth:`magpie.services.ServiceInterface.effective_permissions` - :func:`magpie.api.management.user.user_utils.combine_user_group_permissions` - :meth:`PermissionSet.__lt__` :param permission1: Permission to compare. :param permission2: Permission to compare. :param context: Control the resolution context (local/effective) of the permissions (safeguard against invalid definitions). :param multiple_choice: Alternate explanation to default :data:`PERMISSION_REASON_MULTIPLE` applied if multiple :term:`Permission` refer to distinct :term:`Group` of equal priority and equivalent access definitions, meaning they are interchangeable without impacting resolution to access the same target :term:`Resource`. :returns: Permission with highest priority to resolve access a resource without considering scope. .. py:property:: group_priority :type: Optional[magpie.typedefs.GroupPriority] Priority accessor in case of group inherited permission resolved by :class:`PermissionTuple`. .. py:property:: perm_tuple :type: Optional[ziggurat_foundations.permissions.PermissionTuple] Get the original :class:`PermissionTuple` if available (:class:`PermissionSet` must have been created by one). .. py:property:: implicit_permission :type: Optional[magpie.typedefs.Str] Obtain the implicit string representation of the :class:`PermissionSet` as plain :class:`Permission` name. This representation is backward compatible with prior versions of `Magpie` where explicit representation of permission names in the database did not exist. If the contained modifiers of the :class:`PermissionSet` (notably the :attr:`Access.DENY`) result in a string representation that is *not possible* according to non existing permissions for older `Magpie` instances, the returned value will be ``None``. .. seealso:: - :meth:`explicit_permission` for the new representation. .. py:property:: explicit_permission :type: magpie.typedefs.Str Obtain the explicit string representation of the :class:`PermissionSet`. This format is always guaranteed to be completely defined contrary to :meth:`implicit_permission`. .. seealso:: - :meth:`__str__` (default string value). - :meth:`implicit_permission` for the old representation. .. py:property:: name :type: Permission .. py:attribute:: permission .. py:property:: access :type: Access .. py:property:: scope :type: Scope .. py:property:: type :type: Optional[PermissionType] .. py:method:: _convert(permission: magpie.typedefs.AnyPermissionType) -> Optional[PermissionSet] :classmethod: Converts any permission representation to the :class:`PermissionSet` with applicable enum members. Supports older :class:`Permission` representation such that implicit conversion of permission name without :attr:`access` and :attr:`scope` values are padded with defaults. Also, pre-defined partial or full definition from literal string representation are parsed to generate the :class:`PermissionSet` instance. :param permission: implicit or explicit permission name string, or any other known permission implementation :raises ValueError: when the permission name cannot be identified or parsed .. py:class:: PermissionType Bases: :py:obj:`magpie.utils.ExtendedEnum` Applicable types of :term:`Permission` according to context. .. py:attribute:: ACCESS :value: 'access' .. py:attribute:: ALLOWED :value: 'allowed' .. py:attribute:: APPLIED :value: 'applied' .. py:attribute:: DIRECT :value: 'direct' .. py:attribute:: INHERITED :value: 'inherited' .. py:attribute:: EFFECTIVE :value: 'effective' .. py:attribute:: OWNED :value: 'owned' .. py:function:: format_permissions(permissions: Optional[Collection[magpie.typedefs.AnyPermissionType]], permission_type: Optional[PermissionType] = None, force_unique: bool = True) -> Dict[magpie.typedefs.Str, Union[List[magpie.typedefs.Str], magpie.typedefs.PermissionDict, magpie.typedefs.Str]] Obtains the formatted permission representations after validation that each of their name is a known member of :class:`Permission` enum, and optionally with modifiers as defined by :class:`PermissionSet`. The returned lists are sorted alphabetically by permission *name*, and then in order of resolution priority (from highest to lowest) for each subset or corresponding *name*. The permissions are cleaned from any duplicate entries, unless :paramref:`force_unique` is specified to allow it. If no or empty :paramref:`permissions` is provided, empty lists are returned. .. note:: Field ``permission_names`` provides both the *older* implicit permission names and the *newer* explicit name representation. For this reason, there will be semantically "duplicate" permissions in that list, but there will not be any literal string duplicates. Implicit names are immediately followed by their explicit name, unless implicit names do not apply for the given permission (e.g.: when :attr:`Access.DENY` did not exist). Only detailed and explicit JSON representations are provided in the ``permissions`` list. When :paramref:`permission_type` is equal to :attr:`PermissionType.ALLOWED`, the collection of every applicable :class:`PermissionSet` is automatically generated by expanding all combinations of :class:`Access` and :class:`Scope` with every provided :class:`Permission` name in :paramref:`permissions`. This allows more concise definition of allowed permissions under :class:`magpie.services.Services` and their children :term:`Resource` by only defining :class:`Permission` names without manually listing all variations of :class:`PermissionSet`. For other :paramref:`permission_type` values, which represent :term:`Applied Permission` only explicitly provided :paramref:`permissions` are returned, to effectively return the collection of *active* permissions. :param permissions: multiple permissions of any implementation and type, to be rendered both as names and JSON. :param permission_type: indication of the represented permissions to be formatted, for informative indication. :param force_unique: whether to remove duplicate entries by association of name, access and scope or not. :returns: JSON with the permissions listed as implicit+explicit names, as permission set objects, and their type. .. py:data:: SERVICE_TYPE_DICT .. py:function:: get_all_group_names(db_session: sqlalchemy.orm.session.Session) -> List[magpie.typedefs.Str] Get all existing group names from the database. .. py:function:: get_group_resources(group: magpie.models.Group, db_session: sqlalchemy.orm.session.Session, service_types: Optional[List[magpie.typedefs.Str]] = None) -> magpie.typedefs.JSON Get formatted JSON body describing all service resources the ``group`` as permissions on. .. py:function:: create_group(group_name: magpie.typedefs.Str, description: magpie.typedefs.Str, discoverable: bool, terms: magpie.typedefs.Str, db_session: sqlalchemy.orm.session.Session) -> pyramid.httpexceptions.HTTPException Creates a group if it is permitted and not conflicting. :returns: valid HTTP response on successful operations. :raises HTTPException: error HTTP response of corresponding situation. .. py:function:: get_similar_group_resource_permission(group: magpie.models.Group, resource: magpie.typedefs.ServiceOrResourceType, permission: magpie.permissions.PermissionSet, db_session: sqlalchemy.orm.session.Session) -> Optional[magpie.permissions.PermissionSet] Obtains the group service/resource permission that corresponds to the provided one. Lookup considers only *similar* applied permission such that other permission modifiers don't affect comparison. .. py:function:: create_group_resource_permission_response(group: magpie.models.Group, resource: magpie.typedefs.ServiceOrResourceType, permission: magpie.permissions.PermissionSet, db_session: sqlalchemy.orm.session.Session, overwrite: bool = False) -> pyramid.httpexceptions.HTTPException Creates a permission on a group/resource combination if it is permitted and not conflicting. :param group: group for which to create/update the permission. :param resource: service or resource for which to create the permission. :param permission: permission with modifiers to be applied. :param db_session: database connection. :param overwrite: If the corresponding `(group, resource, permission[name])` exists, there is a conflict. Conflict is considered only by permission-name regardless of other modifiers. If overwrite is ``False``, the conflict will be raised and not be applied. If overwrite is ``True``, the permission modifiers will be replaced by the new ones, or created if missing. :returns: valid HTTP response on successful operations. :raises HTTPException: error HTTP response of corresponding situation. .. py:function:: get_group_resources_permissions_dict(group: magpie.models.Group, db_session: sqlalchemy.orm.session.Session, resource_ids: Optional[Iterable[int]] = None, resource_types: Optional[Iterable[magpie.typedefs.Str]] = None) -> magpie.typedefs.ResourcePermissionMap Get a dictionary of resources and corresponding permissions that a group has on the resources. Filter search by ``resource_ids`` and/or ``resource_types`` if specified. .. py:function:: get_group_resource_permissions_response(group: magpie.models.Group, resource: magpie.models.Resource, db_session: sqlalchemy.orm.session.Session) -> pyramid.httpexceptions.HTTPException Get validated response with group resource permissions as content. :returns: valid HTTP response on successful operations. :raises HTTPException: error HTTP response of corresponding situation. .. py:function:: delete_group_resource_permission_response(group: magpie.models.Group, resource: magpie.typedefs.ServiceOrResourceType, permission: magpie.permissions.PermissionSet, db_session: sqlalchemy.orm.session.Session, similar: bool = True) -> pyramid.httpexceptions.HTTPException Get validated response on deleted group resource permission. :param group: group for which to delete the permission. :param resource: service or resource for which to delete the permission. :param permission: permission with modifiers to be deleted. :param db_session: database connection. :param similar: Allow matching provided permission against any similar database permission. Otherwise, must match exactly. :returns: valid HTTP response on successful operations. :raises HTTPException: error HTTP response of corresponding situation. .. py:function:: get_group_services(resources_permissions_dict: magpie.typedefs.JSON, db_session: sqlalchemy.orm.session.Session, service_types: Optional[List[magpie.typedefs.Str]] = None) -> magpie.typedefs.JSON Nest and regroup the resource permissions under corresponding root service types. .. py:function:: get_group_services_response(group: magpie.models.Group, db_session: sqlalchemy.orm.session.Session, service_types: Optional[List[magpie.typedefs.Str]] = None) -> pyramid.httpexceptions.HTTPException Get validated response of services the group has permissions on. :returns: valid HTTP response on successful operations. :raises HTTPException: error HTTP response of corresponding situation. .. py:function:: get_group_service_permissions(group: magpie.models.Group, service: magpie.models.Service, db_session: sqlalchemy.orm.session.Session) -> List[magpie.permissions.PermissionSet] Get all permissions the group has on a specific service. .. py:function:: get_group_service_permissions_response(group: magpie.models.Group, service: magpie.models.Service, db_session: sqlalchemy.orm.session.Session) -> pyramid.httpexceptions.HTTPException Get validated response of found group service permissions. :returns: valid HTTP response on successful operations. :raises HTTPException: error HTTP response of corresponding situation. .. py:function:: get_group_service_resources_permissions_dict(group: magpie.models.Group, service: magpie.models.Service, db_session: sqlalchemy.orm.session.Session) -> magpie.typedefs.ResourcePermissionMap Get all permissions the group has on a specific service's children resources. .. py:function:: get_group_service_resources_response(group: magpie.models.Group, service: magpie.models.Service, db_session: sqlalchemy.orm.session.Session) -> pyramid.httpexceptions.HTTPException Get validated response of all found service resources which the group has permissions on. :returns: valid HTTP response on successful operations. :raises HTTPException: error HTTP response of corresponding situation.