rsinger86 / drf-access-policy

Declarative access policies/permissions modeled after AWS' IAM policies.
https://rsinger86.github.io/drf-access-policy/
MIT License
466 stars 50 forks source link

[Question] Dealing with unnecessary repetition of DB hits... #81

Open Ombental opened 2 years ago

Ombental commented 2 years ago

So, I have a Membership model, with 3 roles - viewer, member, manager I created a group for all users that aren't django staff or admins to filter out some processing My problem is I have to repeat the same calls to get the membership instance to check for the permission. (A member can be a part of multiple schedules)

class ShiftAccessPolicy(BaseAccessPolicy):

    statements = [
        {
            "action": "*",
            "principal": ["admin", "staff"],
            "effect": "allow",
        },
        {
            "action": ["list", "retrieve"],
            "principal": "group:auth_user",
            "effect": "allow",
            "condition": "is_schedule_member"
        },
        {
            "action": "create",
            "principal": "group:auth_user",
            "effect": "allow",
            "condition_expression": "is_not_viewer_only"
        },
        {
            "action": [ "archive"],
            "principal": "group:auth_user",
            "effect": "allow",
            "condition_expression": "is_schedule_manager"
        }
    ]

    def is_schedule_manager(self, request, view, action):
        membership = request.user.member.membership_set.filter(schedule__id=view.kwargs.get('schedule_pk'))
        if membership.exists():
            membership = membership[0]
            return Roles.SCHEDULE_MANAGER == membership.role
        else:
            return False

    def is_schedule_member(self, request, view, action):
        membership = request.user.member.membership_set.filter(schedule__id=view.kwargs.get('schedule_pk'))
        return membership.exists()

    def is_viewer_only(self, request, view, action):
        membership = request.user.member.membership_set.filter(schedule__id=view.kwargs.get('schedule_pk'))
        if membership.exists():
            membership = membership[0]
            return Roles.VIEWER != membership.role
        else:
            return False

As can be seen - each time I have to get the membership... 3 questions came into mind: 1) Is there a better way to use the access policy module that I'm missing? 2) Should we implement a "shared" instance for the types of cases? 3) in the case that I would use the same condition (lets say is_schedule_member would have been called multiple times) shouldn't we cache the result from the first time it was called?

rsinger86 commented 2 years ago

I have never faced this issue, but maybe there could be a method on the policy class called get_membership()?

You could implement caching there, with the key based on the combination of user id and schedule id.

I can't think of an approach specific to this package.