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

[feature] Implemented view permissions by extending DjangoModelPermissions class #249 #251

Merged
merged 5 commits into from
Jun 1, 2021

Conversation

ManishShah120
Copy link
Member

Closes #249

@ManishShah120 ManishShah120 changed the title [feature] Implemented view permissions by extending DjangoModelPermissions class #249 [feature] Implemented view permissions by extending DjangoModelPermissions class #249 May 22, 2021
@ManishShah120 ManishShah120 self-assigned this May 22, 2021
Comment on lines 93 to 97
if request.method == 'GET':
if request.user.has_perms(perms) or request.user.has_perms(change_perm):
return True
else:
return False
Copy link
Member Author

Choose a reason for hiding this comment

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

Did not handled the case of super user becasue superuser by default has all the permissions.

Copy link
Member

@atb00ker atb00ker May 22, 2021

Choose a reason for hiding this comment

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

I think you can add a check at the top of the function stating:

if user.is_superuser:
    return True

They are allowed to do anything regardless of any permission (this check is there in other has_permission functions too)!

P.S: Not sure if this would be required, please test! 😄

Copy link
Member Author

Choose a reason for hiding this comment

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

@atb00ker I am trying to implement this in more generic way, cause am thinking of opening a similar PR in DRF repo once this gets merged here.

By default superuser has all permissions i.e., it can do anything it wants. suppose manually someone removes all the permissions for any specific model for superuser. Then in that case this would not work, since we will return True if it;s a super user, so that case will fail.

What do you think?

Copy link
Member

Choose a reason for hiding this comment

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

To the best of my knowledge, removing permissions from superuser is an anti-pattern, but I understand your point.

  1. We should look at existing DRF functions and implement it in a similar way.
  2. I'll also wait for @nemesisdesign check this! 😄

Copy link
Member

@atb00ker atb00ker left a comment

Choose a reason for hiding this comment

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

This looks good to me, small comments:

tests/testapp/tests/test_permission_classes.py Outdated Show resolved Hide resolved
openwisp_users/api/permissions.py Outdated Show resolved Hide resolved
Comment on lines 93 to 97
if request.method == 'GET':
if request.user.has_perms(perms) or request.user.has_perms(change_perm):
return True
else:
return False
Copy link
Member

@atb00ker atb00ker May 22, 2021

Choose a reason for hiding this comment

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

I think you can add a check at the top of the function stating:

if user.is_superuser:
    return True

They are allowed to do anything regardless of any permission (this check is there in other has_permission functions too)!

P.S: Not sure if this would be required, please test! 😄

@ManishShah120
Copy link
Member Author

Quoting from the issue #249

Tests:

  • ensure an user of the operator group (not flagged as supersuser) cannot view both endpoints
  • Added
  • ensure an user of the administrator group (not flagged as supersuser) can view both endpoints
  • Added
  • add the view permission to the operator group, ensure the user now can view both endpoints but cannot change anything (put or delete)
  • Added

Copy link
Member

@nemesifier nemesifier left a comment

Choose a reason for hiding this comment

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

Looks on the right track! I'll need to test it.

But definitely please go ahead with the PR to Django REST Framework, hopefully we can get something similar or better merged there.

openwisp_users/api/permissions.py Outdated Show resolved Hide resolved
Copy link
Member

@okraits okraits left a comment

Choose a reason for hiding this comment

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

Looks fine. No objections from me.

Copy link
Member

@okraits okraits left a comment

Choose a reason for hiding this comment

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

Looks fine.

Copy link
Member

@atb00ker atb00ker left a comment

Choose a reason for hiding this comment

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

I think this is looking good, small improvement in the tests.

Comment on lines 131 to 135
user = User.objects.create_user(
username='operator', password='tester', email='operator@test.com'
)
operator_group = Group.objects.filter(name='Operator')
user.groups.set(operator_group)
Copy link
Member

Choose a reason for hiding this comment

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

Just FYI, I think there are functions for common actions. Like _create_operator.
These should be inherited and used! 😄

Copy link
Member Author

@ManishShah120 ManishShah120 May 31, 2021

Choose a reason for hiding this comment

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

Hey, @atb00ker Yes, I tried to use it but with this function, the user gets all the permissions without being in the operator group, and here we only needed testapp.view_template permission.

operator_group = Group.objects.filter(name='Operator')
user.groups.set(operator_group)
org1 = self._get_org()
OrganizationUser.objects.create(user=user, organization=org1, is_admin=True)
Copy link
Member

Choose a reason for hiding this comment

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

atb00ker
atb00ker previously approved these changes May 31, 2021
Copy link
Member

@atb00ker atb00ker left a comment

Choose a reason for hiding this comment

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

Functional Test:

  • superuser has access
  • non-su without view permission & org manager doesn't have access
  • non-su with view permission & not org manager has access but sees nothing
  • non-su with view permission & org manager can see temp for their obj. (not shared obj)

Copy link
Member

@nemesifier nemesifier left a comment

Choose a reason for hiding this comment

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

Great work @ManishShah120 @atb00ker! 👍

Missing:

  • Documentation (please add it near to the other sections related to DRF)
  • Code simplification (see below)

if user.has_perms(perms) or user.has_perms(change_perm):
return True
else:
return False
Copy link
Member

Choose a reason for hiding this comment

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

what about:

# user must have either view permission or change permission
return user.has_perms(perms) or user.has_perms(change_perm)

self.client.force_login(user)
token = self._obtain_auth_token()
auth = dict(HTTP_AUTHORIZATION=f'Bearer {token}')
t1 = self._create_template(organization=org1)
Copy link
Member

Choose a reason for hiding this comment

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

the first lines of each tests look really similar, please try to make a private helper method which prepares the data needed for the test, so we can make the actual test code shorter and easier to read.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done, Please have a look. 👍

@@ -66,3 +66,37 @@ class IsOrganizationOwner(BaseOrganizationPermission):

def validate_membership(self, user, org):
return org and (user.is_superuser or user.is_owner(org))


class ViewDjangoModelPermissions(DjangoModelPermissions):
Copy link
Member

Choose a reason for hiding this comment

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

we could call this simply DjangoModelPermissions

@@ -1,5 +1,5 @@
from django.utils.translation import gettext_lazy as _
from rest_framework.permissions import BasePermission
from rest_framework.permissions import BasePermission, DjangoModelPermissions
Copy link
Member

Choose a reason for hiding this comment

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

here you'd import it as BaseDjangoModelPermissions

Copy link
Member

@atb00ker atb00ker left a comment

Choose a reason for hiding this comment

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

LGTM! 👍

Comment on lines +139 to +141
user = self._get_user()
operator_group = Group.objects.filter(name='Operator')
user.groups.set(operator_group)
Copy link
Member

Choose a reason for hiding this comment

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

Doesn't _get_operator() help here?

Copy link
Member Author

Choose a reason for hiding this comment

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

No @atb00ker.

Copy link
Member

@nemesifier nemesifier left a comment

Choose a reason for hiding this comment

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

👍

@nemesifier nemesifier merged commit 70e0ffa into master Jun 1, 2021
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.

[feature] Add class to implement view permissions in DRF
4 participants