✨(models/api) add document model and API
We do this by making copies of existing Template and TemplateAccess models and API. A little refactoring is done to try to limit duplicate code.
This commit is contained in:
committed by
Anthony LC
parent
0024cc5814
commit
3e0739cd0a
@@ -0,0 +1,47 @@
|
||||
"""
|
||||
Tests for Documents API endpoint in impress's core app: create
|
||||
"""
|
||||
import pytest
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from core import factories
|
||||
from core.models import Document
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
def test_api_documents_create_anonymous():
|
||||
"""Anonymous users should not be allowed to create documents."""
|
||||
response = APIClient().post(
|
||||
"/api/v1.0/documents/",
|
||||
{
|
||||
"title": "my document",
|
||||
},
|
||||
)
|
||||
|
||||
assert response.status_code == 401
|
||||
assert not Document.objects.exists()
|
||||
|
||||
|
||||
def test_api_documents_create_authenticated():
|
||||
"""
|
||||
Authenticated users should be able to create documents and should automatically be declared
|
||||
as the owner of the newly created document.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
response = client.post(
|
||||
"/api/v1.0/documents/",
|
||||
{
|
||||
"title": "my document",
|
||||
},
|
||||
format="json",
|
||||
)
|
||||
|
||||
assert response.status_code == 201
|
||||
document = Document.objects.get()
|
||||
assert document.title == "my document"
|
||||
assert document.accesses.filter(role="owner", user=user).exists()
|
||||
106
src/backend/core/tests/documents/test_api_documents_delete.py
Normal file
106
src/backend/core/tests/documents/test_api_documents_delete.py
Normal file
@@ -0,0 +1,106 @@
|
||||
"""
|
||||
Tests for Documents API endpoint in impress's core app: delete
|
||||
"""
|
||||
import random
|
||||
|
||||
import pytest
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from core import factories, models
|
||||
from core.tests.conftest import TEAM, USER, VIA
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
def test_api_documents_delete_anonymous():
|
||||
"""Anonymous users should not be allowed to destroy a document."""
|
||||
document = factories.DocumentFactory()
|
||||
|
||||
response = APIClient().delete(
|
||||
f"/api/v1.0/documents/{document.id!s}/",
|
||||
)
|
||||
|
||||
assert response.status_code == 401
|
||||
assert models.Document.objects.count() == 1
|
||||
|
||||
|
||||
def test_api_documents_delete_authenticated_unrelated():
|
||||
"""
|
||||
Authenticated users should not be allowed to delete a document to which they are not
|
||||
related.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
is_public = random.choice([True, False])
|
||||
document = factories.DocumentFactory(is_public=is_public)
|
||||
|
||||
response = client.delete(
|
||||
f"/api/v1.0/documents/{document.id!s}/",
|
||||
)
|
||||
|
||||
assert response.status_code == 403 if is_public else 404
|
||||
assert models.Document.objects.count() == 1
|
||||
|
||||
|
||||
@pytest.mark.parametrize("role", ["member", "administrator"])
|
||||
@pytest.mark.parametrize("via", VIA)
|
||||
def test_api_documents_delete_authenticated_member_or_administrator(
|
||||
via, role, mock_user_get_teams
|
||||
):
|
||||
"""
|
||||
Authenticated users should not be allowed to delete a document for which they are
|
||||
only a member or administrator.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
document = factories.DocumentFactory()
|
||||
if via == USER:
|
||||
factories.UserDocumentAccessFactory(document=document, user=user, role=role)
|
||||
elif via == TEAM:
|
||||
mock_user_get_teams.return_value = ["lasuite", "unknown"]
|
||||
factories.TeamDocumentAccessFactory(
|
||||
document=document, team="lasuite", role=role
|
||||
)
|
||||
|
||||
response = client.delete(
|
||||
f"/api/v1.0/documents/{document.id}/",
|
||||
)
|
||||
|
||||
assert response.status_code == 403
|
||||
assert response.json() == {
|
||||
"detail": "You do not have permission to perform this action."
|
||||
}
|
||||
assert models.Document.objects.count() == 1
|
||||
|
||||
|
||||
@pytest.mark.parametrize("via", VIA)
|
||||
def test_api_documents_delete_authenticated_owner(via, mock_user_get_teams):
|
||||
"""
|
||||
Authenticated users should be able to delete a document they own.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
document = factories.DocumentFactory()
|
||||
if via == USER:
|
||||
factories.UserDocumentAccessFactory(document=document, user=user, role="owner")
|
||||
elif via == TEAM:
|
||||
mock_user_get_teams.return_value = ["lasuite", "unknown"]
|
||||
factories.TeamDocumentAccessFactory(
|
||||
document=document, team="lasuite", role="owner"
|
||||
)
|
||||
|
||||
response = client.delete(
|
||||
f"/api/v1.0/documents/{document.id}/",
|
||||
)
|
||||
|
||||
assert response.status_code == 204
|
||||
assert models.Document.objects.exists() is False
|
||||
166
src/backend/core/tests/documents/test_api_documents_list.py
Normal file
166
src/backend/core/tests/documents/test_api_documents_list.py
Normal file
@@ -0,0 +1,166 @@
|
||||
"""
|
||||
Tests for Documents API endpoint in impress's core app: list
|
||||
"""
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
from rest_framework.pagination import PageNumberPagination
|
||||
from rest_framework.status import HTTP_200_OK
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from core import factories
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
def test_api_documents_list_anonymous():
|
||||
"""Anonymous users should only be able to list public documents."""
|
||||
factories.DocumentFactory.create_batch(2, is_public=False)
|
||||
documents = factories.DocumentFactory.create_batch(2, is_public=True)
|
||||
expected_ids = {str(document.id) for document in documents}
|
||||
|
||||
response = APIClient().get("/api/v1.0/documents/")
|
||||
|
||||
assert response.status_code == HTTP_200_OK
|
||||
results = response.json()["results"]
|
||||
assert len(results) == 2
|
||||
results_id = {result["id"] for result in results}
|
||||
assert expected_ids == results_id
|
||||
|
||||
|
||||
def test_api_documents_list_authenticated_direct():
|
||||
"""
|
||||
Authenticated users should be able to list documents they are a direct
|
||||
owner/administrator/member of.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
related_documents = [
|
||||
access.document
|
||||
for access in factories.UserDocumentAccessFactory.create_batch(5, user=user)
|
||||
]
|
||||
public_documents = factories.DocumentFactory.create_batch(2, is_public=True)
|
||||
factories.DocumentFactory.create_batch(2, is_public=False)
|
||||
|
||||
expected_ids = {
|
||||
str(document.id) for document in related_documents + public_documents
|
||||
}
|
||||
|
||||
response = client.get(
|
||||
"/api/v1.0/documents/",
|
||||
)
|
||||
|
||||
assert response.status_code == HTTP_200_OK
|
||||
results = response.json()["results"]
|
||||
assert len(results) == 7
|
||||
results_id = {result["id"] for result in results}
|
||||
assert expected_ids == results_id
|
||||
|
||||
|
||||
def test_api_documents_list_authenticated_via_team(mock_user_get_teams):
|
||||
"""
|
||||
Authenticated users should be able to list documents they are a
|
||||
owner/administrator/member of via a team.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
mock_user_get_teams.return_value = ["team1", "team2", "unknown"]
|
||||
|
||||
documents_team1 = [
|
||||
access.document
|
||||
for access in factories.TeamDocumentAccessFactory.create_batch(2, team="team1")
|
||||
]
|
||||
documents_team2 = [
|
||||
access.document
|
||||
for access in factories.TeamDocumentAccessFactory.create_batch(3, team="team2")
|
||||
]
|
||||
public_documents = factories.DocumentFactory.create_batch(2, is_public=True)
|
||||
factories.DocumentFactory.create_batch(2, is_public=False)
|
||||
|
||||
expected_ids = {
|
||||
str(document.id)
|
||||
for document in documents_team1 + documents_team2 + public_documents
|
||||
}
|
||||
|
||||
response = client.get("/api/v1.0/documents/")
|
||||
|
||||
assert response.status_code == HTTP_200_OK
|
||||
results = response.json()["results"]
|
||||
assert len(results) == 7
|
||||
results_id = {result["id"] for result in results}
|
||||
assert expected_ids == results_id
|
||||
|
||||
|
||||
@mock.patch.object(PageNumberPagination, "get_page_size", return_value=2)
|
||||
def test_api_documents_list_pagination(
|
||||
_mock_page_size,
|
||||
):
|
||||
"""Pagination should work as expected."""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
document_ids = [
|
||||
str(access.document_id)
|
||||
for access in factories.UserDocumentAccessFactory.create_batch(3, user=user)
|
||||
]
|
||||
|
||||
# Get page 1
|
||||
response = client.get(
|
||||
"/api/v1.0/documents/",
|
||||
)
|
||||
|
||||
assert response.status_code == HTTP_200_OK
|
||||
content = response.json()
|
||||
|
||||
assert content["count"] == 3
|
||||
assert content["next"] == "http://testserver/api/v1.0/documents/?page=2"
|
||||
assert content["previous"] is None
|
||||
|
||||
assert len(content["results"]) == 2
|
||||
for item in content["results"]:
|
||||
document_ids.remove(item["id"])
|
||||
|
||||
# Get page 2
|
||||
response = client.get(
|
||||
"/api/v1.0/documents/?page=2",
|
||||
)
|
||||
|
||||
assert response.status_code == HTTP_200_OK
|
||||
content = response.json()
|
||||
|
||||
assert content["count"] == 3
|
||||
assert content["next"] is None
|
||||
assert content["previous"] == "http://testserver/api/v1.0/documents/"
|
||||
|
||||
assert len(content["results"]) == 1
|
||||
document_ids.remove(content["results"][0]["id"])
|
||||
assert document_ids == []
|
||||
|
||||
|
||||
def test_api_documents_list_authenticated_distinct():
|
||||
"""A document with several related users should only be listed once."""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
other_user = factories.UserFactory()
|
||||
|
||||
document = factories.DocumentFactory(users=[user, other_user], is_public=True)
|
||||
|
||||
response = client.get(
|
||||
"/api/v1.0/documents/",
|
||||
)
|
||||
|
||||
assert response.status_code == HTTP_200_OK
|
||||
content = response.json()
|
||||
assert len(content["results"]) == 1
|
||||
assert content["results"][0]["id"] == str(document.id)
|
||||
441
src/backend/core/tests/documents/test_api_documents_retrieve.py
Normal file
441
src/backend/core/tests/documents/test_api_documents_retrieve.py
Normal file
@@ -0,0 +1,441 @@
|
||||
"""
|
||||
Tests for Documents API endpoint in impress's core app: retrieve
|
||||
"""
|
||||
import pytest
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from core import factories
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
def test_api_documents_retrieve_anonymous_public():
|
||||
"""Anonymous users should be allowed to retrieve public documents."""
|
||||
document = factories.DocumentFactory(is_public=True)
|
||||
|
||||
response = APIClient().get(f"/api/v1.0/documents/{document.id!s}/")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {
|
||||
"id": str(document.id),
|
||||
"abilities": {
|
||||
"destroy": False,
|
||||
"manage_accesses": False,
|
||||
"retrieve": True,
|
||||
"update": False,
|
||||
},
|
||||
"accesses": [],
|
||||
"title": document.title,
|
||||
}
|
||||
|
||||
|
||||
def test_api_documents_retrieve_anonymous_not_public():
|
||||
"""Anonymous users should not be able to retrieve a document that is not public."""
|
||||
document = factories.DocumentFactory(is_public=False)
|
||||
|
||||
response = APIClient().get(f"/api/v1.0/documents/{document.id!s}/")
|
||||
|
||||
assert response.status_code == 404
|
||||
assert response.json() == {"detail": "Not found."}
|
||||
|
||||
|
||||
def test_api_documents_retrieve_authenticated_unrelated_public():
|
||||
"""
|
||||
Authenticated users should be able to retrieve a public document to which they are
|
||||
not related.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
document = factories.DocumentFactory(is_public=True)
|
||||
|
||||
response = client.get(
|
||||
f"/api/v1.0/documents/{document.id!s}/",
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {
|
||||
"id": str(document.id),
|
||||
"abilities": {
|
||||
"destroy": False,
|
||||
"manage_accesses": False,
|
||||
"retrieve": True,
|
||||
"update": False,
|
||||
},
|
||||
"accesses": [],
|
||||
"title": document.title,
|
||||
}
|
||||
|
||||
|
||||
def test_api_documents_retrieve_authenticated_unrelated_not_public():
|
||||
"""
|
||||
Authenticated users should not be allowed to retrieve a document that is not public and
|
||||
to which they are not related.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
document = factories.DocumentFactory(is_public=False)
|
||||
|
||||
response = client.get(
|
||||
f"/api/v1.0/documents/{document.id!s}/",
|
||||
)
|
||||
assert response.status_code == 404
|
||||
assert response.json() == {"detail": "Not found."}
|
||||
|
||||
|
||||
def test_api_documents_retrieve_authenticated_related_direct():
|
||||
"""
|
||||
Authenticated users should be allowed to retrieve a document to which they
|
||||
are directly related whatever the role.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
document = factories.DocumentFactory()
|
||||
access1 = factories.UserDocumentAccessFactory(document=document, user=user)
|
||||
access2 = factories.UserDocumentAccessFactory(document=document)
|
||||
|
||||
response = client.get(
|
||||
f"/api/v1.0/documents/{document.id!s}/",
|
||||
)
|
||||
assert response.status_code == 200
|
||||
content = response.json()
|
||||
assert sorted(content.pop("accesses"), key=lambda x: x["user"]) == sorted(
|
||||
[
|
||||
{
|
||||
"id": str(access1.id),
|
||||
"user": str(user.id),
|
||||
"team": "",
|
||||
"role": access1.role,
|
||||
"abilities": access1.get_abilities(user),
|
||||
},
|
||||
{
|
||||
"id": str(access2.id),
|
||||
"user": str(access2.user.id),
|
||||
"team": "",
|
||||
"role": access2.role,
|
||||
"abilities": access2.get_abilities(user),
|
||||
},
|
||||
],
|
||||
key=lambda x: x["user"],
|
||||
)
|
||||
assert response.json() == {
|
||||
"id": str(document.id),
|
||||
"title": document.title,
|
||||
"abilities": document.get_abilities(user),
|
||||
}
|
||||
|
||||
|
||||
def test_api_documents_retrieve_authenticated_related_team_none(mock_user_get_teams):
|
||||
"""
|
||||
Authenticated users should not be able to retrieve a document related to teams in
|
||||
which the user is not.
|
||||
"""
|
||||
mock_user_get_teams.return_value = []
|
||||
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
document = factories.DocumentFactory(is_public=False)
|
||||
|
||||
factories.TeamDocumentAccessFactory(
|
||||
document=document, team="members", role="member"
|
||||
)
|
||||
factories.TeamDocumentAccessFactory(
|
||||
document=document, team="administrators", role="administrator"
|
||||
)
|
||||
factories.TeamDocumentAccessFactory(document=document, team="owners", role="owner")
|
||||
factories.TeamDocumentAccessFactory(document=document)
|
||||
factories.TeamDocumentAccessFactory()
|
||||
|
||||
response = client.get(f"/api/v1.0/documents/{document.id!s}/")
|
||||
assert response.status_code == 404
|
||||
assert response.json() == {"detail": "Not found."}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"teams",
|
||||
[
|
||||
["members"],
|
||||
["unknown", "members"],
|
||||
],
|
||||
)
|
||||
def test_api_documents_retrieve_authenticated_related_team_members(
|
||||
teams, mock_user_get_teams
|
||||
):
|
||||
"""
|
||||
Authenticated users should be allowed to retrieve a document to which they
|
||||
are related via a team whatever the role and see all its accesses.
|
||||
"""
|
||||
mock_user_get_teams.return_value = teams
|
||||
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
document = factories.DocumentFactory(is_public=False)
|
||||
|
||||
access_member = factories.TeamDocumentAccessFactory(
|
||||
document=document, team="members", role="member"
|
||||
)
|
||||
access_administrator = factories.TeamDocumentAccessFactory(
|
||||
document=document, team="administrators", role="administrator"
|
||||
)
|
||||
access_owner = factories.TeamDocumentAccessFactory(
|
||||
document=document, team="owners", role="owner"
|
||||
)
|
||||
other_access = factories.TeamDocumentAccessFactory(document=document)
|
||||
factories.TeamDocumentAccessFactory()
|
||||
|
||||
response = client.get(f"/api/v1.0/documents/{document.id!s}/")
|
||||
assert response.status_code == 200
|
||||
content = response.json()
|
||||
expected_abilities = {
|
||||
"destroy": False,
|
||||
"retrieve": True,
|
||||
"set_role_to": [],
|
||||
"update": False,
|
||||
}
|
||||
assert sorted(content.pop("accesses"), key=lambda x: x["id"]) == sorted(
|
||||
[
|
||||
{
|
||||
"id": str(access_member.id),
|
||||
"user": None,
|
||||
"team": "members",
|
||||
"role": access_member.role,
|
||||
"abilities": expected_abilities,
|
||||
},
|
||||
{
|
||||
"id": str(access_administrator.id),
|
||||
"user": None,
|
||||
"team": "administrators",
|
||||
"role": access_administrator.role,
|
||||
"abilities": expected_abilities,
|
||||
},
|
||||
{
|
||||
"id": str(access_owner.id),
|
||||
"user": None,
|
||||
"team": "owners",
|
||||
"role": access_owner.role,
|
||||
"abilities": expected_abilities,
|
||||
},
|
||||
{
|
||||
"id": str(other_access.id),
|
||||
"user": None,
|
||||
"team": other_access.team,
|
||||
"role": other_access.role,
|
||||
"abilities": expected_abilities,
|
||||
},
|
||||
],
|
||||
key=lambda x: x["id"],
|
||||
)
|
||||
assert response.json() == {
|
||||
"id": str(document.id),
|
||||
"title": document.title,
|
||||
"abilities": document.get_abilities(user),
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"teams",
|
||||
[
|
||||
["administrators"],
|
||||
["members", "administrators"],
|
||||
["unknown", "administrators"],
|
||||
],
|
||||
)
|
||||
def test_api_documents_retrieve_authenticated_related_team_administrators(
|
||||
teams, mock_user_get_teams
|
||||
):
|
||||
"""
|
||||
Authenticated users should be allowed to retrieve a document to which they
|
||||
are related via a team whatever the role and see all its accesses.
|
||||
"""
|
||||
mock_user_get_teams.return_value = teams
|
||||
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
document = factories.DocumentFactory(is_public=False)
|
||||
|
||||
access_member = factories.TeamDocumentAccessFactory(
|
||||
document=document, team="members", role="member"
|
||||
)
|
||||
access_administrator = factories.TeamDocumentAccessFactory(
|
||||
document=document, team="administrators", role="administrator"
|
||||
)
|
||||
access_owner = factories.TeamDocumentAccessFactory(
|
||||
document=document, team="owners", role="owner"
|
||||
)
|
||||
other_access = factories.TeamDocumentAccessFactory(document=document)
|
||||
factories.TeamDocumentAccessFactory()
|
||||
|
||||
response = client.get(f"/api/v1.0/documents/{document.id!s}/")
|
||||
|
||||
# pylint: disable=R0801
|
||||
assert response.status_code == 200
|
||||
content = response.json()
|
||||
assert sorted(content.pop("accesses"), key=lambda x: x["id"]) == sorted(
|
||||
[
|
||||
{
|
||||
"id": str(access_member.id),
|
||||
"user": None,
|
||||
"team": "members",
|
||||
"role": "member",
|
||||
"abilities": {
|
||||
"destroy": True,
|
||||
"retrieve": True,
|
||||
"set_role_to": ["administrator"],
|
||||
"update": True,
|
||||
},
|
||||
},
|
||||
{
|
||||
"id": str(access_administrator.id),
|
||||
"user": None,
|
||||
"team": "administrators",
|
||||
"role": "administrator",
|
||||
"abilities": {
|
||||
"destroy": True,
|
||||
"retrieve": True,
|
||||
"set_role_to": ["member"],
|
||||
"update": True,
|
||||
},
|
||||
},
|
||||
{
|
||||
"id": str(access_owner.id),
|
||||
"user": None,
|
||||
"team": "owners",
|
||||
"role": "owner",
|
||||
"abilities": {
|
||||
"destroy": False,
|
||||
"retrieve": True,
|
||||
"set_role_to": [],
|
||||
"update": False,
|
||||
},
|
||||
},
|
||||
{
|
||||
"id": str(other_access.id),
|
||||
"user": None,
|
||||
"team": other_access.team,
|
||||
"role": other_access.role,
|
||||
"abilities": other_access.get_abilities(user),
|
||||
},
|
||||
],
|
||||
key=lambda x: x["id"],
|
||||
)
|
||||
assert response.json() == {
|
||||
"id": str(document.id),
|
||||
"title": document.title,
|
||||
"abilities": document.get_abilities(user),
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"teams",
|
||||
[
|
||||
["owners"],
|
||||
["owners", "administrators"],
|
||||
["members", "administrators", "owners"],
|
||||
["unknown", "owners"],
|
||||
],
|
||||
)
|
||||
def test_api_documents_retrieve_authenticated_related_team_owners(
|
||||
teams, mock_user_get_teams
|
||||
):
|
||||
"""
|
||||
Authenticated users should be allowed to retrieve a document to which they
|
||||
are related via a team whatever the role and see all its accesses.
|
||||
"""
|
||||
mock_user_get_teams.return_value = teams
|
||||
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
document = factories.DocumentFactory(is_public=False)
|
||||
|
||||
access_member = factories.TeamDocumentAccessFactory(
|
||||
document=document, team="members", role="member"
|
||||
)
|
||||
access_administrator = factories.TeamDocumentAccessFactory(
|
||||
document=document, team="administrators", role="administrator"
|
||||
)
|
||||
access_owner = factories.TeamDocumentAccessFactory(
|
||||
document=document, team="owners", role="owner"
|
||||
)
|
||||
other_access = factories.TeamDocumentAccessFactory(document=document)
|
||||
factories.TeamDocumentAccessFactory()
|
||||
|
||||
response = client.get(f"/api/v1.0/documents/{document.id!s}/")
|
||||
|
||||
# pylint: disable=R0801
|
||||
assert response.status_code == 200
|
||||
content = response.json()
|
||||
assert sorted(content.pop("accesses"), key=lambda x: x["id"]) == sorted(
|
||||
[
|
||||
{
|
||||
"id": str(access_member.id),
|
||||
"user": None,
|
||||
"team": "members",
|
||||
"role": "member",
|
||||
"abilities": {
|
||||
"destroy": True,
|
||||
"retrieve": True,
|
||||
"set_role_to": ["owner", "administrator"],
|
||||
"update": True,
|
||||
},
|
||||
},
|
||||
{
|
||||
"id": str(access_administrator.id),
|
||||
"user": None,
|
||||
"team": "administrators",
|
||||
"role": "administrator",
|
||||
"abilities": {
|
||||
"destroy": True,
|
||||
"retrieve": True,
|
||||
"set_role_to": ["owner", "member"],
|
||||
"update": True,
|
||||
},
|
||||
},
|
||||
{
|
||||
"id": str(access_owner.id),
|
||||
"user": None,
|
||||
"team": "owners",
|
||||
"role": "owner",
|
||||
"abilities": {
|
||||
# editable only if there is another owner role than the user's team...
|
||||
"destroy": other_access.role == "owner",
|
||||
"retrieve": True,
|
||||
"set_role_to": ["administrator", "member"]
|
||||
if other_access.role == "owner"
|
||||
else [],
|
||||
"update": other_access.role == "owner",
|
||||
},
|
||||
},
|
||||
{
|
||||
"id": str(other_access.id),
|
||||
"user": None,
|
||||
"team": other_access.team,
|
||||
"role": other_access.role,
|
||||
"abilities": other_access.get_abilities(user),
|
||||
},
|
||||
],
|
||||
key=lambda x: x["id"],
|
||||
)
|
||||
assert response.json() == {
|
||||
"id": str(document.id),
|
||||
"title": document.title,
|
||||
"abilities": document.get_abilities(user),
|
||||
}
|
||||
230
src/backend/core/tests/documents/test_api_documents_update.py
Normal file
230
src/backend/core/tests/documents/test_api_documents_update.py
Normal file
@@ -0,0 +1,230 @@
|
||||
"""
|
||||
Tests for Documents API endpoint in impress's core app: update
|
||||
"""
|
||||
import random
|
||||
|
||||
import pytest
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from core import factories
|
||||
from core.api import serializers
|
||||
from core.tests.conftest import TEAM, USER, VIA
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
def test_api_documents_update_anonymous():
|
||||
"""Anonymous users should not be allowed to update a document."""
|
||||
document = factories.DocumentFactory()
|
||||
old_document_values = serializers.DocumentSerializer(instance=document).data
|
||||
|
||||
new_document_values = serializers.DocumentSerializer(
|
||||
instance=factories.DocumentFactory()
|
||||
).data
|
||||
response = APIClient().put(
|
||||
f"/api/v1.0/documents/{document.id!s}/",
|
||||
new_document_values,
|
||||
format="json",
|
||||
)
|
||||
assert response.status_code == 401
|
||||
assert response.json() == {
|
||||
"detail": "Authentication credentials were not provided."
|
||||
}
|
||||
|
||||
document.refresh_from_db()
|
||||
document_values = serializers.DocumentSerializer(instance=document).data
|
||||
assert document_values == old_document_values
|
||||
|
||||
|
||||
def test_api_documents_update_authenticated_unrelated():
|
||||
"""
|
||||
Authenticated users should not be allowed to update a document to which they are not related.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
document = factories.DocumentFactory(is_public=False)
|
||||
old_document_values = serializers.DocumentSerializer(instance=document).data
|
||||
|
||||
new_document_values = serializers.DocumentSerializer(
|
||||
instance=factories.DocumentFactory()
|
||||
).data
|
||||
response = client.put(
|
||||
f"/api/v1.0/documents/{document.id!s}/",
|
||||
new_document_values,
|
||||
format="json",
|
||||
)
|
||||
|
||||
assert response.status_code == 404
|
||||
assert response.json() == {"detail": "Not found."}
|
||||
|
||||
document.refresh_from_db()
|
||||
document_values = serializers.DocumentSerializer(instance=document).data
|
||||
assert document_values == old_document_values
|
||||
|
||||
|
||||
@pytest.mark.parametrize("via", VIA)
|
||||
def test_api_documents_update_authenticated_members(via, mock_user_get_teams):
|
||||
"""
|
||||
Users who are members of a document but not administrators should
|
||||
not be allowed to update it.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
document = factories.DocumentFactory()
|
||||
if via == USER:
|
||||
factories.UserDocumentAccessFactory(document=document, user=user, role="member")
|
||||
elif via == TEAM:
|
||||
mock_user_get_teams.return_value = ["lasuite", "unknown"]
|
||||
factories.TeamDocumentAccessFactory(
|
||||
document=document, team="lasuite", role="member"
|
||||
)
|
||||
|
||||
old_document_values = serializers.DocumentSerializer(instance=document).data
|
||||
|
||||
new_document_values = serializers.DocumentSerializer(
|
||||
instance=factories.DocumentFactory()
|
||||
).data
|
||||
response = client.put(
|
||||
f"/api/v1.0/documents/{document.id!s}/",
|
||||
new_document_values,
|
||||
format="json",
|
||||
)
|
||||
|
||||
assert response.status_code == 403
|
||||
assert response.json() == {
|
||||
"detail": "You do not have permission to perform this action."
|
||||
}
|
||||
|
||||
document.refresh_from_db()
|
||||
document_values = serializers.DocumentSerializer(instance=document).data
|
||||
assert document_values == old_document_values
|
||||
|
||||
|
||||
@pytest.mark.parametrize("role", ["administrator", "owner"])
|
||||
@pytest.mark.parametrize("via", VIA)
|
||||
def test_api_documents_update_authenticated_administrator_or_owner(
|
||||
via, role, mock_user_get_teams
|
||||
):
|
||||
"""Administrator or owner of a document should be allowed to update it."""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
document = factories.DocumentFactory()
|
||||
if via == USER:
|
||||
factories.UserDocumentAccessFactory(document=document, user=user, role=role)
|
||||
elif via == TEAM:
|
||||
mock_user_get_teams.return_value = ["lasuite", "unknown"]
|
||||
factories.TeamDocumentAccessFactory(
|
||||
document=document, team="lasuite", role=role
|
||||
)
|
||||
|
||||
old_document_values = serializers.DocumentSerializer(instance=document).data
|
||||
|
||||
new_document_values = serializers.DocumentSerializer(
|
||||
instance=factories.DocumentFactory()
|
||||
).data
|
||||
response = client.put(
|
||||
f"/api/v1.0/documents/{document.id!s}/",
|
||||
new_document_values,
|
||||
format="json",
|
||||
)
|
||||
assert response.status_code == 200
|
||||
|
||||
document.refresh_from_db()
|
||||
document_values = serializers.DocumentSerializer(instance=document).data
|
||||
for key, value in document_values.items():
|
||||
if key in ["id", "accesses"]:
|
||||
assert value == old_document_values[key]
|
||||
else:
|
||||
assert value == new_document_values[key]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("via", VIA)
|
||||
def test_api_documents_update_authenticated_owners(via, mock_user_get_teams):
|
||||
"""Administrators of a document should be allowed to update it."""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
document = factories.DocumentFactory()
|
||||
if via == USER:
|
||||
factories.UserDocumentAccessFactory(document=document, user=user, role="owner")
|
||||
elif via == TEAM:
|
||||
mock_user_get_teams.return_value = ["lasuite", "unknown"]
|
||||
factories.TeamDocumentAccessFactory(
|
||||
document=document, team="lasuite", role="owner"
|
||||
)
|
||||
|
||||
old_document_values = serializers.DocumentSerializer(instance=document).data
|
||||
|
||||
new_document_values = serializers.DocumentSerializer(
|
||||
instance=factories.DocumentFactory()
|
||||
).data
|
||||
|
||||
response = client.put(
|
||||
f"/api/v1.0/documents/{document.id!s}/", new_document_values, format="json"
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
document.refresh_from_db()
|
||||
document_values = serializers.DocumentSerializer(instance=document).data
|
||||
for key, value in document_values.items():
|
||||
if key in ["id", "accesses"]:
|
||||
assert value == old_document_values[key]
|
||||
else:
|
||||
assert value == new_document_values[key]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("via", VIA)
|
||||
def test_api_documents_update_administrator_or_owner_of_another(
|
||||
via, mock_user_get_teams
|
||||
):
|
||||
"""
|
||||
Being administrator or owner of a document should not grant authorization to update
|
||||
another document.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
document = factories.DocumentFactory()
|
||||
if via == USER:
|
||||
factories.UserDocumentAccessFactory(
|
||||
document=document, user=user, role=random.choice(["administrator", "owner"])
|
||||
)
|
||||
elif via == TEAM:
|
||||
mock_user_get_teams.return_value = ["lasuite", "unknown"]
|
||||
factories.TeamDocumentAccessFactory(
|
||||
document=document,
|
||||
team="lasuite",
|
||||
role=random.choice(["administrator", "owner"]),
|
||||
)
|
||||
|
||||
is_public = random.choice([True, False])
|
||||
document = factories.DocumentFactory(title="Old title", is_public=is_public)
|
||||
old_document_values = serializers.DocumentSerializer(instance=document).data
|
||||
|
||||
new_document_values = serializers.DocumentSerializer(
|
||||
instance=factories.DocumentFactory()
|
||||
).data
|
||||
response = client.put(
|
||||
f"/api/v1.0/documents/{document.id!s}/",
|
||||
new_document_values,
|
||||
format="json",
|
||||
)
|
||||
|
||||
assert response.status_code == 403 if is_public else 404
|
||||
|
||||
document.refresh_from_db()
|
||||
document_values = serializers.DocumentSerializer(instance=document).data
|
||||
assert document_values == old_document_values
|
||||
@@ -107,7 +107,7 @@ def test_api_templates_generate_document_related(via, mock_user_get_teams):
|
||||
data = {"body": "# Test markdown body"}
|
||||
|
||||
response = client.post(
|
||||
f"/api/v1.0/templates/{access.template.id!s}/generate-document/",
|
||||
f"/api/v1.0/templates/{access.template_id!s}/generate-document/",
|
||||
data,
|
||||
format="json",
|
||||
)
|
||||
|
||||
@@ -108,7 +108,7 @@ def test_api_templates_list_pagination(
|
||||
client.force_login(user)
|
||||
|
||||
template_ids = [
|
||||
str(access.template.id)
|
||||
str(access.template_id)
|
||||
for access in factories.UserTemplateAccessFactory.create_batch(3, user=user)
|
||||
]
|
||||
|
||||
|
||||
967
src/backend/core/tests/test_api_document_accesses.py
Normal file
967
src/backend/core/tests/test_api_document_accesses.py
Normal file
@@ -0,0 +1,967 @@
|
||||
"""
|
||||
Test document accesses API endpoints for users in impress's core app.
|
||||
"""
|
||||
import random
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from core import factories, models
|
||||
from core.api import serializers
|
||||
from core.tests.conftest import TEAM, USER, VIA
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
def test_api_document_accesses_list_anonymous():
|
||||
"""Anonymous users should not be allowed to list document accesses."""
|
||||
document = factories.DocumentFactory()
|
||||
factories.UserDocumentAccessFactory.create_batch(2, document=document)
|
||||
|
||||
response = APIClient().get(f"/api/v1.0/documents/{document.id!s}/accesses/")
|
||||
assert response.status_code == 401
|
||||
assert response.json() == {
|
||||
"detail": "Authentication credentials were not provided."
|
||||
}
|
||||
|
||||
|
||||
def test_api_document_accesses_list_authenticated_unrelated():
|
||||
"""
|
||||
Authenticated users should not be allowed to list document accesses for a document
|
||||
to which they are not related.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
document = factories.DocumentFactory()
|
||||
factories.UserDocumentAccessFactory.create_batch(3, document=document)
|
||||
|
||||
# Accesses for other documents to which the user is related should not be listed either
|
||||
other_access = factories.UserDocumentAccessFactory(user=user)
|
||||
factories.UserDocumentAccessFactory(document=other_access.document)
|
||||
|
||||
response = client.get(
|
||||
f"/api/v1.0/documents/{document.id!s}/accesses/",
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {
|
||||
"count": 0,
|
||||
"next": None,
|
||||
"previous": None,
|
||||
"results": [],
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("via", VIA)
|
||||
def test_api_document_accesses_list_authenticated_related(via, mock_user_get_teams):
|
||||
"""
|
||||
Authenticated users should be able to list document accesses for a document
|
||||
to which they are directly related, whatever their role in the document.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
document = factories.DocumentFactory()
|
||||
if via == USER:
|
||||
user_access = models.DocumentAccess.objects.create(
|
||||
document=document,
|
||||
user=user,
|
||||
role=random.choice(models.RoleChoices.choices)[0],
|
||||
)
|
||||
elif via == TEAM:
|
||||
mock_user_get_teams.return_value = ["lasuite", "unknown"]
|
||||
user_access = models.DocumentAccess.objects.create(
|
||||
document=document,
|
||||
team="lasuite",
|
||||
role=random.choice(models.RoleChoices.choices)[0],
|
||||
)
|
||||
|
||||
access1 = factories.TeamDocumentAccessFactory(document=document)
|
||||
access2 = factories.UserDocumentAccessFactory(document=document)
|
||||
|
||||
# Accesses for other documents to which the user is related should not be listed either
|
||||
other_access = factories.UserDocumentAccessFactory(user=user)
|
||||
factories.UserDocumentAccessFactory(document=other_access.document)
|
||||
|
||||
response = client.get(
|
||||
f"/api/v1.0/documents/{document.id!s}/accesses/",
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
content = response.json()
|
||||
assert len(content["results"]) == 3
|
||||
assert sorted(content["results"], key=lambda x: x["id"]) == sorted(
|
||||
[
|
||||
{
|
||||
"id": str(user_access.id),
|
||||
"user": str(user.id) if via == "user" else None,
|
||||
"team": "lasuite" if via == "team" else "",
|
||||
"role": user_access.role,
|
||||
"abilities": user_access.get_abilities(user),
|
||||
},
|
||||
{
|
||||
"id": str(access1.id),
|
||||
"user": None,
|
||||
"team": access1.team,
|
||||
"role": access1.role,
|
||||
"abilities": access1.get_abilities(user),
|
||||
},
|
||||
{
|
||||
"id": str(access2.id),
|
||||
"user": str(access2.user.id),
|
||||
"team": "",
|
||||
"role": access2.role,
|
||||
"abilities": access2.get_abilities(user),
|
||||
},
|
||||
],
|
||||
key=lambda x: x["id"],
|
||||
)
|
||||
|
||||
|
||||
def test_api_document_accesses_retrieve_anonymous():
|
||||
"""
|
||||
Anonymous users should not be allowed to retrieve a document access.
|
||||
"""
|
||||
access = factories.UserDocumentAccessFactory()
|
||||
|
||||
response = APIClient().get(
|
||||
f"/api/v1.0/documents/{access.document_id!s}/accesses/{access.id!s}/",
|
||||
)
|
||||
|
||||
assert response.status_code == 401
|
||||
assert response.json() == {
|
||||
"detail": "Authentication credentials were not provided."
|
||||
}
|
||||
|
||||
|
||||
def test_api_document_accesses_retrieve_authenticated_unrelated():
|
||||
"""
|
||||
Authenticated users should not be allowed to retrieve a document access for
|
||||
a document to which they are not related.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
document = factories.DocumentFactory()
|
||||
access = factories.UserDocumentAccessFactory(document=document)
|
||||
|
||||
response = client.get(
|
||||
f"/api/v1.0/documents/{document.id!s}/accesses/{access.id!s}/",
|
||||
)
|
||||
assert response.status_code == 403
|
||||
assert response.json() == {
|
||||
"detail": "You do not have permission to perform this action."
|
||||
}
|
||||
|
||||
# Accesses related to another document should be excluded even if the user is related to it
|
||||
for access in [
|
||||
factories.UserDocumentAccessFactory(),
|
||||
factories.UserDocumentAccessFactory(user=user),
|
||||
]:
|
||||
response = client.get(
|
||||
f"/api/v1.0/documents/{document.id!s}/accesses/{access.id!s}/",
|
||||
)
|
||||
|
||||
assert response.status_code == 404
|
||||
assert response.json() == {"detail": "Not found."}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("via", VIA)
|
||||
def test_api_document_accesses_retrieve_authenticated_related(via, mock_user_get_teams):
|
||||
"""
|
||||
A user who is related to a document should be allowed to retrieve the
|
||||
associated document user accesses.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
document = factories.DocumentFactory()
|
||||
if via == USER:
|
||||
factories.UserDocumentAccessFactory(document=document, user=user)
|
||||
elif via == TEAM:
|
||||
mock_user_get_teams.return_value = ["lasuite", "unknown"]
|
||||
factories.TeamDocumentAccessFactory(document=document, team="lasuite")
|
||||
|
||||
access = factories.UserDocumentAccessFactory(document=document)
|
||||
|
||||
response = client.get(
|
||||
f"/api/v1.0/documents/{document.id!s}/accesses/{access.id!s}/",
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {
|
||||
"id": str(access.id),
|
||||
"user": str(access.user.id),
|
||||
"team": "",
|
||||
"role": access.role,
|
||||
"abilities": access.get_abilities(user),
|
||||
}
|
||||
|
||||
|
||||
def test_api_document_accesses_create_anonymous():
|
||||
"""Anonymous users should not be allowed to create document accesses."""
|
||||
user = factories.UserFactory()
|
||||
document = factories.DocumentFactory()
|
||||
|
||||
response = APIClient().post(
|
||||
f"/api/v1.0/documents/{document.id!s}/accesses/",
|
||||
{
|
||||
"user": str(user.id),
|
||||
"document": str(document.id),
|
||||
"role": random.choice(models.RoleChoices.choices)[0],
|
||||
},
|
||||
format="json",
|
||||
)
|
||||
|
||||
assert response.status_code == 401
|
||||
assert response.json() == {
|
||||
"detail": "Authentication credentials were not provided."
|
||||
}
|
||||
assert models.DocumentAccess.objects.exists() is False
|
||||
|
||||
|
||||
def test_api_document_accesses_create_authenticated_unrelated():
|
||||
"""
|
||||
Authenticated users should not be allowed to create document accesses for a document to
|
||||
which they are not related.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
other_user = factories.UserFactory()
|
||||
document = factories.DocumentFactory()
|
||||
|
||||
response = client.post(
|
||||
f"/api/v1.0/documents/{document.id!s}/accesses/",
|
||||
{
|
||||
"user": str(other_user.id),
|
||||
},
|
||||
format="json",
|
||||
)
|
||||
|
||||
assert response.status_code == 403
|
||||
assert not models.DocumentAccess.objects.filter(user=other_user).exists()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("via", VIA)
|
||||
def test_api_document_accesses_create_authenticated_member(via, mock_user_get_teams):
|
||||
"""Members of a document should not be allowed to create document accesses."""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
document = factories.DocumentFactory()
|
||||
if via == USER:
|
||||
factories.UserDocumentAccessFactory(document=document, user=user, role="member")
|
||||
elif via == TEAM:
|
||||
mock_user_get_teams.return_value = ["lasuite", "unknown"]
|
||||
factories.TeamDocumentAccessFactory(
|
||||
document=document, team="lasuite", role="member"
|
||||
)
|
||||
|
||||
other_user = factories.UserFactory()
|
||||
|
||||
for role in [role[0] for role in models.RoleChoices.choices]:
|
||||
response = client.post(
|
||||
f"/api/v1.0/documents/{document.id!s}/accesses/",
|
||||
{
|
||||
"user": str(other_user.id),
|
||||
"role": role,
|
||||
},
|
||||
format="json",
|
||||
)
|
||||
|
||||
assert response.status_code == 403
|
||||
|
||||
assert not models.DocumentAccess.objects.filter(user=other_user).exists()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("via", VIA)
|
||||
def test_api_document_accesses_create_authenticated_administrator(
|
||||
via, mock_user_get_teams
|
||||
):
|
||||
"""
|
||||
Administrators of a document should be able to create document accesses
|
||||
except for the "owner" role.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
document = factories.DocumentFactory()
|
||||
if via == USER:
|
||||
factories.UserDocumentAccessFactory(
|
||||
document=document, user=user, role="administrator"
|
||||
)
|
||||
elif via == TEAM:
|
||||
mock_user_get_teams.return_value = ["lasuite", "unknown"]
|
||||
factories.TeamDocumentAccessFactory(
|
||||
document=document, team="lasuite", role="administrator"
|
||||
)
|
||||
|
||||
other_user = factories.UserFactory()
|
||||
|
||||
# It should not be allowed to create an owner access
|
||||
response = client.post(
|
||||
f"/api/v1.0/documents/{document.id!s}/accesses/",
|
||||
{
|
||||
"user": str(other_user.id),
|
||||
"role": "owner",
|
||||
},
|
||||
format="json",
|
||||
)
|
||||
|
||||
assert response.status_code == 403
|
||||
assert response.json() == {
|
||||
"detail": "Only owners of a resource can assign other users as owners."
|
||||
}
|
||||
|
||||
# It should be allowed to create a lower access
|
||||
role = random.choice(
|
||||
[role[0] for role in models.RoleChoices.choices if role[0] != "owner"]
|
||||
)
|
||||
|
||||
response = client.post(
|
||||
f"/api/v1.0/documents/{document.id!s}/accesses/",
|
||||
{
|
||||
"user": str(other_user.id),
|
||||
"role": role,
|
||||
},
|
||||
format="json",
|
||||
)
|
||||
|
||||
assert response.status_code == 201
|
||||
assert models.DocumentAccess.objects.filter(user=other_user).count() == 1
|
||||
new_document_access = models.DocumentAccess.objects.filter(user=other_user).get()
|
||||
assert response.json() == {
|
||||
"abilities": new_document_access.get_abilities(user),
|
||||
"id": str(new_document_access.id),
|
||||
"team": "",
|
||||
"role": role,
|
||||
"user": str(other_user.id),
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("via", VIA)
|
||||
def test_api_document_accesses_create_authenticated_owner(via, mock_user_get_teams):
|
||||
"""
|
||||
Owners of a document should be able to create document accesses whatever the role.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
document = factories.DocumentFactory()
|
||||
if via == USER:
|
||||
factories.UserDocumentAccessFactory(document=document, user=user, role="owner")
|
||||
elif via == TEAM:
|
||||
mock_user_get_teams.return_value = ["lasuite", "unknown"]
|
||||
factories.TeamDocumentAccessFactory(
|
||||
document=document, team="lasuite", role="owner"
|
||||
)
|
||||
|
||||
other_user = factories.UserFactory()
|
||||
|
||||
role = random.choice([role[0] for role in models.RoleChoices.choices])
|
||||
|
||||
response = client.post(
|
||||
f"/api/v1.0/documents/{document.id!s}/accesses/",
|
||||
{
|
||||
"user": str(other_user.id),
|
||||
"role": role,
|
||||
},
|
||||
format="json",
|
||||
)
|
||||
|
||||
assert response.status_code == 201
|
||||
assert models.DocumentAccess.objects.filter(user=other_user).count() == 1
|
||||
new_document_access = models.DocumentAccess.objects.filter(user=other_user).get()
|
||||
assert response.json() == {
|
||||
"id": str(new_document_access.id),
|
||||
"user": str(other_user.id),
|
||||
"team": "",
|
||||
"role": role,
|
||||
"abilities": new_document_access.get_abilities(user),
|
||||
}
|
||||
|
||||
|
||||
def test_api_document_accesses_update_anonymous():
|
||||
"""Anonymous users should not be allowed to update a document access."""
|
||||
access = factories.UserDocumentAccessFactory()
|
||||
old_values = serializers.DocumentAccessSerializer(instance=access).data
|
||||
|
||||
new_values = {
|
||||
"id": uuid4(),
|
||||
"user": factories.UserFactory().id,
|
||||
"role": random.choice(models.RoleChoices.choices)[0],
|
||||
}
|
||||
|
||||
api_client = APIClient()
|
||||
for field, value in new_values.items():
|
||||
response = api_client.put(
|
||||
f"/api/v1.0/documents/{access.document_id!s}/accesses/{access.id!s}/",
|
||||
{**old_values, field: value},
|
||||
format="json",
|
||||
)
|
||||
assert response.status_code == 401
|
||||
|
||||
access.refresh_from_db()
|
||||
updated_values = serializers.DocumentAccessSerializer(instance=access).data
|
||||
assert updated_values == old_values
|
||||
|
||||
|
||||
def test_api_document_accesses_update_authenticated_unrelated():
|
||||
"""
|
||||
Authenticated users should not be allowed to update a document access for a document to which
|
||||
they are not related.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
access = factories.UserDocumentAccessFactory()
|
||||
old_values = serializers.DocumentAccessSerializer(instance=access).data
|
||||
|
||||
new_values = {
|
||||
"id": uuid4(),
|
||||
"user": factories.UserFactory().id,
|
||||
"role": random.choice(models.RoleChoices.choices)[0],
|
||||
}
|
||||
|
||||
for field, value in new_values.items():
|
||||
response = client.put(
|
||||
f"/api/v1.0/documents/{access.document_id!s}/accesses/{access.id!s}/",
|
||||
{**old_values, field: value},
|
||||
format="json",
|
||||
)
|
||||
assert response.status_code == 403
|
||||
|
||||
access.refresh_from_db()
|
||||
updated_values = serializers.DocumentAccessSerializer(instance=access).data
|
||||
assert updated_values == old_values
|
||||
|
||||
|
||||
@pytest.mark.parametrize("via", VIA)
|
||||
def test_api_document_accesses_update_authenticated_member(via, mock_user_get_teams):
|
||||
"""Members of a document should not be allowed to update its accesses."""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
document = factories.DocumentFactory()
|
||||
if via == USER:
|
||||
factories.UserDocumentAccessFactory(document=document, user=user, role="member")
|
||||
elif via == TEAM:
|
||||
mock_user_get_teams.return_value = ["lasuite", "unknown"]
|
||||
factories.TeamDocumentAccessFactory(
|
||||
document=document, team="lasuite", role="member"
|
||||
)
|
||||
|
||||
access = factories.UserDocumentAccessFactory(document=document)
|
||||
old_values = serializers.DocumentAccessSerializer(instance=access).data
|
||||
|
||||
new_values = {
|
||||
"id": uuid4(),
|
||||
"user": factories.UserFactory().id,
|
||||
"role": random.choice(models.RoleChoices.choices)[0],
|
||||
}
|
||||
|
||||
for field, value in new_values.items():
|
||||
response = client.put(
|
||||
f"/api/v1.0/documents/{access.document_id!s}/accesses/{access.id!s}/",
|
||||
{**old_values, field: value},
|
||||
format="json",
|
||||
)
|
||||
assert response.status_code == 403
|
||||
|
||||
access.refresh_from_db()
|
||||
updated_values = serializers.DocumentAccessSerializer(instance=access).data
|
||||
assert updated_values == old_values
|
||||
|
||||
|
||||
@pytest.mark.parametrize("via", VIA)
|
||||
def test_api_document_accesses_update_administrator_except_owner(
|
||||
via, mock_user_get_teams
|
||||
):
|
||||
"""
|
||||
A user who is a direct administrator in a document should be allowed to update a user
|
||||
access for this document, as long as they don't try to set the role to owner.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
document = factories.DocumentFactory()
|
||||
if via == USER:
|
||||
factories.UserDocumentAccessFactory(
|
||||
document=document, user=user, role="administrator"
|
||||
)
|
||||
elif via == TEAM:
|
||||
mock_user_get_teams.return_value = ["lasuite", "unknown"]
|
||||
factories.TeamDocumentAccessFactory(
|
||||
document=document, team="lasuite", role="administrator"
|
||||
)
|
||||
|
||||
access = factories.UserDocumentAccessFactory(
|
||||
document=document,
|
||||
role=random.choice(["administrator", "member"]),
|
||||
)
|
||||
old_values = serializers.DocumentAccessSerializer(instance=access).data
|
||||
|
||||
new_values = {
|
||||
"id": uuid4(),
|
||||
"user_id": factories.UserFactory().id,
|
||||
"role": random.choice(["administrator", "member"]),
|
||||
}
|
||||
|
||||
for field, value in new_values.items():
|
||||
new_data = {**old_values, field: value}
|
||||
response = client.put(
|
||||
f"/api/v1.0/documents/{document.id!s}/accesses/{access.id!s}/",
|
||||
data=new_data,
|
||||
format="json",
|
||||
)
|
||||
|
||||
if (
|
||||
new_data["role"] == old_values["role"]
|
||||
): # we are not really updating the role
|
||||
assert response.status_code == 403
|
||||
else:
|
||||
assert response.status_code == 200
|
||||
|
||||
access.refresh_from_db()
|
||||
updated_values = serializers.DocumentAccessSerializer(instance=access).data
|
||||
if field == "role":
|
||||
assert updated_values == {**old_values, "role": new_values["role"]}
|
||||
else:
|
||||
assert updated_values == old_values
|
||||
|
||||
|
||||
@pytest.mark.parametrize("via", VIA)
|
||||
def test_api_document_accesses_update_administrator_from_owner(
|
||||
via, mock_user_get_teams
|
||||
):
|
||||
"""
|
||||
A user who is an administrator in a document, should not be allowed to update
|
||||
the user access of an "owner" for this document.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
document = factories.DocumentFactory()
|
||||
if via == USER:
|
||||
factories.UserDocumentAccessFactory(
|
||||
document=document, user=user, role="administrator"
|
||||
)
|
||||
elif via == TEAM:
|
||||
mock_user_get_teams.return_value = ["lasuite", "unknown"]
|
||||
factories.TeamDocumentAccessFactory(
|
||||
document=document, team="lasuite", role="administrator"
|
||||
)
|
||||
|
||||
other_user = factories.UserFactory()
|
||||
access = factories.UserDocumentAccessFactory(
|
||||
document=document, user=other_user, role="owner"
|
||||
)
|
||||
old_values = serializers.DocumentAccessSerializer(instance=access).data
|
||||
|
||||
new_values = {
|
||||
"id": uuid4(),
|
||||
"user_id": factories.UserFactory().id,
|
||||
"role": random.choice(models.RoleChoices.choices)[0],
|
||||
}
|
||||
|
||||
for field, value in new_values.items():
|
||||
response = client.put(
|
||||
f"/api/v1.0/documents/{document.id!s}/accesses/{access.id!s}/",
|
||||
data={**old_values, field: value},
|
||||
format="json",
|
||||
)
|
||||
|
||||
assert response.status_code == 403
|
||||
access.refresh_from_db()
|
||||
updated_values = serializers.DocumentAccessSerializer(instance=access).data
|
||||
assert updated_values == old_values
|
||||
|
||||
|
||||
@pytest.mark.parametrize("via", VIA)
|
||||
def test_api_document_accesses_update_administrator_to_owner(via, mock_user_get_teams):
|
||||
"""
|
||||
A user who is an administrator in a document, should not be allowed to update
|
||||
the user access of another user to grant document ownership.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
document = factories.DocumentFactory()
|
||||
if via == USER:
|
||||
factories.UserDocumentAccessFactory(
|
||||
document=document, user=user, role="administrator"
|
||||
)
|
||||
elif via == TEAM:
|
||||
mock_user_get_teams.return_value = ["lasuite", "unknown"]
|
||||
factories.TeamDocumentAccessFactory(
|
||||
document=document, team="lasuite", role="administrator"
|
||||
)
|
||||
|
||||
other_user = factories.UserFactory()
|
||||
access = factories.UserDocumentAccessFactory(
|
||||
document=document,
|
||||
user=other_user,
|
||||
role=random.choice(["administrator", "member"]),
|
||||
)
|
||||
old_values = serializers.DocumentAccessSerializer(instance=access).data
|
||||
|
||||
new_values = {
|
||||
"id": uuid4(),
|
||||
"user_id": factories.UserFactory().id,
|
||||
"role": "owner",
|
||||
}
|
||||
|
||||
for field, value in new_values.items():
|
||||
new_data = {**old_values, field: value}
|
||||
response = client.put(
|
||||
f"/api/v1.0/documents/{document.id!s}/accesses/{access.id!s}/",
|
||||
data=new_data,
|
||||
format="json",
|
||||
)
|
||||
# We are not allowed or not really updating the role
|
||||
if field == "role" or new_data["role"] == old_values["role"]:
|
||||
assert response.status_code == 403
|
||||
else:
|
||||
assert response.status_code == 200
|
||||
|
||||
access.refresh_from_db()
|
||||
updated_values = serializers.DocumentAccessSerializer(instance=access).data
|
||||
assert updated_values == old_values
|
||||
|
||||
|
||||
@pytest.mark.parametrize("via", VIA)
|
||||
def test_api_document_accesses_update_owner(via, mock_user_get_teams):
|
||||
"""
|
||||
A user who is an owner in a document should be allowed to update
|
||||
a user access for this document whatever the role.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
document = factories.DocumentFactory()
|
||||
if via == USER:
|
||||
factories.UserDocumentAccessFactory(document=document, user=user, role="owner")
|
||||
elif via == TEAM:
|
||||
mock_user_get_teams.return_value = ["lasuite", "unknown"]
|
||||
factories.TeamDocumentAccessFactory(
|
||||
document=document, team="lasuite", role="owner"
|
||||
)
|
||||
|
||||
factories.UserFactory()
|
||||
access = factories.UserDocumentAccessFactory(
|
||||
document=document,
|
||||
)
|
||||
old_values = serializers.DocumentAccessSerializer(instance=access).data
|
||||
|
||||
new_values = {
|
||||
"id": uuid4(),
|
||||
"user_id": factories.UserFactory().id,
|
||||
"role": random.choice(models.RoleChoices.choices)[0],
|
||||
}
|
||||
|
||||
for field, value in new_values.items():
|
||||
new_data = {**old_values, field: value}
|
||||
response = client.put(
|
||||
f"/api/v1.0/documents/{document.id!s}/accesses/{access.id!s}/",
|
||||
data=new_data,
|
||||
format="json",
|
||||
)
|
||||
|
||||
if (
|
||||
new_data["role"] == old_values["role"]
|
||||
): # we are not really updating the role
|
||||
assert response.status_code == 403
|
||||
else:
|
||||
assert response.status_code == 200
|
||||
|
||||
access.refresh_from_db()
|
||||
updated_values = serializers.DocumentAccessSerializer(instance=access).data
|
||||
|
||||
if field == "role":
|
||||
assert updated_values == {**old_values, "role": new_values["role"]}
|
||||
else:
|
||||
assert updated_values == old_values
|
||||
|
||||
|
||||
@pytest.mark.parametrize("via", VIA)
|
||||
def test_api_document_accesses_update_owner_self(via, mock_user_get_teams):
|
||||
"""
|
||||
A user who is owner of a document should be allowed to update
|
||||
their own user access provided there are other owners in the document.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
document = factories.DocumentFactory()
|
||||
if via == USER:
|
||||
access = factories.UserDocumentAccessFactory(
|
||||
document=document, user=user, role="owner"
|
||||
)
|
||||
elif via == TEAM:
|
||||
mock_user_get_teams.return_value = ["lasuite", "unknown"]
|
||||
access = factories.TeamDocumentAccessFactory(
|
||||
document=document, team="lasuite", role="owner"
|
||||
)
|
||||
|
||||
old_values = serializers.DocumentAccessSerializer(instance=access).data
|
||||
new_role = random.choice(["administrator", "member"])
|
||||
|
||||
response = client.put(
|
||||
f"/api/v1.0/documents/{document.id!s}/accesses/{access.id!s}/",
|
||||
data={**old_values, "role": new_role},
|
||||
format="json",
|
||||
)
|
||||
|
||||
assert response.status_code == 403
|
||||
access.refresh_from_db()
|
||||
assert access.role == "owner"
|
||||
|
||||
# Add another owner and it should now work
|
||||
factories.UserDocumentAccessFactory(document=document, role="owner")
|
||||
|
||||
response = client.put(
|
||||
f"/api/v1.0/documents/{document.id!s}/accesses/{access.id!s}/",
|
||||
data={**old_values, "role": new_role},
|
||||
format="json",
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
access.refresh_from_db()
|
||||
assert access.role == new_role
|
||||
|
||||
|
||||
# Delete
|
||||
|
||||
|
||||
def test_api_document_accesses_delete_anonymous():
|
||||
"""Anonymous users should not be allowed to destroy a document access."""
|
||||
access = factories.UserDocumentAccessFactory()
|
||||
|
||||
response = APIClient().delete(
|
||||
f"/api/v1.0/documents/{access.document_id!s}/accesses/{access.id!s}/",
|
||||
)
|
||||
|
||||
assert response.status_code == 401
|
||||
assert models.DocumentAccess.objects.count() == 1
|
||||
|
||||
|
||||
def test_api_document_accesses_delete_authenticated():
|
||||
"""
|
||||
Authenticated users should not be allowed to delete a document access for a
|
||||
document to which they are not related.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
access = factories.UserDocumentAccessFactory()
|
||||
|
||||
response = client.delete(
|
||||
f"/api/v1.0/documents/{access.document_id!s}/accesses/{access.id!s}/",
|
||||
)
|
||||
|
||||
assert response.status_code == 403
|
||||
assert models.DocumentAccess.objects.count() == 1
|
||||
|
||||
|
||||
@pytest.mark.parametrize("via", VIA)
|
||||
def test_api_document_accesses_delete_member(via, mock_user_get_teams):
|
||||
"""
|
||||
Authenticated users should not be allowed to delete a document access for a
|
||||
document in which they are a simple member.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
document = factories.DocumentFactory()
|
||||
if via == USER:
|
||||
factories.UserDocumentAccessFactory(document=document, user=user, role="member")
|
||||
elif via == TEAM:
|
||||
mock_user_get_teams.return_value = ["lasuite", "unknown"]
|
||||
factories.TeamDocumentAccessFactory(
|
||||
document=document, team="lasuite", role="member"
|
||||
)
|
||||
|
||||
access = factories.UserDocumentAccessFactory(document=document)
|
||||
|
||||
assert models.DocumentAccess.objects.count() == 2
|
||||
assert models.DocumentAccess.objects.filter(user=access.user).exists()
|
||||
|
||||
response = client.delete(
|
||||
f"/api/v1.0/documents/{document.id!s}/accesses/{access.id!s}/",
|
||||
)
|
||||
|
||||
assert response.status_code == 403
|
||||
assert models.DocumentAccess.objects.count() == 2
|
||||
|
||||
|
||||
@pytest.mark.parametrize("via", VIA)
|
||||
def test_api_document_accesses_delete_administrators_except_owners(
|
||||
via, mock_user_get_teams
|
||||
):
|
||||
"""
|
||||
Users who are administrators in a document should be allowed to delete an access
|
||||
from the document provided it is not ownership.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
document = factories.DocumentFactory()
|
||||
if via == USER:
|
||||
factories.UserDocumentAccessFactory(
|
||||
document=document, user=user, role="administrator"
|
||||
)
|
||||
elif via == TEAM:
|
||||
mock_user_get_teams.return_value = ["lasuite", "unknown"]
|
||||
factories.TeamDocumentAccessFactory(
|
||||
document=document, team="lasuite", role="administrator"
|
||||
)
|
||||
|
||||
access = factories.UserDocumentAccessFactory(
|
||||
document=document, role=random.choice(["member", "administrator"])
|
||||
)
|
||||
|
||||
assert models.DocumentAccess.objects.count() == 2
|
||||
assert models.DocumentAccess.objects.filter(user=access.user).exists()
|
||||
|
||||
response = client.delete(
|
||||
f"/api/v1.0/documents/{document.id!s}/accesses/{access.id!s}/",
|
||||
)
|
||||
|
||||
assert response.status_code == 204
|
||||
assert models.DocumentAccess.objects.count() == 1
|
||||
|
||||
|
||||
@pytest.mark.parametrize("via", VIA)
|
||||
def test_api_document_accesses_delete_administrator_on_owners(via, mock_user_get_teams):
|
||||
"""
|
||||
Users who are administrators in a document should not be allowed to delete an ownership
|
||||
access from the document.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
document = factories.DocumentFactory()
|
||||
if via == USER:
|
||||
factories.UserDocumentAccessFactory(
|
||||
document=document, user=user, role="administrator"
|
||||
)
|
||||
elif via == TEAM:
|
||||
mock_user_get_teams.return_value = ["lasuite", "unknown"]
|
||||
factories.TeamDocumentAccessFactory(
|
||||
document=document, team="lasuite", role="administrator"
|
||||
)
|
||||
|
||||
access = factories.UserDocumentAccessFactory(document=document, role="owner")
|
||||
|
||||
assert models.DocumentAccess.objects.count() == 2
|
||||
assert models.DocumentAccess.objects.filter(user=access.user).exists()
|
||||
|
||||
response = client.delete(
|
||||
f"/api/v1.0/documents/{document.id!s}/accesses/{access.id!s}/",
|
||||
)
|
||||
|
||||
assert response.status_code == 403
|
||||
assert models.DocumentAccess.objects.count() == 2
|
||||
|
||||
|
||||
@pytest.mark.parametrize("via", VIA)
|
||||
def test_api_document_accesses_delete_owners(via, mock_user_get_teams):
|
||||
"""
|
||||
Users should be able to delete the document access of another user
|
||||
for a document of which they are owner.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
document = factories.DocumentFactory()
|
||||
if via == USER:
|
||||
factories.UserDocumentAccessFactory(document=document, user=user, role="owner")
|
||||
elif via == TEAM:
|
||||
mock_user_get_teams.return_value = ["lasuite", "unknown"]
|
||||
factories.TeamDocumentAccessFactory(
|
||||
document=document, team="lasuite", role="owner"
|
||||
)
|
||||
|
||||
access = factories.UserDocumentAccessFactory(document=document)
|
||||
|
||||
assert models.DocumentAccess.objects.count() == 2
|
||||
assert models.DocumentAccess.objects.filter(user=access.user).exists()
|
||||
|
||||
response = client.delete(
|
||||
f"/api/v1.0/documents/{document.id!s}/accesses/{access.id!s}/",
|
||||
)
|
||||
|
||||
assert response.status_code == 204
|
||||
assert models.DocumentAccess.objects.count() == 1
|
||||
|
||||
|
||||
@pytest.mark.parametrize("via", VIA)
|
||||
def test_api_document_accesses_delete_owners_last_owner(via, mock_user_get_teams):
|
||||
"""
|
||||
It should not be possible to delete the last owner access from a document
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
document = factories.DocumentFactory()
|
||||
if via == USER:
|
||||
access = factories.UserDocumentAccessFactory(
|
||||
document=document, user=user, role="owner"
|
||||
)
|
||||
elif via == TEAM:
|
||||
mock_user_get_teams.return_value = ["lasuite", "unknown"]
|
||||
access = factories.TeamDocumentAccessFactory(
|
||||
document=document, team="lasuite", role="owner"
|
||||
)
|
||||
|
||||
assert models.DocumentAccess.objects.count() == 1
|
||||
response = client.delete(
|
||||
f"/api/v1.0/documents/{document.id!s}/accesses/{access.id!s}/",
|
||||
)
|
||||
|
||||
assert response.status_code == 403
|
||||
assert models.DocumentAccess.objects.count() == 1
|
||||
@@ -130,7 +130,7 @@ def test_api_template_accesses_retrieve_anonymous():
|
||||
access = factories.UserTemplateAccessFactory()
|
||||
|
||||
response = APIClient().get(
|
||||
f"/api/v1.0/templates/{access.template.id!s}/accesses/{access.id!s}/",
|
||||
f"/api/v1.0/templates/{access.template_id!s}/accesses/{access.id!s}/",
|
||||
)
|
||||
|
||||
assert response.status_code == 401
|
||||
@@ -326,7 +326,7 @@ def test_api_template_accesses_create_authenticated_administrator(
|
||||
|
||||
assert response.status_code == 403
|
||||
assert response.json() == {
|
||||
"detail": "Only owners of a template can assign other users as owners."
|
||||
"detail": "Only owners of a resource can assign other users as owners."
|
||||
}
|
||||
|
||||
# It should be allowed to create a lower access
|
||||
@@ -413,7 +413,7 @@ def test_api_template_accesses_update_anonymous():
|
||||
api_client = APIClient()
|
||||
for field, value in new_values.items():
|
||||
response = api_client.put(
|
||||
f"/api/v1.0/templates/{access.template.id!s}/accesses/{access.id!s}/",
|
||||
f"/api/v1.0/templates/{access.template_id!s}/accesses/{access.id!s}/",
|
||||
{**old_values, field: value},
|
||||
format="json",
|
||||
)
|
||||
@@ -445,7 +445,7 @@ def test_api_template_accesses_update_authenticated_unrelated():
|
||||
|
||||
for field, value in new_values.items():
|
||||
response = client.put(
|
||||
f"/api/v1.0/templates/{access.template.id!s}/accesses/{access.id!s}/",
|
||||
f"/api/v1.0/templates/{access.template_id!s}/accesses/{access.id!s}/",
|
||||
{**old_values, field: value},
|
||||
format="json",
|
||||
)
|
||||
@@ -484,7 +484,7 @@ def test_api_template_accesses_update_authenticated_member(via, mock_user_get_te
|
||||
|
||||
for field, value in new_values.items():
|
||||
response = client.put(
|
||||
f"/api/v1.0/templates/{access.template.id!s}/accesses/{access.id!s}/",
|
||||
f"/api/v1.0/templates/{access.template_id!s}/accesses/{access.id!s}/",
|
||||
{**old_values, field: value},
|
||||
format="json",
|
||||
)
|
||||
@@ -770,7 +770,7 @@ def test_api_template_accesses_delete_anonymous():
|
||||
access = factories.UserTemplateAccessFactory()
|
||||
|
||||
response = APIClient().delete(
|
||||
f"/api/v1.0/templates/{access.template.id!s}/accesses/{access.id!s}/",
|
||||
f"/api/v1.0/templates/{access.template_id!s}/accesses/{access.id!s}/",
|
||||
)
|
||||
|
||||
assert response.status_code == 401
|
||||
@@ -790,7 +790,7 @@ def test_api_template_accesses_delete_authenticated():
|
||||
access = factories.UserTemplateAccessFactory()
|
||||
|
||||
response = client.delete(
|
||||
f"/api/v1.0/templates/{access.template.id!s}/accesses/{access.id!s}/",
|
||||
f"/api/v1.0/templates/{access.template_id!s}/accesses/{access.id!s}/",
|
||||
)
|
||||
|
||||
assert response.status_code == 403
|
||||
|
||||
311
src/backend/core/tests/test_models_document_accesses.py
Normal file
311
src/backend/core/tests/test_models_document_accesses.py
Normal file
@@ -0,0 +1,311 @@
|
||||
"""
|
||||
Unit tests for the DocumentAccess model
|
||||
"""
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
import pytest
|
||||
|
||||
from core import factories
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
def test_models_document_accesses_str():
|
||||
"""
|
||||
The str representation should include user email, document title and role.
|
||||
"""
|
||||
user = factories.UserFactory(email="david.bowman@example.com")
|
||||
access = factories.UserDocumentAccessFactory(
|
||||
role="member",
|
||||
user=user,
|
||||
document__title="admins",
|
||||
)
|
||||
assert str(access) == "david.bowman@example.com is member in document admins"
|
||||
|
||||
|
||||
def test_models_document_accesses_unique_user():
|
||||
"""Document accesses should be unique for a given couple of user and document."""
|
||||
access = factories.UserDocumentAccessFactory()
|
||||
|
||||
with pytest.raises(
|
||||
ValidationError,
|
||||
match="This user is already in this document.",
|
||||
):
|
||||
factories.UserDocumentAccessFactory(user=access.user, document=access.document)
|
||||
|
||||
|
||||
def test_models_document_accesses_several_empty_teams():
|
||||
"""A document can have several document accesses with an empty team."""
|
||||
access = factories.UserDocumentAccessFactory()
|
||||
factories.UserDocumentAccessFactory(document=access.document)
|
||||
|
||||
|
||||
def test_models_document_accesses_unique_team():
|
||||
"""Document accesses should be unique for a given couple of team and document."""
|
||||
access = factories.TeamDocumentAccessFactory()
|
||||
|
||||
with pytest.raises(
|
||||
ValidationError,
|
||||
match="This team is already in this document.",
|
||||
):
|
||||
factories.TeamDocumentAccessFactory(team=access.team, document=access.document)
|
||||
|
||||
|
||||
def test_models_document_accesses_several_null_users():
|
||||
"""A document can have several document accesses with a null user."""
|
||||
access = factories.TeamDocumentAccessFactory()
|
||||
factories.TeamDocumentAccessFactory(document=access.document)
|
||||
|
||||
|
||||
def test_models_document_accesses_user_and_team_set():
|
||||
"""User and team can't both be set on a document access."""
|
||||
with pytest.raises(
|
||||
ValidationError,
|
||||
match="Either user or team must be set, not both.",
|
||||
):
|
||||
factories.UserDocumentAccessFactory(team="my-team")
|
||||
|
||||
|
||||
def test_models_document_accesses_user_and_team_empty():
|
||||
"""User and team can't both be empty on a document access."""
|
||||
with pytest.raises(
|
||||
ValidationError,
|
||||
match="Either user or team must be set, not both.",
|
||||
):
|
||||
factories.UserDocumentAccessFactory(user=None)
|
||||
|
||||
|
||||
# get_abilities
|
||||
|
||||
|
||||
def test_models_document_access_get_abilities_anonymous():
|
||||
"""Check abilities returned for an anonymous user."""
|
||||
access = factories.UserDocumentAccessFactory()
|
||||
abilities = access.get_abilities(AnonymousUser())
|
||||
assert abilities == {
|
||||
"destroy": False,
|
||||
"retrieve": False,
|
||||
"update": False,
|
||||
"set_role_to": [],
|
||||
}
|
||||
|
||||
|
||||
def test_models_document_access_get_abilities_authenticated():
|
||||
"""Check abilities returned for an authenticated user."""
|
||||
access = factories.UserDocumentAccessFactory()
|
||||
user = factories.UserFactory()
|
||||
abilities = access.get_abilities(user)
|
||||
assert abilities == {
|
||||
"destroy": False,
|
||||
"retrieve": False,
|
||||
"update": False,
|
||||
"set_role_to": [],
|
||||
}
|
||||
|
||||
|
||||
# - for owner
|
||||
|
||||
|
||||
def test_models_document_access_get_abilities_for_owner_of_self_allowed():
|
||||
"""
|
||||
Check abilities of self access for the owner of a document when
|
||||
there is more than one owner left.
|
||||
"""
|
||||
access = factories.UserDocumentAccessFactory(role="owner")
|
||||
factories.UserDocumentAccessFactory(document=access.document, role="owner")
|
||||
abilities = access.get_abilities(access.user)
|
||||
assert abilities == {
|
||||
"destroy": True,
|
||||
"retrieve": True,
|
||||
"update": True,
|
||||
"set_role_to": ["administrator", "member"],
|
||||
}
|
||||
|
||||
|
||||
def test_models_document_access_get_abilities_for_owner_of_self_last():
|
||||
"""
|
||||
Check abilities of self access for the owner of a document when there is only one owner left.
|
||||
"""
|
||||
access = factories.UserDocumentAccessFactory(role="owner")
|
||||
abilities = access.get_abilities(access.user)
|
||||
assert abilities == {
|
||||
"destroy": False,
|
||||
"retrieve": True,
|
||||
"update": False,
|
||||
"set_role_to": [],
|
||||
}
|
||||
|
||||
|
||||
def test_models_document_access_get_abilities_for_owner_of_owner():
|
||||
"""Check abilities of owner access for the owner of a document."""
|
||||
access = factories.UserDocumentAccessFactory(role="owner")
|
||||
factories.UserDocumentAccessFactory(document=access.document) # another one
|
||||
user = factories.UserDocumentAccessFactory(
|
||||
document=access.document, role="owner"
|
||||
).user
|
||||
abilities = access.get_abilities(user)
|
||||
assert abilities == {
|
||||
"destroy": True,
|
||||
"retrieve": True,
|
||||
"update": True,
|
||||
"set_role_to": ["administrator", "member"],
|
||||
}
|
||||
|
||||
|
||||
def test_models_document_access_get_abilities_for_owner_of_administrator():
|
||||
"""Check abilities of administrator access for the owner of a document."""
|
||||
access = factories.UserDocumentAccessFactory(role="administrator")
|
||||
factories.UserDocumentAccessFactory(document=access.document) # another one
|
||||
user = factories.UserDocumentAccessFactory(
|
||||
document=access.document, role="owner"
|
||||
).user
|
||||
abilities = access.get_abilities(user)
|
||||
assert abilities == {
|
||||
"destroy": True,
|
||||
"retrieve": True,
|
||||
"update": True,
|
||||
"set_role_to": ["owner", "member"],
|
||||
}
|
||||
|
||||
|
||||
def test_models_document_access_get_abilities_for_owner_of_member():
|
||||
"""Check abilities of member access for the owner of a document."""
|
||||
access = factories.UserDocumentAccessFactory(role="member")
|
||||
factories.UserDocumentAccessFactory(document=access.document) # another one
|
||||
user = factories.UserDocumentAccessFactory(
|
||||
document=access.document, role="owner"
|
||||
).user
|
||||
abilities = access.get_abilities(user)
|
||||
assert abilities == {
|
||||
"destroy": True,
|
||||
"retrieve": True,
|
||||
"update": True,
|
||||
"set_role_to": ["owner", "administrator"],
|
||||
}
|
||||
|
||||
|
||||
# - for administrator
|
||||
|
||||
|
||||
def test_models_document_access_get_abilities_for_administrator_of_owner():
|
||||
"""Check abilities of owner access for the administrator of a document."""
|
||||
access = factories.UserDocumentAccessFactory(role="owner")
|
||||
factories.UserDocumentAccessFactory(document=access.document) # another one
|
||||
user = factories.UserDocumentAccessFactory(
|
||||
document=access.document, role="administrator"
|
||||
).user
|
||||
abilities = access.get_abilities(user)
|
||||
assert abilities == {
|
||||
"destroy": False,
|
||||
"retrieve": True,
|
||||
"update": False,
|
||||
"set_role_to": [],
|
||||
}
|
||||
|
||||
|
||||
def test_models_document_access_get_abilities_for_administrator_of_administrator():
|
||||
"""Check abilities of administrator access for the administrator of a document."""
|
||||
access = factories.UserDocumentAccessFactory(role="administrator")
|
||||
factories.UserDocumentAccessFactory(document=access.document) # another one
|
||||
user = factories.UserDocumentAccessFactory(
|
||||
document=access.document, role="administrator"
|
||||
).user
|
||||
abilities = access.get_abilities(user)
|
||||
assert abilities == {
|
||||
"destroy": True,
|
||||
"retrieve": True,
|
||||
"update": True,
|
||||
"set_role_to": ["member"],
|
||||
}
|
||||
|
||||
|
||||
def test_models_document_access_get_abilities_for_administrator_of_member():
|
||||
"""Check abilities of member access for the administrator of a document."""
|
||||
access = factories.UserDocumentAccessFactory(role="member")
|
||||
factories.UserDocumentAccessFactory(document=access.document) # another one
|
||||
user = factories.UserDocumentAccessFactory(
|
||||
document=access.document, role="administrator"
|
||||
).user
|
||||
abilities = access.get_abilities(user)
|
||||
assert abilities == {
|
||||
"destroy": True,
|
||||
"retrieve": True,
|
||||
"update": True,
|
||||
"set_role_to": ["administrator"],
|
||||
}
|
||||
|
||||
|
||||
# - for member
|
||||
|
||||
|
||||
def test_models_document_access_get_abilities_for_member_of_owner():
|
||||
"""Check abilities of owner access for the member of a document."""
|
||||
access = factories.UserDocumentAccessFactory(role="owner")
|
||||
factories.UserDocumentAccessFactory(document=access.document) # another one
|
||||
user = factories.UserDocumentAccessFactory(
|
||||
document=access.document, role="member"
|
||||
).user
|
||||
abilities = access.get_abilities(user)
|
||||
assert abilities == {
|
||||
"destroy": False,
|
||||
"retrieve": True,
|
||||
"update": False,
|
||||
"set_role_to": [],
|
||||
}
|
||||
|
||||
|
||||
def test_models_document_access_get_abilities_for_member_of_administrator():
|
||||
"""Check abilities of administrator access for the member of a document."""
|
||||
access = factories.UserDocumentAccessFactory(role="administrator")
|
||||
factories.UserDocumentAccessFactory(document=access.document) # another one
|
||||
user = factories.UserDocumentAccessFactory(
|
||||
document=access.document, role="member"
|
||||
).user
|
||||
abilities = access.get_abilities(user)
|
||||
assert abilities == {
|
||||
"destroy": False,
|
||||
"retrieve": True,
|
||||
"update": False,
|
||||
"set_role_to": [],
|
||||
}
|
||||
|
||||
|
||||
def test_models_document_access_get_abilities_for_member_of_member_user(
|
||||
django_assert_num_queries
|
||||
):
|
||||
"""Check abilities of member access for the member of a document."""
|
||||
access = factories.UserDocumentAccessFactory(role="member")
|
||||
factories.UserDocumentAccessFactory(document=access.document) # another one
|
||||
user = factories.UserDocumentAccessFactory(
|
||||
document=access.document, role="member"
|
||||
).user
|
||||
|
||||
with django_assert_num_queries(1):
|
||||
abilities = access.get_abilities(user)
|
||||
|
||||
assert abilities == {
|
||||
"destroy": False,
|
||||
"retrieve": True,
|
||||
"update": False,
|
||||
"set_role_to": [],
|
||||
}
|
||||
|
||||
|
||||
def test_models_document_access_get_abilities_preset_role(django_assert_num_queries):
|
||||
"""No query is done if the role is preset, e.g., with a query annotation."""
|
||||
access = factories.UserDocumentAccessFactory(role="member")
|
||||
user = factories.UserDocumentAccessFactory(
|
||||
document=access.document, role="member"
|
||||
).user
|
||||
access.user_roles = ["member"]
|
||||
|
||||
with django_assert_num_queries(0):
|
||||
abilities = access.get_abilities(user)
|
||||
|
||||
assert abilities == {
|
||||
"destroy": False,
|
||||
"retrieve": True,
|
||||
"update": False,
|
||||
"set_role_to": [],
|
||||
}
|
||||
153
src/backend/core/tests/test_models_documents.py
Normal file
153
src/backend/core/tests/test_models_documents.py
Normal file
@@ -0,0 +1,153 @@
|
||||
"""
|
||||
Unit tests for the Document model
|
||||
"""
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
import pytest
|
||||
|
||||
from core import factories, models
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
def test_models_documents_str():
|
||||
"""The str representation should be the title of the document."""
|
||||
document = factories.DocumentFactory(title="admins")
|
||||
assert str(document) == "admins"
|
||||
|
||||
|
||||
def test_models_documents_id_unique():
|
||||
"""The "id" field should be unique."""
|
||||
document = factories.DocumentFactory()
|
||||
with pytest.raises(ValidationError, match="Document with this Id already exists."):
|
||||
factories.DocumentFactory(id=document.id)
|
||||
|
||||
|
||||
def test_models_documents_title_null():
|
||||
"""The "title" field should not be null."""
|
||||
with pytest.raises(ValidationError, match="This field cannot be null."):
|
||||
models.Document.objects.create(title=None)
|
||||
|
||||
|
||||
def test_models_documents_title_empty():
|
||||
"""The "title" field should not be empty."""
|
||||
with pytest.raises(ValidationError, match="This field cannot be blank."):
|
||||
models.Document.objects.create(title="")
|
||||
|
||||
|
||||
def test_models_documents_title_max_length():
|
||||
"""The "title" field should be 100 characters maximum."""
|
||||
factories.DocumentFactory(title="a" * 255)
|
||||
with pytest.raises(
|
||||
ValidationError,
|
||||
match=r"Ensure this value has at most 255 characters \(it has 256\)\.",
|
||||
):
|
||||
factories.DocumentFactory(title="a" * 256)
|
||||
|
||||
|
||||
# get_abilities
|
||||
|
||||
|
||||
def test_models_documents_get_abilities_anonymous_public():
|
||||
"""Check abilities returned for an anonymous user if the document is public."""
|
||||
document = factories.DocumentFactory(is_public=True)
|
||||
abilities = document.get_abilities(AnonymousUser())
|
||||
assert abilities == {
|
||||
"destroy": False,
|
||||
"retrieve": True,
|
||||
"update": False,
|
||||
"manage_accesses": False,
|
||||
}
|
||||
|
||||
|
||||
def test_models_documents_get_abilities_anonymous_not_public():
|
||||
"""Check abilities returned for an anonymous user if the document is private."""
|
||||
document = factories.DocumentFactory(is_public=False)
|
||||
abilities = document.get_abilities(AnonymousUser())
|
||||
assert abilities == {
|
||||
"destroy": False,
|
||||
"retrieve": False,
|
||||
"update": False,
|
||||
"manage_accesses": False,
|
||||
}
|
||||
|
||||
|
||||
def test_models_documents_get_abilities_authenticated_public():
|
||||
"""Check abilities returned for an authenticated user if the user is public."""
|
||||
document = factories.DocumentFactory(is_public=True)
|
||||
abilities = document.get_abilities(factories.UserFactory())
|
||||
assert abilities == {
|
||||
"destroy": False,
|
||||
"retrieve": True,
|
||||
"update": False,
|
||||
"manage_accesses": False,
|
||||
}
|
||||
|
||||
|
||||
def test_models_documents_get_abilities_authenticated_not_public():
|
||||
"""Check abilities returned for an authenticated user if the document is private."""
|
||||
document = factories.DocumentFactory(is_public=False)
|
||||
abilities = document.get_abilities(factories.UserFactory())
|
||||
assert abilities == {
|
||||
"destroy": False,
|
||||
"retrieve": False,
|
||||
"update": False,
|
||||
"manage_accesses": False,
|
||||
}
|
||||
|
||||
|
||||
def test_models_documents_get_abilities_owner():
|
||||
"""Check abilities returned for the owner of a document."""
|
||||
user = factories.UserFactory()
|
||||
access = factories.UserDocumentAccessFactory(role="owner", user=user)
|
||||
abilities = access.document.get_abilities(access.user)
|
||||
assert abilities == {
|
||||
"destroy": True,
|
||||
"retrieve": True,
|
||||
"update": True,
|
||||
"manage_accesses": True,
|
||||
}
|
||||
|
||||
|
||||
def test_models_documents_get_abilities_administrator():
|
||||
"""Check abilities returned for the administrator of a document."""
|
||||
access = factories.UserDocumentAccessFactory(role="administrator")
|
||||
abilities = access.document.get_abilities(access.user)
|
||||
assert abilities == {
|
||||
"destroy": False,
|
||||
"retrieve": True,
|
||||
"update": True,
|
||||
"manage_accesses": True,
|
||||
}
|
||||
|
||||
|
||||
def test_models_documents_get_abilities_member_user(django_assert_num_queries):
|
||||
"""Check abilities returned for the member of a document."""
|
||||
access = factories.UserDocumentAccessFactory(role="member")
|
||||
|
||||
with django_assert_num_queries(1):
|
||||
abilities = access.document.get_abilities(access.user)
|
||||
|
||||
assert abilities == {
|
||||
"destroy": False,
|
||||
"retrieve": True,
|
||||
"update": False,
|
||||
"manage_accesses": False,
|
||||
}
|
||||
|
||||
|
||||
def test_models_documents_get_abilities_preset_role(django_assert_num_queries):
|
||||
"""No query is done if the role is preset e.g. with query annotation."""
|
||||
access = factories.UserDocumentAccessFactory(role="member")
|
||||
access.document.user_roles = ["member"]
|
||||
|
||||
with django_assert_num_queries(0):
|
||||
abilities = access.document.get_abilities(access.user)
|
||||
|
||||
assert abilities == {
|
||||
"destroy": False,
|
||||
"retrieve": True,
|
||||
"update": False,
|
||||
"manage_accesses": False,
|
||||
}
|
||||
Reference in New Issue
Block a user