magpie.services =============== .. py:module:: magpie.services Attributes ---------- .. autoapisummary:: magpie.services.PERMISSION_REASON_ADMIN magpie.services.PERMISSION_REASON_DEFAULT magpie.services.LOGGER magpie.services.SERVICE_TYPES magpie.services.SERVICE_TYPE_DICT Classes ------- .. autoapisummary:: magpie.services.Access magpie.services.Permission magpie.services.PermissionSet magpie.services.PermissionType magpie.services.Scope magpie.services.classproperty magpie.services.ServiceMeta magpie.services.ServiceInterface magpie.services.ServiceOWS magpie.services.ServiceWPS magpie.services.ServiceBaseWMS magpie.services.ServiceNCWMS2 magpie.services.ServiceGeoserverBase magpie.services.ServiceGeoserverWMS magpie.services.ServiceAccess magpie.services.ServiceAPI magpie.services.ServiceWFS magpie.services.ServiceGeoserverWFS magpie.services.ServiceTHREDDS magpie.services.ServiceGeoserverWPS magpie.services.ServiceGeoserverAPI magpie.services.ServiceGeoserver Functions --------- .. autoapisummary:: magpie.services.get_constant magpie.services.get_connected_session magpie.services.ows_parser_factory magpie.services.fully_qualified_name magpie.services.get_logger magpie.services.get_request_user magpie.services.service_factory magpie.services.get_resource_child_allowed magpie.services.invalidate_service Module Contents --------------- .. py:function:: get_constant(constant_name: magpie.typedefs.Str, settings_container: Optional[magpie.typedefs.AnySettingsContainer] = None, settings_name: Optional[magpie.typedefs.Str] = None, default_value: Optional[magpie.typedefs.SettingValue] = None, raise_not_set: bool = True, raise_missing: bool = True, print_missing: bool = False, empty_missing: bool = False) -> magpie.typedefs.SettingValue Search in order for matched value of :paramref:`constant_name`: 1. search in :py:data:`MAGPIE_CONSTANTS` 2. search in settings if specified 3. search alternative setting names (see below) 4. search in :mod:`magpie.constants` definitions 5. search in environment variables Parameter :paramref:`constant_name` is expected to have the format ``MAGPIE_[VARIABLE_NAME]`` although any value can be passed to retrieve generic settings from all above-mentioned search locations. If :paramref:`settings_name` is provided as alternative name, it is used as is to search for results if :paramref:`constant_name` was not found. Otherwise, ``magpie.[variable_name]`` is used for additional search when the format ``MAGPIE_[VARIABLE_NAME]`` was used for :paramref:`constant_name` (i.e.: ``MAGPIE_ADMIN_USER`` will also search for ``magpie.admin_user`` and so on for corresponding constants). :param constant_name: key to search for a value :param settings_container: WSGI application settings container (if not provided, uses found one in current thread) :param settings_name: alternative name for `settings` if specified :param default_value: default value to be returned if not found anywhere, and exception raises are disabled. :param raise_not_set: raise an exception if the found key is ``None``, search until last case if others are ``None`` :param raise_missing: raise exception if key is not found anywhere :param print_missing: print message if key is not found anywhere, return ``None`` :param empty_missing: consider an empty value for an existing key as if it was missing (i.e.: as if not set). :returns: found value or `default_value` :raises ValueError: if resulting value is invalid based on options (by default raise missing/empty/``None`` value) :raises LookupError: if no appropriate value could be found from all search locations (according to options) .. py:function:: get_connected_session(request: pyramid.request.Request) -> sqlalchemy.orm.session.Session Retrieve the session attached to the request or recreated it to ensure it is open and within scoped transaction. .. py:function:: ows_parser_factory(request: pyramid.request.Request) -> OWSParser Retrieve the appropriate :class:`OWSParser` parser using the ``Content-Type`` header. If the ``Content-Type`` header is missing or ``text/plain``, and the request has a body, try to parse the body as JSON and set the content-type to ``application/json`` if successful. Handle XML-like ``Content-Type`` headers such as ``application/x-www-form-urlencoded`` whenever applicable. Otherwise, use the basic :class:`OWSGetParser` or :class:`OWSPostParser` according to the presence of a body. These provide minimal parsing to handle most typical `OGC Web Services` (:term:`OWS`) request parameters. .. py:data:: PERMISSION_REASON_ADMIN :value: 'administrator' .. py:data:: PERMISSION_REASON_DEFAULT :value: 'no-permission' .. py:class:: Access Bases: :py:obj:`magpie.utils.ExtendedEnum` Applicable access modifier of :term:`Permission` values. .. py:attribute:: ALLOW :value: 'allow' .. py:attribute:: DENY :value: 'deny' .. py:class:: Permission Bases: :py:obj:`magpie.utils.ExtendedEnum` Applicable :term:`Permission` values (names) under certain :term:`Service` and :term:`Resource`. .. py:attribute:: READ :value: 'read' .. py:attribute:: WRITE :value: 'write' .. py:attribute:: ACCESS :value: 'access' .. py:attribute:: BROWSE :value: 'browse' .. py:attribute:: GET_CAPABILITIES :value: 'getcapabilities' .. py:attribute:: GET_MAP :value: 'getmap' .. py:attribute:: GET_FEATURE_INFO :value: 'getfeatureinfo' .. py:attribute:: GET_LEGEND_GRAPHIC :value: 'getlegendgraphic' .. py:attribute:: GET_METADATA :value: 'getmetadata' .. py:attribute:: GET_PROPERTY_VALUE :value: 'getpropertyvalue' .. py:attribute:: GET_FEATURE :value: 'getfeature' .. py:attribute:: GET_FEATURE_WITH_LOCK :value: 'getfeaturewithlock' .. py:attribute:: GET_GML_OBJECT :value: 'getgmlobject' .. py:attribute:: DESCRIBE_FEATURE_TYPE :value: 'describefeaturetype' .. py:attribute:: DESCRIBE_LAYER :value: 'describelayer' .. py:attribute:: DESCRIBE_PROCESS :value: 'describeprocess' .. py:attribute:: EXECUTE :value: 'execute' .. py:attribute:: LOCK_FEATURE :value: 'lockfeature' .. py:attribute:: TRANSACTION :value: 'transaction' .. py:attribute:: CREATE_STORED_QUERY :value: 'createstoredquery' .. py:attribute:: DROP_STORED_QUERY :value: 'dropstoredquery' .. py:attribute:: LIST_STORED_QUERIES :value: 'liststoredqueries' .. py:attribute:: DESCRIBE_STORED_QUERIES :value: 'describestoredqueries' .. 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:class:: Scope Bases: :py:obj:`magpie.utils.ExtendedEnum` Applicable access modifier of :term:`Permission` values. .. py:attribute:: MATCH :value: 'match' .. py:attribute:: RECURSIVE :value: 'recursive' .. py:class:: classproperty Bases: :py:obj:`property` Mimics :class:`property` decorator, but applied onto ``classmethod`` in backward compatible way. .. note:: This decorator purposely only supports getter attribute to define unmodifiable class properties. .. seealso:: https://stackoverflow.com/a/5191224 Initialize self. See help(type(self)) for accurate signature. .. py:method:: __get__(cls, owner) Return an attribute of instance, which is of type owner. .. py:function:: fully_qualified_name(obj: Union[Any, Type[Any]]) -> str Obtains the ``'.'`` full path definition of the object to allow finding and importing it. .. py:function:: get_logger(name: magpie.typedefs.Str, level: Optional[int] = None, force_stdout: bool = None, message_format: Optional[magpie.typedefs.Str] = None, datetime_format: Optional[magpie.typedefs.Str] = None) -> logging.Logger Immediately sets the logger level to avoid duplicate log outputs from the `root logger` and `this logger` when `level` is ``logging.NOTSET``. .. py:function:: get_request_user(request: pyramid.request.Request) -> Optional[magpie.models.User] Obtains the user that corresponds to the authentication details found in the request. Prior to resolving the user matching the authenticated user ID, reattaches the detached session or closed transaction if commit occurred beforehand. This can happen for example when creating a user from a pending-user where the user must be committed to allow webhooks to refer to it immediately, although the request has not completed processing. Operations using the ``request.user`` reference following that user creation could have a detached object state from the session. Ensure that any resolved user is also attached to the reestablished database session reference in the request. After reattaching to the session, following step is the original configuration setup similarly to typical methodology:: config.include("ziggurat_foundations.ext.pyramid.get_user") :param request: request to look for authenticated user. :return: authenticated user or none if unauthenticated. .. py:data:: LOGGER .. py:class:: ServiceMeta Bases: :py:obj:`type` .. py:property:: resource_types :type: List[Type[magpie.models.Resource]] Allowed resources type classes under the service. .. py:property:: resource_type_names :type: List[magpie.typedefs.Str] Allowed resources type names under the service. .. py:property:: child_resource_allowed :type: bool Lists all resources allowed *somewhere* within its resource hierarchy under the service. .. note:: Resources are not necessarily all allowed *directly* under the service. This depends on whether :attr:`ServiceInterface.child_structure_allowed` is defined or not. If not defined, resources are applicable anywhere. Otherwise, they must respect the explicit structure definitions. .. seealso:: Use :meth:`ServiceInterface.nested_resource_allowed` to obtain only scoped types allowed under a given resource considering allowed path structures. .. py:class:: ServiceInterface(service: magpie.models.Service, request: Optional[pyramid.request.Request]) Bases: :py:obj:`object` Initialize the service. :param service: Base service resource that must be handled by this service implementation. :param request: Active request to handle requested resources, permissions and effective access. The request can be omitted if basic service definition details are to be retrieved. It is mandatory for any ``requested`` or ``effective`` component that should be resolved. .. py:property:: service_type :type: Optional[magpie.typedefs.Str] :abstractmethod: Service type identifier (required, unique across implementation). .. py:attribute:: permissions :type: List[magpie.permissions.Permission] :value: [] Permission allowed directly on the service as top-level resource. .. py:attribute:: resource_types_permissions :type: magpie.typedefs.ResourceTypePermissions Mapping of resource types to lists of permissions defining allowed children resource permissions under the service. .. py:attribute:: child_structure_allowed :type: Dict[Type[magpie.typedefs.ServiceOrResourceType], List[Type[magpie.models.Resource]]] Control mapping of resource types limiting the allowed structure of nested children resources. When not defined, any nested resource type combination is allowed if they themselves allow children resources. Otherwise, nested child resource under the service can only be created at specific positions within the hierarchy that matches exactly one of the defined control conditions. For example, the below definition allows only resources typed ``route`` directly under the service. The following nested resource under that first-level ``route`` can then be either another ``route`` followed by a child ``process`` or directly a ``process``. Because ``process`` type doesn't allow any children resource (see :attr:`models.Process.child_resource_allowed`), those are the only allowed combinations (cannot further nest resources under the final ``process`` resource). .. code-block:: python child_structure_allowed = { models.Service: [models.Route], models.Route: [models.Route, models.Process], models.Process: [], } .. seealso:: - Validation of allowed nested children resource insertion of a given type under a parent resource is provided by :meth:`ServiceInterface.validate_nested_resource_type` that employs :attr:`child_structure_allowed`. - Listing of allowed resource types scoped under a given child resource within the hierarchy is provided by :meth:`ServiceInterface.nested_resource_allowed`. .. py:attribute:: _config :type: Optional[magpie.typedefs.ServiceConfiguration] :value: None .. py:attribute:: configurable :value: False Indicates if the service supports custom configuration. .. py:method:: __str__() Return str(self). .. py:method:: permission_requested() -> magpie.typedefs.PermissionRequested :abstractmethod: Defines how to interpret the incoming request into :class:`Permission` definitions for the given service. Each service must implement its own definition. The method must specifically define how to convert generic request path, query, etc. elements into permissions that match the service and its children resources. If ``None`` is returned, the :term:`ACL` will effectively be resolved to denied access. Otherwise, one or more returned :class:`Permission` will indicate which permissions should be looked for to resolve the :term:`ACL` of the authenticated user and its groups. If the request cannot be parsed for any reason to retrieve needed parameters (e.g.: Bad Request), the :exception:`HTTPBadRequest` can be raised to indicate specifically the cause, which will help :class:`magpie.adapter.magpieowssecurity.MagpieOWSSecurity` create a better response with the relevant error details. .. py:method:: resource_requested() -> magpie.typedefs.MultiResourceRequested :abstractmethod: Defines how to interpret the incoming request into the targeted :class:`model.Resource` for the given service. Each service must implement its own definition. The expected return value must be either of the following:: - List<(target-resource, target?)> When multiple resources need validation ('target?' as below for each). - (target-resource, True) when the exact resource is found according to request parsing. - (parent-resource, False) when any parent of the resource is found according to request parsing. - None when invalid request or not found resource. The ``parent-resource`` should indicate the *closest* higher-level resource in the hierarchy that would nest the otherwise desired ``target-resource``. The idea behind this is that `Magpie` will be able to resolve the effective recursive scoped permission even if not all corresponding resources were explicitly defined in the database. For example, if the request *would* be interpreted with the following hierarchy after service-specific resolution:: ServiceA Resource1 <== closest *existing* parent resource [Resource2] <== target (according to service/request resolution), but not existing in database A permission defined as Allow/Recursive on ``Resource1`` should *normally* allow access to ``Resource2``. If ``Resource2`` is not present in the database though, it cannot be looked for, and the corresponding ACL cannot be generated. Because the (real) protected service using `Magpie` can have a large and dynamic hierarchy, it is not convenient to enforce perpetual sync between it and its resource representation in `Magpie`. Using ``(parent-resource, False)`` will allow resolution of permission from the closest available parent. .. note:: In case of ``parent-resource`` returned, only `recursive`-scoped permissions will be considered, since the missing ``target-resource`` is the only one that should be checked for `match`-scoped permissions. For this reason, the service-specific implementation should preferably return the explicit `target` resource whenever possible. If the returned resource is ``None``, the ACL will effectively be resolved to denied access. This can be used to indicate failure to retrieve the expected resource or that corresponding resource does not exist. Otherwise, this method implementation should convert any request path, query parameters, etc. into an existing resource. If a list of ``(target-resource, target?)`` is returned, all of those resources should individually perform :term:`Effective Resolution` and should **ALL** simultaneously be granted access to let the request through. This can be used to resolve ambiguous or equivalent parameter combinations from parsing the request, or to validate access to parameters that allow multi-resource references using some kind of list value representation. .. seealso:: - :meth:`_get_acl` for :term:`Effective Resolution` of over multiple :term:`Resource` references. - :meth:`effective_permissions` for :term:`Effective Resolution` of a single :term:`Resource`. :returns: One or many tuple of reference resource (target/parent), and explicit match status of the corresponding resource (True/False) .. py:method:: user_requested() Obtain the :term:`User` that was identified to obtain protected :term:`Resource` access. .. py:property:: __acl__ :type: magpie.typedefs.AccessControlListType Access Control List (:term:`ACL`) formed of :term:`ACE` defining combinations rules to grant or refuse access. Each :term:`ACE` is defined as ``(outcome, user/group, permission)`` tuples. Called by the configured Pyramid :class:`pyramid.authorization.ACLAuthorizationPolicy`. Caching is automatically handled according to configured application settings and whether the specific ACL combination being requested was already processed recently. .. py:method:: _get_acl_cached(service_name: magpie.typedefs.Str, request_method: magpie.typedefs.Str, request_path: magpie.typedefs.Str, user_id: Optional[int]) -> magpie.typedefs.AccessControlListType Cache this method with :py:mod:`beaker` based on the provided caching key parameters. If the cache is not hit (expired timeout or new key entry), calls :meth:`ServiceInterface.get_acl` to retrieve effective permissions of the requested resource and specific permission for the applicable service and user executing the request. .. note:: Function arguments are required to generate caching keys by which cached elements will be retrieved. Actual arguments are not needed as we employ stored objects in the instance. .. warning:: Anything within this method or any underlying calls that can potentially retrieve database contents, whether for direct object or dynamically generated relationships (eg: ``user.groups``) must attempt to reestablish any detached or invalid session/transaction due to the potentially desynchronized references between objects before/after both incoming ``service`` and this ``acl`` cache regions. .. seealso:: - :meth:`ServiceInterface.permission_requested` - :meth:`ServiceInterface.resource_requested` - :meth:`ServiceInterface.user_requested` .. py:method:: _get_acl(user: magpie.models.User, resources: magpie.typedefs.MultiResourceRequested, permissions: Collection[magpie.permissions.Permission]) -> magpie.typedefs.AccessControlListType Resolves resource-tree and user/group inherited permissions into simplified :term:`ACL` of requested resources. Contrary to :meth:`effective_permissions` that can be resolved only for individual :term:`Resource`, :term:`ACL` is involved during actual request access, which could refer to multiple :term:`Resource` references or distinct :term:`Permission` names if the identified parent :term:`Service` supports it. When more than one item is specified for validation (any combination of :term:`Resource` or :term:`Permission`), **ALL** of them must be granted access to resolve as :data:`Access.ALLOW`. Any denied access blocks the whole set of requested elements. .. seealso:: - Core :term:`Permission` resolution rules of a single :term:`Resource` using :meth:`effective_permissions`. - Support of multi-:term:`Resource` references defined by returned values of :meth:`resource_requested` for individual :class:`ServiceInterface` implementations. .. py:method:: _get_connected_object(obj: Union[magpie.typedefs.ServiceOrResourceType, magpie.models.User]) -> Optional[magpie.typedefs.ServiceOrResourceType] Retrieve the object with an active session and attached state by refreshing connection with request session. This operation is required mostly in cases of mismatching references between cached and active objects obtained according to timing of requests and whether caching took placed between them, and for different caching region levels (service, ACL or both). It also attempts to correct and encountered problems due to concurrent requests. .. py:method:: _get_request_path_parts() -> Optional[List[magpie.typedefs.Str]] Obtain the :attr:`request` path parts stripped of anything prior to the referenced :attr:`service` name. .. py:method:: get_config() -> magpie.typedefs.ServiceConfiguration Obtains the custom configuration of the registered service. .. py:method:: get_resource_permissions(resource_type_name: magpie.typedefs.Str) -> List[magpie.permissions.Permission] :classmethod: Obtains the allowed permissions of the service's child resource fetched by resource type name. .. py:method:: validate_nested_resource_type(parent_resource: magpie.typedefs.ServiceOrResourceType, child_resource_type: magpie.typedefs.Str) -> bool :classmethod: Validate whether a new child resource type is allowed under the parent resource under the service. :param parent_resource: Parent under which the new resource must be validated. This can be the service itself. :param child_resource_type: Type to validate at the position defined under the parent resource. :return: status indicating if insertion is allowed for this type and at this parent position. .. py:method:: nested_resource_allowed(parent_resource: magpie.typedefs.ServiceOrResourceType) -> List[Type[magpie.models.Resource]] :classmethod: Obtain the nested resource types allowed as children resource within structure definitions. .. py:method:: allowed_permissions(resource: magpie.typedefs.ServiceOrResourceType) -> List[magpie.permissions.Permission] Obtains the allowed permissions for or under the service according to provided service or resource. .. py:method:: effective_permissions(user: magpie.models.User, resource: magpie.typedefs.ServiceOrResourceType, permissions: Optional[Collection[magpie.permissions.Permission]] = None, allow_match: bool = True) -> List[magpie.permissions.PermissionSet] Obtains the :term:`Effective Resolution` of permissions the user has over the specified resource. Recursively rewinds the resource tree from the specified resource up to the top-most parent service the resource resides under (or directly if the resource is the service) and retrieve permissions along the way that should be applied to children when using scoped-resource inheritance. Rewinding of the tree can terminate earlier when permissions can be immediately resolved such as when more restrictive conditions enforce denied access. Both user and group permission inheritance is resolved simultaneously to tree hierarchy with corresponding allow and deny conditions. User :term:`Direct Permissions ` have priority over all its groups :term:`Inherited Permissions `, and denied permissions have priority over allowed access ones. All applicable permissions on the resource (as defined by :meth:`allowed_permissions`) will have their resolution (Allow/Deny) provided as output, unless a specific subset of permissions is requested using :paramref:`permissions`. Other permissions are ignored in this case to only resolve requested ones. For example, this parameter can be used to request only ACL resolution from specific permissions applicable for a given request, as obtained by :meth:`permission_requested`. Permissions scoped as `match` can be ignored using :paramref:`allow_match`, such as when the targeted resource does not exist. .. seealso:: - :meth:`ServiceInterface.resource_requested` :param user: :term:`User` for which to perform :term:`Effective Resolution`. :param resource: :term:`Resource` onto which access must be resolved. :param permissions: List of :term:`Permission` for which to perform the resolution. :param allow_match: Indicate if the specific :term:`Resource` was matched to allow :data:`Scope.MATCH` handling. :returns: Resolved set :term:`Effective Permission` for specified parameter combinations. .. py:class:: ServiceOWS(service: magpie.models.Service, request: pyramid.request.Request) Bases: :py:obj:`ServiceInterface` Generic request-to-permission interpretation method of various ``OGC Web Service`` (OWS) implementations. Initialize the service. :param service: Base service resource that must be handled by this service implementation. :param request: Active request to handle requested resources, permissions and effective access. The request can be omitted if basic service definition details are to be retrieved. It is mandatory for any ``requested`` or ``effective`` component that should be resolved. .. py:attribute:: params_expected :type: List[magpie.typedefs.Str] :value: [] Request query parameters that are expected and should be preprocessed by parsing the submitted request. .. py:method:: _get_request() -> pyramid.request.Request .. py:method:: _set_request(request: pyramid.request.Request) -> None .. py:attribute:: request .. py:method:: resource_requested() -> magpie.typedefs.MultiResourceRequested :abstractmethod: Defines how to interpret the incoming request into the targeted :class:`model.Resource` for the given service. Each service must implement its own definition. The expected return value must be either of the following:: - List<(target-resource, target?)> When multiple resources need validation ('target?' as below for each). - (target-resource, True) when the exact resource is found according to request parsing. - (parent-resource, False) when any parent of the resource is found according to request parsing. - None when invalid request or not found resource. The ``parent-resource`` should indicate the *closest* higher-level resource in the hierarchy that would nest the otherwise desired ``target-resource``. The idea behind this is that `Magpie` will be able to resolve the effective recursive scoped permission even if not all corresponding resources were explicitly defined in the database. For example, if the request *would* be interpreted with the following hierarchy after service-specific resolution:: ServiceA Resource1 <== closest *existing* parent resource [Resource2] <== target (according to service/request resolution), but not existing in database A permission defined as Allow/Recursive on ``Resource1`` should *normally* allow access to ``Resource2``. If ``Resource2`` is not present in the database though, it cannot be looked for, and the corresponding ACL cannot be generated. Because the (real) protected service using `Magpie` can have a large and dynamic hierarchy, it is not convenient to enforce perpetual sync between it and its resource representation in `Magpie`. Using ``(parent-resource, False)`` will allow resolution of permission from the closest available parent. .. note:: In case of ``parent-resource`` returned, only `recursive`-scoped permissions will be considered, since the missing ``target-resource`` is the only one that should be checked for `match`-scoped permissions. For this reason, the service-specific implementation should preferably return the explicit `target` resource whenever possible. If the returned resource is ``None``, the ACL will effectively be resolved to denied access. This can be used to indicate failure to retrieve the expected resource or that corresponding resource does not exist. Otherwise, this method implementation should convert any request path, query parameters, etc. into an existing resource. If a list of ``(target-resource, target?)`` is returned, all of those resources should individually perform :term:`Effective Resolution` and should **ALL** simultaneously be granted access to let the request through. This can be used to resolve ambiguous or equivalent parameter combinations from parsing the request, or to validate access to parameters that allow multi-resource references using some kind of list value representation. .. seealso:: - :meth:`_get_acl` for :term:`Effective Resolution` of over multiple :term:`Resource` references. - :meth:`effective_permissions` for :term:`Effective Resolution` of a single :term:`Resource`. :returns: One or many tuple of reference resource (target/parent), and explicit match status of the corresponding resource (True/False) .. py:method:: permission_requested() -> magpie.typedefs.PermissionRequested Defines how to interpret the incoming request into :class:`Permission` definitions for the given service. Each service must implement its own definition. The method must specifically define how to convert generic request path, query, etc. elements into permissions that match the service and its children resources. If ``None`` is returned, the :term:`ACL` will effectively be resolved to denied access. Otherwise, one or more returned :class:`Permission` will indicate which permissions should be looked for to resolve the :term:`ACL` of the authenticated user and its groups. If the request cannot be parsed for any reason to retrieve needed parameters (e.g.: Bad Request), the :exception:`HTTPBadRequest` can be raised to indicate specifically the cause, which will help :class:`magpie.adapter.magpieowssecurity.MagpieOWSSecurity` create a better response with the relevant error details. .. py:class:: ServiceWPS(service: magpie.models.Service, request: pyramid.request.Request) Bases: :py:obj:`ServiceOWS` Service that represents a ``Web Processing Service`` endpoint. Initialize the service. :param service: Base service resource that must be handled by this service implementation. :param request: Active request to handle requested resources, permissions and effective access. The request can be omitted if basic service definition details are to be retrieved. It is mandatory for any ``requested`` or ``effective`` component that should be resolved. .. py:attribute:: service_type :value: 'wps' .. py:attribute:: permissions .. py:attribute:: params_expected :value: ['service', 'request', 'version', 'identifier'] .. py:attribute:: resource_types_permissions .. py:method:: resource_requested() -> Optional[Tuple[magpie.typedefs.ServiceOrResourceType, bool]] Defines how to interpret the incoming request into the targeted :class:`model.Resource` for the given service. Each service must implement its own definition. The expected return value must be either of the following:: - List<(target-resource, target?)> When multiple resources need validation ('target?' as below for each). - (target-resource, True) when the exact resource is found according to request parsing. - (parent-resource, False) when any parent of the resource is found according to request parsing. - None when invalid request or not found resource. The ``parent-resource`` should indicate the *closest* higher-level resource in the hierarchy that would nest the otherwise desired ``target-resource``. The idea behind this is that `Magpie` will be able to resolve the effective recursive scoped permission even if not all corresponding resources were explicitly defined in the database. For example, if the request *would* be interpreted with the following hierarchy after service-specific resolution:: ServiceA Resource1 <== closest *existing* parent resource [Resource2] <== target (according to service/request resolution), but not existing in database A permission defined as Allow/Recursive on ``Resource1`` should *normally* allow access to ``Resource2``. If ``Resource2`` is not present in the database though, it cannot be looked for, and the corresponding ACL cannot be generated. Because the (real) protected service using `Magpie` can have a large and dynamic hierarchy, it is not convenient to enforce perpetual sync between it and its resource representation in `Magpie`. Using ``(parent-resource, False)`` will allow resolution of permission from the closest available parent. .. note:: In case of ``parent-resource`` returned, only `recursive`-scoped permissions will be considered, since the missing ``target-resource`` is the only one that should be checked for `match`-scoped permissions. For this reason, the service-specific implementation should preferably return the explicit `target` resource whenever possible. If the returned resource is ``None``, the ACL will effectively be resolved to denied access. This can be used to indicate failure to retrieve the expected resource or that corresponding resource does not exist. Otherwise, this method implementation should convert any request path, query parameters, etc. into an existing resource. If a list of ``(target-resource, target?)`` is returned, all of those resources should individually perform :term:`Effective Resolution` and should **ALL** simultaneously be granted access to let the request through. This can be used to resolve ambiguous or equivalent parameter combinations from parsing the request, or to validate access to parameters that allow multi-resource references using some kind of list value representation. .. seealso:: - :meth:`_get_acl` for :term:`Effective Resolution` of over multiple :term:`Resource` references. - :meth:`effective_permissions` for :term:`Effective Resolution` of a single :term:`Resource`. :returns: One or many tuple of reference resource (target/parent), and explicit match status of the corresponding resource (True/False) .. py:class:: ServiceBaseWMS(service: magpie.models.Service, request: pyramid.request.Request) Bases: :py:obj:`ServiceOWS` Service that represents basic capabilities of a ``Web Map Service`` endpoint. .. seealso:: https://www.ogc.org/standards/wms (OpenGIS WMS 1.3.0 implementation) Initialize the service. :param service: Base service resource that must be handled by this service implementation. :param request: Active request to handle requested resources, permissions and effective access. The request can be omitted if basic service definition details are to be retrieved. It is mandatory for any ``requested`` or ``effective`` component that should be resolved. .. py:method:: resource_requested() :abstractmethod: Defines how to interpret the incoming request into the targeted :class:`model.Resource` for the given service. Each service must implement its own definition. The expected return value must be either of the following:: - List<(target-resource, target?)> When multiple resources need validation ('target?' as below for each). - (target-resource, True) when the exact resource is found according to request parsing. - (parent-resource, False) when any parent of the resource is found according to request parsing. - None when invalid request or not found resource. The ``parent-resource`` should indicate the *closest* higher-level resource in the hierarchy that would nest the otherwise desired ``target-resource``. The idea behind this is that `Magpie` will be able to resolve the effective recursive scoped permission even if not all corresponding resources were explicitly defined in the database. For example, if the request *would* be interpreted with the following hierarchy after service-specific resolution:: ServiceA Resource1 <== closest *existing* parent resource [Resource2] <== target (according to service/request resolution), but not existing in database A permission defined as Allow/Recursive on ``Resource1`` should *normally* allow access to ``Resource2``. If ``Resource2`` is not present in the database though, it cannot be looked for, and the corresponding ACL cannot be generated. Because the (real) protected service using `Magpie` can have a large and dynamic hierarchy, it is not convenient to enforce perpetual sync between it and its resource representation in `Magpie`. Using ``(parent-resource, False)`` will allow resolution of permission from the closest available parent. .. note:: In case of ``parent-resource`` returned, only `recursive`-scoped permissions will be considered, since the missing ``target-resource`` is the only one that should be checked for `match`-scoped permissions. For this reason, the service-specific implementation should preferably return the explicit `target` resource whenever possible. If the returned resource is ``None``, the ACL will effectively be resolved to denied access. This can be used to indicate failure to retrieve the expected resource or that corresponding resource does not exist. Otherwise, this method implementation should convert any request path, query parameters, etc. into an existing resource. If a list of ``(target-resource, target?)`` is returned, all of those resources should individually perform :term:`Effective Resolution` and should **ALL** simultaneously be granted access to let the request through. This can be used to resolve ambiguous or equivalent parameter combinations from parsing the request, or to validate access to parameters that allow multi-resource references using some kind of list value representation. .. seealso:: - :meth:`_get_acl` for :term:`Effective Resolution` of over multiple :term:`Resource` references. - :meth:`effective_permissions` for :term:`Effective Resolution` of a single :term:`Resource`. :returns: One or many tuple of reference resource (target/parent), and explicit match status of the corresponding resource (True/False) .. py:attribute:: permissions .. py:attribute:: params_expected :value: ['service', 'request', 'version', 'layers', 'layername', 'dataset'] .. py:class:: ServiceNCWMS2(service: magpie.models.Service, request: pyramid.request.Request) Bases: :py:obj:`ServiceBaseWMS` Service that represents a ``Web Map Service`` endpoint with functionalities specific to ``ncWMS2`` . .. seealso:: https://reading-escience-centre.gitbooks.io/ncwms-user-guide/content/04-usage.html Initialize the service. :param service: Base service resource that must be handled by this service implementation. :param request: Active request to handle requested resources, permissions and effective access. The request can be omitted if basic service definition details are to be retrieved. It is mandatory for any ``requested`` or ``effective`` component that should be resolved. .. py:attribute:: service_type :value: 'ncwms' .. py:attribute:: permissions .. py:attribute:: resource_types_permissions .. py:method:: resource_requested() -> Optional[Tuple[magpie.typedefs.ServiceOrResourceType, bool]] Defines how to interpret the incoming request into the targeted :class:`model.Resource` for the given service. Each service must implement its own definition. The expected return value must be either of the following:: - List<(target-resource, target?)> When multiple resources need validation ('target?' as below for each). - (target-resource, True) when the exact resource is found according to request parsing. - (parent-resource, False) when any parent of the resource is found according to request parsing. - None when invalid request or not found resource. The ``parent-resource`` should indicate the *closest* higher-level resource in the hierarchy that would nest the otherwise desired ``target-resource``. The idea behind this is that `Magpie` will be able to resolve the effective recursive scoped permission even if not all corresponding resources were explicitly defined in the database. For example, if the request *would* be interpreted with the following hierarchy after service-specific resolution:: ServiceA Resource1 <== closest *existing* parent resource [Resource2] <== target (according to service/request resolution), but not existing in database A permission defined as Allow/Recursive on ``Resource1`` should *normally* allow access to ``Resource2``. If ``Resource2`` is not present in the database though, it cannot be looked for, and the corresponding ACL cannot be generated. Because the (real) protected service using `Magpie` can have a large and dynamic hierarchy, it is not convenient to enforce perpetual sync between it and its resource representation in `Magpie`. Using ``(parent-resource, False)`` will allow resolution of permission from the closest available parent. .. note:: In case of ``parent-resource`` returned, only `recursive`-scoped permissions will be considered, since the missing ``target-resource`` is the only one that should be checked for `match`-scoped permissions. For this reason, the service-specific implementation should preferably return the explicit `target` resource whenever possible. If the returned resource is ``None``, the ACL will effectively be resolved to denied access. This can be used to indicate failure to retrieve the expected resource or that corresponding resource does not exist. Otherwise, this method implementation should convert any request path, query parameters, etc. into an existing resource. If a list of ``(target-resource, target?)`` is returned, all of those resources should individually perform :term:`Effective Resolution` and should **ALL** simultaneously be granted access to let the request through. This can be used to resolve ambiguous or equivalent parameter combinations from parsing the request, or to validate access to parameters that allow multi-resource references using some kind of list value representation. .. seealso:: - :meth:`_get_acl` for :term:`Effective Resolution` of over multiple :term:`Resource` references. - :meth:`effective_permissions` for :term:`Effective Resolution` of a single :term:`Resource`. :returns: One or many tuple of reference resource (target/parent), and explicit match status of the corresponding resource (True/False) .. py:class:: ServiceGeoserverBase(service: magpie.models.Service, request: pyramid.request.Request) Bases: :py:obj:`ServiceOWS` Provides basic configuration parameters and functionalities shared by `Geoserver` implementations. Initialize the service. :param service: Base service resource that must be handled by this service implementation. :param request: Active request to handle requested resources, permissions and effective access. The request can be omitted if basic service definition details are to be retrieved. It is mandatory for any ``requested`` or ``effective`` component that should be resolved. .. py:property:: service_base :type: magpie.typedefs.Str :abstractmethod: Name of the base :term:`OWS` functionality serviced by `Geoserver`. .. py:property:: resource_scoped :type: bool :classmethod: :abstractmethod: Indicates if the :term:`Service` is allowed to employ scoped :class:`models.Workspace` naming. When allowed, the :class:`models.Workspace` can be inferred from the request parameter defined by :attr:`resource_param` to retrieve scoped name as ``:``. When not allowed, the resource name is left untouched and `Magpie` will not attempt to infer any :class:`models.Workspace` from it. In that case, :class:`models.Workspace` can only be specified in the request path for isolated :term:`Resource` references. .. note:: When this parameter is ``False`` for a given :term:`Service` implementation, children :term:`Resources ` can still be named in a similar ``:`` fashion. The only distinction is that the **full** :term:`Resource` should include this complete definition instead of nesting ```` and ```` into two distinct :term:`Resources `. .. py:property:: resource_multi :type: bool :abstractmethod: Indicates if the :term:`Service` supports multiple simultaneous :term:`Resource` references. When supported, the value retrieved from :attr:`resource_param` can be comma-separated to represent multiple :term:`Resource` of the same nature, which can all be retrieved with the same request. Otherwise, single value only is considered by default. .. note:: Permission modifier :data:`Access.ALLOW` will have to be resolved for all those :term:`Resource` references for the request to be granted access. .. py:property:: resource_param :type: Union[magpie.typedefs.Str, List[magpie.typedefs.Str]] :abstractmethod: Name of the request query parameter(s) to access requested leaf children resource. If a single string is defined, the parameter must be equal to this value (case insensitive). When using a list, any specified name combination will be resolved as the same parameter. .. note:: The resulting parameter(s) are automatically added to :meth:`params_expected` to ensure they are always retrieved in the :attr:`parser` from the request query parameters. .. seealso:: :meth:`resource_param_requested` to obtain the resolved parameter considering any applicable combination. .. py:property:: resource_types_permissions :type: magpie.typedefs.ResourceTypePermissions :abstractmethod: Explicit permissions provided for resources for a given :term:`OWS` implementation. .. py:property:: params_expected :type: List[magpie.typedefs.Str] Specify typical `Geoserver` request query parameters expected for any sub-service implementation. The :attr:`resource_param` is also added to ensure it is always parsed based on the derived implementation. .. py:method:: resource_param_requested() -> List[magpie.typedefs.Str] Obtain the resolved value(s) of the resource query parameter from :term:`OWS` parser applied onto the request. .. py:method:: resource_requested() -> magpie.typedefs.MultiResourceRequested Parse the requested resource down to the applicable :class:`models.Workspace`. .. note:: Further child resource processing must be accomplished by the derived implementation as needed. .. py:class:: ServiceGeoserverWMS(service: magpie.models.Service, request: pyramid.request.Request) Bases: :py:obj:`ServiceGeoserverBase`, :py:obj:`ServiceBaseWMS` Service that represents a `Web Map Service` endpoint with functionalities specific to `Geoserver`. .. seealso:: https://docs.geoserver.org/latest/en/user/services/wms/reference.html Initialize the service. :param service: Base service resource that must be handled by this service implementation. :param request: Active request to handle requested resources, permissions and effective access. The request can be omitted if basic service definition details are to be retrieved. It is mandatory for any ``requested`` or ``effective`` component that should be resolved. .. py:attribute:: service_base :value: 'wms' .. py:attribute:: service_type :value: 'geoserverwms' .. py:attribute:: resource_scoped :value: True .. py:attribute:: resource_multi :value: True .. py:attribute:: resource_param :value: 'layers' .. py:attribute:: permissions .. py:attribute:: child_structure_allowed .. py:attribute:: resource_types_permissions .. py:class:: ServiceAccess(service: magpie.models.Service, request: Optional[pyramid.request.Request]) Bases: :py:obj:`ServiceInterface` Initialize the service. :param service: Base service resource that must be handled by this service implementation. :param request: Active request to handle requested resources, permissions and effective access. The request can be omitted if basic service definition details are to be retrieved. It is mandatory for any ``requested`` or ``effective`` component that should be resolved. .. py:attribute:: service_type :value: 'access' .. py:attribute:: permissions .. py:attribute:: resource_types_permissions .. py:method:: resource_requested() -> magpie.typedefs.TargetResourceRequested Defines how to interpret the incoming request into the targeted :class:`model.Resource` for the given service. Each service must implement its own definition. The expected return value must be either of the following:: - List<(target-resource, target?)> When multiple resources need validation ('target?' as below for each). - (target-resource, True) when the exact resource is found according to request parsing. - (parent-resource, False) when any parent of the resource is found according to request parsing. - None when invalid request or not found resource. The ``parent-resource`` should indicate the *closest* higher-level resource in the hierarchy that would nest the otherwise desired ``target-resource``. The idea behind this is that `Magpie` will be able to resolve the effective recursive scoped permission even if not all corresponding resources were explicitly defined in the database. For example, if the request *would* be interpreted with the following hierarchy after service-specific resolution:: ServiceA Resource1 <== closest *existing* parent resource [Resource2] <== target (according to service/request resolution), but not existing in database A permission defined as Allow/Recursive on ``Resource1`` should *normally* allow access to ``Resource2``. If ``Resource2`` is not present in the database though, it cannot be looked for, and the corresponding ACL cannot be generated. Because the (real) protected service using `Magpie` can have a large and dynamic hierarchy, it is not convenient to enforce perpetual sync between it and its resource representation in `Magpie`. Using ``(parent-resource, False)`` will allow resolution of permission from the closest available parent. .. note:: In case of ``parent-resource`` returned, only `recursive`-scoped permissions will be considered, since the missing ``target-resource`` is the only one that should be checked for `match`-scoped permissions. For this reason, the service-specific implementation should preferably return the explicit `target` resource whenever possible. If the returned resource is ``None``, the ACL will effectively be resolved to denied access. This can be used to indicate failure to retrieve the expected resource or that corresponding resource does not exist. Otherwise, this method implementation should convert any request path, query parameters, etc. into an existing resource. If a list of ``(target-resource, target?)`` is returned, all of those resources should individually perform :term:`Effective Resolution` and should **ALL** simultaneously be granted access to let the request through. This can be used to resolve ambiguous or equivalent parameter combinations from parsing the request, or to validate access to parameters that allow multi-resource references using some kind of list value representation. .. seealso:: - :meth:`_get_acl` for :term:`Effective Resolution` of over multiple :term:`Resource` references. - :meth:`effective_permissions` for :term:`Effective Resolution` of a single :term:`Resource`. :returns: One or many tuple of reference resource (target/parent), and explicit match status of the corresponding resource (True/False) .. py:method:: permission_requested() -> magpie.typedefs.PermissionRequested Defines how to interpret the incoming request into :class:`Permission` definitions for the given service. Each service must implement its own definition. The method must specifically define how to convert generic request path, query, etc. elements into permissions that match the service and its children resources. If ``None`` is returned, the :term:`ACL` will effectively be resolved to denied access. Otherwise, one or more returned :class:`Permission` will indicate which permissions should be looked for to resolve the :term:`ACL` of the authenticated user and its groups. If the request cannot be parsed for any reason to retrieve needed parameters (e.g.: Bad Request), the :exception:`HTTPBadRequest` can be raised to indicate specifically the cause, which will help :class:`magpie.adapter.magpieowssecurity.MagpieOWSSecurity` create a better response with the relevant error details. .. py:class:: ServiceAPI(service: magpie.models.Service, request: Optional[pyramid.request.Request]) Bases: :py:obj:`ServiceInterface` Service that provides resources per individual request path segments. Initialize the service. :param service: Base service resource that must be handled by this service implementation. :param request: Active request to handle requested resources, permissions and effective access. The request can be omitted if basic service definition details are to be retrieved. It is mandatory for any ``requested`` or ``effective`` component that should be resolved. .. py:attribute:: service_type :value: 'api' .. py:attribute:: permissions .. py:attribute:: resource_types_permissions .. py:attribute:: child_structure_allowed .. py:method:: resource_requested() -> magpie.typedefs.TargetResourceRequested Defines how to interpret the incoming request into the targeted :class:`model.Resource` for the given service. Each service must implement its own definition. The expected return value must be either of the following:: - List<(target-resource, target?)> When multiple resources need validation ('target?' as below for each). - (target-resource, True) when the exact resource is found according to request parsing. - (parent-resource, False) when any parent of the resource is found according to request parsing. - None when invalid request or not found resource. The ``parent-resource`` should indicate the *closest* higher-level resource in the hierarchy that would nest the otherwise desired ``target-resource``. The idea behind this is that `Magpie` will be able to resolve the effective recursive scoped permission even if not all corresponding resources were explicitly defined in the database. For example, if the request *would* be interpreted with the following hierarchy after service-specific resolution:: ServiceA Resource1 <== closest *existing* parent resource [Resource2] <== target (according to service/request resolution), but not existing in database A permission defined as Allow/Recursive on ``Resource1`` should *normally* allow access to ``Resource2``. If ``Resource2`` is not present in the database though, it cannot be looked for, and the corresponding ACL cannot be generated. Because the (real) protected service using `Magpie` can have a large and dynamic hierarchy, it is not convenient to enforce perpetual sync between it and its resource representation in `Magpie`. Using ``(parent-resource, False)`` will allow resolution of permission from the closest available parent. .. note:: In case of ``parent-resource`` returned, only `recursive`-scoped permissions will be considered, since the missing ``target-resource`` is the only one that should be checked for `match`-scoped permissions. For this reason, the service-specific implementation should preferably return the explicit `target` resource whenever possible. If the returned resource is ``None``, the ACL will effectively be resolved to denied access. This can be used to indicate failure to retrieve the expected resource or that corresponding resource does not exist. Otherwise, this method implementation should convert any request path, query parameters, etc. into an existing resource. If a list of ``(target-resource, target?)`` is returned, all of those resources should individually perform :term:`Effective Resolution` and should **ALL** simultaneously be granted access to let the request through. This can be used to resolve ambiguous or equivalent parameter combinations from parsing the request, or to validate access to parameters that allow multi-resource references using some kind of list value representation. .. seealso:: - :meth:`_get_acl` for :term:`Effective Resolution` of over multiple :term:`Resource` references. - :meth:`effective_permissions` for :term:`Effective Resolution` of a single :term:`Resource`. :returns: One or many tuple of reference resource (target/parent), and explicit match status of the corresponding resource (True/False) .. py:method:: permission_requested() -> magpie.typedefs.PermissionRequested Defines how to interpret the incoming request into :class:`Permission` definitions for the given service. Each service must implement its own definition. The method must specifically define how to convert generic request path, query, etc. elements into permissions that match the service and its children resources. If ``None`` is returned, the :term:`ACL` will effectively be resolved to denied access. Otherwise, one or more returned :class:`Permission` will indicate which permissions should be looked for to resolve the :term:`ACL` of the authenticated user and its groups. If the request cannot be parsed for any reason to retrieve needed parameters (e.g.: Bad Request), the :exception:`HTTPBadRequest` can be raised to indicate specifically the cause, which will help :class:`magpie.adapter.magpieowssecurity.MagpieOWSSecurity` create a better response with the relevant error details. .. py:class:: ServiceWFS(service: magpie.models.Service, request: pyramid.request.Request) Bases: :py:obj:`ServiceOWS` Service that represents a `Web Feature Service` endpoint. .. seealso:: https://www.ogc.org/standards/wfs (OpenGIS WFS 2.0.0 implementation) Initialize the service. :param service: Base service resource that must be handled by this service implementation. :param request: Active request to handle requested resources, permissions and effective access. The request can be omitted if basic service definition details are to be retrieved. It is mandatory for any ``requested`` or ``effective`` component that should be resolved. .. py:attribute:: service_type :value: 'wfs' .. py:attribute:: permissions .. py:attribute:: params_expected :value: ['service', 'request', 'version', 'typenames'] .. py:attribute:: resource_types_permissions .. py:method:: resource_requested() -> magpie.typedefs.TargetResourceRequested Defines how to interpret the incoming request into the targeted :class:`model.Resource` for the given service. Each service must implement its own definition. The expected return value must be either of the following:: - List<(target-resource, target?)> When multiple resources need validation ('target?' as below for each). - (target-resource, True) when the exact resource is found according to request parsing. - (parent-resource, False) when any parent of the resource is found according to request parsing. - None when invalid request or not found resource. The ``parent-resource`` should indicate the *closest* higher-level resource in the hierarchy that would nest the otherwise desired ``target-resource``. The idea behind this is that `Magpie` will be able to resolve the effective recursive scoped permission even if not all corresponding resources were explicitly defined in the database. For example, if the request *would* be interpreted with the following hierarchy after service-specific resolution:: ServiceA Resource1 <== closest *existing* parent resource [Resource2] <== target (according to service/request resolution), but not existing in database A permission defined as Allow/Recursive on ``Resource1`` should *normally* allow access to ``Resource2``. If ``Resource2`` is not present in the database though, it cannot be looked for, and the corresponding ACL cannot be generated. Because the (real) protected service using `Magpie` can have a large and dynamic hierarchy, it is not convenient to enforce perpetual sync between it and its resource representation in `Magpie`. Using ``(parent-resource, False)`` will allow resolution of permission from the closest available parent. .. note:: In case of ``parent-resource`` returned, only `recursive`-scoped permissions will be considered, since the missing ``target-resource`` is the only one that should be checked for `match`-scoped permissions. For this reason, the service-specific implementation should preferably return the explicit `target` resource whenever possible. If the returned resource is ``None``, the ACL will effectively be resolved to denied access. This can be used to indicate failure to retrieve the expected resource or that corresponding resource does not exist. Otherwise, this method implementation should convert any request path, query parameters, etc. into an existing resource. If a list of ``(target-resource, target?)`` is returned, all of those resources should individually perform :term:`Effective Resolution` and should **ALL** simultaneously be granted access to let the request through. This can be used to resolve ambiguous or equivalent parameter combinations from parsing the request, or to validate access to parameters that allow multi-resource references using some kind of list value representation. .. seealso:: - :meth:`_get_acl` for :term:`Effective Resolution` of over multiple :term:`Resource` references. - :meth:`effective_permissions` for :term:`Effective Resolution` of a single :term:`Resource`. :returns: One or many tuple of reference resource (target/parent), and explicit match status of the corresponding resource (True/False) .. py:class:: ServiceGeoserverWFS(service: magpie.models.Service, request: pyramid.request.Request) Bases: :py:obj:`ServiceGeoserverBase`, :py:obj:`ServiceWFS` Service that represents a `Web Feature Service` endpoint with functionalities specific to `Geoserver`. .. seealso:: https://docs.geoserver.org/latest/en/user/services/wfs/reference.html Initialize the service. :param service: Base service resource that must be handled by this service implementation. :param request: Active request to handle requested resources, permissions and effective access. The request can be omitted if basic service definition details are to be retrieved. It is mandatory for any ``requested`` or ``effective`` component that should be resolved. .. py:attribute:: service_base :value: 'wfs' .. py:attribute:: service_type :value: 'geoserverwfs' .. py:attribute:: resource_scoped :value: True .. py:attribute:: resource_multi :value: True .. py:attribute:: resource_param :value: ['typenames', 'typename'] .. py:attribute:: permissions .. py:attribute:: resource_types_permissions .. py:attribute:: child_structure_allowed .. py:class:: ServiceTHREDDS(service: magpie.models.Service, request: Optional[pyramid.request.Request]) Bases: :py:obj:`ServiceInterface` Service that represents a `THREDDS Data Server` endpoint. Initialize the service. :param service: Base service resource that must be handled by this service implementation. :param request: Active request to handle requested resources, permissions and effective access. The request can be omitted if basic service definition details are to be retrieved. It is mandatory for any ``requested`` or ``effective`` component that should be resolved. .. py:attribute:: service_type :value: 'thredds' .. py:attribute:: permissions .. py:attribute:: resource_types_permissions .. py:attribute:: child_structure_allowed .. py:attribute:: configurable :value: True .. py:method:: get_config() -> magpie.typedefs.ServiceConfiguration Obtains the custom configuration of the registered service. .. py:method:: get_path_parts() -> Optional[List[magpie.typedefs.Str]] .. py:method:: is_match(value: magpie.typedefs.Str, pattern: magpie.typedefs.Str) -> Optional[magpie.typedefs.Str] :staticmethod: .. py:method:: resource_requested() -> magpie.typedefs.TargetResourceRequested Defines how to interpret the incoming request into the targeted :class:`model.Resource` for the given service. Each service must implement its own definition. The expected return value must be either of the following:: - List<(target-resource, target?)> When multiple resources need validation ('target?' as below for each). - (target-resource, True) when the exact resource is found according to request parsing. - (parent-resource, False) when any parent of the resource is found according to request parsing. - None when invalid request or not found resource. The ``parent-resource`` should indicate the *closest* higher-level resource in the hierarchy that would nest the otherwise desired ``target-resource``. The idea behind this is that `Magpie` will be able to resolve the effective recursive scoped permission even if not all corresponding resources were explicitly defined in the database. For example, if the request *would* be interpreted with the following hierarchy after service-specific resolution:: ServiceA Resource1 <== closest *existing* parent resource [Resource2] <== target (according to service/request resolution), but not existing in database A permission defined as Allow/Recursive on ``Resource1`` should *normally* allow access to ``Resource2``. If ``Resource2`` is not present in the database though, it cannot be looked for, and the corresponding ACL cannot be generated. Because the (real) protected service using `Magpie` can have a large and dynamic hierarchy, it is not convenient to enforce perpetual sync between it and its resource representation in `Magpie`. Using ``(parent-resource, False)`` will allow resolution of permission from the closest available parent. .. note:: In case of ``parent-resource`` returned, only `recursive`-scoped permissions will be considered, since the missing ``target-resource`` is the only one that should be checked for `match`-scoped permissions. For this reason, the service-specific implementation should preferably return the explicit `target` resource whenever possible. If the returned resource is ``None``, the ACL will effectively be resolved to denied access. This can be used to indicate failure to retrieve the expected resource or that corresponding resource does not exist. Otherwise, this method implementation should convert any request path, query parameters, etc. into an existing resource. If a list of ``(target-resource, target?)`` is returned, all of those resources should individually perform :term:`Effective Resolution` and should **ALL** simultaneously be granted access to let the request through. This can be used to resolve ambiguous or equivalent parameter combinations from parsing the request, or to validate access to parameters that allow multi-resource references using some kind of list value representation. .. seealso:: - :meth:`_get_acl` for :term:`Effective Resolution` of over multiple :term:`Resource` references. - :meth:`effective_permissions` for :term:`Effective Resolution` of a single :term:`Resource`. :returns: One or many tuple of reference resource (target/parent), and explicit match status of the corresponding resource (True/False) .. py:method:: permission_requested() -> magpie.typedefs.PermissionRequested Defines how to interpret the incoming request into :class:`Permission` definitions for the given service. Each service must implement its own definition. The method must specifically define how to convert generic request path, query, etc. elements into permissions that match the service and its children resources. If ``None`` is returned, the :term:`ACL` will effectively be resolved to denied access. Otherwise, one or more returned :class:`Permission` will indicate which permissions should be looked for to resolve the :term:`ACL` of the authenticated user and its groups. If the request cannot be parsed for any reason to retrieve needed parameters (e.g.: Bad Request), the :exception:`HTTPBadRequest` can be raised to indicate specifically the cause, which will help :class:`magpie.adapter.magpieowssecurity.MagpieOWSSecurity` create a better response with the relevant error details. .. py:class:: ServiceGeoserverWPS(service: magpie.models.Service, request: pyramid.request.Request) Bases: :py:obj:`ServiceGeoserverBase`, :py:obj:`ServiceWPS` Service that represents a `Web Processing Service` under a `Geoserver` instance. Initialize the service. :param service: Base service resource that must be handled by this service implementation. :param request: Active request to handle requested resources, permissions and effective access. The request can be omitted if basic service definition details are to be retrieved. It is mandatory for any ``requested`` or ``effective`` component that should be resolved. .. py:attribute:: service_base :value: 'wps' .. py:attribute:: service_type :value: 'geoserverwps' .. py:attribute:: resource_scoped :value: False .. py:attribute:: resource_multi :value: True .. py:attribute:: resource_param :value: 'identifier' .. py:attribute:: resource_types_permissions .. py:attribute:: child_structure_allowed .. py:class:: ServiceGeoserverAPI(service: magpie.models.Service, request: Optional[pyramid.request.Request]) Bases: :py:obj:`ServiceAPI`, :py:obj:`ServiceGeoserverBase` Service that represents a generic `REST API` under a `Geoserver` instance. Initialize the service. :param service: Base service resource that must be handled by this service implementation. :param request: Active request to handle requested resources, permissions and effective access. The request can be omitted if basic service definition details are to be retrieved. It is mandatory for any ``requested`` or ``effective`` component that should be resolved. .. py:attribute:: service_base :value: 'api' .. py:attribute:: service_type :value: 'geoserverapi' .. py:attribute:: resource_scoped :value: False .. py:attribute:: resource_multi :value: False .. py:attribute:: resource_param :value: None .. py:attribute:: resource_types_permissions .. py:class:: ServiceGeoserver(service: magpie.models.Service, request: Optional[pyramid.request.Request]) Bases: :py:obj:`ServiceGeoserverBase` Service that encapsulates the multiple :term:`OWS` endpoints from `Geoserver` services. .. seealso:: https://docs.geoserver.org/stable/en/user/services/index.html Initialize the service. :param service: Base service resource that must be handled by this service implementation. :param request: Active request to handle requested resources, permissions and effective access. The request can be omitted if basic service definition details are to be retrieved. It is mandatory for any ``requested`` or ``effective`` component that should be resolved. .. py:attribute:: service_base :value: None .. py:attribute:: service_type :value: 'geoserver' .. py:attribute:: service_map .. py:property:: service_supported :type: Set[Type[ServiceGeoserverBase]] .. py:attribute:: child_structure_allowed Allowed children resource structure for `Geoserver`. .. note:: In the context of `Geoserver`, `WPS` are applied on available resources (`WFS`, `WMS`, etc.). For this reason, the :class:`models.Process` also needs to be scoped under :class:`models.Workspace` in order to grant access to those resources to work on them, but the :class:`models.Workspace` name **MUST** be in the path (i.e.: ``identifier=:`` request parameter does not work). Without the :class:`models.Workspace` scope in the path, ``identifier`` parameter fails to be resolved by `Geoserver`, as if it was unspecified. Attribute :attr:`ServiceGeoserverWPS.resource_scoped` controls the behaviour of splitting the defined :attr:`resource_param` into :class:`models.Workspace` and child components. .. note:: The :class:`models.Route` is allowed at the root of the service and for any nested :class:`models.Route` resource to support various endpoints such as the ``/web`` user interface, or the REST interface for the new generation of `OGC API` services typically on ``/ogc/{features|maps|processes}`` endpoints. No special logic is applied for the different services nested under those endpoints. All of them are handled as *typical* RESTful APIs, for which permissions and appropriate sub-paths should be defined accordingly with their respective specifications and schema. .. py:attribute:: configurable :value: True .. py:method:: get_config() -> magpie.typedefs.ServiceConfiguration Obtain the configuration defining which :term:`OWS` services are enabled under this instance. Should provide a mapping of all :term:`OWS` service type names to enabled boolean status. .. py:property:: params_expected :type: List[magpie.typedefs.Str] Specify typical `Geoserver` request query parameters expected for any sub-service implementation. The :attr:`resource_param` is also added to ensure it is always parsed based on the derived implementation. .. py:property:: permissions :type: List[magpie.permissions.Permission] .. py:property:: resource_types_permissions :type: magpie.typedefs.ResourceTypePermissions Explicit permissions provided for resources for a given :term:`OWS` implementation. .. py:method:: _set_request(request: pyramid.request.Request) -> None .. py:method:: service_requested() -> Optional[Type[ServiceGeoserverBase]] Obtain the applicable :term:`OWS` implementation according to parsed request parameters. .. py:property:: resource_scoped :type: bool Indicates if the :term:`Service` is allowed to employ scoped :class:`models.Workspace` naming. When allowed, the :class:`models.Workspace` can be inferred from the request parameter defined by :attr:`resource_param` to retrieve scoped name as ``:``. When not allowed, the resource name is left untouched and `Magpie` will not attempt to infer any :class:`models.Workspace` from it. In that case, :class:`models.Workspace` can only be specified in the request path for isolated :term:`Resource` references. .. note:: When this parameter is ``False`` for a given :term:`Service` implementation, children :term:`Resources ` can still be named in a similar ``:`` fashion. The only distinction is that the **full** :term:`Resource` should include this complete definition instead of nesting ```` and ```` into two distinct :term:`Resources `. .. py:property:: resource_multi :type: bool Indicates if the :term:`Service` supports multiple simultaneous :term:`Resource` references. When supported, the value retrieved from :attr:`resource_param` can be comma-separated to represent multiple :term:`Resource` of the same nature, which can all be retrieved with the same request. Otherwise, single value only is considered by default. .. note:: Permission modifier :data:`Access.ALLOW` will have to be resolved for all those :term:`Resource` references for the request to be granted access. .. py:property:: resource_param :type: Union[magpie.typedefs.Str, List[magpie.typedefs.Str]] Name of the request query parameter(s) to access requested leaf children resource. If a single string is defined, the parameter must be equal to this value (case insensitive). When using a list, any specified name combination will be resolved as the same parameter. .. note:: The resulting parameter(s) are automatically added to :meth:`params_expected` to ensure they are always retrieved in the :attr:`parser` from the request query parameters. .. seealso:: :meth:`resource_param_requested` to obtain the resolved parameter considering any applicable combination. .. py:method:: resource_requested() -> magpie.typedefs.MultiResourceRequested Parse the requested resource down to the applicable :class:`models.Workspace`. .. note:: Further child resource processing must be accomplished by the derived implementation as needed. .. py:method:: permission_requested() -> magpie.typedefs.PermissionRequested Defines how to interpret the incoming request into :class:`Permission` definitions for the given service. Each service must implement its own definition. The method must specifically define how to convert generic request path, query, etc. elements into permissions that match the service and its children resources. If ``None`` is returned, the :term:`ACL` will effectively be resolved to denied access. Otherwise, one or more returned :class:`Permission` will indicate which permissions should be looked for to resolve the :term:`ACL` of the authenticated user and its groups. If the request cannot be parsed for any reason to retrieve needed parameters (e.g.: Bad Request), the :exception:`HTTPBadRequest` can be raised to indicate specifically the cause, which will help :class:`magpie.adapter.magpieowssecurity.MagpieOWSSecurity` create a better response with the relevant error details. .. py:data:: SERVICE_TYPES .. py:data:: SERVICE_TYPE_DICT .. py:function:: service_factory(service: magpie.models.Service, request: pyramid.request.Request) -> ServiceInterface Retrieve the specific service class from the provided database service entry. .. py:function:: get_resource_child_allowed(resource: magpie.typedefs.ServiceOrResourceType) -> bool Verifies if the specified resource allows nesting children resources under it considering its specific type. Makes sure to obtain the specific :class:`Service` or :class:`Resource` implementation to verify children support. If this is not accomplished, the default attribute of base :class:`Service` or :class:`Resource` would erroneously indicate that children are allowed. :param resource: Item for which to verify if children resources are allowed. :return: Whether the resource can nest more resources or not. .. py:function:: invalidate_service(service_name: magpie.typedefs.Str) -> None Invalidates any caching reference to the specified service name.