✨(maildomain_access) add API endpoint to search users
Add new API endpoint to search for new users to whom we can assign new roles.
This commit is contained in:
@@ -8,6 +8,10 @@ and this project adheres to
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- ✨(maildomain_access) add API endpoint to search users #508
|
||||||
|
|
||||||
## [1.8.0] - 2024-12-12
|
## [1.8.0] - 2024-12-12
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
"""API endpoints"""
|
"""API endpoints"""
|
||||||
|
|
||||||
from django.db.models import Subquery
|
from django.db.models import Q, Subquery
|
||||||
|
|
||||||
from rest_framework import exceptions, filters, mixins, viewsets
|
from rest_framework import exceptions, filters, mixins, viewsets
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
|
||||||
from core import models as core_models
|
from core import models as core_models
|
||||||
|
from core.api.client.serializers import UserSerializer
|
||||||
|
|
||||||
from mailbox_manager import enums, models
|
from mailbox_manager import enums, models
|
||||||
from mailbox_manager.api import permissions
|
from mailbox_manager.api import permissions
|
||||||
@@ -76,6 +77,9 @@ class MailDomainAccessViewSet(
|
|||||||
Return list of all domain accesses related to the logged-in user and one
|
Return list of all domain accesses related to the logged-in user and one
|
||||||
domain access if an id is provided.
|
domain access if an id is provided.
|
||||||
|
|
||||||
|
GET /api/v1.0/mail-domains/<domain_slug>/accesses/users/
|
||||||
|
Return list of all users who can have an access to the domain
|
||||||
|
|
||||||
POST /api/v1.0/mail-domains/<domain_slug>/accesses/ with expected data:
|
POST /api/v1.0/mail-domains/<domain_slug>/accesses/ with expected data:
|
||||||
- user: str
|
- user: str
|
||||||
- role: str [owner|admin|viewer]
|
- role: str [owner|admin|viewer]
|
||||||
@@ -183,6 +187,30 @@ class MailDomainAccessViewSet(
|
|||||||
|
|
||||||
return super().destroy(request, *args, **kwargs)
|
return super().destroy(request, *args, **kwargs)
|
||||||
|
|
||||||
|
@action(detail=False, url_path="users", methods=["get"])
|
||||||
|
def get_available_users(self, request, domain_slug):
|
||||||
|
"""API endpoint to search user to give them new access.
|
||||||
|
More filters and permission will be added soon.
|
||||||
|
"""
|
||||||
|
domain = models.MailDomain.objects.get(slug=domain_slug)
|
||||||
|
abilities = domain.get_abilities(request.user)
|
||||||
|
if not abilities["manage_accesses"]:
|
||||||
|
raise exceptions.PermissionDenied()
|
||||||
|
|
||||||
|
queryset = (
|
||||||
|
core_models.User.objects.order_by("-created_at")
|
||||||
|
# exclude inactive users and get users from identified user's organization
|
||||||
|
.filter(is_active=True, organization_id=request.user.organization_id)
|
||||||
|
# exclude all users with already an access config
|
||||||
|
.exclude(mail_domain_accesses__domain__slug=domain_slug)
|
||||||
|
)
|
||||||
|
# Search by case-insensitive and accent-insensitive
|
||||||
|
if query := request.GET.get("q", ""):
|
||||||
|
queryset = queryset.filter(
|
||||||
|
Q(name__unaccent__icontains=query) | Q(email__unaccent__icontains=query)
|
||||||
|
)
|
||||||
|
return Response(UserSerializer(queryset.all(), many=True).data)
|
||||||
|
|
||||||
|
|
||||||
class MailBoxViewSet(
|
class MailBoxViewSet(
|
||||||
mixins.CreateModelMixin,
|
mixins.CreateModelMixin,
|
||||||
|
|||||||
@@ -0,0 +1,203 @@
|
|||||||
|
"""
|
||||||
|
Tests for MailDomainAccess API endpoint in People's mailbox manager app.
|
||||||
|
Focus on get available users.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from rest_framework import status
|
||||||
|
from rest_framework.test import APIClient
|
||||||
|
|
||||||
|
from core import factories as core_factories
|
||||||
|
from core import models as core_models
|
||||||
|
|
||||||
|
from mailbox_manager import enums, factories
|
||||||
|
|
||||||
|
pytestmark = pytest.mark.django_db
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_mail_domain__available_users_anonymous():
|
||||||
|
"""Anonymous users should not be allowed to list users."""
|
||||||
|
maildomain = factories.MailDomainFactory()
|
||||||
|
|
||||||
|
response = APIClient().get(
|
||||||
|
f"/api/v1.0/mail-domains/{maildomain.slug}/accesses/users/"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == status.HTTP_401_UNAUTHORIZED
|
||||||
|
assert response.json() == {
|
||||||
|
"detail": "Authentication credentials were not provided."
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_mail_domain__available_users_forbidden():
|
||||||
|
"""Authenticated user without accesses on maildomain should not be able to see available
|
||||||
|
users.
|
||||||
|
"""
|
||||||
|
authenticated_user = core_factories.UserFactory()
|
||||||
|
client = APIClient()
|
||||||
|
client.force_login(authenticated_user)
|
||||||
|
maildomain = factories.MailDomainFactory()
|
||||||
|
|
||||||
|
response = client.get(f"/api/v1.0/mail-domains/{maildomain.slug}/accesses/users/")
|
||||||
|
|
||||||
|
assert response.status_code == status.HTTP_403_FORBIDDEN
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"role",
|
||||||
|
[
|
||||||
|
enums.MailDomainRoleChoices.OWNER,
|
||||||
|
enums.MailDomainRoleChoices.ADMIN,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_api_mail_domain__list_available_users__with_abilities(role):
|
||||||
|
"""Authenticated users with roles owner and admin should be allowed to list available users
|
||||||
|
for a domain.
|
||||||
|
"""
|
||||||
|
dave = core_factories.UserFactory(email="bowbow@example.com", name="David Bowman")
|
||||||
|
nicole = core_factories.UserFactory(
|
||||||
|
email="nicole_foole@example.com", name="Nicole Foole"
|
||||||
|
)
|
||||||
|
frank = core_factories.UserFactory(
|
||||||
|
email="frank_poole@example.com", name="Frank Poole"
|
||||||
|
)
|
||||||
|
mary = core_factories.UserFactory(email="mary_pol@example.com", name="Mary Pol")
|
||||||
|
|
||||||
|
expected_ids = {str(user.id) for user in core_models.User.objects.all()}
|
||||||
|
|
||||||
|
authenticated_user = core_factories.UserFactory(name="Owen Rights")
|
||||||
|
client = APIClient()
|
||||||
|
client.force_login(authenticated_user)
|
||||||
|
|
||||||
|
maildomain = factories.MailDomainFactory(name="example.com")
|
||||||
|
factories.MailDomainAccessFactory(
|
||||||
|
user=authenticated_user,
|
||||||
|
domain=maildomain,
|
||||||
|
role=role,
|
||||||
|
)
|
||||||
|
|
||||||
|
response = client.get(f"/api/v1.0/mail-domains/{maildomain.slug}/accesses/users/")
|
||||||
|
assert response.status_code == status.HTTP_200_OK
|
||||||
|
results = response.json()
|
||||||
|
assert len(results) == 4
|
||||||
|
results_id = {result["id"] for result in results}
|
||||||
|
assert expected_ids == results_id
|
||||||
|
|
||||||
|
# now test filter user
|
||||||
|
response = client.get(
|
||||||
|
f"/api/v1.0/mail-domains/{maildomain.slug}/accesses/users/?q=OL"
|
||||||
|
)
|
||||||
|
assert response.status_code == status.HTTP_200_OK
|
||||||
|
expected_ids = {str(user.id) for user in [nicole, frank, mary]}
|
||||||
|
results = response.json()
|
||||||
|
assert len(results) == 3
|
||||||
|
results_id = {result["id"] for result in results}
|
||||||
|
assert expected_ids == results_id
|
||||||
|
|
||||||
|
# filter on email info
|
||||||
|
response = client.get(
|
||||||
|
f"/api/v1.0/mail-domains/{maildomain.slug}/accesses/users/?q=bowbow"
|
||||||
|
)
|
||||||
|
assert response.status_code == status.HTTP_200_OK
|
||||||
|
results = response.json()
|
||||||
|
assert len(results) == 1
|
||||||
|
assert results[0]["id"] == str(dave.id)
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_mail_domain__list_available_users__viewer():
|
||||||
|
"""A viewer should not be allowed to list available users for a domain."""
|
||||||
|
core_factories.UserFactory.create_batch(10)
|
||||||
|
|
||||||
|
authenticated_user = core_factories.UserFactory()
|
||||||
|
client = APIClient()
|
||||||
|
client.force_login(authenticated_user)
|
||||||
|
|
||||||
|
maildomain = factories.MailDomainFactory(name="example.com")
|
||||||
|
factories.MailDomainAccessFactory(
|
||||||
|
user=authenticated_user,
|
||||||
|
domain=maildomain,
|
||||||
|
role=enums.MailDomainRoleChoices.VIEWER,
|
||||||
|
)
|
||||||
|
|
||||||
|
response = client.get(f"/api/v1.0/mail-domains/{maildomain.slug}/accesses/users/")
|
||||||
|
assert response.status_code == status.HTTP_403_FORBIDDEN
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"role",
|
||||||
|
[
|
||||||
|
enums.MailDomainRoleChoices.OWNER,
|
||||||
|
enums.MailDomainRoleChoices.ADMIN,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_api_mail_domain__list_available_users__organization(role):
|
||||||
|
"""If an authenticated owner or admin of a domain has an organization,
|
||||||
|
only users from the same organization are available.
|
||||||
|
"""
|
||||||
|
organization = core_factories.OrganizationFactory(with_registration_id=True)
|
||||||
|
other_organization = core_factories.OrganizationFactory(with_registration_id=True)
|
||||||
|
authenticated_user = core_factories.UserFactory(
|
||||||
|
name="Owen Rights", organization=organization
|
||||||
|
)
|
||||||
|
|
||||||
|
client = APIClient()
|
||||||
|
client.force_login(authenticated_user)
|
||||||
|
|
||||||
|
maildomain = factories.MailDomainFactory(name="example.com")
|
||||||
|
factories.MailDomainAccessFactory(
|
||||||
|
user=authenticated_user,
|
||||||
|
domain=maildomain,
|
||||||
|
role=role,
|
||||||
|
)
|
||||||
|
|
||||||
|
dave = core_factories.UserFactory(
|
||||||
|
email="bowbow@example.com",
|
||||||
|
name="David Bowman",
|
||||||
|
organization=organization,
|
||||||
|
)
|
||||||
|
nicole = core_factories.UserFactory(
|
||||||
|
email="nicole_foole@example.com",
|
||||||
|
name="Nicole Foole",
|
||||||
|
organization=organization,
|
||||||
|
)
|
||||||
|
core_factories.UserFactory(
|
||||||
|
email="frank_poole@example.com",
|
||||||
|
name="Frank Poole",
|
||||||
|
organization=other_organization,
|
||||||
|
)
|
||||||
|
core_factories.UserFactory(
|
||||||
|
email="mary_pol@example.com",
|
||||||
|
name="Mary Pol",
|
||||||
|
organization=other_organization,
|
||||||
|
)
|
||||||
|
|
||||||
|
expected_ids = sorted([str(nicole.id), str(dave.id)])
|
||||||
|
response = client.get(f"/api/v1.0/mail-domains/{maildomain.slug}/accesses/users/")
|
||||||
|
assert response.status_code == status.HTTP_200_OK
|
||||||
|
results = response.json()
|
||||||
|
assert len(results) == 2
|
||||||
|
results_id = sorted({result["id"] for result in results})
|
||||||
|
assert expected_ids == results_id
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_mail_domain__list_available_users__organization_viewer():
|
||||||
|
"""A viewer should not be allowed to list available users for a domain."""
|
||||||
|
organization = core_factories.OrganizationFactory(with_registration_id=True)
|
||||||
|
other_organization = core_factories.OrganizationFactory(with_registration_id=True)
|
||||||
|
authenticated_user = core_factories.UserFactory(organization=organization)
|
||||||
|
|
||||||
|
client = APIClient()
|
||||||
|
client.force_login(authenticated_user)
|
||||||
|
|
||||||
|
maildomain = factories.MailDomainFactory()
|
||||||
|
factories.MailDomainAccessFactory(
|
||||||
|
user=authenticated_user,
|
||||||
|
domain=maildomain,
|
||||||
|
role=enums.MailDomainRoleChoices.VIEWER,
|
||||||
|
)
|
||||||
|
|
||||||
|
core_factories.UserFactory.create_batch(10, organization=organization)
|
||||||
|
core_factories.UserFactory.create_batch(5, organization=other_organization)
|
||||||
|
|
||||||
|
response = client.get(f"/api/v1.0/mail-domains/{maildomain.slug}/accesses/users/")
|
||||||
|
assert response.status_code == status.HTTP_403_FORBIDDEN
|
||||||
Reference in New Issue
Block a user