Exemple #1
0
def _send_validation_email(user):
    token = make_token(
        {
            "sub": user.username,
            "mail": user.mail
        },
        audience=Audience.email_validation,
        ttl=current_app.config["ACTIVATION_TOKEN_EXPIRATION"],
    )
    email_context = {"token": token, "user": user}
    email = Message(
        body=render_template("email-validation.txt", **email_context),
        html=render_template("email-validation.html", **email_context),
        recipients=[user.mail],
        subject=_("Verify your email address"),
    )
    if current_app.config["DEBUG"]:  # pragma: no cover
        current_app.logger.debug(email)
    try:
        mailer.send(email)
    except ConnectionRefusedError as e:
        current_app.logger.error(
            f"Impossible to send an address validation email: {e}")
        flash(
            _("We could not send you the address validation email, please retry later"
              ),
            "danger",
        )
Exemple #2
0
def test_spamcheck(client, dummy_stageuser, mocker, spamcheck_status,
                   spamcheck_on):
    user = User(ipa_admin.stageuser_show("dummy")["result"])
    assert user.status_note != spamcheck_status
    token = make_token({"sub": "dummy"}, audience=Audience.spam_check)
    with mailer.record_messages() as outbox:
        response = client.post(
            "/register/spamcheck-hook",
            json={
                "token": token,
                "status": spamcheck_status
            },
        )
    assert response.status_code == 200
    assert response.json == {"status": "success"}
    # Check that the status was changed
    user = User(ipa_admin.stageuser_show("dummy")["result"])
    assert user.status_note == spamcheck_status
    # Sent email
    if spamcheck_status == "active":
        assert len(outbox) == 1
        message = outbox[0]
        assert message.subject == "Verify your email address"
        assert message.recipients == ["*****@*****.**"]
    else:
        assert len(outbox) == 0
Exemple #3
0
def request_basset_check(sender, **kwargs):
    user = sender
    basset_url = current_app.config.get("BASSET_URL")
    if not basset_url:
        return

    token = make_token(
        {"sub": user.username},
        audience=Audience.spam_check,
        ttl=current_app.config["SPAMCHECK_TOKEN_EXPIRATION"],
    )
    user_dict = user.as_dict()
    user_dict["email"] = user_dict["mail"]
    user_dict["human_name"] = user_dict["commonname"]

    response = requests.post(
        basset_url,
        json={
            "action": "fedora.noggin.registration",
            "time": int(time.time()),
            "data": {
                "user": user_dict,
                "request_headers": dict(request.headers),
                "request_ip": request.remote_addr,
                "token": token,
                "callback": url_for('.spamcheck_hook', _external=True),
            },
        },
    )
    if not response.ok:
        current_app.logger.warning(
            "Error requesting a Basset check: "
            f"{response.status_code} {response.reason}: {response.text}")
Exemple #4
0
def token_for_dummy_user(dummy_stageuser):
    return make_token(
        {
            "sub": dummy_stageuser.username,
            "mail": dummy_stageuser.mail
        },
        audience=Audience.email_validation,
        ttl=current_app.config["ACTIVATION_TOKEN_EXPIRATION"],
    )
def token_for_dummy_user(dummy_user):
    user = User(ipa_admin.user_show("dummy")["result"])
    return make_token(
        {
            "sub": user.username,
            "lpc": user.last_password_change
        },
        audience=Audience.password_reset,
    )
Exemple #6
0
def test_spamcheck_wrong_status(client, dummy_user, mocker, spamcheck_on):
    token = make_token({"sub": "dummy"}, audience=Audience.spam_check)
    response = client.post(
        "/register/spamcheck-hook",
        json={
            "token": token,
            "status": "this-is-wrong"
        },
    )
    assert response.status_code == 400
    assert response.json == {"error": "Invalid status: this-is-wrong."}
Exemple #7
0
def test_spamcheck_invalid_token(client, dummy_user, mocker, spamcheck_on):
    token = make_token({"sub": "dummy"}, audience=Audience.email_validation)
    response = client.post(
        "/register/spamcheck-hook",
        json={
            "token": token,
            "status": "active"
        },
    )
    assert response.status_code == 400
    assert response.json["error"] == "Invalid token: Invalid audience"
Exemple #8
0
def test_spamcheck_expired_token(client, dummy_user, mocker, spamcheck_on):
    token = make_token({"sub": "dummy"}, audience=Audience.spam_check, ttl=-1)
    response = client.post(
        "/register/spamcheck-hook",
        json={
            "token": token,
            "status": "active"
        },
    )
    assert response.status_code == 400
    assert response.json == {"error": "The token has expired"}
Exemple #9
0
def test_step_3_invalid_token(client, dummy_stageuser, mocker):
    """Registration activation page with an invalid token"""
    token = make_token(
        {
            "sub": dummy_stageuser.username,
            "mail": dummy_stageuser.mail
        },
        audience=Audience.email_validation,
        ttl=-1,
    )
    result = client.get(f'/register/activate?token={token}')
    assert_redirects_with_flash(
        result,
        expected_url="/?tab=register",
        expected_message=
        "This token is no longer valid, please register again.",
        expected_category="warning",
    )
Exemple #10
0
def forgot_password_change():
    token = request.args.get('token')
    if not token:
        flash('No token provided, please request one.', 'warning')
        return redirect(url_for('.forgot_password_ask'))
    try:
        token_data = read_token(token, audience=Audience.password_reset)
    except jwt.exceptions.DecodeError:
        flash(_("The token is invalid, please request a new one."), "warning")
        return redirect(url_for('.forgot_password_ask'))
    username = token_data["sub"]
    lock = PasswordResetLock(username)
    valid_until = lock.valid_until()
    now = datetime.datetime.now()
    if valid_until is None or now > valid_until:
        lock.delete()
        flash(_("The token has expired, please request a new one."), "warning")
        return redirect(url_for('.forgot_password_ask'))
    user = User(ipa_admin.user_show(a_uid=username)['result'])
    if user.last_password_change != token_data["lpc"]:
        lock.delete()
        flash(
            _("Your password has been changed since you requested this token, please request "
              "a new one."),
            "warning",
        )
        return redirect(url_for('.forgot_password_ask'))

    form = NewPasswordForm()
    if form.validate_on_submit():
        password = form.password.data
        # Generate a random temporary number.
        temp_password = ''.join(
            random.choices(string.ascii_letters + string.digits, k=24))
        try:
            # Force change password to the random password, so that the password is not actually
            # changed to the given one in case the next step fails (because the OTP is wrong for
            # example)
            ipa_admin.user_mod(username, userpassword=temp_password)
            # Change the password as the user, so it's not expired.
            ipa = untouched_ipa_client(current_app)
            ipa.change_password(
                username,
                new_password=password,
                old_password=temp_password,
                otp=form.otp.data,
            )
        except python_freeipa.exceptions.PWChangePolicyError as e:
            lock.delete()
            flash(
                _(
                    'Your password has been changed, but it does not comply with the policy '
                    '(%(policy_error)s) and has thus been set as expired. You will be asked to '
                    'change it after logging in.',
                    policy_error=e.policy_error,
                ),
                'warning',
            )
            current_app.logger.info(
                f"Password for {username} was changed to a non-compliant password after "
                f"completing the forgotten password process.")
            # Send them to the login page, they will have to change their password
            # after login.
            return redirect(url_for('.root'))
        except python_freeipa.exceptions.PWChangeInvalidPassword:
            # The provided OTP was wrong
            current_app.logger.info(
                f"Password for {username} was changed to a random string because "
                f"the OTP token they provided was wrong.")
            # Oh noes, the token is now invalid since the user's password was changed! Let's
            # re-generate a token so they can keep going.
            user = User(ipa_admin.user_show(a_uid=username)['result'])
            token = make_token(
                {
                    "sub": user.username,
                    "lpc": user.last_password_change
                },
                audience=Audience.password_reset,
            )
            form.otp.errors.append(_("Incorrect value."))
        except python_freeipa.exceptions.FreeIPAError as e:
            # If we made it here, we hit something weird not caught above.
            current_app.logger.error(
                f'An unhandled error {e.__class__.__name__} happened while reseting '
                f'the password for user {username}: {e.message}')
            form.non_field_errors.errors.append(
                _('Could not change password, please try again.'))
        else:
            lock.delete()
            flash(_('Your password has been changed.'), 'success')
            current_app.logger.info(
                f"Password for {username} was changed after completing the forgotten "
                f"password process.")
            messaging.publish(
                UserUpdateV1({
                    "msg": {
                        "agent": username,
                        "user": username,
                        "fields": ["password"],
                    }
                }))
            return redirect(url_for('.root'))
    return render_template('forgot-password-change.html',
                           username=username,
                           form=form,
                           token=token)
Exemple #11
0
def forgot_password_ask():
    form = ForgottenPasswordForm()
    if form.validate_on_submit():
        username = form.username.data
        lock = PasswordResetLock(username)
        valid_until = lock.valid_until()
        now = datetime.datetime.now()
        with handle_form_errors(form):
            if valid_until is not None and now < valid_until:
                wait_min = int((valid_until - now).total_seconds() / 60)
                wait_sec = int((valid_until - now).total_seconds() % 60)
                raise FormError(
                    "non_field_errors",
                    _(
                        'You have already requested a password reset, you need to wait '
                        '%(wait_min)s minute(s) and %(wait_sec)s seconds before you can request '
                        'another.',
                        wait_min=wait_min,
                        wait_sec=wait_sec,
                    ),
                )
            try:
                user = User(ipa_admin.user_show(a_uid=username)['result'])
            except python_freeipa.exceptions.NotFound:
                raise FormError(
                    "username",
                    _("User %(username)s does not exist", username=username))
            token = make_token(
                {
                    "sub": user.username,
                    "lpc": user.last_password_change
                },
                audience=Audience.password_reset,
            )
            # Send the email
            email_context = {"token": token, "username": username}
            email = Message(
                body=render_template("forgot-password-email.txt",
                                     **email_context),
                html=render_template("forgot-password-email.html",
                                     **email_context),
                recipients=[user.mail],
                subject="Password reset procedure",
            )
            try:
                mailer.send(email)
            except ConnectionRefusedError as e:
                current_app.logger.error(
                    f"Impossible to send a password reset email: {e}")
                flash(_("We could not send you an email, please retry later"),
                      "danger")
                return redirect(url_for('.root'))
            if current_app.config["DEBUG"]:  # pragma: no cover
                current_app.logger.debug(email)
            lock.store()
            current_app.logger.info(
                f'{username} forgot their password and requested a token')
            flash(
                _('An email has been sent to your address with instructions on how to reset '
                  'your password'),
                "success",
            )
            return redirect(url_for('.root'))
    return render_template('forgot-password-ask.html', form=form)