def otp_sync(): form = SyncTokenForm() if form.validate_on_submit(): with handle_form_errors(form): try: ipa = untouched_ipa_client(current_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: current_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)
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(a_uid=username)['result']) except python_freeipa.exceptions.NotFound: raise FormError( "username", _("User %(username)s does not exist", username=username)) token = make_token( { "sub": user.username, "lpc": user.last_password_change }, audience=Audience.password_reset, ) # 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: current_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 current_app.config["DEBUG"]: # pragma: no cover current_app.logger.debug(email) lock.store() current_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)