Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
registerable.py177 linesDownload Raw Back to flask_security
1"""2    flask_security.registerable3    ~~~~~~~~~~~~~~~~~~~~~~~~~~~4 5    Flask-Security registerable module6 7    :copyright: (c) 2012 by Matt Wright.8    :copyright: (c) 2019-2024 by J. Christopher Wagner (jwag).9    :license: MIT, see LICENSE for more details.10"""11 12from __future__ import annotations13 14import typing as t15 16from flask import current_app17 18from .confirmable import generate_confirmation_link19from .forms import form_errors_munge20from .proxies import _security, _datastore21from .signals import user_registered, user_not_registered22from .utils import (23    config_value as cv,24    do_flash,25    get_message,26    hash_password,27    send_mail,28    url_for_security,29)30 31if t.TYPE_CHECKING:32    from .forms import ConfirmRegisterForm33 34 35def register_user(registration_form):36    """37    Calls datastore to create user, triggers post-registration logic38    (e.g. sending confirmation link, sending registration mail)39    :param registration_form: form with user registration data40    :return: user instance41    """42 43    user_model_kwargs = registration_form.to_dict(only_user=True)44 45    # passwords are not always required -46    # with UNIFIED_SIGNIN and PASSWORD_REQUIRED=False47    if user_model_kwargs["password"]:48        user_model_kwargs["password"] = hash_password(user_model_kwargs["password"])49    user = _datastore.create_user(**user_model_kwargs)50 51    # if they didn't give a password - auto-setup email magic links (if UNIFIED SIGNIN)52    if not user_model_kwargs["password"] and cv("UNIFIED_SIGNIN"):53        _datastore.us_setup_email(user)54 55    confirmation_link, token = None, None56    if _security.confirmable:57        confirmation_link, token = generate_confirmation_link(user)58        do_flash(*get_message("CONFIRM_REGISTRATION", email=user.email))59 60    user_registered.send(61        current_app._get_current_object(),62        _async_wrapper=current_app.ensure_sync,63        user=user,64        confirm_token=token,65        confirmation_token=token,66        form_data=registration_form.to_dict(only_user=False),67    )68 69    if cv("SEND_REGISTER_EMAIL"):70        send_mail(71            cv("EMAIL_SUBJECT_REGISTER"),72            user.email,73            "welcome",74            user=user,75            confirmation_link=confirmation_link,76            confirmation_token=token,77        )78 79    return user80 81 82def register_existing(form: ConfirmRegisterForm) -> bool:83    """84    In the case of generic responses we want to mitigate any possible85    email/username enumeration.86    For an existing email we send an email to that address and tell them they87    are already registered (and provide their username if any).88 89    N.B. This (and forgot and confirm) could be used to DDOS an email by constantly90    issuing requests. One way to mitigate that is to use signals and add specific91    application code.92 93    Returning False means to return normal error messages.94    Returns True if the only 'error' is an existing email/user. In this case we95    simulate a normal registration and email the existing account to inform.96 97    """98 99    if not (100        cv("RETURN_GENERIC_RESPONSES")101        or form.existing_username_user102        or form.existing_email_user103    ):  # pragma: no cover104        return False105 106    # There are 2 classes of error - an existing email/username and non-compliant107    # email/username/password. We want to give the user feedback on a non-compliant108    # input - but not give away whether the email/username is already taken.109    # Since in this case we have an 'existing' entry - we simply Null out those110    # errors.111    # This also means for JSON there is no way to tell if things worked or not.112    fields_to_squash: dict[str, dict[str, str]] = dict()113    if form.existing_email_user:114        fields_to_squash["email"] = dict()115    if hasattr(form, "username") and form.existing_username_user:116        fields_to_squash["username"] = dict()117    form_errors_munge(form, fields_to_squash)118    if form.errors:119        # some other illegal password/username - return an error120        return False121 122    # only errors were existing email/username123    hash_password("not-a-password")  # reduce timing between successful and not.124 125    # Same as is done in register_user()126    if _security.confirmable:127        do_flash(*get_message("CONFIRM_REGISTRATION", email=form.email.data))128 129    # 2 cases:130    # 1) existing email (an already registered account) empty or same username131    # 2) new email with existing username (which corresponds to some OTHER account)132 133    if form.existing_email_user:134        user_not_registered.send(135            current_app._get_current_object(),  # type: ignore136            _async_wrapper=current_app.ensure_sync,137            user=form.existing_email_user,138            existing_email=True,139            existing_username=form.existing_username_user is not None,140            form_data=form.to_dict(only_user=False),141        )142        # Send a nice email saying they are already registered - tell them their143        # existing username if they have one, and suggest how to reset password.144        recovery_link = None145        if _security.recoverable:146            recovery_link = url_for_security("forgot_password", _external=True)147        if cv("SEND_REGISTER_EMAIL"):148            send_mail(149                cv("EMAIL_SUBJECT_REGISTER"),150                form.existing_email_user.email,151                "welcome_existing",152                user=form.existing_email_user,153                recovery_link=recovery_link,154            )155    elif form.existing_username_user:156        # New email, already taken username.157        # Note that we send email to NEW email - so it is possible for a bad-actor158        # to enumerate usernames (slowly).159        user_not_registered.send(160            current_app._get_current_object(),  # type: ignore[attr-defined]161            _async_wrapper=current_app.ensure_sync,162            user=None,163            existing_email=False,164            existing_username=True,165            form_data=form.to_dict(only_user=False),166        )167        if cv("SEND_REGISTER_EMAIL"):168            send_mail(169                cv("EMAIL_SUBJECT_REGISTER"),170                form.email.data,171                "welcome_existing_username",172                email=form.email.data,173                username=form.username.data if hasattr(form, "username") else None,174            )175 176    return True177 
codekingpro/portable-devtools · Team Ai