def test_password_changes_user(client, logged_in_dummy_user): """Verify that password changes""" with fml_testing.mock_sends( UserUpdateV1({ "msg": { "agent": "dummy", "user": "******", "fields": ["password"] } })): result = client.post( '/user/dummy/settings/password', data={ "username": "******", "current_password": "******", "password": "******", "password_confirm": "secretpw", }, ) assert_redirects_with_flash( result, expected_url="/", expected_message="Your password has been changed", expected_category="success", )
def test_user_edit_post(client, logged_in_dummy_user): """Test posting to the user edit page: /user/<username>/settings/profile/""" with fml_testing.mock_sends( UserUpdateV1({ "msg": { "agent": "dummy", "user": "******", "fields": [ 'timezone', 'locale', 'ircnick', 'github', 'gitlab', 'rhbz_mail', ], } })): result = client.post('/user/dummy/settings/profile/', data=POST_CONTENTS) assert_redirects_with_flash( result, expected_url="/user/dummy/settings/profile/", expected_message= "Profile Updated: <a href=\"/user/dummy/\">view your profile</a>", expected_category="success", )
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))
def test_change_post( client, dummy_user, token_for_dummy_user, patched_lock_active, mocker ): logger = mocker.patch.object(current_app._get_current_object(), "logger") with fml_testing.mock_sends( UserUpdateV1( {"msg": {"agent": "dummy", "user": "******", "fields": ["password"]}} ) ): result = client.post( f'/forgot-password/change?token={token_for_dummy_user}', data={"password": "******", "password_confirm": "newpassword"}, ) patched_lock_active["delete"].assert_called() assert_redirects_with_flash( result, expected_url="/", expected_message="Your password has been changed.", expected_category="success", ) # Log message logger.info.assert_called_once() log_msg = logger.info.call_args[0][0] assert "dummy" in log_msg assert "newpassword" not in log_msg
def test_user_edit_post_no_change(client, logged_in_dummy_user): """Test posting to the user edit page and making no change""" # Do it once with fml_testing.mock_sends( UserUpdateV1({ "msg": { "agent": "dummy", "user": "******", "fields": [ 'timezone', 'locale', 'ircnick', 'github', 'gitlab', 'rhbz_mail', ], } })): result = client.post('/user/dummy/settings/profile/', data=POST_CONTENTS) assert result.status_code == 302 # Now do it again with fml_testing.mock_sends(): result = client.post('/user/dummy/settings/profile/', data=POST_CONTENTS) assert_form_generic_error(result, 'no modifications to be performed')
def test_change_post_with_otp(client, dummy_user, dummy_user_with_otp, token_for_dummy_user, patched_lock_active): otp = get_otp(otp_secret_from_uri(dummy_user_with_otp.uri)) with fml_testing.mock_sends( UserUpdateV1({ "msg": { "agent": "dummy", "user": "******", "fields": ["password"] } })): result = client.post( f'/forgot-password/change?token={token_for_dummy_user}', data={ "password": "******", "password_confirm": "newpassword", "otp": otp, }, ) patched_lock_active["delete"].assert_called() assert_redirects_with_flash( result, expected_url="/", expected_message="Your password has been changed.", expected_category="success", )
def test_user_update_v1(self): """ Test UserUpdateV1 The expected message here is: { "body": { "msg": { "agent": "dudemcpants", "user": "******", "fields": ["firstname", "lastname", "gpgkeyid"] } }, "headers": { "fedora_messaging_schema": "noggin.user.update.v1", "fedora_messaging_severity": 20, "sent-at": "2020-03-02T08:53:38+00:00" }, "id": "c795df0d-3a95-47a9-85c4-7fabf3129ddf", "queue": null, "topic": "fas.user.update" } """ msg = UserUpdateV1({ "msg": { "agent": "dudemcpants", "user": "******", "fields": ["firstname", "lastname", "gpgkeyid"], } }) msg.validate() msg_dump = json.loads(message.dumps(msg)) assert msg_dump["body"]["msg"]["agent"] == "dudemcpants" assert msg_dump["body"]["msg"]["user"] == "testuser" assert msg_dump["body"]["msg"]["fields"] == [ "firstname", "lastname", "gpgkeyid", ] assert msg_dump["headers"][ "fedora_messaging_schema"] == "noggin.user.update.v1" assert msg_dump["topic"] == "fas.user.update"
def test_user_settings_keys_post(client, logged_in_dummy_user): """Test posting to the user edit page: /user/<username>/settings/keys/""" with fml_testing.mock_sends( UserUpdateV1({ "msg": { "agent": "dummy", "user": "******", "fields": ['sshpubkeys'] } })): result = client.post('/user/dummy/settings/keys/', data=POST_CONTENTS_KEYS) assert_redirects_with_flash( result, expected_url="/user/dummy/settings/keys/", expected_message= "Profile Updated: <a href=\"/user/dummy/\">view your profile</a>", expected_category="success", )
def test_user_settings_keys_post_no_change(client, logged_in_dummy_user): """Test posting to the user edit page and making no change""" # Do it once with fml_testing.mock_sends( UserUpdateV1({ "msg": { "agent": "dummy", "user": "******", "fields": ['sshpubkeys'] } })): result = client.post('/user/dummy/settings/keys/', data=POST_CONTENTS_KEYS) assert result.status_code == 302 # Now do it again with fml_testing.mock_sends(): result = client.post('/user/dummy/settings/keys/', data=POST_CONTENTS_KEYS) assert_form_generic_error(result, 'no modifications to be performed')
def test_user_settings_keys_post_whitespace(client, logged_in_dummy_user): """Test adding an SSH key with whitespace""" post_contents = POST_CONTENTS_KEYS.copy() post_contents["sshpubkeys-0"] = f" {post_contents['sshpubkeys-0']} \n " with fml_testing.mock_sends( UserUpdateV1({ "msg": { "agent": "dummy", "user": "******", "fields": ['sshpubkeys'] } })): result = client.post('/user/dummy/settings/keys/', data=post_contents) assert_redirects_with_flash( result, expected_url="/user/dummy/settings/keys/", expected_message= "Profile Updated: <a href=\"/user/dummy/\">view your profile</a>", expected_category="success", ) user = User(ipa_admin.user_show("dummy")["result"]) assert user.sshpubkeys == [POST_CONTENTS_KEYS["sshpubkeys-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
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)