Beispiel #1
0
def check_update_project_parent_project(instance, *args, **kwargs):
    """
    Checks whether the current user is allowed to change the parent project of the given project instance
    :param instance:
    :param args:
    :param kwargs:
    :return:
    """
    # do not check for raw inserts or ChangeSet or ChangeRecord insert - those are always allowed
    if kwargs.get('raw') or isinstance(instance, ChangeSet) or isinstance(
            instance, ChangeRecord):
        return

    if permission_checks_disabled(instance):
        return

    mng = instance.__class__.objects

    # check if instance exists
    if mng.filter(pk=instance.pk).count() == 0:
        # new instance --> ignore (check_create_roles will handle that case)
        return

    # check whether the current user is allowed to do stuff with the current project
    # therefore we need to remove the original sender
    kwargs['original_sender'] = kwargs.pop('sender')
    check_create_roles_for_parent_project(get_current_user(), Project,
                                          instance, *args, **kwargs)
Beispiel #2
0
def check_delete_roles(sender, instance, *args, **kwargs):
    """
    On pre_delete, check delete permission for every database object where the queryset implements "deletable"
    raises a PermissionDenied exception on error
    """
    if isinstance(instance, Metadata):
        # deleting meta data is fine
        return

    if permission_checks_disabled(instance):
        return

    # ensure that the object has been soft deleted already, else raise an exception
    raise_error_if_object_not_soft_deleted(sender, instance, *args, **kwargs)

    user = get_current_user()

    # check if user has global delete permission for this class
    if user.has_perm(get_permission_name(instance.__class__, 'delete')):
        return

    mng = instance.__class__.objects

    # check if deletable exists and the instance.pk is in the deletable queryset
    if hasattr(mng, "deletable") and callable(
            mng.deletable) and mng.deletable().filter(
                pk=instance.pk).count() == 0:
        raise PermissionDenied
Beispiel #3
0
def prevent_delete_of_project_manager(sender, instance, *args, **kwargs):
    """
    For each project there must be at least one project manager
    This pre_delete handler prevents deletion of the last project manager for a given project
    """
    if permission_checks_disabled(instance):
        return

    if hasattr(instance, 'is_deleteable') and not instance.is_deleteable():
        raise ValidationError({
            'non_field_errors':
            ValidationError(_(
                "The last project manager of this project can not be removed"),
                            params={'assignment': instance},
                            code='invalid')
        })
Beispiel #4
0
def check_model_privileges(instance, *args, **kwargs):
    """
    Checks whether editing ModelPrivileges is allowed or not; A ModelPrivilege entry is always related to an object
    (such as Task, Note, ...).
    - Allowed: A ModelPrivilege entry is created when a new entity is created (with the current user)
    - Allowed: Users are allowed to edit ModelPrivileges only if they are the owner of the related object
    :param instance:
    :param args:
    :param kwargs:
    :return:
    """

    # skip raw inserts
    if kwargs.get('raw'):
        return

    # verify whether permission checks are enabled
    if permission_checks_disabled(instance):
        return

    # verify whether there are already model privileges for the current content_object
    if not ModelPrivilege.objects.filter(
            content_type=instance.content_type,
            object_id=instance.object_id
    ).exists():
        # no model privilege exists for the given content_type and object_id
        return

    user = get_current_user()

    if user.is_superuser:
        return

    if user.is_anonymous:
        raise PermissionDenied

    # verify whether the current user is an owner of the given content_type and object_id
    if not ModelPrivilege.objects.filter(
            content_type=instance.content_type,
            object_id=instance.object_id,
            user=user,
            full_access_privilege=ModelPrivilege.ALLOW,
    ).exists():
        raise PermissionDenied
Beispiel #5
0
def prevent_update_of_last_project_manager(sender, instance, *args, **kwargs):
    """
    For each project there must be at least one project manager
    This pre_save handler prevents that the last project manager is being demoted
    """
    if permission_checks_disabled(instance):
        return

    if instance.pk:
        obj = ProjectRoleUserAssignment.objects.filter(pk=instance.pk).first()
        if obj and not obj.is_deleteable():
            raise ValidationError({
                'non_field_errors':
                ValidationError(_(
                    "The last project manager of this project can not be removed"
                ),
                                params={'assignment': instance},
                                code='invalid')
            })
Beispiel #6
0
def make_sure_workbench_entities_are_not_deleted_by_normal_users(
        instance, *args, **kwargs):
    """
    Only superusers may delete workbench entities. Normal users may trash only.
    """

    # not a protected model -> let the request through
    if not isinstance(instance, models_only_superuser_can_delete):
        return

    # nothing to do if permission checks are disabled
    if permission_checks_disabled(instance):
        return

    # check if user is superuser
    user = get_current_user()
    if not user.is_superuser:
        model_name = str(type(instance))
        raise PermissionDenied(
            _("Only admins can delete {model} objects").format(
                model=model_name))
Beispiel #7
0
def on_save_relation(sender, instance, *args, **kwargs):
    """
    On save of a relation, verify that the current user actually has access to the left and right content object
    :param sender:
    :param instance:
    :param args:
    :param kwargs:
    :return:
    """
    if permission_checks_disabled(instance):
        return

    # ignore raw
    if kwargs.get('raw'):
        return

    # verify that the current user has access to left and right content object
    left = instance.left_content_object
    right = instance.right_content_object

    if not left._meta.model.objects.viewable().filter(pk=left.pk).exists():
        raise ValidationError({
            'left_content_object':
            ValidationError(
                _('You do not have permission to relate to this object'),
                params={'relation': instance},
                code='invalid')
        })

    if not right._meta.model.objects.viewable().filter(pk=right.pk).exists():
        raise ValidationError({
            'right_content_object':
            ValidationError(
                _('You do not have permission to relate to this object'),
                params={'relation': instance},
                code='invalid')
        })
Beispiel #8
0
def check_update_roles(sender, instance, *args, **kwargs):
    """
    Check update permission for each database object where the queryset implements "changeable"
    raises a PermissionDenied exception on error
    """
    # do not check for raw inserts or ChangeSet or ChangeRecord insert - those are always allowed
    if kwargs.get('raw') or isinstance(instance, ChangeSet) or isinstance(instance, ChangeRecord) \
            or isinstance(instance, Metadata):
        return

    if permission_checks_disabled(instance):
        return

    mng = instance.__class__.objects

    # get the existing instance (if it exists)
    old_instance = mng.filter(pk=instance.pk).first()

    # check if instance exists
    if not old_instance:
        return check_create_roles(sender, instance, *args, **kwargs)

    if hasattr(instance, 'deleted'):
        # we need to do some checks for soft deleted objects
        # check if this instance is being soft deleted or restored
        if old_instance.deleted != instance.deleted:
            # delegate permission check to another function
            return check_soft_delete_and_restore_roles(mng, instance,
                                                       old_instance)

        # prevent updates of soft-deleted (trashed) objects
        if instance.deleted:
            raise ValidationError({
                'non_field_errors':
                ValidationError(
                    _("You are not allowed to edit an already trashed object"),
                    params={'instance': instance},
                    code='invalid')
            })

        # prevent updates of locked elements
        if ElementLock.objects.for_model(
                instance.__class__, instance.pk
        ).filter(
                Q(webdav_lock=False,
                  locked_at__gte=timezone.now() - timezone.timedelta(
                      minutes=site_preferences.element_lock_time_in_minutes))
                | Q(webdav_lock=True,
                    locked_at__gte=timezone.now() -
                    timezone.timedelta(minutes=site_preferences.
                                       element_lock_webdav_time_in_minutes))
        ).exclude(
                # ignore if the element is locked by the current user
                locked_by=get_current_user()).exists():
            # element is locked by another user
            raise ValidationError({
                'non_field_errors':
                ValidationError(
                    _("This object is currently locked by another user"),
                    params={'instance': instance},
                    code='invalid')
            })

    # !!! from now on we know for sure that this is an update !!!

    # check if the user has global change roles
    user = get_current_user()

    if user.has_perm(get_permission_name(instance.__class__, 'change')):
        return

    # allow the user to edit if he was attending, so he can remove himself
    if hasattr(instance,
               'attending_users') and user in instance.attending_users.all():
        logger.debug(
            "In check_update_roles: User is attending so user is allowed to edit"
        )
    else:
        # check if this instance is editable
        if hasattr(instance, 'is_editable') and not instance.is_editable():
            logger.debug(
                "In check_update_roles: Checking editable() viewset - "
                "could not find object -> PermissionDenied")
            raise PermissionDenied
Beispiel #9
0
def check_workbench_element_relation_with_projects(sender, instance, action,
                                                   model, *args, **kwargs):
    """
    Each workbench element can be related to many projects (hence m2m_changed). Relations with projects are important,
    as users have roles in projects, and those roles provide permissions on the elements within a project.

    Therefore, everytime this relation changes (action = add or remove), we need to verify that the current user is
    actually allowed to change it

    :param sender:
    :param instance: BaseModel
    :param action:
    :param args:
    :param kwargs:
    :return:
    """

    # do not handle raw inserts, or ChangeSet or ChangeRecord insert - those are always allowed
    if kwargs.get('raw') or isinstance(instance, ChangeSet) or isinstance(
            instance, ChangeRecord):
        return

    # check if disablePermissionChecks is currently set for this class
    if permission_checks_disabled(instance):
        return

    # check if the instance is actually related to projects
    if not hasattr(instance, 'projects'):
        # not related, ignore
        return

    # only handle pre_add and pre_remove actions
    if action != 'pre_add' and action != 'pre_remove':
        return

    # this handler only checks for projects
    if model != Project:
        return

    # We temporarily must deactivate all permission checks for files because of SITUMEWB-819
    if instance.__class__ == File:
        return

    user = get_current_user()

    # first of all, check if the object is editable by the current user
    if hasattr(instance, 'is_editable') and not instance.is_editable():
        raise PermissionDenied

    # on both actions (pre_add and pre_remove), we can get the set of primary keys that is affected from kwargs
    project_pk_set = kwargs.get('pk_set')
    # get all viewable projects of this primary key set, a newly generated cache_id is added to the request in order
    # to fetch a fresh result from the DB, instead of a cached result
    projects = Project.objects.viewable(cache_id=uuid.uuid4()).filter(
        pk__in=project_pk_set)

    # TODO: We must change the following permission checks as we run into several logic errors all the time.
    # We don't need to check if the user has specific permissions on all linked projects. It's satisfying enough to have
    # specific permissions on only one linked project as it overrules all the other ones.
    #
    # The problem of running into permission errors occurs if e.g. a user uploads a file to a storage which inherits
    # permissions from multiple projects. Although permission checks are disabled they can still throw PermissionDenied
    # errors if you link more than one project or use a parent/child project. Use this setup to reproduce this behavior:
    #
    # Project A by User A -> set User B as Observer
    # Project B by User A (Project A is the parent project for Project B) -> set User B as Project Member
    # Storage A by User A with linked projects Project A and Project B -> User B can't upload although he should be able
    # to because he is Project Member in Project B but not in Project A.

    if action == 'pre_add':
        # pre add: verify that the user has the permission to create a new instance of within the projects
        if user.has_perm(get_permission_name(instance.__class__, 'add')):
            return

        # for each project, verify that we have add roles
        for project in projects:
            if not get_permission_name(
                    instance.__class__,
                    'add') in project.current_users_project_permissions_list:
                raise PermissionDenied
    elif action == 'pre_remove':
        # pre remove: verify that the user is allowed to remove an instance within the projects
        if user.has_perm(get_permission_name(instance.__class__, 'delete')):
            return

        # for each project, verify that we have remove roles
        for project in projects:
            if not get_permission_name(
                    instance.__class__, 'delete'
            ) in project.current_users_project_permissions_list:
                raise PermissionDenied