예제 #1
0
파일: user.py 프로젝트: nphilipp/noggin
def _user_mod(ipa, form, user, details, redirect_to):
    with handle_form_errors(form):
        try:
            updated_user = User(
                ipa.user_mod(user.username, **details, all=True))
        except python_freeipa.exceptions.BadRequest as e:
            if e.message == 'no modifications to be performed':
                raise FormError("non_field_errors", e.message)
            else:
                app.logger.error(
                    f'An error happened while editing user {user.username}: {e.message}'
                )
                raise FormError("non_field_errors", e.message)
        flash(
            Markup(
                f'Profile Updated: <a href=\"{url_for("user", username=user.username)}\">'
                'view your profile</a>'),
            'success',
        )

        messaging.publish(
            UserUpdateV1({
                "msg": {
                    "agent": user.username,
                    "user": user.username,
                    "fields": user.diff_fields(updated_user),
                }
            }))

        return redirect(url_for(redirect_to, username=user.username))
예제 #2
0
def send_registered_message(sender, **kwargs):
    user = sender
    messaging.publish(
        UserCreateV1({"msg": {
            "agent": user.username,
            "user": user.username
        }}))
예제 #3
0
def test_publish():
    with mock.patch("fedora_messaging.api.publish") as api_publish:
        messaging.publish(
            MemberSponsorV1(
                {"msg": {"agent": "dummy", "user": "******", "group": "dummy-group"}}
            )
        )
        api_publish.assert_called_once()
예제 #4
0
def test_publish_with_errors():
    with mock.patch("fedora_messaging.api.publish") as api_publish:
        api_publish.side_effect = fml_exceptions.ConnectionException()
        messaging.publish(
            MemberSponsorV1(
                {"msg": {"agent": "dummy", "user": "******", "group": "dummy-group"}}
            )
        )
        assert api_publish.call_count == 3
예제 #5
0
def test_publish(app_context, mocker):
    api_publish = mocker.patch("fedora_messaging.api.publish")
    messaging.publish(
        MemberSponsorV1({
            "msg": {
                "agent": "dummy",
                "user": "******",
                "group": "dummy-group"
            }
        }))
    api_publish.assert_called_once()
예제 #6
0
def test_publish_disabled(app_context, mocker):
    mocker.patch.dict(current_app.config, {"FEDORA_MESSAGING_ENABLED": False})
    api_publish = mocker.patch("fedora_messaging.api.publish")
    messaging.publish(
        MemberSponsorV1({
            "msg": {
                "agent": "dummy",
                "user": "******",
                "group": "dummy-group"
            }
        }))
    api_publish.assert_not_called()
예제 #7
0
def _validate_change_pw_form(form, username, ipa=None):
    if ipa is None:
        ipa = untouched_ipa_client(current_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.
        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.'))

    if res and res.ok:
        flash(_('Your password has been changed'), 'success')
        current_app.logger.info(f'Password for {username} was changed')
        messaging.publish(
            UserUpdateV1({
                "msg": {
                    "agent": username,
                    "user": username,
                    "fields": ["password"]
                }
            }))
    return res
예제 #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)
예제 #9
0
def group_add_member(ipa, groupname):
    group_or_404(ipa, groupname)
    sponsor_form = AddGroupMemberForm()
    if sponsor_form.validate_on_submit():
        username = sponsor_form.new_member_username.data
        # First make sure the user exists
        try:
            ipa.user_show(username)
        except python_freeipa.exceptions.NotFound:
            flash(
                _('User %(username)s was not found in the system.',
                  username=username),
                'danger',
            )
            return redirect(url_for('.group', groupname=groupname))
        try:
            ipa.group_add_member(a_cn=groupname, o_user=username)
        except python_freeipa.exceptions.ValidationError as e:
            # e.message is a dict that we have to process ourselves for now:
            # https://github.com/opennode/python-freeipa/issues/24
            for error in e.message['member']['user']:
                flash(
                    _(
                        'Unable to add user %(username)s: %(errormessage)s',
                        username=error[0],
                        errormessage=error[1],
                    ),
                    'danger',
                )
            return redirect(url_for('.group', groupname=groupname))

        flash_text = _(
            'You got it! %(username)s has been added to %(groupname)s.',
            username=username,
            groupname=groupname,
        )
        flash(
            flash_text + undo_button(
                url_for(".group_remove_member", groupname=groupname),
                "username",
                username,
                sponsor_form.hidden_tag(),
            ),
            'success',
        )

        messaging.publish(
            MemberSponsorV1({
                "msg": {
                    "agent": g.current_user.username,
                    "user": username,
                    "group": groupname,
                }
            }))

        return redirect(url_for('.group', groupname=groupname))

    for field_errors in sponsor_form.errors.values():
        for error in field_errors:
            flash(error, 'danger')
    return redirect(url_for('.group', groupname=groupname))
예제 #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)