Example #1
0
    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)
Example #2
0
    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)
Example #3
0
    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)
Example #4
0
    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)
Example #5
0
    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())
Example #6
0
    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())
Example #7
0
    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())
Example #8
0
    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)
Example #9
0
    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)
Example #10
0
    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)
Example #11
0
    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)
Example #12
0
    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)
Example #13
0
    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)
Example #14
0
    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)
Example #15
0
    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))
Example #16
0
    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))
Example #17
0
    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)
Example #18
0
    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)
Example #19
0
    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)
Example #20
0
    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)
Example #21
0
    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)
Example #22
0
    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
Example #23
0
    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)
Example #24
0
    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)
Example #25
0
    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)
Example #26
0
    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)
Example #27
0
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'))
Example #28
0
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'))
Example #29
0
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)
Example #30
0
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)
Example #31
0
    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)
Example #32
0
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)
Example #33
0
    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)
Example #34
0
    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))
Example #35
0
    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)
Example #36
0
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)
Example #37
0
    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)
Example #38
0
    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)
Example #39
0
    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)()
Example #40
0
    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))
Example #41
0
    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)
Example #42
0
    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)
Example #43
0
    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)()
Example #44
0
    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()
Example #45
0
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)
Example #46
0
        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)
Example #47
0
    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)
Example #48
0
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)
Example #49
0
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)
Example #50
0
        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)
Example #51
0
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'))
Example #52
0
    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)
Example #53
0
    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)
Example #54
0
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)
Example #55
0
    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))
Example #56
0
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))
Example #57
0
    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)
Example #58
0
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'))
Example #59
0
    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)
Example #60
0
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)