mirror of
https://github.com/suitenumerique/docs.git
synced 2026-04-26 01:25:05 +02:00
Compare commits
3 Commits
feat/e2e-e
...
feat/comme
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f51246b392 | ||
|
|
a11a1911bc | ||
|
|
604e5e0eb2 |
@@ -8,6 +8,10 @@ and this project adheres to
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- ✨(backend) Comments on text editor #1309
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- ⚡️(frontend) improve accessibility:
|
- ⚡️(frontend) improve accessibility:
|
||||||
|
|||||||
@@ -171,3 +171,19 @@ class ResourceAccessPermission(IsAuthenticated):
|
|||||||
|
|
||||||
action = view.action
|
action = view.action
|
||||||
return abilities.get(action, False)
|
return abilities.get(action, False)
|
||||||
|
|
||||||
|
|
||||||
|
class CommentPermission(permissions.BasePermission):
|
||||||
|
"""Permission class for comments."""
|
||||||
|
|
||||||
|
def has_permission(self, request, view):
|
||||||
|
"""Check permission for a given object."""
|
||||||
|
if view.action in ["create", "list"]:
|
||||||
|
document_abilities = view.get_document_or_404().get_abilities(request.user)
|
||||||
|
return document_abilities["comment"]
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def has_object_permission(self, request, view, obj):
|
||||||
|
"""Check permission for a given object."""
|
||||||
|
return obj.get_abilities(request.user).get(view.action, False)
|
||||||
|
|||||||
@@ -801,3 +801,47 @@ class MoveDocumentSerializer(serializers.Serializer):
|
|||||||
choices=enums.MoveNodePositionChoices.choices,
|
choices=enums.MoveNodePositionChoices.choices,
|
||||||
default=enums.MoveNodePositionChoices.LAST_CHILD,
|
default=enums.MoveNodePositionChoices.LAST_CHILD,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CommentSerializer(serializers.ModelSerializer):
|
||||||
|
"""Serialize comments."""
|
||||||
|
|
||||||
|
user = UserLightSerializer(read_only=True)
|
||||||
|
abilities = serializers.SerializerMethodField(read_only=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = models.Comment
|
||||||
|
fields = [
|
||||||
|
"id",
|
||||||
|
"content",
|
||||||
|
"created_at",
|
||||||
|
"updated_at",
|
||||||
|
"user",
|
||||||
|
"document",
|
||||||
|
"abilities",
|
||||||
|
]
|
||||||
|
read_only_fields = [
|
||||||
|
"id",
|
||||||
|
"created_at",
|
||||||
|
"updated_at",
|
||||||
|
"user",
|
||||||
|
"document",
|
||||||
|
"abilities",
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_abilities(self, comment) -> dict:
|
||||||
|
"""Return abilities of the logged-in user on the instance."""
|
||||||
|
request = self.context.get("request")
|
||||||
|
if request:
|
||||||
|
return comment.get_abilities(request.user)
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
"""Validate invitation data."""
|
||||||
|
request = self.context.get("request")
|
||||||
|
user = getattr(request, "user", None)
|
||||||
|
|
||||||
|
attrs["document_id"] = self.context["resource_id"]
|
||||||
|
attrs["user_id"] = user.id if user else None
|
||||||
|
|
||||||
|
return attrs
|
||||||
|
|||||||
@@ -2072,3 +2072,36 @@ class ConfigView(drf.views.APIView):
|
|||||||
)
|
)
|
||||||
|
|
||||||
return theme_customization
|
return theme_customization
|
||||||
|
|
||||||
|
|
||||||
|
class CommentViewSet(
|
||||||
|
viewsets.ModelViewSet,
|
||||||
|
):
|
||||||
|
"""API ViewSet for comments."""
|
||||||
|
|
||||||
|
permission_classes = [permissions.CommentPermission]
|
||||||
|
queryset = models.Comment.objects.select_related("user", "document").all()
|
||||||
|
serializer_class = serializers.CommentSerializer
|
||||||
|
pagination_class = Pagination
|
||||||
|
_document = None
|
||||||
|
|
||||||
|
def get_document_or_404(self):
|
||||||
|
"""Get the document related to the viewset or raise a 404 error."""
|
||||||
|
if self._document is None:
|
||||||
|
try:
|
||||||
|
self._document = models.Document.objects.get(
|
||||||
|
pk=self.kwargs["resource_id"],
|
||||||
|
)
|
||||||
|
except models.Document.DoesNotExist as e:
|
||||||
|
raise drf.exceptions.NotFound("Document not found.") from e
|
||||||
|
return self._document
|
||||||
|
|
||||||
|
def get_serializer_context(self):
|
||||||
|
"""Extra context provided to the serializer class."""
|
||||||
|
context = super().get_serializer_context()
|
||||||
|
context["resource_id"] = self.kwargs["resource_id"]
|
||||||
|
return context
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
"""Return the queryset according to the action."""
|
||||||
|
return super().get_queryset().filter(document=self.kwargs["resource_id"])
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ class LinkRoleChoices(PriorityTextChoices):
|
|||||||
"""Defines the possible roles a link can offer on a document."""
|
"""Defines the possible roles a link can offer on a document."""
|
||||||
|
|
||||||
READER = "reader", _("Reader") # Can read
|
READER = "reader", _("Reader") # Can read
|
||||||
|
COMMENTATOR = "commentator", _("Commentator") # Can read and comment
|
||||||
EDITOR = "editor", _("Editor") # Can read and edit
|
EDITOR = "editor", _("Editor") # Can read and edit
|
||||||
|
|
||||||
|
|
||||||
@@ -40,6 +41,7 @@ class RoleChoices(PriorityTextChoices):
|
|||||||
"""Defines the possible roles a user can have in a resource."""
|
"""Defines the possible roles a user can have in a resource."""
|
||||||
|
|
||||||
READER = "reader", _("Reader") # Can read
|
READER = "reader", _("Reader") # Can read
|
||||||
|
COMMENTATOR = "commentator", _("Commentator") # Can read and comment
|
||||||
EDITOR = "editor", _("Editor") # Can read and edit
|
EDITOR = "editor", _("Editor") # Can read and edit
|
||||||
ADMIN = "administrator", _("Administrator") # Can read, edit, delete and share
|
ADMIN = "administrator", _("Administrator") # Can read, edit, delete and share
|
||||||
OWNER = "owner", _("Owner")
|
OWNER = "owner", _("Owner")
|
||||||
|
|||||||
@@ -256,3 +256,14 @@ class InvitationFactory(factory.django.DjangoModelFactory):
|
|||||||
document = factory.SubFactory(DocumentFactory)
|
document = factory.SubFactory(DocumentFactory)
|
||||||
role = factory.fuzzy.FuzzyChoice([role[0] for role in models.RoleChoices.choices])
|
role = factory.fuzzy.FuzzyChoice([role[0] for role in models.RoleChoices.choices])
|
||||||
issuer = factory.SubFactory(UserFactory)
|
issuer = factory.SubFactory(UserFactory)
|
||||||
|
|
||||||
|
|
||||||
|
class CommentFactory(factory.django.DjangoModelFactory):
|
||||||
|
"""A factory to create comments for a document"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = models.Comment
|
||||||
|
|
||||||
|
document = factory.SubFactory(DocumentFactory)
|
||||||
|
user = factory.SubFactory(UserFactory)
|
||||||
|
content = factory.Faker("text")
|
||||||
|
|||||||
@@ -0,0 +1,146 @@
|
|||||||
|
# Generated by Django 5.2.4 on 2025-08-26 08:11
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("core", "0024_add_is_masked_field_to_link_trace"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="document",
|
||||||
|
name="link_role",
|
||||||
|
field=models.CharField(
|
||||||
|
choices=[
|
||||||
|
("reader", "Reader"),
|
||||||
|
("commentator", "Commentator"),
|
||||||
|
("editor", "Editor"),
|
||||||
|
],
|
||||||
|
default="reader",
|
||||||
|
max_length=20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="documentaccess",
|
||||||
|
name="role",
|
||||||
|
field=models.CharField(
|
||||||
|
choices=[
|
||||||
|
("reader", "Reader"),
|
||||||
|
("commentator", "Commentator"),
|
||||||
|
("editor", "Editor"),
|
||||||
|
("administrator", "Administrator"),
|
||||||
|
("owner", "Owner"),
|
||||||
|
],
|
||||||
|
default="reader",
|
||||||
|
max_length=20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="documentaskforaccess",
|
||||||
|
name="role",
|
||||||
|
field=models.CharField(
|
||||||
|
choices=[
|
||||||
|
("reader", "Reader"),
|
||||||
|
("commentator", "Commentator"),
|
||||||
|
("editor", "Editor"),
|
||||||
|
("administrator", "Administrator"),
|
||||||
|
("owner", "Owner"),
|
||||||
|
],
|
||||||
|
default="reader",
|
||||||
|
max_length=20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="invitation",
|
||||||
|
name="role",
|
||||||
|
field=models.CharField(
|
||||||
|
choices=[
|
||||||
|
("reader", "Reader"),
|
||||||
|
("commentator", "Commentator"),
|
||||||
|
("editor", "Editor"),
|
||||||
|
("administrator", "Administrator"),
|
||||||
|
("owner", "Owner"),
|
||||||
|
],
|
||||||
|
default="reader",
|
||||||
|
max_length=20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="templateaccess",
|
||||||
|
name="role",
|
||||||
|
field=models.CharField(
|
||||||
|
choices=[
|
||||||
|
("reader", "Reader"),
|
||||||
|
("commentator", "Commentator"),
|
||||||
|
("editor", "Editor"),
|
||||||
|
("administrator", "Administrator"),
|
||||||
|
("owner", "Owner"),
|
||||||
|
],
|
||||||
|
default="reader",
|
||||||
|
max_length=20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="Comment",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.UUIDField(
|
||||||
|
default=uuid.uuid4,
|
||||||
|
editable=False,
|
||||||
|
help_text="primary key for the record as UUID",
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="id",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"created_at",
|
||||||
|
models.DateTimeField(
|
||||||
|
auto_now_add=True,
|
||||||
|
help_text="date and time at which a record was created",
|
||||||
|
verbose_name="created on",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"updated_at",
|
||||||
|
models.DateTimeField(
|
||||||
|
auto_now=True,
|
||||||
|
help_text="date and time at which a record was last updated",
|
||||||
|
verbose_name="updated on",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("content", models.TextField()),
|
||||||
|
(
|
||||||
|
"document",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name="comments",
|
||||||
|
to="core.document",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"user",
|
||||||
|
models.ForeignKey(
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.SET_NULL,
|
||||||
|
related_name="comments",
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"verbose_name": "Comment",
|
||||||
|
"verbose_name_plural": "Comments",
|
||||||
|
"db_table": "impress_comment",
|
||||||
|
"ordering": ("-created_at",),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -762,6 +762,7 @@ class Document(MP_Node, BaseModel):
|
|||||||
can_update = (
|
can_update = (
|
||||||
is_owner_or_admin or role == RoleChoices.EDITOR
|
is_owner_or_admin or role == RoleChoices.EDITOR
|
||||||
) and not is_deleted
|
) and not is_deleted
|
||||||
|
can_comment = (can_update or role == RoleChoices.COMMENTATOR) and not is_deleted
|
||||||
|
|
||||||
ai_allow_reach_from = settings.AI_ALLOW_REACH_FROM
|
ai_allow_reach_from = settings.AI_ALLOW_REACH_FROM
|
||||||
ai_access = any(
|
ai_access = any(
|
||||||
@@ -786,6 +787,7 @@ class Document(MP_Node, BaseModel):
|
|||||||
"children_list": can_get,
|
"children_list": can_get,
|
||||||
"children_create": can_update and user.is_authenticated,
|
"children_create": can_update and user.is_authenticated,
|
||||||
"collaboration_auth": can_get,
|
"collaboration_auth": can_get,
|
||||||
|
"comment": can_comment,
|
||||||
"cors_proxy": can_get,
|
"cors_proxy": can_get,
|
||||||
"descendants": can_get,
|
"descendants": can_get,
|
||||||
"destroy": is_owner,
|
"destroy": is_owner,
|
||||||
@@ -1145,7 +1147,12 @@ class DocumentAccess(BaseAccess):
|
|||||||
set_role_to = []
|
set_role_to = []
|
||||||
if is_owner_or_admin:
|
if is_owner_or_admin:
|
||||||
set_role_to.extend(
|
set_role_to.extend(
|
||||||
[RoleChoices.READER, RoleChoices.EDITOR, RoleChoices.ADMIN]
|
[
|
||||||
|
RoleChoices.READER,
|
||||||
|
RoleChoices.COMMENTATOR,
|
||||||
|
RoleChoices.EDITOR,
|
||||||
|
RoleChoices.ADMIN,
|
||||||
|
]
|
||||||
)
|
)
|
||||||
if role == RoleChoices.OWNER:
|
if role == RoleChoices.OWNER:
|
||||||
set_role_to.append(RoleChoices.OWNER)
|
set_role_to.append(RoleChoices.OWNER)
|
||||||
@@ -1277,6 +1284,48 @@ class DocumentAskForAccess(BaseModel):
|
|||||||
self.document.send_email(subject, [email], context, language)
|
self.document.send_email(subject, [email], context, language)
|
||||||
|
|
||||||
|
|
||||||
|
class Comment(BaseModel):
|
||||||
|
"""User comment on a document."""
|
||||||
|
|
||||||
|
document = models.ForeignKey(
|
||||||
|
Document,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name="comments",
|
||||||
|
)
|
||||||
|
user = models.ForeignKey(
|
||||||
|
User,
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
related_name="comments",
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
)
|
||||||
|
content = models.TextField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = "impress_comment"
|
||||||
|
ordering = ("-created_at",)
|
||||||
|
verbose_name = _("Comment")
|
||||||
|
verbose_name_plural = _("Comments")
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
author = self.user or _("Anonymous")
|
||||||
|
return f"{author!s} on {self.document!s}"
|
||||||
|
|
||||||
|
def get_abilities(self, user):
|
||||||
|
"""Compute and return abilities for a given user."""
|
||||||
|
role = self.document.get_role(user)
|
||||||
|
can_comment = self.document.get_abilities(user)["comment"]
|
||||||
|
return {
|
||||||
|
"destroy": self.user == user
|
||||||
|
or role in [RoleChoices.OWNER, RoleChoices.ADMIN],
|
||||||
|
"update": self.user == user
|
||||||
|
or role in [RoleChoices.OWNER, RoleChoices.ADMIN],
|
||||||
|
"partial_update": self.user == user
|
||||||
|
or role in [RoleChoices.OWNER, RoleChoices.ADMIN],
|
||||||
|
"retrieve": can_comment,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class Template(BaseModel):
|
class Template(BaseModel):
|
||||||
"""HTML and CSS code used for formatting the print around the MarkDown body."""
|
"""HTML and CSS code used for formatting the print around the MarkDown body."""
|
||||||
|
|
||||||
|
|||||||
@@ -292,6 +292,7 @@ def test_api_document_accesses_retrieve_set_role_to_child():
|
|||||||
}
|
}
|
||||||
assert result_dict[str(document_access_other_user.id)] == [
|
assert result_dict[str(document_access_other_user.id)] == [
|
||||||
"reader",
|
"reader",
|
||||||
|
"commentator",
|
||||||
"editor",
|
"editor",
|
||||||
"administrator",
|
"administrator",
|
||||||
"owner",
|
"owner",
|
||||||
@@ -300,7 +301,7 @@ def test_api_document_accesses_retrieve_set_role_to_child():
|
|||||||
|
|
||||||
# Add an access for the other user on the parent
|
# Add an access for the other user on the parent
|
||||||
parent_access_other_user = factories.UserDocumentAccessFactory(
|
parent_access_other_user = factories.UserDocumentAccessFactory(
|
||||||
document=parent, user=other_user, role="editor"
|
document=parent, user=other_user, role="commentator"
|
||||||
)
|
)
|
||||||
|
|
||||||
response = client.get(f"/api/v1.0/documents/{document.id!s}/accesses/")
|
response = client.get(f"/api/v1.0/documents/{document.id!s}/accesses/")
|
||||||
@@ -313,6 +314,7 @@ def test_api_document_accesses_retrieve_set_role_to_child():
|
|||||||
result["id"]: result["abilities"]["set_role_to"] for result in content
|
result["id"]: result["abilities"]["set_role_to"] for result in content
|
||||||
}
|
}
|
||||||
assert result_dict[str(document_access_other_user.id)] == [
|
assert result_dict[str(document_access_other_user.id)] == [
|
||||||
|
"commentator",
|
||||||
"editor",
|
"editor",
|
||||||
"administrator",
|
"administrator",
|
||||||
"owner",
|
"owner",
|
||||||
@@ -320,6 +322,7 @@ def test_api_document_accesses_retrieve_set_role_to_child():
|
|||||||
assert result_dict[str(parent_access.id)] == []
|
assert result_dict[str(parent_access.id)] == []
|
||||||
assert result_dict[str(parent_access_other_user.id)] == [
|
assert result_dict[str(parent_access_other_user.id)] == [
|
||||||
"reader",
|
"reader",
|
||||||
|
"commentator",
|
||||||
"editor",
|
"editor",
|
||||||
"administrator",
|
"administrator",
|
||||||
"owner",
|
"owner",
|
||||||
@@ -332,28 +335,28 @@ def test_api_document_accesses_retrieve_set_role_to_child():
|
|||||||
[
|
[
|
||||||
["administrator", "reader", "reader", "reader"],
|
["administrator", "reader", "reader", "reader"],
|
||||||
[
|
[
|
||||||
["reader", "editor", "administrator"],
|
["reader", "commentator", "editor", "administrator"],
|
||||||
[],
|
[],
|
||||||
[],
|
[],
|
||||||
["reader", "editor", "administrator"],
|
["reader", "commentator", "editor", "administrator"],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
["owner", "reader", "reader", "reader"],
|
["owner", "reader", "reader", "reader"],
|
||||||
[
|
[
|
||||||
["reader", "editor", "administrator", "owner"],
|
["reader", "commentator", "editor", "administrator", "owner"],
|
||||||
[],
|
[],
|
||||||
[],
|
[],
|
||||||
["reader", "editor", "administrator", "owner"],
|
["reader", "commentator", "editor", "administrator", "owner"],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
["owner", "reader", "reader", "owner"],
|
["owner", "reader", "reader", "owner"],
|
||||||
[
|
[
|
||||||
["reader", "editor", "administrator", "owner"],
|
["reader", "commentator", "editor", "administrator", "owner"],
|
||||||
[],
|
[],
|
||||||
[],
|
[],
|
||||||
["reader", "editor", "administrator", "owner"],
|
["reader", "commentator", "editor", "administrator", "owner"],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
@@ -414,44 +417,44 @@ def test_api_document_accesses_list_authenticated_related_same_user(roles, resul
|
|||||||
[
|
[
|
||||||
["administrator", "reader", "reader", "reader"],
|
["administrator", "reader", "reader", "reader"],
|
||||||
[
|
[
|
||||||
["reader", "editor", "administrator"],
|
["reader", "commentator", "editor", "administrator"],
|
||||||
[],
|
[],
|
||||||
[],
|
[],
|
||||||
["reader", "editor", "administrator"],
|
["reader", "commentator", "editor", "administrator"],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
["owner", "reader", "reader", "reader"],
|
["owner", "reader", "reader", "reader"],
|
||||||
[
|
[
|
||||||
["reader", "editor", "administrator", "owner"],
|
["reader", "commentator", "editor", "administrator", "owner"],
|
||||||
[],
|
[],
|
||||||
[],
|
[],
|
||||||
["reader", "editor", "administrator", "owner"],
|
["reader", "commentator", "editor", "administrator", "owner"],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
["owner", "reader", "reader", "owner"],
|
["owner", "reader", "reader", "owner"],
|
||||||
[
|
[
|
||||||
["reader", "editor", "administrator", "owner"],
|
["reader", "commentator", "editor", "administrator", "owner"],
|
||||||
[],
|
[],
|
||||||
[],
|
[],
|
||||||
["reader", "editor", "administrator", "owner"],
|
["reader", "commentator", "editor", "administrator", "owner"],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
["reader", "reader", "reader", "owner"],
|
["reader", "reader", "reader", "owner"],
|
||||||
[
|
[
|
||||||
["reader", "editor", "administrator", "owner"],
|
["reader", "commentator", "editor", "administrator", "owner"],
|
||||||
[],
|
[],
|
||||||
[],
|
[],
|
||||||
["reader", "editor", "administrator", "owner"],
|
["reader", "commentator", "editor", "administrator", "owner"],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
["reader", "administrator", "reader", "editor"],
|
["reader", "administrator", "reader", "editor"],
|
||||||
[
|
[
|
||||||
["reader", "editor", "administrator"],
|
["reader", "commentator", "editor", "administrator"],
|
||||||
["reader", "editor", "administrator"],
|
["reader", "commentator", "editor", "administrator"],
|
||||||
[],
|
[],
|
||||||
[],
|
[],
|
||||||
],
|
],
|
||||||
@@ -459,7 +462,7 @@ def test_api_document_accesses_list_authenticated_related_same_user(roles, resul
|
|||||||
[
|
[
|
||||||
["editor", "editor", "administrator", "editor"],
|
["editor", "editor", "administrator", "editor"],
|
||||||
[
|
[
|
||||||
["reader", "editor", "administrator"],
|
["reader", "commentator", "editor", "administrator"],
|
||||||
[],
|
[],
|
||||||
["editor", "administrator"],
|
["editor", "administrator"],
|
||||||
[],
|
[],
|
||||||
|
|||||||
588
src/backend/core/tests/documents/test_api_documents_comments.py
Normal file
588
src/backend/core/tests/documents/test_api_documents_comments.py
Normal file
@@ -0,0 +1,588 @@
|
|||||||
|
"""Test API for comments on documents."""
|
||||||
|
|
||||||
|
import random
|
||||||
|
|
||||||
|
from django.contrib.auth.models import AnonymousUser
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from rest_framework.test import APIClient
|
||||||
|
|
||||||
|
from core import factories, models
|
||||||
|
|
||||||
|
pytestmark = pytest.mark.django_db
|
||||||
|
|
||||||
|
# List comments
|
||||||
|
|
||||||
|
|
||||||
|
def test_list_comments_anonymous_user_public_document():
|
||||||
|
"""Anonymous users should be allowed to list comments on a public document."""
|
||||||
|
document = factories.DocumentFactory(
|
||||||
|
link_reach="public", link_role=models.LinkRoleChoices.COMMENTATOR
|
||||||
|
)
|
||||||
|
comment1, comment2 = factories.CommentFactory.create_batch(2, document=document)
|
||||||
|
# other comments not linked to the document
|
||||||
|
factories.CommentFactory.create_batch(2)
|
||||||
|
|
||||||
|
response = APIClient().get(f"/api/v1.0/documents/{document.id!s}/comments/")
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.json() == {
|
||||||
|
"count": 2,
|
||||||
|
"next": None,
|
||||||
|
"previous": None,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"id": str(comment2.id),
|
||||||
|
"content": comment2.content,
|
||||||
|
"created_at": comment2.created_at.isoformat().replace("+00:00", "Z"),
|
||||||
|
"updated_at": comment2.updated_at.isoformat().replace("+00:00", "Z"),
|
||||||
|
"user": {
|
||||||
|
"full_name": comment2.user.full_name,
|
||||||
|
"short_name": comment2.user.short_name,
|
||||||
|
},
|
||||||
|
"document": str(comment2.document.id),
|
||||||
|
"abilities": comment2.get_abilities(AnonymousUser()),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": str(comment1.id),
|
||||||
|
"content": comment1.content,
|
||||||
|
"created_at": comment1.created_at.isoformat().replace("+00:00", "Z"),
|
||||||
|
"updated_at": comment1.updated_at.isoformat().replace("+00:00", "Z"),
|
||||||
|
"user": {
|
||||||
|
"full_name": comment1.user.full_name,
|
||||||
|
"short_name": comment1.user.short_name,
|
||||||
|
},
|
||||||
|
"document": str(comment1.document.id),
|
||||||
|
"abilities": comment1.get_abilities(AnonymousUser()),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("link_reach", ["restricted", "authenticated"])
|
||||||
|
def test_list_comments_anonymous_user_non_public_document(link_reach):
|
||||||
|
"""Anonymous users should not be allowed to list comments on a non-public document."""
|
||||||
|
document = factories.DocumentFactory(
|
||||||
|
link_reach=link_reach, link_role=models.LinkRoleChoices.COMMENTATOR
|
||||||
|
)
|
||||||
|
factories.CommentFactory(document=document)
|
||||||
|
# other comments not linked to the document
|
||||||
|
factories.CommentFactory.create_batch(2)
|
||||||
|
|
||||||
|
response = APIClient().get(f"/api/v1.0/documents/{document.id!s}/comments/")
|
||||||
|
assert response.status_code == 401
|
||||||
|
|
||||||
|
|
||||||
|
def test_list_comments_authenticated_user_accessible_document():
|
||||||
|
"""Authenticated users should be allowed to list comments on an accessible document."""
|
||||||
|
user = factories.UserFactory()
|
||||||
|
document = factories.DocumentFactory(
|
||||||
|
link_reach="restricted", users=[(user, models.LinkRoleChoices.COMMENTATOR)]
|
||||||
|
)
|
||||||
|
comment1 = factories.CommentFactory(document=document)
|
||||||
|
comment2 = factories.CommentFactory(document=document, user=user)
|
||||||
|
# other comments not linked to the document
|
||||||
|
factories.CommentFactory.create_batch(2)
|
||||||
|
|
||||||
|
client = APIClient()
|
||||||
|
client.force_login(user)
|
||||||
|
|
||||||
|
response = client.get(f"/api/v1.0/documents/{document.id!s}/comments/")
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.json() == {
|
||||||
|
"count": 2,
|
||||||
|
"next": None,
|
||||||
|
"previous": None,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"id": str(comment2.id),
|
||||||
|
"content": comment2.content,
|
||||||
|
"created_at": comment2.created_at.isoformat().replace("+00:00", "Z"),
|
||||||
|
"updated_at": comment2.updated_at.isoformat().replace("+00:00", "Z"),
|
||||||
|
"user": {
|
||||||
|
"full_name": comment2.user.full_name,
|
||||||
|
"short_name": comment2.user.short_name,
|
||||||
|
},
|
||||||
|
"document": str(comment2.document.id),
|
||||||
|
"abilities": comment2.get_abilities(user),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": str(comment1.id),
|
||||||
|
"content": comment1.content,
|
||||||
|
"created_at": comment1.created_at.isoformat().replace("+00:00", "Z"),
|
||||||
|
"updated_at": comment1.updated_at.isoformat().replace("+00:00", "Z"),
|
||||||
|
"user": {
|
||||||
|
"full_name": comment1.user.full_name,
|
||||||
|
"short_name": comment1.user.short_name,
|
||||||
|
},
|
||||||
|
"document": str(comment1.document.id),
|
||||||
|
"abilities": comment1.get_abilities(user),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_list_comments_authenticated_user_non_accessible_document():
|
||||||
|
"""Authenticated users should not be allowed to list comments on a non-accessible document."""
|
||||||
|
user = factories.UserFactory()
|
||||||
|
document = factories.DocumentFactory(link_reach="restricted")
|
||||||
|
factories.CommentFactory(document=document)
|
||||||
|
# other comments not linked to the document
|
||||||
|
factories.CommentFactory.create_batch(2)
|
||||||
|
|
||||||
|
client = APIClient()
|
||||||
|
client.force_login(user)
|
||||||
|
|
||||||
|
response = client.get(f"/api/v1.0/documents/{document.id!s}/comments/")
|
||||||
|
assert response.status_code == 403
|
||||||
|
|
||||||
|
|
||||||
|
def test_list_comments_authenticated_user_not_enough_access():
|
||||||
|
"""
|
||||||
|
Authenticated users should not be allowed to list comments on a document they don't have
|
||||||
|
comment access to.
|
||||||
|
"""
|
||||||
|
user = factories.UserFactory()
|
||||||
|
document = factories.DocumentFactory(
|
||||||
|
link_reach="restricted", users=[(user, models.LinkRoleChoices.READER)]
|
||||||
|
)
|
||||||
|
factories.CommentFactory(document=document)
|
||||||
|
# other comments not linked to the document
|
||||||
|
factories.CommentFactory.create_batch(2)
|
||||||
|
|
||||||
|
client = APIClient()
|
||||||
|
client.force_login(user)
|
||||||
|
|
||||||
|
response = client.get(f"/api/v1.0/documents/{document.id!s}/comments/")
|
||||||
|
assert response.status_code == 403
|
||||||
|
|
||||||
|
|
||||||
|
# Create comment
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_comment_anonymous_user_public_document():
|
||||||
|
"""Anonymous users should not be allowed to create comments on a public document."""
|
||||||
|
document = factories.DocumentFactory(
|
||||||
|
link_reach="public", link_role=models.LinkRoleChoices.COMMENTATOR
|
||||||
|
)
|
||||||
|
client = APIClient()
|
||||||
|
response = client.post(
|
||||||
|
f"/api/v1.0/documents/{document.id!s}/comments/", {"content": "test"}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 201
|
||||||
|
|
||||||
|
assert response.json() == {
|
||||||
|
"id": str(response.json()["id"]),
|
||||||
|
"content": "test",
|
||||||
|
"created_at": response.json()["created_at"],
|
||||||
|
"updated_at": response.json()["updated_at"],
|
||||||
|
"user": None,
|
||||||
|
"document": str(document.id),
|
||||||
|
"abilities": {
|
||||||
|
"destroy": False,
|
||||||
|
"update": False,
|
||||||
|
"partial_update": False,
|
||||||
|
"retrieve": True,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_comment_anonymous_user_non_accessible_document():
|
||||||
|
"""Anonymous users should not be allowed to create comments on a non-accessible document."""
|
||||||
|
document = factories.DocumentFactory(
|
||||||
|
link_reach="public", link_role=models.LinkRoleChoices.READER
|
||||||
|
)
|
||||||
|
client = APIClient()
|
||||||
|
response = client.post(
|
||||||
|
f"/api/v1.0/documents/{document.id!s}/comments/", {"content": "test"}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 401
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_comment_authenticated_user_accessible_document():
|
||||||
|
"""Authenticated users should be allowed to create comments on an accessible document."""
|
||||||
|
user = factories.UserFactory()
|
||||||
|
document = factories.DocumentFactory(
|
||||||
|
link_reach="restricted", users=[(user, models.LinkRoleChoices.COMMENTATOR)]
|
||||||
|
)
|
||||||
|
client = APIClient()
|
||||||
|
client.force_login(user)
|
||||||
|
response = client.post(
|
||||||
|
f"/api/v1.0/documents/{document.id!s}/comments/", {"content": "test"}
|
||||||
|
)
|
||||||
|
assert response.status_code == 201
|
||||||
|
|
||||||
|
assert response.json() == {
|
||||||
|
"id": str(response.json()["id"]),
|
||||||
|
"content": "test",
|
||||||
|
"created_at": response.json()["created_at"],
|
||||||
|
"updated_at": response.json()["updated_at"],
|
||||||
|
"user": {
|
||||||
|
"full_name": user.full_name,
|
||||||
|
"short_name": user.short_name,
|
||||||
|
},
|
||||||
|
"document": str(document.id),
|
||||||
|
"abilities": {
|
||||||
|
"destroy": True,
|
||||||
|
"update": True,
|
||||||
|
"partial_update": True,
|
||||||
|
"retrieve": True,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_comment_authenticated_user_not_enough_access():
|
||||||
|
"""
|
||||||
|
Authenticated users should not be allowed to create comments on a document they don't have
|
||||||
|
comment access to.
|
||||||
|
"""
|
||||||
|
user = factories.UserFactory()
|
||||||
|
document = factories.DocumentFactory(
|
||||||
|
link_reach="restricted", users=[(user, models.LinkRoleChoices.READER)]
|
||||||
|
)
|
||||||
|
client = APIClient()
|
||||||
|
client.force_login(user)
|
||||||
|
response = client.post(
|
||||||
|
f"/api/v1.0/documents/{document.id!s}/comments/", {"content": "test"}
|
||||||
|
)
|
||||||
|
assert response.status_code == 403
|
||||||
|
|
||||||
|
|
||||||
|
# Retrieve comment
|
||||||
|
|
||||||
|
|
||||||
|
def test_retrieve_comment_anonymous_user_public_document():
|
||||||
|
"""Anonymous users should be allowed to retrieve comments on a public document."""
|
||||||
|
document = factories.DocumentFactory(
|
||||||
|
link_reach="public", link_role=models.LinkRoleChoices.COMMENTATOR
|
||||||
|
)
|
||||||
|
comment = factories.CommentFactory(document=document)
|
||||||
|
client = APIClient()
|
||||||
|
response = client.get(
|
||||||
|
f"/api/v1.0/documents/{document.id!s}/comments/{comment.id!s}/"
|
||||||
|
)
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.json() == {
|
||||||
|
"id": str(comment.id),
|
||||||
|
"content": comment.content,
|
||||||
|
"created_at": comment.created_at.isoformat().replace("+00:00", "Z"),
|
||||||
|
"updated_at": comment.updated_at.isoformat().replace("+00:00", "Z"),
|
||||||
|
"user": {
|
||||||
|
"full_name": comment.user.full_name,
|
||||||
|
"short_name": comment.user.short_name,
|
||||||
|
},
|
||||||
|
"document": str(comment.document.id),
|
||||||
|
"abilities": comment.get_abilities(AnonymousUser()),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_retrieve_comment_anonymous_user_non_accessible_document():
|
||||||
|
"""Anonymous users should not be allowed to retrieve comments on a non-accessible document."""
|
||||||
|
document = factories.DocumentFactory(
|
||||||
|
link_reach="public", link_role=models.LinkRoleChoices.READER
|
||||||
|
)
|
||||||
|
comment = factories.CommentFactory(document=document)
|
||||||
|
client = APIClient()
|
||||||
|
response = client.get(
|
||||||
|
f"/api/v1.0/documents/{document.id!s}/comments/{comment.id!s}/"
|
||||||
|
)
|
||||||
|
assert response.status_code == 401
|
||||||
|
|
||||||
|
|
||||||
|
def test_retrieve_comment_authenticated_user_accessible_document():
|
||||||
|
"""Authenticated users should be allowed to retrieve comments on an accessible document."""
|
||||||
|
user = factories.UserFactory()
|
||||||
|
document = factories.DocumentFactory(
|
||||||
|
link_reach="restricted", users=[(user, models.LinkRoleChoices.COMMENTATOR)]
|
||||||
|
)
|
||||||
|
comment = factories.CommentFactory(document=document)
|
||||||
|
client = APIClient()
|
||||||
|
client.force_login(user)
|
||||||
|
response = client.get(
|
||||||
|
f"/api/v1.0/documents/{document.id!s}/comments/{comment.id!s}/"
|
||||||
|
)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
|
def test_retrieve_comment_authenticated_user_not_enough_access():
|
||||||
|
"""
|
||||||
|
Authenticated users should not be allowed to retrieve comments on a document they don't have
|
||||||
|
comment access to.
|
||||||
|
"""
|
||||||
|
user = factories.UserFactory()
|
||||||
|
document = factories.DocumentFactory(
|
||||||
|
link_reach="restricted", users=[(user, models.LinkRoleChoices.READER)]
|
||||||
|
)
|
||||||
|
comment = factories.CommentFactory(document=document)
|
||||||
|
client = APIClient()
|
||||||
|
client.force_login(user)
|
||||||
|
response = client.get(
|
||||||
|
f"/api/v1.0/documents/{document.id!s}/comments/{comment.id!s}/"
|
||||||
|
)
|
||||||
|
assert response.status_code == 403
|
||||||
|
|
||||||
|
|
||||||
|
# Update comment
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_comment_anonymous_user_public_document():
|
||||||
|
"""Anonymous users should not be allowed to update comments on a public document."""
|
||||||
|
document = factories.DocumentFactory(
|
||||||
|
link_reach="public", link_role=models.LinkRoleChoices.COMMENTATOR
|
||||||
|
)
|
||||||
|
comment = factories.CommentFactory(document=document, content="test")
|
||||||
|
client = APIClient()
|
||||||
|
response = client.put(
|
||||||
|
f"/api/v1.0/documents/{document.id!s}/comments/{comment.id!s}/",
|
||||||
|
{"content": "other content"},
|
||||||
|
)
|
||||||
|
assert response.status_code == 401
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_comment_anonymous_user_non_accessible_document():
|
||||||
|
"""Anonymous users should not be allowed to update comments on a non-accessible document."""
|
||||||
|
document = factories.DocumentFactory(
|
||||||
|
link_reach="public", link_role=models.LinkRoleChoices.READER
|
||||||
|
)
|
||||||
|
comment = factories.CommentFactory(document=document, content="test")
|
||||||
|
client = APIClient()
|
||||||
|
response = client.put(
|
||||||
|
f"/api/v1.0/documents/{document.id!s}/comments/{comment.id!s}/",
|
||||||
|
{"content": "other content"},
|
||||||
|
)
|
||||||
|
assert response.status_code == 401
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_comment_authenticated_user_accessible_document():
|
||||||
|
"""Authenticated users should not be able to update comments not their own."""
|
||||||
|
user = factories.UserFactory()
|
||||||
|
document = factories.DocumentFactory(
|
||||||
|
link_reach="restricted",
|
||||||
|
users=[
|
||||||
|
(
|
||||||
|
user,
|
||||||
|
random.choice(
|
||||||
|
[models.LinkRoleChoices.COMMENTATOR, models.LinkRoleChoices.EDITOR]
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
comment = factories.CommentFactory(document=document, content="test")
|
||||||
|
client = APIClient()
|
||||||
|
client.force_login(user)
|
||||||
|
response = client.put(
|
||||||
|
f"/api/v1.0/documents/{document.id!s}/comments/{comment.id!s}/",
|
||||||
|
{"content": "other content"},
|
||||||
|
)
|
||||||
|
assert response.status_code == 403
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_comment_authenticated_user_own_comment():
|
||||||
|
"""Authenticated users should be able to update comments not their own."""
|
||||||
|
user = factories.UserFactory()
|
||||||
|
document = factories.DocumentFactory(
|
||||||
|
link_reach="restricted",
|
||||||
|
users=[
|
||||||
|
(
|
||||||
|
user,
|
||||||
|
random.choice(
|
||||||
|
[models.LinkRoleChoices.COMMENTATOR, models.LinkRoleChoices.EDITOR]
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
comment = factories.CommentFactory(document=document, content="test", user=user)
|
||||||
|
client = APIClient()
|
||||||
|
client.force_login(user)
|
||||||
|
response = client.put(
|
||||||
|
f"/api/v1.0/documents/{document.id!s}/comments/{comment.id!s}/",
|
||||||
|
{"content": "other content"},
|
||||||
|
)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
comment.refresh_from_db()
|
||||||
|
assert comment.content == "other content"
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_comment_authenticated_user_not_enough_access():
|
||||||
|
"""
|
||||||
|
Authenticated users should not be allowed to update comments on a document they don't
|
||||||
|
have comment access to.
|
||||||
|
"""
|
||||||
|
user = factories.UserFactory()
|
||||||
|
document = factories.DocumentFactory(
|
||||||
|
link_reach="restricted", users=[(user, models.LinkRoleChoices.READER)]
|
||||||
|
)
|
||||||
|
comment = factories.CommentFactory(document=document, content="test")
|
||||||
|
client = APIClient()
|
||||||
|
client.force_login(user)
|
||||||
|
response = client.put(
|
||||||
|
f"/api/v1.0/documents/{document.id!s}/comments/{comment.id!s}/",
|
||||||
|
{"content": "other content"},
|
||||||
|
)
|
||||||
|
assert response.status_code == 403
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_comment_authenticated_no_access():
|
||||||
|
"""
|
||||||
|
Authenticated users should not be allowed to update comments on a document they don't
|
||||||
|
have access to.
|
||||||
|
"""
|
||||||
|
user = factories.UserFactory()
|
||||||
|
document = factories.DocumentFactory(link_reach="restricted")
|
||||||
|
comment = factories.CommentFactory(document=document, content="test")
|
||||||
|
client = APIClient()
|
||||||
|
client.force_login(user)
|
||||||
|
response = client.put(
|
||||||
|
f"/api/v1.0/documents/{document.id!s}/comments/{comment.id!s}/",
|
||||||
|
{"content": "other content"},
|
||||||
|
)
|
||||||
|
assert response.status_code == 403
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("role", [models.RoleChoices.ADMIN, models.RoleChoices.OWNER])
|
||||||
|
def test_update_comment_authenticated_admin_or_owner_can_update_any_comment(role):
|
||||||
|
"""
|
||||||
|
Authenticated users should be able to update comments on a document they don't have access to.
|
||||||
|
"""
|
||||||
|
user = factories.UserFactory()
|
||||||
|
document = factories.DocumentFactory(users=[(user, role)])
|
||||||
|
comment = factories.CommentFactory(document=document, content="test")
|
||||||
|
client = APIClient()
|
||||||
|
client.force_login(user)
|
||||||
|
|
||||||
|
response = client.put(
|
||||||
|
f"/api/v1.0/documents/{document.id!s}/comments/{comment.id!s}/",
|
||||||
|
{"content": "other content"},
|
||||||
|
)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
comment.refresh_from_db()
|
||||||
|
assert comment.content == "other content"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("role", [models.RoleChoices.ADMIN, models.RoleChoices.OWNER])
|
||||||
|
def test_update_comment_authenticated_admin_or_owner_can_update_own_comment(role):
|
||||||
|
"""
|
||||||
|
Authenticated users should be able to update comments on a document they don't have access to.
|
||||||
|
"""
|
||||||
|
user = factories.UserFactory()
|
||||||
|
document = factories.DocumentFactory(users=[(user, role)])
|
||||||
|
comment = factories.CommentFactory(document=document, content="test", user=user)
|
||||||
|
client = APIClient()
|
||||||
|
client.force_login(user)
|
||||||
|
|
||||||
|
response = client.put(
|
||||||
|
f"/api/v1.0/documents/{document.id!s}/comments/{comment.id!s}/",
|
||||||
|
{"content": "other content"},
|
||||||
|
)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
comment.refresh_from_db()
|
||||||
|
assert comment.content == "other content"
|
||||||
|
|
||||||
|
|
||||||
|
# Delete comment
|
||||||
|
|
||||||
|
|
||||||
|
def test_delete_comment_anonymous_user_public_document():
|
||||||
|
"""Anonymous users should not be allowed to delete comments on a public document."""
|
||||||
|
document = factories.DocumentFactory(
|
||||||
|
link_reach="public", link_role=models.LinkRoleChoices.COMMENTATOR
|
||||||
|
)
|
||||||
|
comment = factories.CommentFactory(document=document)
|
||||||
|
client = APIClient()
|
||||||
|
response = client.delete(
|
||||||
|
f"/api/v1.0/documents/{document.id!s}/comments/{comment.id!s}/"
|
||||||
|
)
|
||||||
|
assert response.status_code == 401
|
||||||
|
|
||||||
|
|
||||||
|
def test_delete_comment_anonymous_user_non_accessible_document():
|
||||||
|
"""Anonymous users should not be allowed to delete comments on a non-accessible document."""
|
||||||
|
document = factories.DocumentFactory(
|
||||||
|
link_reach="public", link_role=models.LinkRoleChoices.READER
|
||||||
|
)
|
||||||
|
comment = factories.CommentFactory(document=document)
|
||||||
|
client = APIClient()
|
||||||
|
response = client.delete(
|
||||||
|
f"/api/v1.0/documents/{document.id!s}/comments/{comment.id!s}/"
|
||||||
|
)
|
||||||
|
assert response.status_code == 401
|
||||||
|
|
||||||
|
|
||||||
|
def test_delete_comment_authenticated_user_accessible_document_own_comment():
|
||||||
|
"""Authenticated users should be able to delete comments on an accessible document."""
|
||||||
|
user = factories.UserFactory()
|
||||||
|
document = factories.DocumentFactory(
|
||||||
|
link_reach="restricted", users=[(user, models.LinkRoleChoices.COMMENTATOR)]
|
||||||
|
)
|
||||||
|
comment = factories.CommentFactory(document=document, user=user)
|
||||||
|
client = APIClient()
|
||||||
|
client.force_login(user)
|
||||||
|
response = client.delete(
|
||||||
|
f"/api/v1.0/documents/{document.id!s}/comments/{comment.id!s}/"
|
||||||
|
)
|
||||||
|
assert response.status_code == 204
|
||||||
|
|
||||||
|
|
||||||
|
def test_delete_comment_authenticated_user_accessible_document_not_own_comment():
|
||||||
|
"""Authenticated users should not be able to delete comments on an accessible document."""
|
||||||
|
user = factories.UserFactory()
|
||||||
|
document = factories.DocumentFactory(
|
||||||
|
link_reach="restricted", users=[(user, models.LinkRoleChoices.COMMENTATOR)]
|
||||||
|
)
|
||||||
|
comment = factories.CommentFactory(document=document)
|
||||||
|
client = APIClient()
|
||||||
|
client.force_login(user)
|
||||||
|
response = client.delete(
|
||||||
|
f"/api/v1.0/documents/{document.id!s}/comments/{comment.id!s}/"
|
||||||
|
)
|
||||||
|
assert response.status_code == 403
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("role", [models.RoleChoices.ADMIN, models.RoleChoices.OWNER])
|
||||||
|
def test_delete_comment_authenticated_user_admin_or_owner_can_delete_any_comment(role):
|
||||||
|
"""Authenticated users should be able to delete comments on a document they have access to."""
|
||||||
|
user = factories.UserFactory()
|
||||||
|
document = factories.DocumentFactory(users=[(user, role)])
|
||||||
|
comment = factories.CommentFactory(document=document)
|
||||||
|
client = APIClient()
|
||||||
|
client.force_login(user)
|
||||||
|
response = client.delete(
|
||||||
|
f"/api/v1.0/documents/{document.id!s}/comments/{comment.id!s}/"
|
||||||
|
)
|
||||||
|
assert response.status_code == 204
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("role", [models.RoleChoices.ADMIN, models.RoleChoices.OWNER])
|
||||||
|
def test_delete_comment_authenticated_user_admin_or_owner_can_delete_own_comment(role):
|
||||||
|
"""Authenticated users should be able to delete comments on a document they have access to."""
|
||||||
|
user = factories.UserFactory()
|
||||||
|
document = factories.DocumentFactory(users=[(user, role)])
|
||||||
|
comment = factories.CommentFactory(document=document, user=user)
|
||||||
|
client = APIClient()
|
||||||
|
client.force_login(user)
|
||||||
|
response = client.delete(
|
||||||
|
f"/api/v1.0/documents/{document.id!s}/comments/{comment.id!s}/"
|
||||||
|
)
|
||||||
|
assert response.status_code == 204
|
||||||
|
|
||||||
|
|
||||||
|
def test_delete_comment_authenticated_user_not_enough_access():
|
||||||
|
"""
|
||||||
|
Authenticated users should not be able to delete comments on a document they don't
|
||||||
|
have access to.
|
||||||
|
"""
|
||||||
|
user = factories.UserFactory()
|
||||||
|
document = factories.DocumentFactory(
|
||||||
|
link_reach="restricted", users=[(user, models.LinkRoleChoices.READER)]
|
||||||
|
)
|
||||||
|
comment = factories.CommentFactory(document=document)
|
||||||
|
client = APIClient()
|
||||||
|
client.force_login(user)
|
||||||
|
response = client.delete(
|
||||||
|
f"/api/v1.0/documents/{document.id!s}/comments/{comment.id!s}/"
|
||||||
|
)
|
||||||
|
assert response.status_code == 403
|
||||||
@@ -36,6 +36,7 @@ def test_api_documents_retrieve_anonymous_public_standalone():
|
|||||||
"children_create": False,
|
"children_create": False,
|
||||||
"children_list": True,
|
"children_list": True,
|
||||||
"collaboration_auth": True,
|
"collaboration_auth": True,
|
||||||
|
"comment": document.link_role in ["commentator", "editor"],
|
||||||
"cors_proxy": True,
|
"cors_proxy": True,
|
||||||
"descendants": True,
|
"descendants": True,
|
||||||
"destroy": False,
|
"destroy": False,
|
||||||
@@ -45,8 +46,8 @@ def test_api_documents_retrieve_anonymous_public_standalone():
|
|||||||
"invite_owner": False,
|
"invite_owner": False,
|
||||||
"link_configuration": False,
|
"link_configuration": False,
|
||||||
"link_select_options": {
|
"link_select_options": {
|
||||||
"authenticated": ["reader", "editor"],
|
"authenticated": ["reader", "commentator", "editor"],
|
||||||
"public": ["reader", "editor"],
|
"public": ["reader", "commentator", "editor"],
|
||||||
"restricted": None,
|
"restricted": None,
|
||||||
},
|
},
|
||||||
"mask": False,
|
"mask": False,
|
||||||
@@ -111,6 +112,7 @@ def test_api_documents_retrieve_anonymous_public_parent():
|
|||||||
"children_create": False,
|
"children_create": False,
|
||||||
"children_list": True,
|
"children_list": True,
|
||||||
"collaboration_auth": True,
|
"collaboration_auth": True,
|
||||||
|
"comment": grand_parent.link_role in ["commentator", "editor"],
|
||||||
"descendants": True,
|
"descendants": True,
|
||||||
"cors_proxy": True,
|
"cors_proxy": True,
|
||||||
"destroy": False,
|
"destroy": False,
|
||||||
@@ -216,6 +218,7 @@ def test_api_documents_retrieve_authenticated_unrelated_public_or_authenticated(
|
|||||||
"children_create": document.link_role == "editor",
|
"children_create": document.link_role == "editor",
|
||||||
"children_list": True,
|
"children_list": True,
|
||||||
"collaboration_auth": True,
|
"collaboration_auth": True,
|
||||||
|
"comment": document.link_role in ["commentator", "editor"],
|
||||||
"descendants": True,
|
"descendants": True,
|
||||||
"cors_proxy": True,
|
"cors_proxy": True,
|
||||||
"destroy": False,
|
"destroy": False,
|
||||||
@@ -224,8 +227,8 @@ def test_api_documents_retrieve_authenticated_unrelated_public_or_authenticated(
|
|||||||
"invite_owner": False,
|
"invite_owner": False,
|
||||||
"link_configuration": False,
|
"link_configuration": False,
|
||||||
"link_select_options": {
|
"link_select_options": {
|
||||||
"authenticated": ["reader", "editor"],
|
"authenticated": ["reader", "commentator", "editor"],
|
||||||
"public": ["reader", "editor"],
|
"public": ["reader", "commentator", "editor"],
|
||||||
"restricted": None,
|
"restricted": None,
|
||||||
},
|
},
|
||||||
"mask": True,
|
"mask": True,
|
||||||
@@ -298,6 +301,7 @@ def test_api_documents_retrieve_authenticated_public_or_authenticated_parent(rea
|
|||||||
"children_create": grand_parent.link_role == "editor",
|
"children_create": grand_parent.link_role == "editor",
|
||||||
"children_list": True,
|
"children_list": True,
|
||||||
"collaboration_auth": True,
|
"collaboration_auth": True,
|
||||||
|
"comment": grand_parent.link_role in ["commentator", "editor"],
|
||||||
"descendants": True,
|
"descendants": True,
|
||||||
"cors_proxy": True,
|
"cors_proxy": True,
|
||||||
"destroy": False,
|
"destroy": False,
|
||||||
@@ -488,10 +492,11 @@ def test_api_documents_retrieve_authenticated_related_parent():
|
|||||||
"ai_transform": access.role != "reader",
|
"ai_transform": access.role != "reader",
|
||||||
"ai_translate": access.role != "reader",
|
"ai_translate": access.role != "reader",
|
||||||
"attachment_upload": access.role != "reader",
|
"attachment_upload": access.role != "reader",
|
||||||
"can_edit": access.role != "reader",
|
"can_edit": access.role not in ["reader", "commentator"],
|
||||||
"children_create": access.role != "reader",
|
"children_create": access.role != "reader",
|
||||||
"children_list": True,
|
"children_list": True,
|
||||||
"collaboration_auth": True,
|
"collaboration_auth": True,
|
||||||
|
"comment": access.role != "reader",
|
||||||
"descendants": True,
|
"descendants": True,
|
||||||
"cors_proxy": True,
|
"cors_proxy": True,
|
||||||
"destroy": access.role == "owner",
|
"destroy": access.role == "owner",
|
||||||
|
|||||||
@@ -79,16 +79,17 @@ def test_api_documents_trashbin_format():
|
|||||||
"children_create": True,
|
"children_create": True,
|
||||||
"children_list": True,
|
"children_list": True,
|
||||||
"collaboration_auth": True,
|
"collaboration_auth": True,
|
||||||
"descendants": True,
|
"comment": True,
|
||||||
"cors_proxy": True,
|
"cors_proxy": True,
|
||||||
|
"descendants": True,
|
||||||
"destroy": True,
|
"destroy": True,
|
||||||
"duplicate": True,
|
"duplicate": True,
|
||||||
"favorite": True,
|
"favorite": True,
|
||||||
"invite_owner": True,
|
"invite_owner": True,
|
||||||
"link_configuration": True,
|
"link_configuration": True,
|
||||||
"link_select_options": {
|
"link_select_options": {
|
||||||
"authenticated": ["reader", "editor"],
|
"authenticated": ["reader", "commentator", "editor"],
|
||||||
"public": ["reader", "editor"],
|
"public": ["reader", "commentator", "editor"],
|
||||||
"restricted": None,
|
"restricted": None,
|
||||||
},
|
},
|
||||||
"mask": True,
|
"mask": True,
|
||||||
|
|||||||
273
src/backend/core/tests/test_models_comment.py
Normal file
273
src/backend/core/tests/test_models_comment.py
Normal file
@@ -0,0 +1,273 @@
|
|||||||
|
"""Test the comment model."""
|
||||||
|
|
||||||
|
import random
|
||||||
|
|
||||||
|
from django.contrib.auth.models import AnonymousUser
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from core import factories
|
||||||
|
from core.models import LinkReachChoices, LinkRoleChoices, RoleChoices
|
||||||
|
|
||||||
|
pytestmark = pytest.mark.django_db
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"role,can_comment",
|
||||||
|
[
|
||||||
|
(LinkRoleChoices.READER, False),
|
||||||
|
(LinkRoleChoices.COMMENTATOR, True),
|
||||||
|
(LinkRoleChoices.EDITOR, True),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_comment_get_abilities_anonymous_user_public_document(role, can_comment):
|
||||||
|
"""Anonymous users cannot comment on a document."""
|
||||||
|
document = factories.DocumentFactory(
|
||||||
|
link_role=role, link_reach=LinkReachChoices.PUBLIC
|
||||||
|
)
|
||||||
|
comment = factories.CommentFactory(document=document)
|
||||||
|
user = AnonymousUser()
|
||||||
|
|
||||||
|
assert comment.get_abilities(user) == {
|
||||||
|
"destroy": False,
|
||||||
|
"update": False,
|
||||||
|
"partial_update": False,
|
||||||
|
"retrieve": can_comment,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"link_reach", [LinkReachChoices.RESTRICTED, LinkReachChoices.AUTHENTICATED]
|
||||||
|
)
|
||||||
|
def test_comment_get_abilities_anonymous_user_restricted_document(link_reach):
|
||||||
|
"""Anonymous users cannot comment on a restricted document."""
|
||||||
|
document = factories.DocumentFactory(link_reach=link_reach)
|
||||||
|
comment = factories.CommentFactory(document=document)
|
||||||
|
user = AnonymousUser()
|
||||||
|
|
||||||
|
assert comment.get_abilities(user) == {
|
||||||
|
"destroy": False,
|
||||||
|
"update": False,
|
||||||
|
"partial_update": False,
|
||||||
|
"retrieve": False,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"link_role,link_reach,can_comment",
|
||||||
|
[
|
||||||
|
(LinkRoleChoices.READER, LinkReachChoices.PUBLIC, False),
|
||||||
|
(LinkRoleChoices.COMMENTATOR, LinkReachChoices.PUBLIC, True),
|
||||||
|
(LinkRoleChoices.EDITOR, LinkReachChoices.PUBLIC, True),
|
||||||
|
(LinkRoleChoices.READER, LinkReachChoices.RESTRICTED, False),
|
||||||
|
(LinkRoleChoices.COMMENTATOR, LinkReachChoices.RESTRICTED, False),
|
||||||
|
(LinkRoleChoices.EDITOR, LinkReachChoices.RESTRICTED, False),
|
||||||
|
(LinkRoleChoices.READER, LinkReachChoices.AUTHENTICATED, False),
|
||||||
|
(LinkRoleChoices.COMMENTATOR, LinkReachChoices.AUTHENTICATED, True),
|
||||||
|
(LinkRoleChoices.EDITOR, LinkReachChoices.AUTHENTICATED, True),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_comment_get_abilities_user_reader(link_role, link_reach, can_comment):
|
||||||
|
"""Readers cannot comment on a document."""
|
||||||
|
user = factories.UserFactory()
|
||||||
|
document = factories.DocumentFactory(
|
||||||
|
link_role=link_role, link_reach=link_reach, users=[(user, RoleChoices.READER)]
|
||||||
|
)
|
||||||
|
comment = factories.CommentFactory(document=document)
|
||||||
|
|
||||||
|
assert comment.get_abilities(user) == {
|
||||||
|
"destroy": False,
|
||||||
|
"update": False,
|
||||||
|
"partial_update": False,
|
||||||
|
"retrieve": can_comment,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"link_role,link_reach,can_comment",
|
||||||
|
[
|
||||||
|
(LinkRoleChoices.READER, LinkReachChoices.PUBLIC, False),
|
||||||
|
(LinkRoleChoices.COMMENTATOR, LinkReachChoices.PUBLIC, True),
|
||||||
|
(LinkRoleChoices.EDITOR, LinkReachChoices.PUBLIC, True),
|
||||||
|
(LinkRoleChoices.READER, LinkReachChoices.RESTRICTED, False),
|
||||||
|
(LinkRoleChoices.COMMENTATOR, LinkReachChoices.RESTRICTED, False),
|
||||||
|
(LinkRoleChoices.EDITOR, LinkReachChoices.RESTRICTED, False),
|
||||||
|
(LinkRoleChoices.READER, LinkReachChoices.AUTHENTICATED, False),
|
||||||
|
(LinkRoleChoices.COMMENTATOR, LinkReachChoices.AUTHENTICATED, True),
|
||||||
|
(LinkRoleChoices.EDITOR, LinkReachChoices.AUTHENTICATED, True),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_comment_get_abilities_user_reader_own_comment(
|
||||||
|
link_role, link_reach, can_comment
|
||||||
|
):
|
||||||
|
"""User with reader role on a document has all accesses to its own comment."""
|
||||||
|
user = factories.UserFactory()
|
||||||
|
document = factories.DocumentFactory(
|
||||||
|
link_role=link_role, link_reach=link_reach, users=[(user, RoleChoices.READER)]
|
||||||
|
)
|
||||||
|
comment = factories.CommentFactory(
|
||||||
|
document=document, user=user if can_comment else None
|
||||||
|
)
|
||||||
|
|
||||||
|
assert comment.get_abilities(user) == {
|
||||||
|
"destroy": can_comment,
|
||||||
|
"update": can_comment,
|
||||||
|
"partial_update": can_comment,
|
||||||
|
"retrieve": can_comment,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"link_role,link_reach",
|
||||||
|
[
|
||||||
|
(LinkRoleChoices.READER, LinkReachChoices.PUBLIC),
|
||||||
|
(LinkRoleChoices.COMMENTATOR, LinkReachChoices.PUBLIC),
|
||||||
|
(LinkRoleChoices.EDITOR, LinkReachChoices.PUBLIC),
|
||||||
|
(LinkRoleChoices.READER, LinkReachChoices.RESTRICTED),
|
||||||
|
(LinkRoleChoices.COMMENTATOR, LinkReachChoices.RESTRICTED),
|
||||||
|
(LinkRoleChoices.EDITOR, LinkReachChoices.RESTRICTED),
|
||||||
|
(LinkRoleChoices.READER, LinkReachChoices.AUTHENTICATED),
|
||||||
|
(LinkRoleChoices.COMMENTATOR, LinkReachChoices.AUTHENTICATED),
|
||||||
|
(LinkRoleChoices.EDITOR, LinkReachChoices.AUTHENTICATED),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_comment_get_abilities_user_commentator(link_role, link_reach):
|
||||||
|
"""Commentators can comment on a document."""
|
||||||
|
user = factories.UserFactory()
|
||||||
|
document = factories.DocumentFactory(
|
||||||
|
link_role=link_role,
|
||||||
|
link_reach=link_reach,
|
||||||
|
users=[(user, RoleChoices.COMMENTATOR)],
|
||||||
|
)
|
||||||
|
comment = factories.CommentFactory(document=document)
|
||||||
|
|
||||||
|
assert comment.get_abilities(user) == {
|
||||||
|
"destroy": False,
|
||||||
|
"update": False,
|
||||||
|
"partial_update": False,
|
||||||
|
"retrieve": True,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"link_role,link_reach",
|
||||||
|
[
|
||||||
|
(LinkRoleChoices.READER, LinkReachChoices.PUBLIC),
|
||||||
|
(LinkRoleChoices.COMMENTATOR, LinkReachChoices.PUBLIC),
|
||||||
|
(LinkRoleChoices.EDITOR, LinkReachChoices.PUBLIC),
|
||||||
|
(LinkRoleChoices.READER, LinkReachChoices.RESTRICTED),
|
||||||
|
(LinkRoleChoices.COMMENTATOR, LinkReachChoices.RESTRICTED),
|
||||||
|
(LinkRoleChoices.EDITOR, LinkReachChoices.RESTRICTED),
|
||||||
|
(LinkRoleChoices.READER, LinkReachChoices.AUTHENTICATED),
|
||||||
|
(LinkRoleChoices.COMMENTATOR, LinkReachChoices.AUTHENTICATED),
|
||||||
|
(LinkRoleChoices.EDITOR, LinkReachChoices.AUTHENTICATED),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_comment_get_abilities_user_commentator_own_comment(link_role, link_reach):
|
||||||
|
"""Commentators have all accesses to its own comment."""
|
||||||
|
user = factories.UserFactory()
|
||||||
|
document = factories.DocumentFactory(
|
||||||
|
link_role=link_role,
|
||||||
|
link_reach=link_reach,
|
||||||
|
users=[(user, RoleChoices.COMMENTATOR)],
|
||||||
|
)
|
||||||
|
comment = factories.CommentFactory(document=document, user=user)
|
||||||
|
|
||||||
|
assert comment.get_abilities(user) == {
|
||||||
|
"destroy": True,
|
||||||
|
"update": True,
|
||||||
|
"partial_update": True,
|
||||||
|
"retrieve": True,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"link_role,link_reach",
|
||||||
|
[
|
||||||
|
(LinkRoleChoices.READER, LinkReachChoices.PUBLIC),
|
||||||
|
(LinkRoleChoices.COMMENTATOR, LinkReachChoices.PUBLIC),
|
||||||
|
(LinkRoleChoices.EDITOR, LinkReachChoices.PUBLIC),
|
||||||
|
(LinkRoleChoices.READER, LinkReachChoices.RESTRICTED),
|
||||||
|
(LinkRoleChoices.COMMENTATOR, LinkReachChoices.RESTRICTED),
|
||||||
|
(LinkRoleChoices.EDITOR, LinkReachChoices.RESTRICTED),
|
||||||
|
(LinkRoleChoices.READER, LinkReachChoices.AUTHENTICATED),
|
||||||
|
(LinkRoleChoices.COMMENTATOR, LinkReachChoices.AUTHENTICATED),
|
||||||
|
(LinkRoleChoices.EDITOR, LinkReachChoices.AUTHENTICATED),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_comment_get_abilities_user_editor(link_role, link_reach):
|
||||||
|
"""Editors can comment on a document."""
|
||||||
|
user = factories.UserFactory()
|
||||||
|
document = factories.DocumentFactory(
|
||||||
|
link_role=link_role, link_reach=link_reach, users=[(user, RoleChoices.EDITOR)]
|
||||||
|
)
|
||||||
|
comment = factories.CommentFactory(document=document)
|
||||||
|
|
||||||
|
assert comment.get_abilities(user) == {
|
||||||
|
"destroy": False,
|
||||||
|
"update": False,
|
||||||
|
"partial_update": False,
|
||||||
|
"retrieve": True,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"link_role,link_reach",
|
||||||
|
[
|
||||||
|
(LinkRoleChoices.READER, LinkReachChoices.PUBLIC),
|
||||||
|
(LinkRoleChoices.COMMENTATOR, LinkReachChoices.PUBLIC),
|
||||||
|
(LinkRoleChoices.EDITOR, LinkReachChoices.PUBLIC),
|
||||||
|
(LinkRoleChoices.READER, LinkReachChoices.RESTRICTED),
|
||||||
|
(LinkRoleChoices.COMMENTATOR, LinkReachChoices.RESTRICTED),
|
||||||
|
(LinkRoleChoices.EDITOR, LinkReachChoices.RESTRICTED),
|
||||||
|
(LinkRoleChoices.READER, LinkReachChoices.AUTHENTICATED),
|
||||||
|
(LinkRoleChoices.COMMENTATOR, LinkReachChoices.AUTHENTICATED),
|
||||||
|
(LinkRoleChoices.EDITOR, LinkReachChoices.AUTHENTICATED),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_comment_get_abilities_user_editor_own_comment(link_role, link_reach):
|
||||||
|
"""Editors have all accesses to its own comment."""
|
||||||
|
user = factories.UserFactory()
|
||||||
|
document = factories.DocumentFactory(
|
||||||
|
link_role=link_role, link_reach=link_reach, users=[(user, RoleChoices.EDITOR)]
|
||||||
|
)
|
||||||
|
comment = factories.CommentFactory(document=document, user=user)
|
||||||
|
|
||||||
|
assert comment.get_abilities(user) == {
|
||||||
|
"destroy": True,
|
||||||
|
"update": True,
|
||||||
|
"partial_update": True,
|
||||||
|
"retrieve": True,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_comment_get_abilities_user_admin():
|
||||||
|
"""Admins have all accesses to a comment."""
|
||||||
|
user = factories.UserFactory()
|
||||||
|
document = factories.DocumentFactory(users=[(user, RoleChoices.ADMIN)])
|
||||||
|
comment = factories.CommentFactory(
|
||||||
|
document=document, user=random.choice([user, None])
|
||||||
|
)
|
||||||
|
|
||||||
|
assert comment.get_abilities(user) == {
|
||||||
|
"destroy": True,
|
||||||
|
"update": True,
|
||||||
|
"partial_update": True,
|
||||||
|
"retrieve": True,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_comment_get_abilities_user_owner():
|
||||||
|
"""Owners have all accesses to a comment."""
|
||||||
|
user = factories.UserFactory()
|
||||||
|
document = factories.DocumentFactory(users=[(user, RoleChoices.OWNER)])
|
||||||
|
comment = factories.CommentFactory(
|
||||||
|
document=document, user=random.choice([user, None])
|
||||||
|
)
|
||||||
|
|
||||||
|
assert comment.get_abilities(user) == {
|
||||||
|
"destroy": True,
|
||||||
|
"update": True,
|
||||||
|
"partial_update": True,
|
||||||
|
"retrieve": True,
|
||||||
|
}
|
||||||
@@ -123,7 +123,7 @@ def test_models_document_access_get_abilities_for_owner_of_self_allowed():
|
|||||||
"retrieve": True,
|
"retrieve": True,
|
||||||
"update": True,
|
"update": True,
|
||||||
"partial_update": True,
|
"partial_update": True,
|
||||||
"set_role_to": ["reader", "editor", "administrator", "owner"],
|
"set_role_to": ["reader", "commentator", "editor", "administrator", "owner"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -166,7 +166,7 @@ def test_models_document_access_get_abilities_for_owner_of_self_last_on_child(
|
|||||||
"retrieve": True,
|
"retrieve": True,
|
||||||
"update": True,
|
"update": True,
|
||||||
"partial_update": True,
|
"partial_update": True,
|
||||||
"set_role_to": ["reader", "editor", "administrator", "owner"],
|
"set_role_to": ["reader", "commentator", "editor", "administrator", "owner"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -183,7 +183,7 @@ def test_models_document_access_get_abilities_for_owner_of_owner():
|
|||||||
"retrieve": True,
|
"retrieve": True,
|
||||||
"update": True,
|
"update": True,
|
||||||
"partial_update": True,
|
"partial_update": True,
|
||||||
"set_role_to": ["reader", "editor", "administrator", "owner"],
|
"set_role_to": ["reader", "commentator", "editor", "administrator", "owner"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -200,7 +200,7 @@ def test_models_document_access_get_abilities_for_owner_of_administrator():
|
|||||||
"retrieve": True,
|
"retrieve": True,
|
||||||
"update": True,
|
"update": True,
|
||||||
"partial_update": True,
|
"partial_update": True,
|
||||||
"set_role_to": ["reader", "editor", "administrator", "owner"],
|
"set_role_to": ["reader", "commentator", "editor", "administrator", "owner"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -217,7 +217,7 @@ def test_models_document_access_get_abilities_for_owner_of_editor():
|
|||||||
"retrieve": True,
|
"retrieve": True,
|
||||||
"update": True,
|
"update": True,
|
||||||
"partial_update": True,
|
"partial_update": True,
|
||||||
"set_role_to": ["reader", "editor", "administrator", "owner"],
|
"set_role_to": ["reader", "commentator", "editor", "administrator", "owner"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -234,7 +234,7 @@ def test_models_document_access_get_abilities_for_owner_of_reader():
|
|||||||
"retrieve": True,
|
"retrieve": True,
|
||||||
"update": True,
|
"update": True,
|
||||||
"partial_update": True,
|
"partial_update": True,
|
||||||
"set_role_to": ["reader", "editor", "administrator", "owner"],
|
"set_role_to": ["reader", "commentator", "editor", "administrator", "owner"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -271,7 +271,7 @@ def test_models_document_access_get_abilities_for_administrator_of_administrator
|
|||||||
"retrieve": True,
|
"retrieve": True,
|
||||||
"update": True,
|
"update": True,
|
||||||
"partial_update": True,
|
"partial_update": True,
|
||||||
"set_role_to": ["reader", "editor", "administrator"],
|
"set_role_to": ["reader", "commentator", "editor", "administrator"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -288,7 +288,7 @@ def test_models_document_access_get_abilities_for_administrator_of_editor():
|
|||||||
"retrieve": True,
|
"retrieve": True,
|
||||||
"update": True,
|
"update": True,
|
||||||
"partial_update": True,
|
"partial_update": True,
|
||||||
"set_role_to": ["reader", "editor", "administrator"],
|
"set_role_to": ["reader", "commentator", "editor", "administrator"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -305,7 +305,7 @@ def test_models_document_access_get_abilities_for_administrator_of_reader():
|
|||||||
"retrieve": True,
|
"retrieve": True,
|
||||||
"update": True,
|
"update": True,
|
||||||
"partial_update": True,
|
"partial_update": True,
|
||||||
"set_role_to": ["reader", "editor", "administrator"],
|
"set_role_to": ["reader", "commentator", "editor", "administrator"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -134,10 +134,13 @@ def test_models_documents_soft_delete(depth):
|
|||||||
[
|
[
|
||||||
(True, "restricted", "reader"),
|
(True, "restricted", "reader"),
|
||||||
(True, "restricted", "editor"),
|
(True, "restricted", "editor"),
|
||||||
|
(True, "restricted", "commentator"),
|
||||||
(False, "restricted", "reader"),
|
(False, "restricted", "reader"),
|
||||||
(False, "restricted", "editor"),
|
(False, "restricted", "editor"),
|
||||||
|
(False, "restricted", "commentator"),
|
||||||
(False, "authenticated", "reader"),
|
(False, "authenticated", "reader"),
|
||||||
(False, "authenticated", "editor"),
|
(False, "authenticated", "editor"),
|
||||||
|
(False, "authenticated", "commentator"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_models_documents_get_abilities_forbidden(
|
def test_models_documents_get_abilities_forbidden(
|
||||||
@@ -164,6 +167,7 @@ def test_models_documents_get_abilities_forbidden(
|
|||||||
"destroy": False,
|
"destroy": False,
|
||||||
"duplicate": False,
|
"duplicate": False,
|
||||||
"favorite": False,
|
"favorite": False,
|
||||||
|
"comment": False,
|
||||||
"invite_owner": False,
|
"invite_owner": False,
|
||||||
"mask": False,
|
"mask": False,
|
||||||
"media_auth": False,
|
"media_auth": False,
|
||||||
@@ -171,8 +175,8 @@ def test_models_documents_get_abilities_forbidden(
|
|||||||
"move": False,
|
"move": False,
|
||||||
"link_configuration": False,
|
"link_configuration": False,
|
||||||
"link_select_options": {
|
"link_select_options": {
|
||||||
"authenticated": ["reader", "editor"],
|
"authenticated": ["reader", "commentator", "editor"],
|
||||||
"public": ["reader", "editor"],
|
"public": ["reader", "commentator", "editor"],
|
||||||
"restricted": None,
|
"restricted": None,
|
||||||
},
|
},
|
||||||
"partial_update": False,
|
"partial_update": False,
|
||||||
@@ -222,6 +226,7 @@ def test_models_documents_get_abilities_reader(
|
|||||||
"children_create": False,
|
"children_create": False,
|
||||||
"children_list": True,
|
"children_list": True,
|
||||||
"collaboration_auth": True,
|
"collaboration_auth": True,
|
||||||
|
"comment": False,
|
||||||
"descendants": True,
|
"descendants": True,
|
||||||
"cors_proxy": True,
|
"cors_proxy": True,
|
||||||
"destroy": False,
|
"destroy": False,
|
||||||
@@ -230,8 +235,77 @@ def test_models_documents_get_abilities_reader(
|
|||||||
"invite_owner": False,
|
"invite_owner": False,
|
||||||
"link_configuration": False,
|
"link_configuration": False,
|
||||||
"link_select_options": {
|
"link_select_options": {
|
||||||
"authenticated": ["reader", "editor"],
|
"authenticated": ["reader", "commentator", "editor"],
|
||||||
"public": ["reader", "editor"],
|
"public": ["reader", "commentator", "editor"],
|
||||||
|
"restricted": None,
|
||||||
|
},
|
||||||
|
"mask": is_authenticated,
|
||||||
|
"media_auth": True,
|
||||||
|
"media_check": True,
|
||||||
|
"move": False,
|
||||||
|
"partial_update": False,
|
||||||
|
"restore": False,
|
||||||
|
"retrieve": True,
|
||||||
|
"tree": True,
|
||||||
|
"update": False,
|
||||||
|
"versions_destroy": False,
|
||||||
|
"versions_list": False,
|
||||||
|
"versions_retrieve": False,
|
||||||
|
}
|
||||||
|
nb_queries = 1 if is_authenticated else 0
|
||||||
|
with django_assert_num_queries(nb_queries):
|
||||||
|
assert document.get_abilities(user) == expected_abilities
|
||||||
|
|
||||||
|
document.soft_delete()
|
||||||
|
document.refresh_from_db()
|
||||||
|
assert all(
|
||||||
|
value is False
|
||||||
|
for key, value in document.get_abilities(user).items()
|
||||||
|
if key not in ["link_select_options", "ancestors_links_definition"]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@override_settings(
|
||||||
|
AI_ALLOW_REACH_FROM=random.choice(["public", "authenticated", "restricted"])
|
||||||
|
)
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"is_authenticated,reach",
|
||||||
|
[
|
||||||
|
(True, "public"),
|
||||||
|
(False, "public"),
|
||||||
|
(True, "authenticated"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_models_documents_get_abilities_commentator(
|
||||||
|
is_authenticated, reach, django_assert_num_queries
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Check abilities returned for a document giving commentator role to link holders
|
||||||
|
i.e anonymous users or authenticated users who have no specific role on the document.
|
||||||
|
"""
|
||||||
|
document = factories.DocumentFactory(link_reach=reach, link_role="commentator")
|
||||||
|
user = factories.UserFactory() if is_authenticated else AnonymousUser()
|
||||||
|
expected_abilities = {
|
||||||
|
"accesses_manage": False,
|
||||||
|
"accesses_view": False,
|
||||||
|
"ai_transform": False,
|
||||||
|
"ai_translate": False,
|
||||||
|
"attachment_upload": False,
|
||||||
|
"can_edit": False,
|
||||||
|
"children_create": False,
|
||||||
|
"children_list": True,
|
||||||
|
"collaboration_auth": True,
|
||||||
|
"comment": True,
|
||||||
|
"descendants": True,
|
||||||
|
"cors_proxy": True,
|
||||||
|
"destroy": False,
|
||||||
|
"duplicate": is_authenticated,
|
||||||
|
"favorite": is_authenticated,
|
||||||
|
"invite_owner": False,
|
||||||
|
"link_configuration": False,
|
||||||
|
"link_select_options": {
|
||||||
|
"authenticated": ["reader", "commentator", "editor"],
|
||||||
|
"public": ["reader", "commentator", "editor"],
|
||||||
"restricted": None,
|
"restricted": None,
|
||||||
},
|
},
|
||||||
"mask": is_authenticated,
|
"mask": is_authenticated,
|
||||||
@@ -287,6 +361,7 @@ def test_models_documents_get_abilities_editor(
|
|||||||
"children_create": is_authenticated,
|
"children_create": is_authenticated,
|
||||||
"children_list": True,
|
"children_list": True,
|
||||||
"collaboration_auth": True,
|
"collaboration_auth": True,
|
||||||
|
"comment": True,
|
||||||
"descendants": True,
|
"descendants": True,
|
||||||
"cors_proxy": True,
|
"cors_proxy": True,
|
||||||
"destroy": False,
|
"destroy": False,
|
||||||
@@ -295,8 +370,8 @@ def test_models_documents_get_abilities_editor(
|
|||||||
"invite_owner": False,
|
"invite_owner": False,
|
||||||
"link_configuration": False,
|
"link_configuration": False,
|
||||||
"link_select_options": {
|
"link_select_options": {
|
||||||
"authenticated": ["reader", "editor"],
|
"authenticated": ["reader", "commentator", "editor"],
|
||||||
"public": ["reader", "editor"],
|
"public": ["reader", "commentator", "editor"],
|
||||||
"restricted": None,
|
"restricted": None,
|
||||||
},
|
},
|
||||||
"mask": is_authenticated,
|
"mask": is_authenticated,
|
||||||
@@ -341,6 +416,7 @@ def test_models_documents_get_abilities_owner(django_assert_num_queries):
|
|||||||
"children_create": True,
|
"children_create": True,
|
||||||
"children_list": True,
|
"children_list": True,
|
||||||
"collaboration_auth": True,
|
"collaboration_auth": True,
|
||||||
|
"comment": True,
|
||||||
"descendants": True,
|
"descendants": True,
|
||||||
"cors_proxy": True,
|
"cors_proxy": True,
|
||||||
"destroy": True,
|
"destroy": True,
|
||||||
@@ -349,8 +425,8 @@ def test_models_documents_get_abilities_owner(django_assert_num_queries):
|
|||||||
"invite_owner": True,
|
"invite_owner": True,
|
||||||
"link_configuration": True,
|
"link_configuration": True,
|
||||||
"link_select_options": {
|
"link_select_options": {
|
||||||
"authenticated": ["reader", "editor"],
|
"authenticated": ["reader", "commentator", "editor"],
|
||||||
"public": ["reader", "editor"],
|
"public": ["reader", "commentator", "editor"],
|
||||||
"restricted": None,
|
"restricted": None,
|
||||||
},
|
},
|
||||||
"mask": True,
|
"mask": True,
|
||||||
@@ -392,6 +468,7 @@ def test_models_documents_get_abilities_administrator(django_assert_num_queries)
|
|||||||
"children_create": True,
|
"children_create": True,
|
||||||
"children_list": True,
|
"children_list": True,
|
||||||
"collaboration_auth": True,
|
"collaboration_auth": True,
|
||||||
|
"comment": True,
|
||||||
"descendants": True,
|
"descendants": True,
|
||||||
"cors_proxy": True,
|
"cors_proxy": True,
|
||||||
"destroy": False,
|
"destroy": False,
|
||||||
@@ -400,8 +477,8 @@ def test_models_documents_get_abilities_administrator(django_assert_num_queries)
|
|||||||
"invite_owner": False,
|
"invite_owner": False,
|
||||||
"link_configuration": True,
|
"link_configuration": True,
|
||||||
"link_select_options": {
|
"link_select_options": {
|
||||||
"authenticated": ["reader", "editor"],
|
"authenticated": ["reader", "commentator", "editor"],
|
||||||
"public": ["reader", "editor"],
|
"public": ["reader", "commentator", "editor"],
|
||||||
"restricted": None,
|
"restricted": None,
|
||||||
},
|
},
|
||||||
"mask": True,
|
"mask": True,
|
||||||
@@ -446,6 +523,7 @@ def test_models_documents_get_abilities_editor_user(django_assert_num_queries):
|
|||||||
"children_create": True,
|
"children_create": True,
|
||||||
"children_list": True,
|
"children_list": True,
|
||||||
"collaboration_auth": True,
|
"collaboration_auth": True,
|
||||||
|
"comment": True,
|
||||||
"descendants": True,
|
"descendants": True,
|
||||||
"cors_proxy": True,
|
"cors_proxy": True,
|
||||||
"destroy": False,
|
"destroy": False,
|
||||||
@@ -454,8 +532,8 @@ def test_models_documents_get_abilities_editor_user(django_assert_num_queries):
|
|||||||
"invite_owner": False,
|
"invite_owner": False,
|
||||||
"link_configuration": False,
|
"link_configuration": False,
|
||||||
"link_select_options": {
|
"link_select_options": {
|
||||||
"authenticated": ["reader", "editor"],
|
"authenticated": ["reader", "commentator", "editor"],
|
||||||
"public": ["reader", "editor"],
|
"public": ["reader", "commentator", "editor"],
|
||||||
"restricted": None,
|
"restricted": None,
|
||||||
},
|
},
|
||||||
"mask": True,
|
"mask": True,
|
||||||
@@ -507,6 +585,8 @@ def test_models_documents_get_abilities_reader_user(
|
|||||||
"children_create": access_from_link,
|
"children_create": access_from_link,
|
||||||
"children_list": True,
|
"children_list": True,
|
||||||
"collaboration_auth": True,
|
"collaboration_auth": True,
|
||||||
|
"comment": document.link_reach != "restricted"
|
||||||
|
and document.link_role in ["commentator", "editor"],
|
||||||
"descendants": True,
|
"descendants": True,
|
||||||
"cors_proxy": True,
|
"cors_proxy": True,
|
||||||
"destroy": False,
|
"destroy": False,
|
||||||
@@ -515,8 +595,72 @@ def test_models_documents_get_abilities_reader_user(
|
|||||||
"invite_owner": False,
|
"invite_owner": False,
|
||||||
"link_configuration": False,
|
"link_configuration": False,
|
||||||
"link_select_options": {
|
"link_select_options": {
|
||||||
"authenticated": ["reader", "editor"],
|
"authenticated": ["reader", "commentator", "editor"],
|
||||||
"public": ["reader", "editor"],
|
"public": ["reader", "commentator", "editor"],
|
||||||
|
"restricted": None,
|
||||||
|
},
|
||||||
|
"mask": True,
|
||||||
|
"media_auth": True,
|
||||||
|
"media_check": True,
|
||||||
|
"move": False,
|
||||||
|
"partial_update": access_from_link,
|
||||||
|
"restore": False,
|
||||||
|
"retrieve": True,
|
||||||
|
"tree": True,
|
||||||
|
"update": access_from_link,
|
||||||
|
"versions_destroy": False,
|
||||||
|
"versions_list": True,
|
||||||
|
"versions_retrieve": True,
|
||||||
|
}
|
||||||
|
|
||||||
|
with override_settings(AI_ALLOW_REACH_FROM=ai_access_setting):
|
||||||
|
with django_assert_num_queries(1):
|
||||||
|
assert document.get_abilities(user) == expected_abilities
|
||||||
|
|
||||||
|
document.soft_delete()
|
||||||
|
document.refresh_from_db()
|
||||||
|
assert all(
|
||||||
|
value is False
|
||||||
|
for key, value in document.get_abilities(user).items()
|
||||||
|
if key not in ["link_select_options", "ancestors_links_definition"]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("ai_access_setting", ["public", "authenticated", "restricted"])
|
||||||
|
def test_models_documents_get_abilities_commentator_user(
|
||||||
|
ai_access_setting, django_assert_num_queries
|
||||||
|
):
|
||||||
|
"""Check abilities returned for the commentator of a document."""
|
||||||
|
user = factories.UserFactory()
|
||||||
|
document = factories.DocumentFactory(users=[(user, "commentator")])
|
||||||
|
|
||||||
|
access_from_link = (
|
||||||
|
document.link_reach != "restricted" and document.link_role == "editor"
|
||||||
|
)
|
||||||
|
|
||||||
|
expected_abilities = {
|
||||||
|
"accesses_manage": False,
|
||||||
|
"accesses_view": True,
|
||||||
|
# If you get your editor rights from the link role and not your access role
|
||||||
|
# You should not access AI if it's restricted to users with specific access
|
||||||
|
"ai_transform": access_from_link and ai_access_setting != "restricted",
|
||||||
|
"ai_translate": access_from_link and ai_access_setting != "restricted",
|
||||||
|
"attachment_upload": access_from_link,
|
||||||
|
"can_edit": access_from_link,
|
||||||
|
"children_create": access_from_link,
|
||||||
|
"children_list": True,
|
||||||
|
"collaboration_auth": True,
|
||||||
|
"comment": True,
|
||||||
|
"descendants": True,
|
||||||
|
"cors_proxy": True,
|
||||||
|
"destroy": False,
|
||||||
|
"duplicate": True,
|
||||||
|
"favorite": True,
|
||||||
|
"invite_owner": False,
|
||||||
|
"link_configuration": False,
|
||||||
|
"link_select_options": {
|
||||||
|
"authenticated": ["reader", "commentator", "editor"],
|
||||||
|
"public": ["reader", "commentator", "editor"],
|
||||||
"restricted": None,
|
"restricted": None,
|
||||||
},
|
},
|
||||||
"mask": True,
|
"mask": True,
|
||||||
@@ -566,6 +710,7 @@ def test_models_documents_get_abilities_preset_role(django_assert_num_queries):
|
|||||||
"children_create": False,
|
"children_create": False,
|
||||||
"children_list": True,
|
"children_list": True,
|
||||||
"collaboration_auth": True,
|
"collaboration_auth": True,
|
||||||
|
"comment": False,
|
||||||
"descendants": True,
|
"descendants": True,
|
||||||
"cors_proxy": True,
|
"cors_proxy": True,
|
||||||
"destroy": False,
|
"destroy": False,
|
||||||
@@ -574,8 +719,8 @@ def test_models_documents_get_abilities_preset_role(django_assert_num_queries):
|
|||||||
"invite_owner": False,
|
"invite_owner": False,
|
||||||
"link_configuration": False,
|
"link_configuration": False,
|
||||||
"link_select_options": {
|
"link_select_options": {
|
||||||
"authenticated": ["reader", "editor"],
|
"authenticated": ["reader", "commentator", "editor"],
|
||||||
"public": ["reader", "editor"],
|
"public": ["reader", "commentator", "editor"],
|
||||||
"restricted": None,
|
"restricted": None,
|
||||||
},
|
},
|
||||||
"mask": True,
|
"mask": True,
|
||||||
@@ -1198,7 +1343,14 @@ def test_models_documents_restore_complex_bis(django_assert_num_queries):
|
|||||||
"public",
|
"public",
|
||||||
"reader",
|
"reader",
|
||||||
{
|
{
|
||||||
"public": ["reader", "editor"],
|
"public": ["reader", "commentator", "editor"],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"public",
|
||||||
|
"commentator",
|
||||||
|
{
|
||||||
|
"public": ["commentator", "editor"],
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
("public", "editor", {"public": ["editor"]}),
|
("public", "editor", {"public": ["editor"]}),
|
||||||
@@ -1206,8 +1358,16 @@ def test_models_documents_restore_complex_bis(django_assert_num_queries):
|
|||||||
"authenticated",
|
"authenticated",
|
||||||
"reader",
|
"reader",
|
||||||
{
|
{
|
||||||
"authenticated": ["reader", "editor"],
|
"authenticated": ["reader", "commentator", "editor"],
|
||||||
"public": ["reader", "editor"],
|
"public": ["reader", "commentator", "editor"],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"authenticated",
|
||||||
|
"commentator",
|
||||||
|
{
|
||||||
|
"authenticated": ["commentator", "editor"],
|
||||||
|
"public": ["commentator", "editor"],
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@@ -1220,8 +1380,17 @@ def test_models_documents_restore_complex_bis(django_assert_num_queries):
|
|||||||
"reader",
|
"reader",
|
||||||
{
|
{
|
||||||
"restricted": None,
|
"restricted": None,
|
||||||
"authenticated": ["reader", "editor"],
|
"authenticated": ["reader", "commentator", "editor"],
|
||||||
"public": ["reader", "editor"],
|
"public": ["reader", "commentator", "editor"],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"restricted",
|
||||||
|
"commentator",
|
||||||
|
{
|
||||||
|
"restricted": None,
|
||||||
|
"authenticated": ["commentator", "editor"],
|
||||||
|
"public": ["commentator", "editor"],
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@@ -1238,15 +1407,15 @@ def test_models_documents_restore_complex_bis(django_assert_num_queries):
|
|||||||
"public",
|
"public",
|
||||||
None,
|
None,
|
||||||
{
|
{
|
||||||
"public": ["reader", "editor"],
|
"public": ["reader", "commentator", "editor"],
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
None,
|
None,
|
||||||
"reader",
|
"reader",
|
||||||
{
|
{
|
||||||
"public": ["reader", "editor"],
|
"public": ["reader", "commentator", "editor"],
|
||||||
"authenticated": ["reader", "editor"],
|
"authenticated": ["reader", "commentator", "editor"],
|
||||||
"restricted": None,
|
"restricted": None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -1254,8 +1423,8 @@ def test_models_documents_restore_complex_bis(django_assert_num_queries):
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
{
|
{
|
||||||
"public": ["reader", "editor"],
|
"public": ["reader", "commentator", "editor"],
|
||||||
"authenticated": ["reader", "editor"],
|
"authenticated": ["reader", "commentator", "editor"],
|
||||||
"restricted": None,
|
"restricted": None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -26,7 +26,11 @@ document_related_router.register(
|
|||||||
viewsets.InvitationViewset,
|
viewsets.InvitationViewset,
|
||||||
basename="invitations",
|
basename="invitations",
|
||||||
)
|
)
|
||||||
|
document_related_router.register(
|
||||||
|
"comments",
|
||||||
|
viewsets.CommentViewSet,
|
||||||
|
basename="comments",
|
||||||
|
)
|
||||||
document_related_router.register(
|
document_related_router.register(
|
||||||
"ask-for-access",
|
"ask-for-access",
|
||||||
viewsets.DocumentAskForAccessViewSet,
|
viewsets.DocumentAskForAccessViewSet,
|
||||||
|
|||||||
Reference in New Issue
Block a user