def root(): ipa = maybe_ipa_session(app, session) username = session.get('noggin_username') if ipa and username: return redirect(url_for('user', username=username)) # Kick any non-authed user back to the login form. activetab = request.args.get("tab", "login") register_form = RegisterUserForm(prefix="register") login_form = LoginUserForm(prefix="login") if login_form.validate_on_submit(): with handle_form_errors(login_form): return handle_login_form(login_form) if register_form.validate_on_submit(): with handle_form_errors(register_form): return handle_register_form(register_form) return render_template( 'index.html', register_form=register_form, login_form=login_form, activetab=activetab, )
def test_handle_form_errors(client): with current_app.test_request_context('/'): form = LoginUserForm() error = FormError("username", "error_message") with mock.patch.object(error, "populate_form") as populate_form: with handle_form_errors(form): raise error populate_form.assert_called_once_with(form)
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(username)) except python_freeipa.exceptions.NotFound: raise FormError( "username", _("User %(username)s does not exist", username=username)) token = PasswordResetToken.from_user(user).as_string() # 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: 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 app.config["DEBUG"]: # pragma: no cover app.logger.debug(email) lock.store() 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)
def _user_mod(ipa, form, username, details, redirect_to): with handle_form_errors(form): try: ipa.user_mod(username, **details) 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 {username}: {e.message}' ) raise FormError("non_field_errors", e.message) flash( Markup( f'Profile Updated: <a href=\"{url_for("user", username=username)}\">' 'view your profile</a>'), 'success', ) return redirect(url_for(redirect_to, username=username))
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)
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)