Exemple #1
0
def test_ipa_untouched_client(client):
    with client.session_transaction() as sess:
        ipa = untouched_ipa_client(current_app)
        assert ipa is not None
        assert 'noggin_session' not in sess
        assert 'noggin_ipa_server_hostname' not in sess
        assert 'noggin_username' not in sess
Exemple #2
0
def _validate_change_pw_form(form, username, ipa=None):
    if ipa is None:
        ipa = untouched_ipa_client(app)

    current_password = form.current_password.data
    password = form.password.data
    otp = form.otp.data

    res = None
    try:
        res = ipa.change_password(username, password, current_password, otp)
    except python_freeipa.exceptions.PWChangeInvalidPassword:
        form.current_password.errors.append(
            _("The old password or username is not correct"))
    except python_freeipa.exceptions.PWChangePolicyError as e:
        form.password.errors.append(e.policy_error)
    except python_freeipa.exceptions.FreeIPAError as e:
        # If we made it here, we hit something weird not caught above. We didn't
        # bomb out, but we don't have IPA creds, either.
        app.logger.error(
            f'An unhandled error {e.__class__.__name__} happened while reseting '
            f'the password for user {username}: {e.message}')
        form.errors['non_field_errors'] = [_('Could not change password.')]

    if res and res.ok:
        flash(_('Your password has been changed'), 'success')
        app.logger.info(f'Password for {username} was changed')
    return res
Exemple #3
0
def otp_sync():
    form = SyncTokenForm()
    if form.validate_on_submit():
        with handle_form_errors(form):
            try:
                ipa = untouched_ipa_client(app)
                ipa.otptoken_sync(
                    user=form.username.data,
                    password=form.password.data,
                    first_code=form.first_code.data,
                    second_code=form.second_code.data,
                    token=form.token.data,
                )

                flash(_('Token successfully synchronized'), category='success')
                return redirect(url_for('root'))

            except python_freeipa.exceptions.BadRequest as e:
                app.logger.error(
                    f'An error {e.__class__.__name__} happened while syncing a token for user '
                    f'{form.username}: {e}'
                )
                raise FormError("non_field_errors", e.message)

    return render_template('sync-token.html', sync_form=form)
Exemple #4
0
 def _make_user(name):
     now = datetime.datetime.utcnow().replace(microsecond=0)
     password = f'{name}_password'
     ipa_admin.user_add(
         name,
         name.title(),
         'User',
         f'{name.title()} User',
         mail="*****@*****.**",
         user_password=password,
         login_shell='/bin/bash',
         fascreationtime=f"{now.isoformat()}Z",
     )
     ipa = untouched_ipa_client(app)
     ipa.change_password(name, password, password)
     created_users.append(name)
Exemple #5
0
 def _make_user(name):
     now = datetime.datetime.utcnow().replace(microsecond=0)
     password = f'{name}_password'
     ipa_admin.user_add(
         a_uid=name,
         o_givenname=name.title(),
         o_sn='User',
         o_cn=f'{name.title()} User',
         o_mail=f"{name}@example.com",
         o_userpassword=password,
         o_loginshell='/bin/bash',
         fascreationtime=f"{now.isoformat()}Z",
     )
     ipa = untouched_ipa_client(app)
     ipa.change_password(name, password, password)
     created_users.append(name)
def test_change_recent_password_change(
    client, dummy_user, dummy_group, token_for_dummy_user, patched_lock_active
):
    ipa = untouched_ipa_client(current_app)
    ipa.change_password("dummy", "dummy_password", "dummy_password")
    result = client.get(f'/forgot-password/change?token={token_for_dummy_user}')
    patched_lock_active["delete"].assert_called_once()
    assert_redirects_with_flash(
        result,
        expected_url="/forgot-password/ask",
        expected_message=(
            "Your password has been changed since you requested this token, please "
            "request a new one."
        ),
        expected_category="warning",
    )
Exemple #7
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_obj = PasswordResetToken.from_string(token)
    except jwt.exceptions.DecodeError:
        flash(_("The token is invalid, please request a new one."), "warning")
        return redirect(url_for('forgot_password_ask'))
    username = token_obj.username
    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(username))
    if not token_obj.validate_last_change(user):
        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(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',
            )
            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
            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(username))
            token = PasswordResetToken.from_user(user).as_string()
            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.
            app.logger.error(
                f'An unhandled error {e.__class__.__name__} happened while reseting '
                f'the password for user {username}: {e.message}')
            form.errors['non_field_errors'] = [
                _('Could not change password, please try again.')
            ]
        else:
            lock.delete()
            flash(_('Your password has been changed.'), 'success')
            app.logger.info(
                f"Password for {username} was changed after completing the forgotten "
                f"password process.")
            return redirect(url_for('root'))
    return render_template('forgot-password-change.html',
                           username=username,
                           form=form,
                           token=token)
Exemple #8
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 = EmailValidationToken.from_string(token_string)
    except jwt.exceptions.DecodeError:
        flash(_("The token is invalid, please register again."), "warning")
        return redirect(register_url)
    if not token.is_valid():
        flash(_("This token is no longer valid, please register again."),
              "warning")
        return redirect(register_url)
    try:
        user = User(ipa_admin.stageuser_show(token.username))
    except python_freeipa.exceptions.NotFound:
        flash(_("This user cannot be found, please register again."),
              "warning")
        return redirect(register_url)
    if not user.mail == token.mail:
        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:
                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 activating your account, "
                      "please try again later."),
                )
            # User activation succeeded. Send message.
            messaging.publish(
                UserCreateV1(
                    {"msg": {
                        "agent": user.username,
                        "user": user.username
                    }}))
            # 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(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 activated, 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:
                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 activated, 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(app, session, user.username, password)
            except python_freeipa.exceptions.FreeIPAError:
                ipa = None
            if ipa:
                flash(
                    _(
                        'Congratulations, your account is now active! 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 is now active! Go ahead and sign in '
                      'to proceed.'),
                    'success',
                )
            return redirect(url_for('root'))

    return render_template('registration-activation.html',
                           user=user,
                           form=form)