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

Feat/add cache to permissions #9003

Closed

Conversation

Ehsan200
Copy link
Contributor

Description

Add new base class for permissions to cache result of has_permission and 'has_object_permission' methods to prevent multi call in some senarios.

refs #8987

@auvipy auvipy self-requested a review June 10, 2023 11:24
@Ehsan200
Copy link
Contributor Author

no more progress in this PR

Copy link
Member

@auvipy auvipy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@encode/django-rest-framework Should we consider the caching of permissions by principle?

@sevdog
Copy link
Contributor

sevdog commented Jul 12, 2023

Caching with a simple lru_cache without any limits would just waste a lot of memory, since one element in the cache would lead to a memory-DoS quite easy.

If a cache should be used it should avoid this kind of problems.

This implementation however does not resolve the real issue since the permission classes are instantiated twice due to how APIView.get_permission is implemented:

def get_permissions(self):
"""
Instantiates and returns the list of permissions that this view requires.
"""
return [permission() for permission in self.permission_classes]

def check_permissions(self, request):
"""
Check if the request should be permitted.
Raises an appropriate exception if the request is not permitted.
"""
for permission in self.get_permissions():
if not permission.has_permission(request, self):
self.permission_denied(
request,
message=getattr(permission, 'message', None),
code=getattr(permission, 'code', None)
)
def check_object_permissions(self, request, obj):
"""
Check if the request should be permitted for a given object.
Raises an appropriate exception if the request is not permitted.
"""
for permission in self.get_permissions():
if not permission.has_object_permission(request, self, obj):
self.permission_denied(
request,
message=getattr(permission, 'message', None),
code=getattr(permission, 'code', None)
)

Thus, this kind of LRU is only going to waste memory and slow down the program.

I agree that #7522 may cause some problems, also because the behaviour is not so intuitive and is only documented in the changelog and not on the permissions API reference.

@Ehsan200
Copy link
Contributor Author

Ehsan200 commented Jul 12, 2023

Caching with a simple lru_cache without any limits would just waste a lot of memory, since one element in the cache would lead to a memory-DoS quite easy.

If a cache should be used it should avoid this kind of problems.

This implementation however does not resolve the real issue since the permission classes are instantiated twice due to how APIView.get_permission is implemented:

def get_permissions(self):
"""
Instantiates and returns the list of permissions that this view requires.
"""
return [permission() for permission in self.permission_classes]

def check_permissions(self, request):
"""
Check if the request should be permitted.
Raises an appropriate exception if the request is not permitted.
"""
for permission in self.get_permissions():
if not permission.has_permission(request, self):
self.permission_denied(
request,
message=getattr(permission, 'message', None),
code=getattr(permission, 'code', None)
)
def check_object_permissions(self, request, obj):
"""
Check if the request should be permitted for a given object.
Raises an appropriate exception if the request is not permitted.
"""
for permission in self.get_permissions():
if not permission.has_object_permission(request, self, obj):
self.permission_denied(
request,
message=getattr(permission, 'message', None),
code=getattr(permission, 'code', None)
)

Thus, this kind of LRU is only going to waste memory and slow down the program.

I agree that #7522 may cause some problems, also because the behaviour is not so intuitive and is only documented in the changelog and not on the permissions API reference.

Hello there!
You are right about lru_cache and I will change that.
but about the main problem, it will solve the issue because view classes will use the cached_permissions property to use the same permissions in that class.

@tomchristie
Copy link
Member

Should we consider the caching of permissions by principle?

Short: We shouldn't be making functional changes to REST framework now, other than tracking new Django releases.

Copy link
Member

@auvipy auvipy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am going to close this for now. i think this can be done as per projects need/customised.

@auvipy auvipy closed this Jul 12, 2023
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 this pull request may close these issues.

4 participants