magpie.api.management.register.register_utils ============================================= .. py:module:: magpie.api.management.register.register_utils Attributes ---------- .. autoapisummary:: magpie.api.management.register.register_utils.CONTENT_TYPE_JSON magpie.api.management.register.register_utils.LOGGER Classes ------- .. autoapisummary:: magpie.api.management.register.register_utils.Group magpie.api.management.register.register_utils.TemporaryToken magpie.api.management.register.register_utils.TokenOperation magpie.api.management.register.register_utils.UserPending magpie.api.management.register.register_utils.UserSearchService magpie.api.management.register.register_utils.UserStatuses magpie.api.management.register.register_utils.BaseViews Functions --------- .. autoapisummary:: magpie.api.management.register.register_utils.get_email_template magpie.api.management.register.register_utils.send_email magpie.api.management.register.register_utils.generate_callback_url magpie.api.management.register.register_utils.webhook_update_error_status magpie.api.management.register.register_utils.get_constant magpie.api.management.register.register_utils.get_logger magpie.api.management.register.register_utils.get_magpie_url magpie.api.management.register.register_utils.handle_temporary_token magpie.api.management.register.register_utils.get_discoverable_groups magpie.api.management.register.register_utils.get_discoverable_group_by_name magpie.api.management.register.register_utils.register_pending_user magpie.api.management.register.register_utils.handle_user_registration_confirmation magpie.api.management.register.register_utils.request_admin_approval magpie.api.management.register.register_utils.handle_user_registration_admin_decision magpie.api.management.register.register_utils.complete_user_registration Module Contents --------------- .. py:function:: get_email_template(template_constant: magpie.typedefs.Str, container: Optional[magpie.typedefs.AnySettingsContainer] = None) -> mako.template.Template Retrieves the template file with email content matching the custom application setting or the corresponding default. Allowed values of :paramref:`template_constant` are: - :envvar:`MAGPIE_GROUP_TERMS_APPROVED_EMAIL_TEMPLATE` - :envvar:`MAGPIE_GROUP_TERMS_SUBMISSION_EMAIL_TEMPLATE` - :envvar:`MAGPIE_USER_REGISTRATION_SUBMISSION_EMAIL_TEMPLATE` - :envvar:`MAGPIE_USER_REGISTRATION_APPROVAL_EMAIL_TEMPLATE` - :envvar:`MAGPIE_USER_REGISTRATION_APPROVED_EMAIL_TEMPLATE` - :envvar:`MAGPIE_USER_REGISTRATION_DECLINED_EMAIL_TEMPLATE` - :envvar:`MAGPIE_USER_REGISTRATION_NOTIFY_EMAIL_TEMPLATE` :raises IOError: if an explicit override value of the requested template cannot be located. :returns: template formatter from the requested template file. .. py:function:: send_email(recipient: magpie.typedefs.Str, container: magpie.typedefs.AnySettingsContainer, template: mako.template.Template, parameters: Optional[TemplateParameters] = None) -> bool Send email notification using provided template and parameters. The preparation steps of the email (retrieve SMTP configuration, setup the SMTP connection, define email content parameters and attempt template generation) will directly raise if invalid as they correspond to incorrect application code or configuration settings. Following step to send the email with the established SMTP connection is caught and logged if raising an exception. This is to allow the calling operation to ignore failing email notification and act accordingly using the resulting email status. :param recipient: Email address of the intended recipient to which the email must be sent. :param template: Mako template used for the email contents. :param container: Any container to retrieve application settings. :param parameters: Parameters to provide for templating email contents. They are applied on top of various defaults values provided to all emails. :raises: any SMTP server configuration, template generator or parameter parsing error for email setup. :returns: success status of the notification email (sent without error, no guarantee of reception). .. py:function:: generate_callback_url(operation: magpie.models.TokenOperation, db_session: sqlalchemy.orm.session.Session, user: Optional[magpie.models.AnyUser] = None, group: Optional[magpie.models.Group] = None) -> magpie.typedefs.Str Generates a callback URL using `Magpie` temporary tokens for use by the webhook implementation. :param operation: targeted operation that employs the callback URL for reference. :param db_session: database session to store the generated temporary token. :param user: user reference associated to the operation as applicable. :param group: group reference associated to the operation as applicable. :return: generated callback URL. .. py:function:: webhook_update_error_status(user_name: magpie.typedefs.Str) -> None Updates the user's status to indicate an error occurred with the webhook requests. .. 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:class:: Group Bases: :py:obj:`ziggurat_foundations.models.group.GroupMixin`, :py:obj:`Base` Mixin for Group model .. py:attribute:: _priority :value: None .. py:method:: get_member_count(db_session: Optional[sqlalchemy.orm.session.Session] = None) -> int .. py:property:: discoverable Indicates if the group is discoverable for users to self-register to it. .. py:property:: terms Text containing the terms and conditions. .. py:property:: priority :type: magpie.typedefs.GroupPriority Sorting priority weight of the group for resolving conflicting permissions. .. py:class:: TemporaryToken(*_, **__) Bases: :py:obj:`ziggurat_foundations.models.base.BaseModel`, :py:obj:`Base` Model that defines a token for temporary URL completion of a given pending operation. .. py:attribute:: __tablename__ :value: 'tmp_tokens' .. py:attribute:: token .. py:attribute:: operation .. py:attribute:: created .. py:attribute:: user_id .. py:attribute:: _user .. py:attribute:: user_pending_id .. py:attribute:: _pending_user .. py:attribute:: group_id .. py:attribute:: group .. py:method:: user() -> AnyUser .. py:method:: url(settings: magpie.typedefs.AnySettingsContainer = None) -> magpie.typedefs.Str .. py:method:: expired() -> bool .. py:method:: by_token(token: Union[magpie.typedefs.Str, sqlalchemy.dialects.postgresql.UUID], db_session: Optional[sqlalchemy.orm.session.Session] = None) -> Optional[TemporaryToken] :staticmethod: .. py:method:: by_user(user: AnyUser, db_session: Optional[sqlalchemy.orm.session.Session] = None) -> Optional[sqlalchemy.orm.query.Query] :staticmethod: .. py:method:: json() -> magpie.typedefs.JSON .. py:class:: TokenOperation Bases: :py:obj:`magpie.utils.ExtendedEnum` Supported operations by the temporary tokens. .. py:attribute:: GROUP_ACCEPT_TERMS :value: 'group-accept-terms' Temporary token associated to an URL endpoint called by a user that accepts the terms and conditions (T&C) to join a particular group. .. py:attribute:: USER_PASSWORD_RESET :value: 'user-password-reset' Temporary token associated to an URL endpoint to request a user password reset. .. py:attribute:: USER_REGISTRATION_CONFIRM_EMAIL :value: 'user-registration-confirm-email' Temporary token associated to a pending user registration that requires email validation by visiting the link. .. py:attribute:: USER_REGISTRATION_ADMIN_APPROVE :value: 'user-registration-admin-approve' Temporary token associated to a pending user registration that will be approved by an administrator when visited. .. py:attribute:: USER_REGISTRATION_ADMIN_DECLINE :value: 'user-registration-admin-decline' Temporary token associated to a pending user registration that will be declined by an administrator when visited. .. py:attribute:: WEBHOOK_USER_STATUS_ERROR :value: 'webhook-user-status-error' Temporary token employed to provide a callback URL that a registered webhook can call following the triggered event to indicate that the corresponding operation resulted into an invalid user status. .. py:class:: UserPending Bases: :py:obj:`Base` Temporary definition of a :class:`User` pending for approval by an administrator. .. py:property:: __tablename__ .. py:property:: id Unique identifier of user. .. py:property:: user_name Unique user name. .. py:property:: user_password Password hash of the user. .. py:property:: email Email of the user. .. py:property:: registered_date Date of user's registration. .. py:property:: status Pending user status is enforced. Avoid error in case the corresponding attribute of :class:`User` was accessed. .. py:property:: groups Pending user is not a member of any group. Avoid error in case this field gets accessed when simultaneously handling :class:`User` and :class`UserPending`. .. py:method:: get_groups_by_status(status: UserGroupStatus, db_session: sqlalchemy.orm.session.Session = None) -> List[magpie.typedefs.Str] Pending user is not a member of any group. Avoid error in case this method gets accessed when simultaneously handling :class:`User` and :class`UserPending`. .. py:method:: upgrade(db_session: Optional[sqlalchemy.orm.session.Session] = None) -> User Upgrades this :class`UserPending` instance to a complete and corresponding :class:`User` definition. Automatically handles instance updates in the database. All relevant :class:`User` metadata is transferred from available :class:`UserPending` details. All operations that should take place during normal :class:`User` creation will take effect, including minimal :class:`Group` membership creation and :term:`Webhook` triggers. This current :class:`UserPending` instance is finally removed and should not be accessed following upgrade. :param db_session: Database connection to use, otherwise retrieved from the user pending object. :returns: created user instance .. py:property:: passwordmanager Employ the same password manager attached to :class:`User` instances from :class:`UserService`. This allows all functionalities of password generation, encryption and comparison to be directly transferable between this pending user until it eventually gets upgraded to a full :class:`User` once validated. .. py:class:: UserSearchService Bases: :py:obj:`ziggurat_foundations.models.services.user.UserService` Extends the :mod:`ziggurat_foundations` :class:`UserService` with additional features provided by `Magpie`. .. note:: For any search result where parameter ``status`` is equal to or contains :attr:`UserStatuses.Pending` combined with any other :class:`UserStatuses` members, or through the *all* representation, the returned iterable could be a mix of both :term:`User` models or only :class:`UserPending`. Therefore, only fields supported by both of those models should be accessed from the result. .. py:method:: by_status(status: Optional[UserStatuses] = None, db_session: Optional[sqlalchemy.orm.session.Session] = None) -> Iterable[AnyUser] :classmethod: Search for appropriate :class:`User` and/or :class:`UserPending` according to specified :class:`UserStatuses`. When the :paramref:`status` is ``None``, *normal* retrieval of all non-pending :class:`User` is executed, as if directly using the :class:`UserService` implementation. Otherwise, a combination of appropriate search criterion is executed based on the :paramref:`status` flags. .. py:method:: by_user_name(user_name: magpie.typedefs.Str, status: Optional[UserStatuses] = None, db_session: Optional[sqlalchemy.orm.session.Session] = None) -> Optional[AnyUser] :classmethod: Retrieves the user matching the given name. Search is always accomplished against :class:`User` table unless :attr:`UserStatuses.Pending` is provided in the :paramref:`status`. If more that one status is provided such that both :class:`UserPending` and :class:`User` could yield results, the :class:`User` is returned first, as there should not be any conflict between those two models. .. py:method:: by_name_or_email(user_name: magpie.typedefs.Str, email: magpie.typedefs.Str, status: Optional[UserStatuses] = None, db_session: Optional[sqlalchemy.orm.session.Session] = None) -> Optional[AnyUser] :classmethod: Retrieves the first matched user by either name or email, whichever comes first. If the :paramref:`status` is provided, search is executed against relevant :class:`User` and/or :class`UserPending` definitions. The :paramref:`user_name` is looked for first across both tables (as needed) and then by :paramref:`email` if not previously matched. .. seealso:: :meth:`by_user_name` :meth:`by_email` :meth:`by_email_and_username` .. py:class:: UserStatuses Bases: :py:obj:`enum.IntFlag`, :py:obj:`magpie.utils.FlexibleNameEnum` Values applicable to :term:`User` statues. Provides allowed values for the ``status`` search query of :class:`User` and :class:`UserPending` entries. Also, defines the possible values of :attr:`User.status` field, omitting :attr:`UserStatuses.Pending` reserved for objects defined by :class:`UserPending`. Initialize self. See help(type(self)) for accurate signature. .. py:attribute:: OK :value: 1 .. py:attribute:: WebhookError :value: 2 .. py:attribute:: Pending :value: 4 .. py:method:: _get_one(status: AnyUserStatus) -> Optional[UserStatuses] :classmethod: .. py:method:: get(status: Union[None, int, magpie.typedefs.Str, UserStatuses, Iterable[None, int, magpie.typedefs.Str, UserStatuses]], default: Optional[UserStatuses] = None) -> Optional[UserStatuses] :classmethod: Obtains the combined flag :class:`UserStatuses` .. py:method:: allowed() -> List[Union[None, int, magpie.typedefs.Str]] :classmethod: Returns all supported representation values that can be mapped to a valid status for :class:`UserSearchService`. .. py:method:: all() -> UserStatuses :classmethod: Representation of all flags combined. .. py:method:: __or__(other: Union[UserStatuses, int]) -> UserStatuses Return self|value. .. py:method:: __and__(other: Union[UserStatuses, int]) -> UserStatuses Return self&value. .. py:method:: __xor__(other: Union[UserStatuses, int]) -> UserStatuses Return self^value. .. py:method:: __iter__() -> Iterable[UserStatuses] .. py:method:: __len__() .. py:class:: BaseViews(request) Bases: :py:obj:`object` Base methods for Magpie UI pages. .. py:attribute:: MAGPIE_FIXED_GROUP_MEMBERSHIPS :value: [] Special :term:`Group` memberships that cannot be edited. .. py:attribute:: MAGPIE_FIXED_GROUP_EDITS :value: [] Special :term:`Group` details that cannot be edited. .. py:attribute:: MAGPIE_FIXED_USERS :value: [] Special :term:`User` details that cannot be edited. .. py:attribute:: MAGPIE_FIXED_USERS_REFS :value: [] Special :term:`User` that cannot have any relationship edited. This includes both :term:`Group` memberships and :term:`Permission` references. .. py:attribute:: MAGPIE_USER_PWD_LOCKED :value: [] Special :term:`User` that *could* self-edit themselves, but is disabled since conflicting with other policies. .. py:attribute:: MAGPIE_USER_PWD_DISABLED :value: [] Special :term:`User` where password cannot be edited (managed by `Magpie` configuration settings). .. py:attribute:: MAGPIE_ANONYMOUS_GROUP :value: None Reference to :py:data:`magpie.constants.MAGPIE_ANONYMOUS_GROUP` for convenience in UI pages. .. py:method:: add_template_data(data: Optional[Dict[magpie.typedefs.Str, Any]] = None) -> Dict[magpie.typedefs.Str, Any] Adds required template data for the 'heading' mako template applied to every UI page. .. py:method:: render(template: magpie.typedefs.Str, data: Optional[Dict[magpie.typedefs.Str, Any]] = None) -> pyramid.response.Response Render the response with an explicit Mako template reference. Views that are decorated by :func:`pyramid.view.view_config` or registered by :meth:`pyramid.config.Configurator.add_view` with a ``renderer`` parameter do not require to call this function as it is auto-resolved with the submitted :paramref:`data`. .. py:data:: CONTENT_TYPE_JSON :value: 'application/json' .. 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_magpie_url(container: Optional[magpie.typedefs.AnySettingsContainer] = None) -> magpie.typedefs.Str Obtains the configured Magpie URL entrypoint based on the various combinations of supported configuration settings. .. seealso:: Documentation section :ref:`config_app_settings` for available setting combinations. :param container: container that provides access to application settings. :return: resolved Magpie URL .. py:data:: LOGGER .. py:function:: handle_temporary_token(tmp_token: magpie.models.TemporaryToken, request: pyramid.request.Request) -> Union[pyramid.httpexceptions.HTTPException, pyramid.response.Response] Handles the operation according to the provided temporary token. :returns: Basic JSON response with successful indicate of correct handling of the token by default. If overridden, can be any HTML rendered response. .. py:function:: get_discoverable_groups(db_session: sqlalchemy.orm.session.Session) -> List[magpie.models.Group] Get all existing group that are marked as publicly discoverable from the database. .. py:function:: get_discoverable_group_by_name(group_name: magpie.typedefs.Str, db_session: sqlalchemy.orm.session.Session) -> magpie.models.Group Obtains the requested discoverable group by name. .. note:: For security reason, an existing group that is **NOT** discoverable will return NotFound instead of Forbidden. Otherwise we give an indication to a potentially non-admin user that *some group* of that name exists. :return: found group matched by name :raises HTTPNotFound: if the group cannot be found or if matched group name is not discoverable. .. py:function:: register_pending_user(user_name: magpie.typedefs.Str, email: magpie.typedefs.Str, password: magpie.typedefs.Str, request: pyramid.request.Request) -> pyramid.httpexceptions.HTTPException Registers a temporary user pending approval. Procedure and validation workflow is similar to normal user creation by an administrator, but employs reduced fields and different target table. Some operations are also simplified as they are not required for pending user. There is also no user creation :term:`Webhook` triggers as :term:`User` doesn't exist yet. .. seealso:: See :func:`magpie.api.management.user.user_utils.create_user` for similarities and distinctions of operations between a *normal* :term:`User` and a :term:`Pending User`. Implements steps (1) and (2) of the user registration procedure. .. seealso:: - See :ref:`proc_user_registration` for the procedure step details. - See :func:`request_admin_approval` and :func:`complete_user_registration` for following steps of the procedure following reception of the confirmation email. :return: HTTP created with relevant details if successful. :raises HTTPException: HTTP error with relevant details upon any failing condition. .. py:function:: handle_user_registration_confirmation(tmp_token: magpie.models.TemporaryToken, request: pyramid.request.Request) -> pyramid.response.Response Applies the appropriate step of the user registration workflow following reception of the confirmation URL visit. Implements steps (3A) and (3B) redirection of the user registration procedure. Generates the appropriate response that will be displayed to the :term:`Pending User` that confirmed its email. .. seealso:: - See :ref:`proc_user_registration` for the procedure step details. - See :ref:`request_admin_approval` for step 3B. - See :ref:`complete_user_registration` for step 5 (from 3A). .. py:function:: request_admin_approval(tmp_token: magpie.models.TemporaryToken, request: pyramid.request.Request) -> None Sends the email to the administrator to approve or refuse the :term:`Pending User` registration. Implements step (3B) of the user registration procedure. .. seealso:: - See :ref:`proc_user_registration` for the procedure step details. .. py:function:: handle_user_registration_admin_decision(tmp_token: magpie.models.TemporaryToken, request: pyramid.request.Request) -> pyramid.response.Response Applies the appropriate operation according to the decision the administrator took for the pending registration. - *approved*: Moves to step (5) - *declined*: Removes the pending user request. Generates the appropriate response that will be displayed to the administrator. Implements step (4) of the user registration procedure. .. seealso:: - See :ref:`proc_user_registration` for the procedure step details. - :func:`complete_user_registration` for step 5 following approval. .. py:function:: complete_user_registration(tmp_token: magpie.models.TemporaryToken, approval_required: bool, request: pyramid.request.Request) -> None Completes the successful user registration following any required validation steps. Generates the :term:`User` from the :term:`Pending User`. Then, sends configured notification emails about successful :term:`User` creation. Implements steps (5) and (6) of the user registration procedure. .. seealso:: - See :ref:`proc_user_registration` for the procedure step details. - :func:`register_pending_user` for initial steps that started the process. - :func:`request_admin_approval` for intermediate steps if approval feature was enabled.