Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How do I access the post object in a predicate? #118

Closed
Routhinator opened this issue Mar 28, 2020 · 3 comments · May be fixed by #160
Closed

How do I access the post object in a predicate? #118

Routhinator opened this issue Mar 28, 2020 · 3 comments · May be fixed by #160

Comments

@Routhinator
Copy link

I'm trying to check if a user posting a discussion has access to the topic they are trying to associate the discussion with. However when the user posts, the second argument in the predicate, object is always None - I need to access the post body to check the topic attribute and compare to the users groups.

How do I access the post object? Is it available in the predicate context somewhere if I bind the predicate?

This is the logic I have so far which works for reading discussion objects, but it looks like I need different logic for POST requests due to the object being null:

@rules.predicate
def is_allowed(user, content):
    """
    | Accepts a user and content object and checks if the user is allowed to view it.
    :param user:
    :param content:
    :return:
    """
    if content is None or not (hasattr(content, 'topic') or not hasattr(content, 'group')):
        LOGGER.debug('Content was null or did not have topic')
        LOGGER.debug('Content: %s', repr(content))
        LOGGER.debug('User: %s', repr(user))
        return False
    if hasattr(content, 'group'):
        if content.group is None or not hasattr(content.group, 'name'):
            LOGGER.debug('Topic group was none or did not have name')
            return True
        LOGGER.debug('Checking if member is in group %s', content.group.name)
        if content.group.name in user.groups.all():
            return True
    if hasattr(content, 'topic'):
        if content.topic.group is None or not hasattr(content.topic.group, 'name'):
            LOGGER.debug('Topic group was none or did not have name')
            return True
        LOGGER.debug('Checking if member is in group %s', content.topic.group.name)
        if content.topic.group.name in user.groups.all():
            return True
    return False
@Routhinator
Copy link
Author

Routhinator commented Mar 29, 2020

After trying a few things, I don't think this is possible for a new object. I've resorted to only validating that they are authenticated, and I guess I'll build the permissions check for a new discussion into the create method of the serializer.

@dfunckt
Copy link
Owner

dfunckt commented May 18, 2020

Yes, it is not possible. The arguments Django passes to rules only contain the user and the object, and on create views specifically, not even the object (because there's none).

I'm closing this as it's been a while and it also doesn't really have anything to do with rules, as this is Django's standard way of checking for the create permission.

@dfunckt dfunckt closed this as completed May 18, 2020
@sergioisidoro
Copy link
Contributor

sergioisidoro commented Dec 22, 2021

For DRF mixin, would something like this work?

        elif self.action == "create":
            # Pass the permission a mock object that is not yet saved.
            # This replicates the behavior of the CreateMixin.
            tmp_serializer = self.get_serializer(
                data=self.request.data
            )
            tmp_serializer.is_valid(raise_exception=False)
            obj = model(**tmp_serializer.validated_data)

It would also be possible to override perform_create in the AutoPermissionViewSetMixin but that would make things sensitive to the order of the mixins.

But since the DRF is already plugging into `

EDIT: This actually doesn't work for more complex objects and because of: TypeError: Direct assignment to the forward side of a many-to-many set is prohibited. Use ... instead

But passing the serialised data does work, with a couple of caveats:

    def perform_create(self, serializer):
        model = self.get_queryset().model
        perm = self.get_permission_for_model(model, "add")

        if not self.request.user.has_perm(perm, serializer.validated_data):
            raise PermissionDenied

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants