Exemplo n.º 1
0
def user_settings_otp(ipa, username):
    addotpform = UserSettingsAddOTPForm()
    user = User(user_or_404(ipa, username))
    if addotpform.validate_on_submit():
        try:
            maybe_ipa_login(current_app, session, username,
                            addotpform.password.data)
            result = ipa.otptoken_add(
                o_ipatokenowner=username,
                o_ipatokenotpalgorithm='sha512',
                o_description=addotpform.description.data,
            )['result']

            uri = urlparse(result['uri'])

            # Use the provided description in the token, so it shows up in the user's app instead of
            # the token's UUID
            principal = uri.path.split(":", 1)[0]
            new_uri = uri._replace(
                path=f"{principal.lower()}:{quote(addotpform.description.data)}"
            )
            session['otp_uri'] = new_uri.geturl()
        except python_freeipa.exceptions.InvalidSessionPassword:
            addotpform.password.errors.append(_("Incorrect password"))
        except python_freeipa.exceptions.FreeIPAError as e:
            current_app.logger.error(
                f'An error happened while creating an OTP token for user {username}: {e.message}'
            )
            addotpform.non_field_errors.errors.append(
                _('Cannot create the token.'))
        else:
            return redirect(url_for('.user_settings_otp', username=username))

    otp_uri = session.get('otp_uri')
    session['otp_uri'] = None

    tokens = [
        OTPToken(t)
        for t in ipa.otptoken_find(o_ipatokenowner=username)["result"]
    ]
    tokens.sort(key=lambda t: t.description or "")

    return render_template(
        'user-settings-otp.html',
        addotpform=addotpform,
        user=user,
        activetab="otp",
        tokens=tokens,
        otp_uri=otp_uri,
    )
Exemplo n.º 2
0
def handle_login_form(form):
    username = form.username.data.lower()
    password = form.password.data

    try:
        # This call will set the cookie itself, we don't have to.
        ipa = maybe_ipa_login(current_app, session, username, password)
    except python_freeipa.exceptions.PasswordExpired:
        flash(_('Password expired. Please reset it.'), 'danger')
        return redirect(url_for('.password_reset', username=username))
    except python_freeipa.exceptions.Unauthorized as e:
        raise FormError("non_field_errors", e.message)
    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 logging in user '
            f'{username}: {e.message}')
        raise FormError("non_field_errors",
                        _('Could not log in to the IPA server.'))

    if not ipa:
        current_app.logger.error(
            f'An unhandled situation happened while logging in user {username}: '
            f'could not connect to the IPA server')
        raise FormError("non_field_errors",
                        _('Could not log in to the IPA server.'))

    flash(_('Welcome, %(username)s!', username=username), 'success')
    return redirect(url_for('.user', username=username))
Exemplo n.º 3
0
def logged_in_dummy_user(client, dummy_user):
    with client.session_transaction() as sess:
        ipa = maybe_ipa_login(app, sess, "dummy", "dummy_password")
    yield ipa
    ipa.logout()
    with client.session_transaction() as sess:
        sess.clear()
Exemplo n.º 4
0
def test_ipa_login(client, dummy_user):
    with client.session_transaction() as sess:
        ipa = maybe_ipa_login(current_app, sess, "dummy", "dummy_password")
    assert ipa is not None
    with client.session_transaction() as sess:
        assert sess.get('noggin_session')
        assert sess.get('noggin_ipa_server_hostname') == "ipa.example.com"
        assert sess.get('noggin_username') == "dummy"
        # Test that the session is valid Fernet
        ipa_session = Fernet(current_app.config['FERNET_SECRET']).decrypt(
            sess.get('noggin_session'))
        assert str(ipa_session, 'ascii').startswith("MagBearerToken=")
Exemplo n.º 5
0
def test_with_ipa(client, dummy_user):
    """Test the with_ipa decorator"""
    view = mock.Mock()
    with current_app.test_request_context('/'):
        ipa = maybe_ipa_login(current_app, session, "dummy", "dummy_password")
        wrapped = with_ipa()(view)
        wrapped("arg")
        view.assert_called_once()
        assert "ipa" in view.call_args_list[0][1]
        assert isinstance(view.call_args_list[0][1]["ipa"], ipa.__class__)
        assert "arg" in view.call_args_list[0][0]
        assert "ipa" in g
        assert isinstance(g.ipa, ipa.__class__)
        assert "current_user" in g
        assert g.current_user.username == "dummy"
Exemplo n.º 6
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)
Exemplo n.º 7
0
def user_settings_otp(ipa, username):
    addotpform = UserSettingsAddOTPForm(prefix="add-")
    confirmotpform = UserSettingsConfirmOTPForm(prefix="confirm-")
    user = User(user_or_404(ipa, username))
    secret = None

    if addotpform.validate_on_submit():
        description = addotpform.description.data
        password = addotpform.password.data
        if addotpform.otp.data:
            password += addotpform.otp.data

        try:
            maybe_ipa_login(current_app, session, username, password)
        except python_freeipa.exceptions.InvalidSessionPassword:
            addotpform.password.errors.append(_("Incorrect password"))
        else:
            secret = b32encode(os.urandom(OTP_KEY_LENGTH)).decode('ascii')
            # Prefill the form for the next step
            confirmotpform.process(
                MultiDict({
                    "confirm-secret": secret,
                    "confirm-description": description
                }))
    if confirmotpform.validate_on_submit():
        try:
            ipa.otptoken_add(
                o_ipatokenowner=username,
                o_description=confirmotpform.description.data,
                o_ipatokenotpkey=confirmotpform.secret.data,
            )
        except python_freeipa.exceptions.FreeIPAError as e:
            current_app.logger.error(
                f'An error happened while creating an OTP token for user {username}: {e.message}'
            )
            confirmotpform.non_field_errors.errors.append(
                _('Cannot create the token.'))
        else:
            flash(_('The token has been created.'), "success")
            return redirect(url_for('.user_settings_otp', username=username))

    if confirmotpform.is_submitted():
        # This form is inside the modal. Keep a value in otp_uri or the modal will not open
        # to show the errors.
        secret = confirmotpform.secret.data

    # Compute the token URI
    if secret:
        description = addotpform.description.data or confirmotpform.description.data
        token = TOTP(secret)
        otp_uri = token.provisioning_uri(name=description,
                                         issuer_name=user.krbname)
    else:
        otp_uri = None

    # List existing tokens
    tokens = [
        OTPToken(t)
        for t in ipa.otptoken_find(o_ipatokenowner=username)["result"]
    ]
    tokens.sort(key=lambda t: t.description or "")

    return render_template(
        'user-settings-otp.html',
        addotpform=addotpform,
        confirmotpform=confirmotpform,
        user=user,
        activetab="otp",
        tokens=tokens,
        otp_uri=otp_uri,
    )