Example #1
0
    def _copy_perms_from_kpi_to_kc(self, asset: Asset):
        user_codenames = (ObjectPermission.objects.filter(
            asset_id=asset.pk, deny=False,
            inherited=False).exclude(user_id=asset.owner_id).exclude(
                permission_id=self._perm_from_kc_only_pk
            ).values('user_id').annotate(
                all_codenames=ArrayAgg('permission__codename', distinct=True)))

        for uc in user_codenames:
            if self._verbosity >= 1:
                self.stdout.write(
                    f"\tPushing permissions for user #{uc['user_id']} to KoBoCAT..."
                )
            if self._verbosity >= 2:
                self.stdout.write(f"\t\tPermissions: {uc['all_codenames']}")
            assign_applicable_kc_permissions(asset, uc['user_id'],
                                             uc['all_codenames'])
Example #2
0
    def assign_perm(self,
                    user_obj,
                    perm,
                    deny=False,
                    defer_recalc=False,
                    skip_kc=False,
                    partial_perms=None):
        r"""
            Assign `user_obj` the given `perm` on this object, or break
            inheritance from a parent object. By default, recalculate
            descendant objects' permissions and apply any applicable KC
            permissions.
            :type user_obj: :py:class:`User` or :py:class:`AnonymousUser`
            :param perm: str. The `codename` of the `Permission`
            :param deny: bool. When `True`, break inheritance from parent object
            :param defer_recalc: bool. When `True`, skip recalculating
                descendants
            :param skip_kc: bool. When `True`, skip assignment of applicable KC
                permissions
            :param partial_perms: dict. Filters used to narrow down query for
              partial permissions
        """
        app_label, codename = perm_parse(perm, self)
        if codename not in self.get_assignable_permissions():
            # Some permissions are calculated and not stored in the database
            raise ValidationError(
                '{} cannot be assigned explicitly to {} objects.'.format(
                    codename, self._meta.model_name))
        if isinstance(user_obj,
                      AnonymousUser) or (user_obj.pk
                                         == settings.ANONYMOUS_USER_ID):
            # Is an anonymous user allowed to have this permission?
            fq_permission = f'{app_label}.{codename}'
            if (not deny and fq_permission
                    not in settings.ALLOWED_ANONYMOUS_PERMISSIONS):
                raise ValidationError(
                    'Anonymous users cannot be granted the permission {}.'.
                    format(codename))
            # Get the User database representation for AnonymousUser
            user_obj = get_anonymous_user()
        perm_model = Permission.objects.get(content_type__app_label=app_label,
                                            codename=codename)
        existing_perms = ObjectPermission.objects.filter_for_object(
            self,
            user=user_obj,
        )
        identical_existing_perm = existing_perms.filter(
            inherited=False,
            permission_id=perm_model.pk,
            deny=deny,
        )
        if identical_existing_perm.exists():
            # We need to always update partial permissions because
            # they may have changed even if `perm` is the same.
            self._update_partial_permissions(user_obj.pk,
                                             perm,
                                             partial_perms=partial_perms)
            # The user already has this permission directly applied
            return identical_existing_perm.first()

        # Remove any explicitly-defined contradictory grants or denials
        contradictory_filters = models.Q(user=user_obj,
                                         permission_id=perm_model.pk,
                                         deny=not deny,
                                         inherited=False)
        if not deny and perm in self.CONTRADICTORY_PERMISSIONS.keys():
            contradictory_filters |= models.Q(
                user=user_obj,
                permission__codename__in=self.CONTRADICTORY_PERMISSIONS.get(
                    perm),
            )
        contradictory_perms = existing_perms.filter(contradictory_filters)
        contradictory_codenames = list(
            contradictory_perms.values_list('permission__codename', flat=True))

        contradictory_perms.delete()
        # Check if any KC permissions should be removed as well
        if deny and not skip_kc:
            remove_applicable_kc_permissions(self, user_obj,
                                             contradictory_codenames)
        # Create the new permission
        new_permission = ObjectPermission.objects.create(
            content_object=self,
            user=user_obj,
            permission_id=perm_model.pk,
            deny=deny,
            inherited=False)
        # Assign any applicable KC permissions
        if not deny and not skip_kc:
            assign_applicable_kc_permissions(self, user_obj, codename)
        # Resolve implied permissions, e.g. granting change implies granting
        # view
        implied_perms = self.get_implied_perms(codename, reverse=deny)
        for implied_perm in implied_perms:
            self.assign_perm(user_obj,
                             implied_perm,
                             deny=deny,
                             defer_recalc=True)
        # We might have been called by ourself to assign a related
        # permission. In that case, don't recalculate here.
        if defer_recalc:
            return new_permission

        self._update_partial_permissions(user_obj.pk,
                                         perm,
                                         partial_perms=partial_perms)

        # Recalculate all descendants, re-fetching ourself first to guard
        # against stale MPTT values
        fresh_self = type(self).objects.get(pk=self.pk)
        fresh_self.recalculate_descendants_perms()
        return new_permission