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
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
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 _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)
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", )
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)
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)