Exemple #1
0
    def test_bitwise_xor_success(self):
        """
            Test the bitwise xor method.

            Expected result: The method returns the same result as performing the operation manually.
        """

        expectation = Permission.EditRole
        result = Permission.bitwise_xor(Permission.EditRole)
        self.assertEqual(expectation, result)

        expectation = Permission.EditRole | Permission.EditUser
        result = Permission.bitwise_xor(Permission.EditRole, Permission.EditUser)
        self.assertEqual(expectation, result)

        expectation = Permission.EditRole | Permission.EditUser | Permission.EditGlobalSettings
        result = Permission.bitwise_xor(Permission.EditRole, Permission.EditUser, Permission.EditGlobalSettings)
        self.assertEqual(expectation, result)

        combination_1 = Permission.EditRole | Permission.EditGlobalSettings
        combination_2 = Permission.EditGlobalSettings | Permission.EditRole
        expectation = combination_1 ^ combination_2
        result = Permission.bitwise_xor(combination_1, combination_2)
        self.assertEqual(expectation, result)

        combination_1 = Permission.EditRole | Permission.EditGlobalSettings
        combination_2 = Permission.EditGlobalSettings | Permission.EditRole
        expectation = combination_1 ^ combination_2 ^ Permission.EditUser
        result = Permission.bitwise_xor(combination_1, combination_2, Permission.EditUser)
        self.assertEqual(expectation, result)
Exemple #2
0
    def assert_permission_required_all(self,
                                       url: str,
                                       *permissions: Permission,
                                       method: str = 'GET') -> None:
        """
            Assert that accessing the URL via the given method requires all of the specified permissions and that
            accessing the URL with the permissions is actually possible.

            :param url: The URL to access.
            :param permissions: The permissions that must be required to access the URL.
            :param method: The HTTP method to access the URL by. Defaults to `'GET'`.
        """

        all_permissions = Permission.get_permissions(
            include_empty_permission=True, all_combinations=True)

        #
        allowed_permission = Permission(0)
        for permission in permissions:
            allowed_permission |= permission

        allowed_permissions = {
            p
            for p in all_permissions
            if p.includes_permission(allowed_permission)
        }

        self._assert_permissions(url, allowed_permissions, method)
Exemple #3
0
def create_permission_form(form_class: Type[BasePermissionForm],
                           preset_permissions: Permission,
                           *args: Any,
                           disabled_permissions: Optional[Permission] = None,
                           **kwargs: Any) -> BasePermissionForm:
    """
        Create a form object of the given class with fields to select permissions.

        :param form_class: The *class* of the form of which the object will be created.
        :param preset_permissions: The permissions whose fields will be preselected.
        :param disabled_permissions: The permissions whose state cannot be changed.
        :param args: Further arguments that will be passed into the form constructor.
        :param kwargs: Further keyword arguments that will be passed into the form constructor.
        :return: An object of the given form class, extended with the fields for setting permissions.
        :raise ValueError: If `form_class` is not a subclass of :class:`BasePermissionForm`.
    """
    class ExtendedPermissionForm(form_class):  # type: ignore
        """
            A subclass of the given permission form class to which the permission fields will be added and which will
            be instantiated and returned.
        """

        pass

    # Ensure we have all required functionality.
    if not issubclass(form_class, BasePermissionForm):
        raise ValueError('The form does not inherit from BasePermissionForm')

    if not disabled_permissions:
        disabled_permissions = Permission(0)

    # Insert the permission fields.
    permissions = Permission.get_permissions()
    for permission in sorted(permissions, key=lambda p: p.title):

        # Preset the permission field if necessary.
        default = preset_permissions.includes_permission(permission)

        # Disable the field if necessary.
        disabled = disabled_permissions.includes_permission(permission)

        # Create the field.
        field_name = permission.name.lower()
        field = BooleanField(permission.title,
                             description=permission.description,
                             default=default,
                             render_kw={'disabled': disabled})

        # Add the field to the form and remember which permission it belongs to.
        setattr(ExtendedPermissionForm, field_name, field)
        ExtendedPermissionForm.permission_fields[field_name] = permission

    extended_form = ExtendedPermissionForm(*args, **kwargs)
    extended_form.order_fields()

    return extended_form
Exemple #4
0
    def test_bitwise_or_failure(self):
        """
            Test the bitwise or method with an invalid permission.

            Expected result: The method raises an error.
        """
        with self.assertRaises(ValueError) as exception_cm:
            # noinspection PyTypeChecker
            Permission.bitwise_or(Permission.EditGlobalSettings, None, Permission.EditRole)
            self.assertEqual('None is not a valid permission', str(exception_cm.exception))
Exemple #5
0
    def test_has_permissions_one_of_empty_permission(self):
        """
            Test the has_permissions_one_of() method with the empty permission.

            Expected result: `False`.
        """

        role = Role('Administrator')
        db.session.add(role)
        db.session.commit()

        self.assertEqual(0, role._permissions)
        self.assertTrue(role.has_permissions_one_of(Permission(0)))

        role.permissions = Permission.EditRole
        self.assertTrue(role.has_permissions_one_of(Permission(0)))
Exemple #6
0
    def _assert_permissions(self, url: str,
                            allowed_permissions: Set[Permission],
                            method: str) -> None:
        """
            Assert that the given URL can be accessed with the allowed permissions, but not with any other permission.

            :param url: The URL to access.
            :param allowed_permissions: List of permissions that must be able to access the URL.
            :param method: The HTTP method to access the URL by.
        """

        for allowed_permission in allowed_permissions:
            self._assert_permission_grants_access(url, allowed_permission,
                                                  method)

        all_permissions = Permission.get_permissions(
            include_empty_permission=True, all_combinations=True)
        prohibited_permissions = {
            permission
            for permission in all_permissions
            if permission not in allowed_permissions
        }
        for prohibited_permission in prohibited_permissions:
            self._assert_permission_does_not_grant_access(
                url, prohibited_permission, method)
Exemple #7
0
    def test_selected_permissions_get(self):
        """
            Test getting the set permissions from the form.

            Expected result: The `permissions` property returns all permissions that are selected in the form.
        """
        class PermissionTestForm(BasePermissionForm):
            """
                A simple form to which permission fields will be added.
            """

            pass

        # The preselected permissions.
        form = create_permission_form(PermissionTestForm, self.permissions)
        self.assertEqual(self.permissions, form.permissions)

        # Changed permissions with some selected.
        form = create_permission_form(PermissionTestForm, self.permissions)
        form.edituser.data = True
        form.editglobalsettings.data = False
        self.assertEqual(Permission.EditRole | Permission.EditUser,
                         form.permissions)

        # No selection of permissions.
        form = create_permission_form(PermissionTestForm, self.permissions)
        form.editglobalsettings.data = False
        form.editrole.data = False
        self.assertEqual(Permission(0), form.permissions)
Exemple #8
0
    def test_selected_permissions_get_no_fields(self):
        """
            Test getting the tested permissions if no permission fields exist.

            Expected result: The empty permission is returned without any errors.
        """

        form = BasePermissionForm()
        self.assertEqual(Permission(0), form.permissions)
Exemple #9
0
    def test_permissions_get_none(self):
        """
            Test getting the permission enum member if there are no permissions.

            Expected result: The empty permission.
        """

        role = Role('Administrator')

        self.assertIsNone(role._permissions)
        self.assertEqual(Permission(0), role.permissions)
Exemple #10
0
    def permissions(self) -> Permission:
        """
            The (combination of) permissions that represent the permissions this role has.

            Assigning permissions will overwrite all existing permissions.

            If this role is the only one allowed to edit roles, but the new permissions do not include the permission to
            edit roles, this permission will automatically be added to the role to prevent cases where there is no role
            that can edit roles.

            :return: An enum member of :class:`app.userprofile.Permission` representing the role's permissions.
        """

        # For some reason, PyCharm thinks, self._permissions has type Permission...
        # noinspection PyTypeChecker
        if self._permissions is None or self._permissions < 0:
            return Permission(0)

        # noinspection PyTypeChecker
        return Permission(self._permissions)
Exemple #11
0
    def test_permissions_get_less_than_zero(self):
        """
            Test getting the permission enum member if the permission integer is < 0.

            Expected result: The empty permission.
        """

        role = Role('Administrator')
        role._permissions = -1

        self.assertEqual(Permission(0), role.permissions)
Exemple #12
0
    def load_roles_with_permissions_all(*permissions: Permission) -> List['Role']:
        """
            Get all roles that have all the given permissions.

            :param permissions: The permissions that the roles must have.
            :return: A list of roles that have all of the given permissions.
        """
        permission = Permission.bitwise_or(*permissions)
        raw_value = permission.value

        return Role.query.filter(Role._permissions.op('&')(raw_value) == raw_value).all()
Exemple #13
0
    def test_permissions_get_zero(self):
        """
            Test getting the permission enum member if the permission integer is 0.

            Expected result: The empty permission.
        """

        role = Role('Administrator')
        db.session.add(role)
        db.session.commit()

        self.assertEqual(0, role._permissions)
        self.assertEqual(Permission(0), role.permissions)
Exemple #14
0
    def test_permissions_set_none(self):
        """
            Test setting permissions without giving a permission.

            Expected result: The empty permission is set.
        """

        role = Role('Administrator')
        role._permissions = Permission.EditRole.value
        self.assertEqual(Permission.EditRole, role.permissions)

        role.permissions = None
        self.assertEqual(Permission(0).value, role._permissions)
Exemple #15
0
    def test_selected_permissions_get_faulty_field_dict(self):
        """
            Test getting the tested permissions if no permission fields exist, but the permission fields dict say
            otherwise..

            Expected result: The empty permission is returned without any errors.
        """

        form = BasePermissionForm()
        permission = Permission.EditGlobalSettings
        form.permission_fields = OrderedDict([(permission.name, permission)])

        self.assertEqual(Permission(0), form.permissions)
Exemple #16
0
    def test_bitwise_and_success(self):
        """
            Test the bitwise and method.

            Expected result: The method returns the same result as performing the operation manually.
        """

        expectation = Permission.EditRole
        result = Permission.bitwise_and(Permission.EditRole)
        self.assertEqual(expectation, result)

        combination = Permission.EditRole | Permission.EditGlobalSettings
        expectation = combination & Permission.EditUser
        result = Permission.bitwise_and(combination, Permission.EditUser)
        self.assertEqual(expectation, result)

        combination_1 = Permission.EditGlobalSettings | Permission.EditRole | Permission.EditUser
        combination_2 = Permission.EditGlobalSettings | Permission.EditRole
        combination_3 = Permission.EditGlobalSettings | Permission.EditUser

        expectation = combination_1 & combination_2 & combination_3
        result = Permission.bitwise_and(combination_1, combination_2, combination_3)
        self.assertEqual(expectation, result)
Exemple #17
0
    def has_permissions_all(self, *permissions: Permission) -> bool:
        """
            Determine if the role has all of the given permissions.

            If the empty permission `Permission(0)` is given, the result will always be `False`.

            :param permissions: The permissions to check for.
            :return: `True` if the role has all of the requested permissions, `False` otherwise.
        """
        permission = Permission.bitwise_or(*permissions)

        if permission.value == 0:
            return False

        return permission & self.permissions == permission
Exemple #18
0
    def test_has_permissions_one_of_multiple_permissions(self):
        """
            Test the has_permissions_one_of() method if a role has the requested permission (and others).

            Expected result: `True` when requesting the set permissions.
        """

        role = Role('Administrator')

        role.permissions = Permission.EditRole | Permission.EditUser
        self.assertTrue(role.has_permissions_one_of(Permission.EditRole))
        self.assertTrue(role.has_permissions_one_of(Permission.EditUser))
        self.assertTrue(
            role.has_permissions_one_of(Permission.EditRole,
                                        Permission.EditUser, Permission(0)))
Exemple #19
0
    def assert_no_permission_required(self,
                                      url: str,
                                      method: str = 'GET') -> None:
        """
            Assert that accessing the URL via the given method requires no permission at all and that accessing the URL
            with any permission is actually possible.

            The test checks for a response code of 403. This can lead to a false positive if a route aborts with an
            error 403.

            :param url: The URL to access.
            :param method: The HTTP method to access the URL by. Defaults to `'GET'`.
        """

        allowed_permissions = Permission.get_permissions(
            include_empty_permission=True, all_combinations=True)
        self._assert_permissions(url, allowed_permissions, method)
Exemple #20
0
    def permissions(self) -> Permission:
        """
            Get the permissions (:class:`.app.userprofile.Permission`) given by the form's data.

            :return: The (combined) permissions given by the form's data.
        """

        permissions = Permission(0)
        for field_name, permission in self.permission_fields.items():
            field = getattr(self, field_name, None)
            if not field:
                continue

            if field.data:
                permissions |= permission

        return permissions
Exemple #21
0
def role_new() -> ResponseType:
    """
        Show and process a form to create a new role.

        :return: The response for this view.
    """

    new_role_form = create_permission_form(RoleNewForm, Permission(0))
    if new_role_form.validate_on_submit():
        role = Role(name=new_role_form.name.data)
        role.permissions = new_role_form.permissions
        db.session.add(role)
        db.session.commit()

        flash(_('The new role has been created.'))
        return redirect(url_for('.roles_list'))

    return render_template('administration/role_new.html', new_role_form=new_role_form)
Exemple #22
0
    def test_assert_permission_required_without_required_permissions(
            self) -> None:
        """
            Test the assertion `assert_permission_required` assuming that a URL requires a permission, while in fact, it
            does not.

            Expected result: An error is raised for any other permission than the assumed one.
        """

        self.app.add_url_rule('/example', 'example', self.example_route)

        with self.assertRaises(self.failureException) as exception_cm:
            self.assert_permission_required('/example', Permission.EditUser)

        expected_messages = self._get_messages_for_accessible_url({
            Permission(0),
            Permission.EditRole,
            Permission.EditGlobalSettings,
            Permission.EditRole | Permission.EditGlobalSettings,
        })
        self.assertIn(str(exception_cm.exception), expected_messages)
Exemple #23
0
    def test_assert_no_permission_required_but_url_requires_a_permission(
            self) -> None:
        """
            Test the assertion `assert_no_permission_required` if the accessed URL requires a permission.

            Expected result: An error is raised for any permission other than the required one.
        """

        decorator = permission_required(Permission.EditUser)
        decorated_view = decorator(self.example_route)
        self.app.add_url_rule('/example', 'example', decorated_view)

        with self.assertRaises(self.failureException) as exception_cm:
            self.assert_no_permission_required('/example')

        # The assertion can fail for any of these permissions since the assertion uses sets.
        expected_messages = self._get_messages_for_inaccessible_url({
            Permission(0), Permission.EditRole, Permission.EditGlobalSettings,
            Permission.EditRole | Permission.EditGlobalSettings
        })

        self.assertIn(str(exception_cm.exception), expected_messages)
Exemple #24
0
    def permissions(self, permission: Optional[Permission]) -> None:
        """
            Set the given permissions for this role.

            This will overwrite all existing permissions.

            If this role is the only one allowed to edit roles, but the new permissions do not include the permission to
            edit roles, this permission will automatically be added to the role to prevent cases where there is no role
            that can edit roles.

            :param permission: An enum member of :class:`app.userprofile.Permission` representing the role's new
                               permissions (may be a combination of multiple enum members).
        """

        if permission is None:
            permission = Permission(0)

        # If the permission to edit roles is not included in the new permissions, but this is the only role allowed to
        # edit roles, re-add the permission to avoid a situation where no one can edit roles anymore.
        if permission & Permission.EditRole != Permission.EditRole and self.is_only_role_allowed_to_edit_roles(
        ):
            permission |= Permission.EditRole

        self._permissions = permission.value
Exemple #25
0
    def assert_permission_required(self,
                                   url: str,
                                   permission: Permission,
                                   method: str = 'GET') -> None:
        """
            Assert that accessing the URL via the given method requires the specified permission and that accessing the
            URL with the permission is actually possible.

            The test checks for a response code of 403. This can lead to a false positive if a route does not require
            the specified permission but aborts with an error 403 in some other case.

            :param url: The URL to access.
            :param permission: The permission that must be required to access the URL.
            :param method: The HTTP method to access the URL by. Defaults to `'GET'`.
        """

        all_permissions = Permission.get_permissions(
            include_empty_permission=True, all_combinations=True)
        allowed_permissions = {
            p
            for p in all_permissions if p.includes_permission(permission)
        }

        self._assert_permissions(url, allowed_permissions, method)
Exemple #26
0
    def test_permission_required_one_of_no_permission(self):
        """
            Test the `permission_required_one_of` decorator if the user does not have any of 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')

        db.session.add(user)
        db.session.commit()

        user.login(email, password)

        self.assertEqual(Permission(0), user.role.permissions)

        with self.assertRaises(Forbidden):
            permission_required_one_of(
                Permission.EditRole, Permission.EditUser)(self.view_function)()
Exemple #27
0
    def assert_permission_required_one_of(self,
                                          url: str,
                                          *permissions: Permission,
                                          method: str = 'GET') -> None:
        """
            Assert that accessing the URL via the given method requires one of the specified permissions and that
            accessing the URL with the permission is actually possible, while accessing the URL with any other
            permission fails.

            :param url: The url to access.
            :param permissions: The permissions that must be required to access the URL.
            :param method: The HTTP method to access the URL by. Defaults to `'GET'`.
        """

        all_permissions = Permission.get_permissions(
            include_empty_permission=True, all_combinations=True)
        allowed_permissions = set({})
        for permission in permissions:
            allowed_permissions.update({
                p
                for p in all_permissions if p.includes_permission(permission)
            })

        self._assert_permissions(url, allowed_permissions, method)