Пример #1
0
def spamcheck_hook():
    if not current_app.config.get("BASSET_URL"):
        return jsonify({"error": "Spamcheck disabled"}), 501

    data = request.get_json()
    if not data:
        return jsonify({"error": "Bad payload"}), 400

    try:
        token = data["token"]
        status = data["status"]
    except KeyError as e:
        return jsonify({"error": f"Missing key: {e}"}), 400

    try:
        token_data = read_token(token, audience=Audience.spam_check)
    except jwt.ExpiredSignatureError:
        return jsonify({"error": "The token has expired"}), 400
    except jwt.InvalidTokenError as e:
        return jsonify({"error": f"Invalid token: {e}"}), 400

    username = token_data["sub"]

    if status not in ("active", "spamcheck_denied", "spamcheck_manual"):
        return jsonify({"error": f"Invalid status: {status}."}), 400
    result = ipa_admin.stageuser_mod(a_uid=username, fasstatusnote=status)
    user = User(result["result"])

    if status == "active":
        # Send the address validation email
        _send_validation_email(user)

    return jsonify({"status": "success"})
Пример #2
0
def test_ask_post(client, dummy_user, patched_lock):
    with mailer.record_messages() as outbox:
        result = client.post('/forgot-password/ask', data={"username": "******"})
    # Confirmation message
    assert_redirects_with_flash(
        result,
        expected_url="/",
        expected_message=(
            "An email has been sent to your address with instructions on how to reset "
            "your password"
        ),
        expected_category="success",
    )
    # Sent email
    assert len(outbox) == 1
    message = outbox[0]
    assert message.subject == "Password reset procedure"
    assert message.recipients == ["*****@*****.**"]
    # Valid token
    token_match = re.search(r"\?token=([^\s\"']+)", message.body)
    assert token_match is not None
    token = token_match.group(1)
    token_data = read_token(token, audience=Audience.password_reset)
    assert token_data.get("sub") == "dummy"
    assert "lpc" in token_data
    # Lock activated
    patched_lock["store"].assert_called_once()
Пример #3
0
def test_signal_basset(client, mocker, dummy_user):
    mocked_requests = mocker.patch("noggin.signals.requests")
    mocker.patch.dict(current_app.config, {"BASSET_URL": "http://basset.test"})
    user = User(ipa_admin.user_show("dummy")["result"])
    with current_app.test_request_context('/'):
        request_basset_check(user)
    call_args = mocked_requests.post.call_args_list[0]
    assert list(call_args[0]) == ["http://basset.test"]
    json_data = call_args[1]["json"]
    assert json_data["action"] == "fedora.noggin.registration"
    expected_dict = user.as_dict()
    expected_dict["human_name"] = user.commonname
    expected_dict["email"] = user.mail
    assert json_data["data"]["user"] == expected_dict
    assert json_data["data"]["request_headers"] == {"Host": "localhost"}
    assert json_data["data"]["callback"] == "http://localhost/register/spamcheck-hook"
    token = json_data["data"]["token"]
    token_data = read_token(token, audience=Audience.spam_check)
    assert token_data["sub"] == "dummy"
Пример #4
0
def activate_account():
    register_url = f"{url_for('.root')}?tab=register"
    token_string = request.args.get('token')
    if not token_string:
        flash(_('No token provided, please check your email validation link.'),
              'warning')
        return redirect(register_url)

    try:
        token = read_token(token_string, audience=Audience.email_validation)
    except jwt.exceptions.DecodeError:
        flash(_("The token is invalid, please register again."), "warning")
        return redirect(register_url)
    except jwt.exceptions.ExpiredSignatureError:
        flash(_("This token is no longer valid, please register again."),
              "warning")
        return redirect(register_url)

    try:
        user = User(ipa_admin.stageuser_show(token["sub"])["result"])
    except python_freeipa.exceptions.NotFound:
        flash(_("This user cannot be found, please register again."),
              "warning")
        return redirect(register_url)

    token_mail = token["mail"]
    if not user.mail == token_mail:
        current_app.logger.error(
            f'User {user.username} tried to validate a token for address {token_mail} while they '
            f'are registered with address {user.mail}, something fishy may be going on.'
        )
        flash(
            _("The username and the email address don't match the token you used, "
              "please register again."),
            "warning",
        )
        return redirect(register_url)

    form = PasswordSetForm()

    if form.validate_on_submit():
        with handle_form_errors(form):
            password = form.password.data
            # First we activate the stage user
            try:
                ipa_admin.stageuser_activate(user.username)
            except python_freeipa.exceptions.FreeIPAError as e:
                current_app.logger.error(
                    f'An unhandled error {e.__class__.__name__} happened while activating '
                    f'stage user {user.username}: {e.message}')
                raise FormError(
                    "non_field_errors",
                    _("Something went wrong while creating your account, "
                      "please try again later."),
                )
            # User activation succeeded. Send signal.
            user_registered.send(user, request=request._get_current_object())
            # Now we set the password.
            try:
                # First, set it as an admin. This will mark it as expired.
                ipa_admin.user_mod(user.username, userpassword=password)
                # And now we set it again as the user, so it is not expired any more.
                ipa = untouched_ipa_client(current_app)
                ipa.change_password(user.username,
                                    new_password=password,
                                    old_password=password)
            except python_freeipa.exceptions.PWChangePolicyError as e:
                # The user is active but the password does not match the policy.
                # Tell the user what's going to happen.
                flash(
                    _(
                        'Your account has been created, but the password you chose 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',
                )
                return redirect(url_for(".root"))
            except python_freeipa.exceptions.ValidationError as e:
                # for example: invalid username. We don't know which field to link it to
                _handle_registration_validation_error(user.username, e)
            except python_freeipa.exceptions.FreeIPAError as e:
                current_app.logger.error(
                    f'An unhandled error {e.__class__.__name__} happened while changing initial '
                    f'password for user {user.username}: {e.message}')
                # At this point the user has been activated, they can't register again. Send them to
                # the login page with an appropriate warning.
                flash(
                    _(
                        'Your account has been created, but an error occurred while setting your '
                        'password (%(message)s). You may need to change it after logging in.',
                        message=e.message,
                    ),
                    'warning',
                )
                return redirect(url_for(".root"))

            # Try to log them in directly, so they don't have to type their password again.
            try:
                ipa = maybe_ipa_login(current_app, session, user.username,
                                      password)
            except python_freeipa.exceptions.FreeIPAError:
                ipa = None
            if ipa:
                flash(
                    _(
                        'Congratulations, your account has been created! Welcome, %(name)s.',
                        name=user.name,
                    ),
                    'success',
                )
            else:
                # No shortcut for you, you'll have to login properly (maybe the password is
                # expired).
                flash(
                    _('Congratulations, your account has been created! Go ahead and sign in '
                      'to proceed.'),
                    'success',
                )
            return redirect(url_for('.root'))

    return render_template('registration-activation.html',
                           user=user,
                           form=form)
Пример #5
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)