示例#1
0
def check_soft_delete_and_restore_roles(mng, instance, old_instance):
    """
    Checks for soft delete and trash roles of the provided instance
    :param instance: SoftDeleteMixin
    """
    user = get_current_user()

    # find out whether this is a restore or trash operation

    if old_instance.deleted:
        assert instance.deleted is False
        # restore instance

        # check if user has global restore permission
        if user.has_perm(get_permission_name(instance.__class__, 'restore')):
            return

        # check if object is restorable according to the objects manager
        if hasattr(instance, 'is_restorable') and not instance.is_restorable():
            raise PermissionDenied

    else:
        assert instance.deleted is True
        # trash instance

        # check if user has global trash permission
        if user.has_perm(get_permission_name(instance.__class__, 'trash')):
            return

        # check if object is trashable according to the objects manager
        if hasattr(instance, 'is_trashable') and not instance.is_trashable():
            raise PermissionDenied
示例#2
0
    def check_create_without_project(self, request, *args, **kwargs):
        """
        On create/update without projects, verify that the current user is allowed to do that
        :param request:
        :param args:
        :param kwargs:
        :return:
        """
        # ignore partial updates unless it contains projects
        if kwargs.get('partial') and 'projects' not in request.data:
            return

        if 'projects' not in request.data or len(
                request.data['projects']) == 0:
            user = request.user

            if user.has_perm(
                    get_permission_name(self.serializer_class.Meta.model,
                                        'add') + "_without_project"):
                # user is allowed to create this entity without a project relationship
                return
            else:
                raise ValidationError({
                    'projects':
                    ValidationError(_('You need to select a project'),
                                    params={'projects': []},
                                    code='invalid')
                })
示例#3
0
def auto_create_project_for_user(sender, user, *args, **kwargs):
    """
    On post_auth, automatically create a project on login (if the user does not have any)
    :param sender:
    :param user:
    :param args:
    :param kwargs:
    :return:
    """

    # set current requests user (as during auth, that user is not set yet)
    request = get_current_request()
    if request and (not hasattr(request, 'user') or request.user.is_anonymous):
        request.user = user

    if Project.objects.viewable().count() == 0:
        # no projects found, check if user has add_project permission
        if user.has_perm(get_permission_name(Project, 'add')):
            logger.info(
                "Auto-creating project {project_name} for user {user_name}".
                format(project_name=_("My Project"), user_name=user.username))
            Project.objects.create(name=_("My Project"),
                                   start_date=timezone.now(),
                                   description="<div>%s</div>" %
                                   _("Automatically generated project"))
示例#4
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
示例#5
0
    def viewable(self, *args, **kwargs):
        """
        Returns an `all` QuerySet if current user has the 'APP.view_MODEL' permission (whereas "APP" corresponds to the
        managed models app label and "MODEL" corresponds to the managed models name). Returns a `none` QuerySet else.

        :rtype: models.QuerySet
        """
        user = get_current_user()
        if user.has_perm(get_permission_name(self.model, 'view')):
            return self.all()
        return self.none()
示例#6
0
def check_create_roles(sender, instance, *args, **kwargs):
    """
    Check if the current user is allowed to create an object (which can be related to a project)
    :param sender:
    :param instance:
    :param args:
    :param kwargs:
    :return:
    """
    user = get_current_user()

    if not user.pk or user.is_anonymous:
        return

    # check for global create permission of this object class
    if user.has_perm(get_permission_name(instance.__class__, 'add')):
        if type(instance) == Project:
            check_create_roles_for_parent_project(user, sender, instance,
                                                  *args, **kwargs)
        return
    else:
        # user does not have global permission, check if the user is trying to create a project
        if type(instance) == Project:
            # user does not have the add_project permission
            raise PermissionDenied

    # check if this the instance is related to projects
    if not hasattr(instance, 'projects'):
        # not project related,
        # several workbench elements, such as meetings, kanban boards, labbooks etc... can deny creating related
        # objects (such as kanban board columns, labbook elements, ...)
        # We delegate this check to another signal

        check_create_roles_for_other_workbench_elements.send(sender=sender,
                                                             instance=instance,
                                                             user=user)

        # This signal used to be the following call:
        # if hasattr(instance, 'meeting'):
        #     check_create_roles_for_meeting(user, sender, instance, *args, **kwargs)
        # elif hasattr(instance, 'kanban_board'):
        #     check_create_roles_for_kanbanboard(user, sender, instance, *args, **kwargs)
        # elif hasattr(instance, 'Kanban_board_column'):
        #     check_create_roles_for_kanbanboard_column(user, sender, instance, *args, **kwargs)

    # last but not least, check if this is related to the Project Model and has a parent_project
    if hasattr(instance, 'parent_project'):
        check_create_roles_for_parent_project(user, sender, instance, *args,
                                              **kwargs)
示例#7
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
示例#8
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
示例#9
0
def check_create_roles_for_parent_project(user, sender, instance, *args,
                                          **kwargs):
    """
    If a project is created (or the parent_project property is changed), we need to check if the current user has
    permissions to do that
    E.g., as a project member, you are usually not allowed to create a sub-project
    However, as a project manager, you are allowed to do that
    :param user:
    :param sender:
    :param instance:
    :param args:
    :param kwargs:
    :return:
    """
    with transaction.atomic():
        Project.objects.rebuild()
    # the user is already logged in and does not have the global permission to create something, therefore we need
    # to check the add_project permissions of instance.parent_project
    if not hasattr(instance, 'parent_project'):
        return

    if not instance.parent_project:
        return

    # get parent project from the provided instance
    parent_project = instance.parent_project

    # get the current project (it might not exist yet)
    current_project = Project.objects.filter(pk=instance.pk).first()

    # check if this is an existing project
    if current_project:
        # Check if parent_project has changed
        old_parent_project = current_project.parent_project

        if parent_project == old_parent_project:
            # nothing changed, ignoring
            return

        # Check if current user has the change_parent_project permission on the current project
        if not get_permission_name(
                instance.__class__, 'change_parent'
        ) in instance.current_users_project_permissions_list:
            raise ValidationError({
                'parent_project':
                ValidationError(
                    _('You are not allowed to change the parent project'),
                    params={'project': parent_project},
                    code='invalid')
            })

    # check if current user has the add_project permission on the parent_project
    if not get_permission_name(
            instance.__class__,
            'add') in parent_project.current_users_project_permissions_list:
        # user is not allowed to create this entity without relating it to a project
        # You do not have permissions to create a new task without selecting a project
        raise ValidationError({
            'parent_project':
            ValidationError(_('You are not allowed to select this project'),
                            params={'project': parent_project},
                            code='invalid')
        })