-
-
Notifications
You must be signed in to change notification settings - Fork 88
[refactor] Use FilterByParent mixin in BaseEmailView #354 #426
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
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,11 +1,9 @@ | ||||||
| from allauth.account.models import EmailAddress | ||||||
| from django.contrib.auth import get_user_model | ||||||
| from django.core.exceptions import ValidationError | ||||||
| from django.utils.translation import gettext_lazy as _ | ||||||
| from drf_yasg.utils import swagger_auto_schema | ||||||
| from rest_framework import pagination | ||||||
| from rest_framework.authtoken.views import ObtainAuthToken | ||||||
| from rest_framework.exceptions import NotFound | ||||||
| from rest_framework.generics import ( | ||||||
| GenericAPIView, | ||||||
| ListCreateAPIView, | ||||||
|
|
@@ -20,6 +18,7 @@ | |||||
|
|
||||||
| from openwisp_users.api.permissions import DjangoModelPermissions | ||||||
|
|
||||||
| from .mixins import FilterByParent | ||||||
| from .mixins import ProtectedAPIMixin as BaseProtectedAPIMixin | ||||||
| from .serializers import ( | ||||||
| ChangePasswordSerializer, | ||||||
|
|
@@ -198,7 +197,7 @@ def update(self, request, *args, **kwargs): | |||||
| ) | ||||||
|
|
||||||
|
|
||||||
| class BaseEmailView(ProtectedAPIMixin, GenericAPIView): | ||||||
| class BaseEmailView(ProtectedAPIMixin, FilterByParent, GenericAPIView): | ||||||
| model = EmailAddress | ||||||
| serializer_class = EmailAddressSerializer | ||||||
|
|
||||||
|
|
@@ -209,28 +208,20 @@ def initial(self, *args, **kwargs): | |||||
| super().initial(*args, **kwargs) | ||||||
| self.assert_parent_exists() | ||||||
|
|
||||||
| def assert_parent_exists(self): | ||||||
| try: | ||||||
| assert self.get_parent_queryset().exists() | ||||||
| except (AssertionError, ValidationError): | ||||||
| user_id = self.kwargs['pk'] | ||||||
| raise NotFound(detail=_("User with ID '{}' not found.".format(user_id))) | ||||||
|
|
||||||
| def get_parent_queryset(self): | ||||||
| user = self.request.user | ||||||
|
|
||||||
| if user.is_superuser: | ||||||
| return User.objects.filter(pk=self.kwargs['pk']) | ||||||
|
|
||||||
| org_users = OrganizationUser.objects.filter(user=user).select_related( | ||||||
| 'organization' | ||||||
| ) | ||||||
| qs_user = User.objects.none() | ||||||
| for org_user in org_users: | ||||||
| if org_user.is_admin: | ||||||
| qs_user = qs_user | org_user.organization.users.all().distinct() | ||||||
| qs_user = qs_user.filter(is_superuser=False) | ||||||
| return qs_user.filter(pk=self.kwargs['pk']) | ||||||
| qs = User.objects.filter(pk=self.kwargs['pk']) | ||||||
| if self.request.user.is_superuser: | ||||||
| return qs | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if the user performing the request is superuser, just return the parent without further checks (superusers can do anything). |
||||||
| return self.get_organization_queryset(qs) | ||||||
|
|
||||||
| def get_organization_queryset(self, qs): | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The goal of this method, if I am not mistaken, is to ensure that the parent object is related to one of the organizations the user performing the API request manages, otherwise the API shall return 404 because nothing is found (the query doens't return any result). |
||||||
| orgs = self.request.user.organizations_managed | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Therefore we use this handy method to get the list of organization IDs the user manages. |
||||||
| return qs.filter( | ||||||
| # exclude superusers | ||||||
| is_superuser=False, | ||||||
|
||||||
| # ensure user is member of the org | ||||||
| openwisp_users_organizationuser__organization_id__in=orgs | ||||||
|
||||||
| openwisp_users_organizationuser__organization_id__in=orgs | |
| f'{app_label}_organizationuser__organization_id__in=orgs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, updated.
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure this is really needed anymore.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The distinct is required. There could be a scenario where org manager manages two organizations and an end user is part of both organizations. Then, that user will appear twice in the queryset.
In [2]: org_ids = Organization.objects.values_list('id', flat=True)
In [3]: org_ids
Out[3]: (0.000) SELECT "openwisp_users_organization"."id" FROM "openwisp_users_organization" ORDER BY "openwisp_users_organization"."name" ASC LIMIT 21; args=(); alias=default
<QuerySet [UUID('57197e42-b7a9-4342-b1ef-672d7fd6ed59'), UUID('33150131-ad21-40fb-8642-365499fb01d9')]>
In [4]: User.objects.filter(openwisp_users_organizationuser__organization_id__in=org_ids)
Out[4]: (0.000) SELECT "openwisp_users_user"."password", "openwisp_users_user"."last_login", "openwisp_users_user"."is_superuser", "openwisp_users_user"."username", "openwisp_users_user"."first_name", "openwisp_users_user"."last_name", "openwisp_users_user"."is_staff", "openwisp_users_user"."is_active", "openwisp_users_user"."date_joined", "openwisp_users_user"."id", "openwisp_users_user"."email", "openwisp_users_user"."bio", "openwisp_users_user"."url", "openwisp_users_user"."company", "openwisp_users_user"."location", "openwisp_users_user"."phone_number", "openwisp_users_user"."birth_date", "openwisp_users_user"."notes", "openwisp_users_user"."language", "openwisp_users_user"."password_updated" FROM "openwisp_users_user" INNER JOIN "openwisp_users_organizationuser" ON ("openwisp_users_user"."id" = "openwisp_users_organizationuser"."user_id") WHERE "openwisp_users_organizationuser"."organization_id" IN (SELECT U0."id" FROM "openwisp_users_organization" U0) LIMIT 21; args=(); alias=default
<QuerySet [<User: orguser>, <User: orguser>]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for double checking! 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method is shipped by
FilterByParentso we can remove it.