示例#1
0
def alter_permissions(user: User, to_add: Set[str], to_ungrant: Set[str],
                      to_delete: Set[str]) -> None:
    """
    Apply the permission changes to the database. The permission model to
    apply the changes to is passed as a parameter.

    :param perm_model: The permissions model, must inherit PermissionMixin
    :param user:       The user to apply the permissions to
    :param to_add:     The permissions to add
    :param to_ungrant: The permissions to ungrant
    :param to_delete:  The permissions to delete
    """
    for permission in to_delete:
        model = UserPermission.from_attrs(user_id=user.id,
                                          permission=permission)
        db.session.delete(model)
    db.session.commit()
    for perm_name in to_add:
        db.session.add(UserPermission(user_id=user.id, permission=perm_name))
    for perm_name in to_ungrant:
        db.session.add(
            UserPermission(user_id=user.id,
                           permission=perm_name,
                           granted=False))
    db.session.commit()
示例#2
0
    def permissions(self) -> List[str]:
        """
        A general function to get the permissions of a user from a permission
        model and attributes of their user classes. Locked users are restricted
        to the permissions defined for them in the config.

        :param key:   The cache key to cache the permissions under
        :param model: The model to query custom permissions from
        :param attr:  The attribute of the userclasses that should be queried
        """
        from core.permissions.models import SecondaryClass
        from core.permissions.models import UserPermission

        if self.locked:  # Locked accounts have restricted permissions.
            return app.config['LOCKED_ACCOUNT_PERMISSIONS']
        key = self.__cache_key_permissions__.format(id=self.id)
        permissions = cache.get(key)
        if not permissions:
            permissions = copy(self.user_class_model.permissions)
            for class_ in SecondaryClass.from_user(self.id):
                permissions += class_.permissions
            permissions = set(permissions)  # De-dupe

            for perm, granted in UserPermission.from_user(self.id).items():
                if not granted and perm in permissions:
                    permissions.remove(perm)
                if granted and perm not in permissions:
                    permissions.add(perm)

            cache.set(key, permissions)
        return permissions
def test_change_forum_permissions_failure(app, authed_client):
    db.engine.execute('DELETE FROM users_permissions')
    add_permissions(app, 'users_moderate', 'forumaccess_thread_1')
    db.engine.execute("""UPDATE user_classes
                      SET permissions = '{"forumaccess_forum_2"}'""")

    response = authed_client.put(
        '/users/1',
        data=json.dumps({
            'permissions': {
                'forumaccess_forum_2': True,
                'forumaccess_thread_1': False,
                'forumaccess_thread_4': False,
                'forumaccess_thread_2': True,
            }
        }),
    )

    check_json_response(
        response,
        'The following permissions could not be added: forumaccess_forum_2. '
        'The following permissions could not be deleted: forumaccess_thread_4.',
    )
    f_perms = UserPermission.from_user(1, prefix='forumaccess')
    assert f_perms == {'forumaccess_thread_1': True}
def test_change_forum_permissions(app, authed_client):
    db.engine.execute('DELETE FROM users_permissions')
    add_permissions(app, 'users_moderate', 'forumaccess_forum_1',
                    'forumaccess_thread_1')
    db.engine.execute("""UPDATE user_classes
                      SET permissions = '{"forumaccess_forum_2"}'""")

    response = authed_client.put(
        '/users/1',
        data=json.dumps({
            'permissions': {
                'forumaccess_forum_2': False,
                'forumaccess_thread_1': False,
                'forumaccess_thread_2': True,
            }
        }),
    ).get_json()

    print(response['response'])
    assert set(response['response']['forum_permissions']) == {
        'forumaccess_forum_1',
        'forumaccess_thread_2',
    }

    f_perms = UserPermission.from_user(1, prefix='forumaccess')
    assert f_perms == {
        'forumaccess_forum_2': False,
        'forumaccess_forum_1': True,
        'forumaccess_thread_2': True,
    }
示例#5
0
    def can_access(self, permission: str = None, error: bool = False) -> bool:
        """Determines whether or not the user has the permissions to access the thread."""
        if flask.g.user is None:  # pragma: no cover
            if error:
                raise _403Exception
            return False

        # Explicit thread access
        permission_key = self.__permission_key__.format(id=self.id)
        if flask.g.user.has_permission(permission_key) or (
            permission is not None and flask.g.user.has_permission(permission)
        ):
            return True

        # Access to forum gives access to all threads by default.
        # If user has been ungranted the thread, they cannot view it regardless.
        ungranted_threads = [
            p
            for p, g in UserPermission.from_user(
                flask.g.user.id, prefix='forumaccess_thread'
            ).items()
            if g is False
        ]
        if permission_key not in ungranted_threads and (
            flask.g.user.has_permission(
                Forum.__permission_key__.format(id=self.forum_id)
            )
        ):
            return True
        if error:
            raise _403Exception
        return False
示例#6
0
def check_permissions(
    user: User,
    permissions: Dict[str, bool]  # noqa: C901 (McCabe complexity)
) -> Tuple[Set[str], Set[str], Set[str]]:
    """
    The abstracted meat of the permission checkers. Takes the input and
    some model-specific information and returns permission information.

    :param user:        The recipient of the permission changes
    :param permissions: A dictionary of permission changes, with permission name
                        and boolean (True = Add, False = Remove) key value pairs
    :param perm_model:  The permission model to be checked
    :param perm_attr:   The attribute of the user classes which represents the permissions
    """
    add: Set[str] = set()
    ungrant: Set[str] = set()
    delete: Set[str] = set()
    errors: Dict[str, Set[str]] = defaultdict(set)

    uc_permissions: Set[str] = set(user.user_class_model.permissions)
    for class_ in SecondaryClass.from_user(user.id):
        uc_permissions |= set(class_.permissions)
    custom_permissions: Dict[str, bool] = UserPermission.from_user(user.id)

    for perm, active in permissions.items():
        if active is True:
            if perm in custom_permissions:
                if custom_permissions[perm] is False:
                    delete.add(perm)
                    add.add(perm)
            elif perm not in uc_permissions:
                add.add(perm)
            if perm not in add.union(delete):
                errors['add'].add(perm)
        else:
            if perm in custom_permissions and custom_permissions[perm] is True:
                delete.add(perm)
            if perm in uc_permissions:
                ungrant.add(perm)
            if perm not in delete.union(ungrant):
                errors['delete'].add(perm)

    if errors:
        message = []
        if 'add' in errors:
            message.append(f'The following permissions could not be added: '
                           f'{", ".join(errors["add"])}.')
        if 'delete' in errors:
            message.append(f'The following permissions could not be deleted: '
                           f'{", ".join(errors["delete"])}.')
        raise APIException(' '.join(message))

    return add, ungrant, delete
示例#7
0
def test_permissions_from_user(app, client):
    add_permissions(app, 'perm_one', 'perm_two')
    db.engine.execute(
        """INSERT INTO users_permissions (user_id, permission, granted)
                      VALUES (1, 'perm_three', 'f')""")
    perms = UserPermission.from_user(1)
    assert perms == {
        'perm_one': True,
        'perm_two': True,
        'perm_three': False,
        'userclasses_list': True,
        'userclasses_modify': True,
    }
def test_change_permissions(app, authed_client):
    add_permissions(
        app, 'users_change_password', 'userclasses_list', 'userclasses_modify'
    )
    db.engine.execute(
        """INSERT INTO users_permissions (user_id, permission, granted)
                      VALUES (1, 'invites_send', 'f')"""
    )
    db.engine.execute(
        """UPDATE user_classes
        SET permissions = '{"users_moderate", "users_moderate_advanced", "invites_view"}'"""
    )

    response = authed_client.put(
        '/users/1',
        data=json.dumps(
            {
                'permissions': {
                    'users_moderate': False,
                    'users_change_password': False,
                    'invites_view': False,
                    'invites_send': True,
                }
            }
        ),
    ).get_json()

    print(response['response'])
    assert set(response['response']['permissions']) == {
        'users_moderate_advanced',
        'userclasses_modify',
        'invites_send',
        'userclasses_list',
    }

    u_perms = UserPermission.from_user(1)
    assert u_perms == {
        'userclasses_list': True,
        'userclasses_modify': True,
        'invites_send': True,
        'invites_view': False,
        'users_moderate': False,
    }