def test_delete_has_users_new_role(self): """ Test deleting a role if there are still users and a valid new role is given. Expected result: The role is deleted. The role is assigned to all users who had the old role (but not to others). """ # The role that will be deleted. name = 'Administrator' role = Role(name=name) user = User('*****@*****.**', 'Jane Doe') user.role = role db.session.add(role) db.session.add(user) # The new role for the user. new_role = Role(name='Guest') db.session.add(new_role) # Another role and user who will stay untouched. other_role = Role(name='User') other_user = User('*****@*****.**', 'John Doe') other_user.role = other_role db.session.add(other_role) db.session.add(other_user) db.session.commit() role.delete(new_role) loaded_role = Role.load_from_name(name) self.assertIsNone(loaded_role) self.assertEqual(new_role, user.role) self.assertEqual(other_role, other_user.role)
def test_delete_same_role(self): """ Test deleting a role if the same role is given. Expected result: An error is raised. """ name = 'Administrator' role = Role(name=name) user = User('*****@*****.**', 'Jane Doe') user.role = role db.session.add(role) db.session.add(user) db.session.commit() with self.assertRaises(ValueError) as exception_cm: role.delete(role) loaded_role = Role.load_from_name(name) self.assertEqual( 'The new role must not be the role that will be deleted.', str(exception_cm.exception)) self.assertIsNotNone(loaded_role) self.assertEqual(loaded_role, user.role)
def test_login_refresh_get_fresh(self): """ Test accessing the login refresh page with an freshly authorized user. Expected result: The user is redirected to the home page. """ email = '*****@*****.**' password = '******' name = 'John Doe' user_id = 1 user = User(email, name) user.set_password(password) db.session.add(user) db.session.commit() self.assertEqual(user_id, user.id) self.client.post('/user/login', follow_redirects=True, data=dict( email=email, password=password )) response = self.client.get('/user/login/refresh', follow_redirects=True) data = response.get_data(as_text=True) self.assertNotIn('<h1>Confirm Login</h1>', data) self.assertIn('<h1>Dashboard</h1>', data)
def test_user_settings_get(self): """ Test getting the user settings. Expected result: The form is shown with prepopulated data. """ email = '*****@*****.**' name = 'Jane Doe' password = '******' user = User(email, name) user.set_password(password) db.session.add(user) db.session.commit() self.client.post('/user/login', follow_redirects=True, data=dict( email=email, password=password )) response = self.client.get('/user/settings', follow_redirects=True) data = response.get_data(as_text=True) self.assertIn('Settings', data) self.assertNotIn('Your changes have been saved.', data) # Ensure that the user's current language is preselected in the form. self.assertIn(f'<option selected value="{user.settings.language}">', data)
def test_change_email_failure_invalid_token(self): """ Test accessing the change email page with an invalid token. Expected result: The email address is not changed and a 404 error page is shown. """ email = '*****@*****.**' name = 'John Doe' user = User(email, name) db.session.add(user) db.session.commit() user_id = user.id new_email = '*****@*****.**' token_obj = ChangeEmailAddressToken() token_obj.user_id = user_id token_obj.new_email = new_email token = token_obj.create() response = self.client.get(f'/user/change-email-address/invalid-{token}', follow_redirects=True) user = User.load_from_id(user_id) self.assertEqual(404, response.status_code) self.assertEqual(email, user.get_email())
def test_change_email_failure_email_in_use(self): """ Test accessing the change email page with an email address that already is in use by another user. Expected result: The email address is not changed. """ existing_email = '*****@*****.**' existing_name = 'Jane Doe' existing_user = User(existing_email, existing_name) email = '*****@*****.**' name = 'John Doe' user = User(email, name) db.session.add(existing_user) db.session.add(user) db.session.commit() user_id = user.id token_obj = ChangeEmailAddressToken() token_obj.user_id = user_id token_obj.new_email = existing_email token = token_obj.create() response = self.client.get(f'/user/change-email-address/{token}', follow_redirects=True) data = response.get_data(as_text=True) user = User.load_from_id(user_id) self.assertIn('The email address already is in use.', data) self.assertEqual(email, user.get_email())
def test_change_email_success(self): """ Test accessing the change email page with a valid token. Expected result: The email address is changed. """ email = '*****@*****.**' name = 'John Doe' user = User(email, name) db.session.add(user) db.session.commit() user_id = user.id new_email = '*****@*****.**' token_obj = ChangeEmailAddressToken() token_obj.user_id = user_id token_obj.new_email = new_email token = token_obj.create() response = self.client.get(f'/user/change-email-address/{token}', follow_redirects=True) data = response.get_data(as_text=True) user = User.load_from_id(user_id) self.assertIn('Your email address has successfully been changed.', data) self.assertEqual(new_email, user.get_email())
def test_user_profile_get(self): """ Test accessing the user profile page. Expected result: The form is shown with prepopulated data. """ email = '*****@*****.**' name = 'John Doe' password = '******' user = User(email, name) user.set_password(password) db.session.add(user) db.session.commit() self.client.post('/user/login', follow_redirects=True, data=dict( email=email, password=password )) with mail.record_messages() as outgoing: response = self.client.get('/user/profile', follow_redirects=True) data = response.get_data(as_text=True) self.assertEqual(0, len(outgoing)) self.assertIn('User Profile', data) self.assertIn(f'value="{name}"', data) self.assertIn(f'value="{email}"', data) self.assertNotIn('Your changes have been saved.', data) self.assertNotIn('An email has been sent to the new address', data)
def test_user_settings_reset_failure(self): """ Test resetting the user settings via accessing the page with an invalid form. Expected result: The user settings are not reset to their default values. """ email = '*****@*****.**' name = 'Jane Doe' password = '******' user = User(email, name) user.set_password(password) db.session.add(user) language = 'de' user.settings.language = language db.session.commit() self.assertEqual(language, user.settings.language) self.client.post('/user/login', follow_redirects=True, data=dict( email=email, password=password )) response = self.client.post('/user/settings/reset', follow_redirects=True, data=dict()) data = response.get_data(as_text=True) self.assertNotIn('The settings have been set to their default values.', data) self.assertIn('Einstellungen', data) self.assertNotIn('Your changes have been saved.', data) self.assertEqual(language, user.settings.language)
def test_login_refresh_post_valid_password(self, mock_login_fresh: MagicMock): """ Test refreshing the login with a valid password. Expected result: The refresh home page is shown, the login is refreshed. """ mock_login_fresh.return_value = False email = '*****@*****.**' password = '******' name = 'John Doe' user_id = 1 user = User(email, name) user.set_password(password) db.session.add(user) db.session.commit() self.assertEqual(user_id, user.id) self.client.post('/user/login', follow_redirects=True, data=dict( email=email, password=password )) response = self.client.post('/user/login/refresh', follow_redirects=True, data=dict( password=password )) data = response.get_data(as_text=True) self.assertNotIn('<h1>Confirm Login</h1>', data) self.assertNotIn('Invalid password', data) self.assertIn(f'Welcome, {name}!', data) self.assertIn('<h1>Dashboard</h1>', data)
def test_logout_logged_in(self): """ Test logging out with a logged in user. Expected result: The user is logged out, redirected to the home page (and from there to the login page), and shown a success message. """ email = '*****@*****.**' password = '******' name = 'John Doe' user_id = 1 user = User(email, name) user.set_password(password) db.session.add(user) db.session.commit() self.assertEqual(user_id, user.id) self.client.post('/user/login', follow_redirects=True, data=dict( email=email, password=password )) response = self.client.get('/user/logout', follow_redirects=True) data = response.get_data(as_text=True) self.assertIn('You were successfully logged out.', data) self.assertIn('<h1>Log In</h1>', data)
def test_delete_profile_request_failure(self): """ Test requesting the deletion of the user's account with an invalid form. Expected result: No email is sent. """ email = '*****@*****.**' password = '******' name = 'John Doe' user_id = 1 user = User(email, name) user.set_password(password) db.session.add(user) db.session.commit() self.assertEqual(user_id, user.id) self.client.post('/user/login', follow_redirects=True, data=dict( email=email, password=password )) with mail.record_messages() as outgoing: response = self.client.post('/user/delete', follow_redirects=True, data=dict()) data = response.get_data(as_text=True) self.assertEqual(0, len(outgoing)) self.assertNotIn('An email has been sent to your email address.', data) self.assertNotIn('to delete your user profile.', data) self.assertIn('<h1>User Profile</h1>', data)
def test_index_logged_in(self): """ Test accessing the dashboard with a logged in user. Expected result: The user is shown the dashboard. """ email = '*****@*****.**' password = '******' name = 'John Doe' user_id = 1 user = User(email, name) user.set_password(password) db.session.add(user) db.session.commit() self.assertEqual(user_id, user.id) self.client.post('/user/login', follow_redirects=True, data=dict( email=email, password=password )) response = self.client.get('/', follow_redirects=True) data = response.get_data(as_text=True) self.assertIn('<h1>Dashboard</h1>', data) self.assertNotIn('<h1>Log In</h1>', data)
def test_reset_password_get_logged_in(self): """ Test accessing the password reset form with a user who is logged in, and a valid token. Expected result: The user is redirected to the home page. """ email = '*****@*****.**' password = '******' name = 'John Doe' user_id = 1 user = User(email, name) user.set_password(password) db.session.add(user) db.session.commit() self.assertEqual(user_id, user.id) self.client.post('/user/login', follow_redirects=True, data=dict( email=email, password=password )) token_obj = ResetPasswordToken() token_obj.user_id = user_id token = token_obj.create() response = self.client.get(f'/user/reset-password/{token}', follow_redirects=True) data = response.get_data(as_text=True) self.assertIn('Dashboard', data) self.assertNotIn('The token is invalid.', data) self.assertNotIn('Reset Your Password', data) self.assertNotIn('Your password has successfully been changed.', data)
def test_reset_password_post_failure_invalid_token(self): """ Test posting to the password reset form with an anonymous user, an invalid token, and a valid form. Expected result: The password is not updated and the user is shown a 404 error page. """ email = '*****@*****.**' password = '******' name = 'John Doe' user_id = 1 user = User(email, name) user.set_password(password) db.session.add(user) db.session.commit() self.assertEqual(user_id, user.id) new_password = '******' response = self.client.post('/user/reset-password/just-some-token', follow_redirects=True, data=dict( password=new_password, password_confirmation=new_password )) data = response.get_data(as_text=True) self.assertEqual(404, response.status_code) self.assertNotIn('Your password has successfully been changed.', data) self.assertFalse(user.check_password(new_password)) self.assertTrue(user.check_password(password))
def test_reset_password_post_failure_invalid_input(self): """ Test posting to the password reset form with an anonymous user, a valid token, and an invalid form. Expected result: The password is not updated and the user is shown the reset password form. """ email = '*****@*****.**' password = '******' name = 'John Doe' user_id = 1 user = User(email, name) user.set_password(password) db.session.add(user) db.session.commit() self.assertEqual(user_id, user.id) token_obj = ResetPasswordToken() token_obj.user_id = user_id token = token_obj.create() new_password = '******' response = self.client.post(f'/user/reset-password/{token}', follow_redirects=True, data=dict( password=new_password, password_confirmation=new_password + 'ghi' )) data = response.get_data(as_text=True) self.assertIn('Reset Your Password', data) self.assertNotIn('The token is invalid.', data) self.assertNotIn('Your password has successfully been changed.', data) self.assertFalse(user.check_password(new_password)) self.assertTrue(user.check_password(password))
def test_init_has_users(self): """ Test that the form is correctly initialized if the role has users. Expected result: The new_role field exists and is initialized with all other roles. """ role = Role(name='Administrator') user = User('*****@*****.**', 'Jane Doe') user.role = role other_role_1 = Role(name='Visitor') other_role_2 = Role(name='Guest') db.session.add(role) db.session.add(user) db.session.add(other_role_1) db.session.add(other_role_2) db.session.commit() # The role choices are ordered by name and skip the role to delete. choices = [ (0, ''), (other_role_2.id, other_role_2.name), (other_role_1.id, other_role_1.name), ] self.assertLess(other_role_1.id, other_role_2.id) self.assertListEqual([user], role.users.all()) form = RoleDeleteForm(role) self.assertIsNotNone(form.new_role) self.assertListEqual(choices, form.new_role.choices)
def test_reset_password_request_logged_in(self): """ Test accessing the password reset request form with a user who is logged in. Expected result: The user is redirected to the home page. """ email = '*****@*****.**' password = '******' name = 'John Doe' user_id = 1 user = User(email, name) user.set_password(password) db.session.add(user) db.session.commit() self.assertEqual(user_id, user.id) self.client.post('/user/login', follow_redirects=True, data=dict( email=email, password=password )) response = self.client.get('/user/reset-password', follow_redirects=True) data = response.get_data(as_text=True) self.assertIn('Dashboard', data) self.assertNotIn('Forgot Your Password?', data)
def test_delete_profile_failure(self): """ Test deleting the account with an invalid token. Expected result: The account is not deleted. """ email = '*****@*****.**' password = '******' name = 'John Doe' user_id = 1 user = User(email, name) user.set_password(password) db.session.add(user) db.session.commit() self.assertEqual(user_id, user.id) self.client.post('/user/login', follow_redirects=True, data=dict( email=email, password=password )) response = self.client.get('/user/delete/invalid-token', follow_redirects=True) data = response.get_data(as_text=True) self.assertEqual(404, response.status_code) self.assertIsNotNone(User.load_from_id(user_id)) self.assertNotIn('Your user profile and all data linked to it have been deleted.', data)
def test_user_settings_post(self): """ Test posting to the user settings page. Expected result: The form is shown with the new data, the language is updated. """ email = '*****@*****.**' name = 'Jane Doe' password = '******' user = User(email, name) user.set_password(password) db.session.add(user) db.session.commit() self.assertEqual('en', user.settings.language) self.client.post('/user/login', follow_redirects=True, data=dict( email=email, password=password )) new_language = 'de' response = self.client.post('/user/settings', follow_redirects=True, data=dict( language=new_language, )) data = response.get_data(as_text=True) self.assertNotIn('Settings', data) self.assertNotIn('Your changes have been saved.', data) self.assertIn('Einstellungen', data) self.assertIn('Deine Änderungen wurden gespeichert.', data) self.assertEqual(new_language, user.settings.language)
def test_login_post_failure(self): """ Test logging in with invalid credentials. Expected result: The login fails and the user stays on the login page. """ email = '*****@*****.**' password = '******' name = 'John Doe' user_id = 1 user = User(email, name) user.set_password(password) db.session.add(user) db.session.commit() self.assertEqual(user_id, user.id) response = self.client.post('/user/login', follow_redirects=True, data=dict( email=email, password='******' + password )) data = response.get_data(as_text=True) self.assertIn('<h1>Log In</h1>', data) self.assertIn('Invalid email address or password.', data) self.assertNotIn('<h1>Dashboard</h1>', data)
def create_user(email: str, name: str, password: str, role: Optional[Role] = None) -> User: """ Create a user with the given parameters. If a role is given, assign the role to the user. Commit this user to the DB. :param email: The email address of the user. :param name: The name of the user. :param password: The password of the user. :param role: The role for the user. Defaults to `None`. :return: The created user. """ user = User(email, name) user.set_password(password) if role: user.role = role db.session.add(user) db.session.commit() return user
def test_reset_password_request_post_existing_user(self): """ Test accessing the password reset request form via POST with an existing user. Expected result: The password reset mail is sent to the user and a message is displayed. """ email = '*****@*****.**' password = '******' name = 'John Doe' user_id = 1 user = User(email, name) user.set_password(password) db.session.add(user) db.session.commit() self.assertEqual(user_id, user.id) with mail.record_messages() as outgoing: response = self.client.post('/user/reset-password', follow_redirects=True, data=dict( email=email )) data = response.get_data(as_text=True) self.assertEqual(1, len(outgoing)) self.assertIn('Reset Your Password', outgoing[0].subject) self.assertIn('<h1>Log In</h1>', data) self.assertIn('An email has been sent to the specified address.', data)
def test_delete_profile_success(self): """ Test deleting the account with a valid token. Expected result: The account is successfully deleted. """ email = '*****@*****.**' password = '******' name = 'John Doe' user_id = 1 user = User(email, name) user.set_password(password) db.session.add(user) db.session.commit() self.assertEqual(user_id, user.id) self.client.post('/user/login', follow_redirects=True, data=dict( email=email, password=password )) token_obj = DeleteAccountToken() token_obj.user_id = user.id token = token_obj.create() response = self.client.get('/user/delete/' + token, follow_redirects=True) data = response.get_data(as_text=True) self.assertIsNone(User.load_from_id(user_id)) self.assertIn('Your user profile and all data linked to it have been deleted.', data)
def test_login_post_success(self): """ Test logging in with valid credentials. Expected result: The login succeeds, the user is redirected to the home page and greeted. """ email = '*****@*****.**' password = '******' name = 'John Doe' user_id = 1 user = User(email, name) user.set_password(password) db.session.add(user) db.session.commit() self.assertEqual(user_id, user.id) response = self.client.post('/user/login', follow_redirects=True, data=dict( email=email, password=password )) data = response.get_data(as_text=True) self.assertNotIn('<h1>Log In</h1>', data) self.assertIn('<h1>Dashboard</h1>', data) self.assertIn(f'Welcome, {name}!', data)
def logout() -> str: """ Log out the user and redirect them to the homepage. :return: The HTML response. """ if current_user.is_authenticated: User.logout() flash(_('You were successfully logged out.')) return redirect(url_for('main.index'))
def logout() -> ResponseType: """ Log the user out and redirect them to the homepage. :return: The response for this view. """ if current_user.is_authenticated: User.logout() flash(_('You were successfully logged out.')) return redirect(url_for('main.index'))
def login_refresh() -> ResponseType: """ Show a form to refresh a user's login after their login has become stale. :return: The response for this view. """ if login_fresh(): return redirect(url_for('main.index')) form = LoginRefreshForm() if form.validate_on_submit(): user = User.refresh_login(form.password.data) if user: # Login refresh succeeded. flash(_('Welcome, %(name)s!', name=user.name)) next_page = get_next_page() return redirect(next_page) flash(_('Invalid password.'), 'error') return render_template('userprofile/login.html', title=_('Confirm Login'), form=form)
def user_settings(user_id: int) -> ResponseType: """ Show and process a form to edit a user's settings. :param user_id: The ID of the user whose settings will be managed. :return: The response for this view. """ user = User.load_from_id(user_id) if user is None: abort(404) settings = user.settings settings_form = UserSettingsForm(obj=settings) if settings_form.validate_on_submit(): # Get the data from the form and save it. settings.language = settings_form.language.data db.session.commit() # Refresh the language in case the current user is editing themselves and they have changed their language. refresh() flash(_('Your changes have been saved.')) return redirect(url_for('.user_settings', user_id=user_id)) reset_form = UserSettingsResetForm() return render_template('administration/user_settings.html', user=user, settings_form=settings_form, reset_form=reset_form)
def test_change_email_failure_email_in_use(self): """ Test accessing the change email page with an email address that already is in use by another user. Expected result: The email address is not changed. """ existing_email = '*****@*****.**' existing_name = 'Jane Doe' self.create_user(email=existing_email, name=existing_name, password='******') email = '*****@*****.**' name = 'John Doe' user = self.create_user(email=email, name=name, password='******') token_obj = ChangeEmailAddressToken() token_obj.user_id = user.id token_obj.new_email = existing_email token = token_obj.create() data = self.get(f'/user/change-email-address/{token}') user = User.load_from_id(user.id) self.assertIn('The email address already is in use.', data) self.assertEqual(email, user.email)
def login() -> ResponseType: """ Show a login form to the user. If they submitted the login form, try to log them in and redirect them to the homepage. :return: The response for this view. """ form = LoginForm() if form.validate_on_submit(): # Try to login the user. user = User.login(email=form.email.data, password=form.password.data, remember_me=form.remember_me.data) if user: # Login succeeded. flash(_('Welcome, %(name)s!', name=user.name)) next_page = get_next_page() return redirect(next_page) # Login failed. Just show the login form again. flash(_('Invalid email address or password.'), 'error') return render_template('userprofile/login.html', title=_('Log In'), form=form)
def test_call_email_of_current_user(self): """ Test the validator on a field with the current user's email address. Expected result: No error is raised. """ class UniqueEmailForm(FlaskForm): email = StringField('Email') # Create a test user. name = 'John Doe' email = '*****@*****.**' user = User(email, name) db.session.add(user) db.session.commit() # Log in the test user. login_user(user) form = UniqueEmailForm() form.email.data = email validator = UniqueEmail() # noinspection PyNoneFunctionAssignment validation = validator(form, form.email) self.assertIsNone(validation)
def test_user_profile_post_only_name(self): """ Test posting to the user profile page with only the name changed. Expected result: The form is shown with the new data. The user's name is changed, everything else is not. """ email = '*****@*****.**' name = 'John Doe' password = '******' user = self.create_and_login_user(email=email, name=name, password=password) new_name = 'Jane Doe' with mail.record_messages() as outgoing: data = self.post('/user/profile', data=dict(name=new_name, email=email)) self.assertEqual(0, len(outgoing)) self.assertIn('User Profile', data) self.assertIn(f'value="{new_name}"', data) self.assertIn(f'value="{email}"', data) self.assertIn('Your changes have been saved.', data) self.assertNotIn('An email has been sent to the new address', data) user = User.load_from_id(user.id) self.assertEqual(new_name, user.name) self.assertEqual(email, user.email) self.assertTrue(user.check_password(password))
def test_call_email_of_different_user(self): """ Test the validator on a field with a different user's email address. Expected result: An error is raised. """ class UniqueEmailForm(FlaskForm): email = StringField('Email') # Create a test user. name = 'John Doe' email = '*****@*****.**' user = User(email, name) db.session.add(user) db.session.commit() message = 'Another user already claims this email address.' form = UniqueEmailForm() form.email.data = email validator = UniqueEmail() with self.assertRaises(ValidationError) as thrown_message: # noinspection PyNoneFunctionAssignment validation = validator(form, form.email) self.assertIsNone(validation) self.assertEqual(message, thrown_message)
def reset_password_request() -> str: """ Show a form to request resetting the password and process it upon submission. :return: The HTML response. """ form = EmailForm() if form.validate_on_submit(): user = User.load_from_email(form.email.data) if user is not None: token = user.send_password_reset_email() else: # Create a fake token to get the validity. token = ChangeEmailAddressToken() validity = token.get_validity(in_minutes=True) # Display a success message even if the specified address does not belong to a user account. Otherwise, # infiltrators could deduce if an account exists and use this information for attacks. flash(_('An email has been sent to the specified address. Please be aware that the included link for resetting \ the password is only valid for %(validity)d minutes.', validity=validity)) return redirect(url_for('userprofile.login')) return render_template('userprofile/reset_password_request.html', title=_('Forgot Your Password?'), form=form)
def test_change_email_failure_invalid_token(self): """ Test accessing the change email page with an invalid token. Expected result: The email address is not changed and a 404 error page is shown. """ email = '*****@*****.**' user = self.create_user(email=email, name='John Doe', password='******') new_email = '*****@*****.**' token_obj = ChangeEmailAddressToken() token_obj.user_id = user.id token_obj.new_email = new_email token = token_obj.create() data = self.get(f'/user/change-email-address/invalid-{token}', expected_status=404) user = User.load_from_id(user.id) self.assertNotIn('Your email address has successfully been changed.', data) self.assertEqual(email, user.email)
def test_permission_required_one_of_has_permission(self): """ Test the `permission_required` decorator if the user has one of the requested permission, but not all. Expected result: The decorated view function is returned. """ email = '*****@*****.**' name = 'Jane Doe' password = '******' user = User(email, name) user.set_password(password) user.role = Role('Administrator') user.role.permissions = Permission.EditRole db.session.add(user) db.session.commit() user.login(email, password) self.assertTrue(user.role.has_permission(Permission.EditRole)) self.assertFalse(user.role.has_permission(Permission.EditUser)) view_function = permission_required_one_of( Permission.EditRole, Permission.EditUser)(self.view_function) response = view_function() self.assertEqual(self.view_function(), response)
def test_permission_required_all_not_all_permissions(self): """ Test the `permission_required_all` decorator if the user does not have all the requested permissions. Expected result: The request is aborted with an error 403. """ email = '*****@*****.**' name = 'Jane Doe' password = '******' user = User(email, name) user.set_password(password) user.role = Role('Administrator') user.role.permissions = Permission.EditRole db.session.add(user) db.session.commit() user.login(email, password) self.assertTrue(user.role.has_permission(Permission.EditRole)) self.assertFalse(user.role.has_permission(Permission.EditUser)) with self.assertRaises(Forbidden): permission_required_all(Permission.EditRole, Permission.EditUser)(self.view_function)()
def test_user_profile_post_name_and_password_and_email(self): """ Test posting to the user profile page with the name, the password, and the email changed. Expected result: The form is shown with the new data. The user's name and password are changed, the email is not, but a mail has been sent to the new address. """ email = '*****@*****.**' name = 'John Doe' password = '******' user = User(email, name) user.set_password(password + '!') with mail.record_messages() as outgoing: user.set_password(password) self.assertEqual(1, len(outgoing)) self.assertIn('Your Password Has Been Changed', outgoing[0].subject) db.session.add(user) db.session.commit() user_id = user.id self.client.post('/user/login', follow_redirects=True, data=dict( email=email, password=password )) new_name = 'Jane Doe' new_password = '******' new_email = '*****@*****.**' with mail.record_messages() as outgoing: response = self.client.post('/user/profile', follow_redirects=True, data=dict( name=new_name, email=new_email, password=new_password, password_confirmation=new_password )) data = response.get_data(as_text=True) self.assertEqual(2, len(outgoing)) self.assertIn('Change Your Email Address', outgoing[1].subject) self.assertEqual([new_email], outgoing[1].recipients) self.assertIn('User Profile', data) self.assertIn(f'value="{new_name}"', data) self.assertIn(f'value="{email}"', data) self.assertIn('Your changes have been saved.', data) self.assertIn('An email has been sent to the new address', data) user = User.load_from_id(user_id) self.assertEqual(new_name, user.name) self.assertEqual(email, user.get_email()) self.assertTrue(user.check_password(new_password))
def test_permission_required_one_of_has_permission(self): """ Test the `permission_required` decorator if the user has one of the requested permission, but not all. Expected result: The decorated view function is returned. """ email = '*****@*****.**' name = 'Jane Doe' password = '******' user = User(email, name) user.set_password(password) user.role = Role('Administrator') user.role.permissions = Permission.EditRole db.session.add(user) db.session.commit() user.login(email, password) self.assertTrue(user.role.has_permission(Permission.EditRole)) self.assertFalse(user.role.has_permission(Permission.EditUser)) view_function = permission_required_one_of(Permission.EditRole, Permission.EditUser)(self.view_function) response = view_function() self.assertEqual(self.view_function(), response)
def test_get_locale_from_user(self, mock_request: MagicMock): """ Test getting the locale from a user who is logged in. Expected result: The user's preferred language is returned. """ # Mock the best_match() function to ensure it is not called. mock_request.accept_languages = MagicMock() mock_request.accept_languages.best_match = MagicMock(return_value='de') email = '*****@*****.**' name = 'Jane Doe' password = '******' user = User(email, name) user.set_password(password) db.session.add(user) db.session.commit() user.login(email, password) user.settings._language = 'fr' language = get_locale() self.assertEqual(user.settings._language, language) mock_request.accept_languages.best_match.assert_not_called()
def user_edit(user_id: int) -> ResponseType: """ Show and process a form to edit an existing user. :param user_id: The ID of the user. :return: The response for this view. """ user = User.load_from_id(user_id) if user is None: abort(404) return render_template('administration/user_header.html', user=user)
def wrapped_permission_required_all(*args: Any, **kwargs: Any) -> str: """ If the current user does not have all of the requested permissions, abort with error 403. Otherwise, execute the wrapped view function. :param args: The arguments of the view function. :param kwargs: The keyword arguments of the view function. :return: The response of either an 403 error or the wrapped view function. """ if not User.current_user_has_permissions_all(*permissions): return abort(403) return view_function(*args, **kwargs)
def test_delete_has_users_no_role(self): """ Test deleting a role if there are still users and no role is given. Expected result: An error is raised. """ name = 'Administrator' role = Role(name=name) user = User('*****@*****.**', 'Jane Doe') user.role = role db.session.add(role) db.session.add(user) db.session.commit() with self.assertRaises(ValueError) as exception_cm: role.delete() loaded_role = Role.load_from_name(name) self.assertIn('A new role must be given', str(exception_cm.exception)) self.assertIsNotNone(loaded_role) self.assertEqual(loaded_role, user.role)
def users_list() -> ResponseType: """ Show a list of all users. :return: The response for this view. """ # Get a search term and the resulting query. If no search term is given, all users will be returned. search_form = SearchForm() user_query = User.get_search_query(search_term=search_form.search_term) pagination = UserPagination(user_query.order_by(User.name)) title = _('Users') return render_template('administration/users.html', title=title, pagination=pagination, search_form=search_form)
def user_security(user_id: int) -> ResponseType: """ Show options to edit a user's security settings. :param user_id: The ID of the user whose security settings will be managed. :return: The response for this view. """ user = User.load_from_id(user_id) if user is None: abort(404) password_reset_form = UserPasswordResetForm() return render_template('administration/user_security.html', user=user, password_reset_form=password_reset_form)
def wrapped_permission_required(*args: Any, **kwargs: Any) -> ResponseType: """ If the current user does not have the requested permission, abort with error 403. Otherwise, execute the wrapped view function. :param args: The arguments of the view function. :param kwargs: The keyword arguments of the view function. :return: The response of either a 403 error or the wrapped view function. """ if not User.current_user_has_permission(permission): return abort(403) return view_function(*args, **kwargs)
def delete_profile(token: str) -> ResponseType: """ Delete the account of the user given in the token. Then redirect to the home page. :return: The response for this view. """ deleted = User.delete_account_from_token(token) if not deleted: return abort(404) flash( _('Your user profile and all data linked to it have been deleted. We would be happy to see you again in the \ future!')) return redirect(url_for('main.index'))
def test_delete_profile_failure(self): """ Test deleting the account with an invalid token. Expected result: The account is not deleted. """ user = self.create_and_login_user() user_id = user.id data = self.get('/user/delete/invalid-token', expected_status=404) self.assertIsNotNone(User.load_from_id(user_id)) self.assertNotIn( 'Your user profile and all data linked to it have been deleted.', data)
def __call__(self, form: Form, field: Field) -> None: """ Execute the validator. :param form: The form to which the field belongs. :param field: The field to which this validator is attached. :raise ValidationError: In case the validation fails. """ email = field.data if not email: return # If there already is a user with that email address and this user is not the current user, this is an error. user = User.load_from_email(email) if user and user != current_user: raise ValidationError(self.message)
def user_profile() -> ResponseType: """ Show and process a form to edit account details. :return: The response for this view. """ profile_form = UserProfileForm(obj=current_user, email=current_user.email) if profile_form.validate_on_submit(): # Always change the name. user = User.load_from_id(current_user.get_id()) user.name = profile_form.name.data # If the user entered a password, change that as well. if profile_form.password.data: user.set_password(profile_form.password.data) # Write the changes to the database. db.session.commit() # If the email address changed send a confirmation mail to the new address. if user.email != profile_form.email.data: token_validity = user.request_email_address_change( profile_form.email.data) validity = timedelta_to_minutes(token_validity) flash(Markup( _('An email has been sent to the new address %(email)s. Please open the link included in the \ mail within the next %(validity)d minutes to confirm your new email address. Otherwise, \ your email address will not be changed.', email='<em>{email}</em>'.format( email=profile_form.email.data), validity=validity)), category='warning') flash(_('Your changes have been saved.')) return redirect(url_for('userprofile.user_profile')) delete_form = DeleteUserProfileForm() return render_template('userprofile/profile.html', title=_('User Profile'), profile_form=profile_form, delete_form=delete_form)
def test_create_user_without_role(self) -> None: """ Test creating a new user without a role. Expected result: The user is created with the given parameters and without a role. The user is saved on the DB. """ email = '*****@*****.**' name = 'John Doe' password = '******' user = self.create_user(email, name, password) self.assertIsNotNone(user) self.assertEqual(email, user.email) self.assertEqual(name, user.name) self.assertTrue(user.check_password(password)) self.assertIsNone(user.role) self.assertEqual(user, User.load_from_id(user.id))
def user_reset_password(user_id: int) -> ResponseType: """ Process a request to reset a user's password. :param user_id: The ID of the user whose password will be reset. :return: The response for this view. """ user = User.load_from_id(user_id) if user is None: abort(404) password_reset_form = UserPasswordResetForm() if password_reset_form.validate_on_submit(): user.request_password_reset() flash(_('The user\'s password has been reset. An email has been sent to their mail account allowing them to \ set a new password.')) return redirect(url_for('.user_security', user_id=user_id))
def test_logout_required_logged_in(self): """ Test the `logout_required` decorator with a logged-in user. Expected result: The redirect response to the home page is returned. """ email = '*****@*****.**' name = 'John Doe' user = User(email, name) db.session.add(user) db.session.commit() login_user(user) redirect_function = logout_required(self.view_function) response = redirect_function() self.assertIsInstance(response, Response) self.assertEqual(302, response.status_code) self.assertEqual(url_for('main.index'), response.location)
def change_email(token: str) -> ResponseType: """ Change the email address of the user given in the token to the new address specified in the token. Then redirect to the home page. :param token: The change-email token. :return: The response for this view. """ try: changed_email = User.set_email_address_from_token(token) except (EasyJWTError, ValueError): return abort(404) if changed_email: flash(_('Your email address has successfully been changed.')) else: flash(_('The email address already is in use.'), category='error') return redirect(url_for('main.index'))
def test_delete_profile_success(self): """ Test deleting the account with a valid token. Expected result: The account is successfully deleted. """ user = self.create_and_login_user() user_id = user.id token_obj = DeleteAccountToken() token_obj.user_id = user_id token = token_obj.create() data = self.get('/user/delete/' + token) self.assertIsNone(User.load_from_id(user_id)) self.assertIn( 'Your user profile and all data linked to it have been deleted.', data)
def role_users(name: str) -> str: """ Show a list of all users to whom the given role is assigned. :param name: The name of the role. :return: The response for this view. """ role = Role.load_from_name(name) if role is None: abort(404) # Get a search term and the resulting query. If no search term is given, all users will be returned. search_form = SearchForm() user_query = User.get_search_query(query=role.users, search_term=search_form.search_term) # noinspection PyProtectedMember pagination = UserPagination(user_query.order_by(User.name, User._email)) return render_template('administration/role_users.html', role=name, pagination=pagination, search_form=search_form)