♻️(models) rename document/template access rights

The "member" access right does not make sense for documents and templates.
What we really need are "editor" and "reader" access rights.
This commit is contained in:
Samuel Paccoud - DINUM
2024-05-25 08:15:34 +02:00
committed by Samuel Paccoud
parent 51325df7d9
commit 926fe37e85
22 changed files with 601 additions and 265 deletions

View File

@@ -91,7 +91,7 @@ class DocumentAdmin(admin.ModelAdmin):
"""Document admin interface declaration.""" """Document admin interface declaration."""
inlines = (DocumentAccessInline,) inlines = (DocumentAccessInline,)
@admin.register(models.Invitation) @admin.register(models.Invitation)
class InvitationAdmin(admin.ModelAdmin): class InvitationAdmin(admin.ModelAdmin):
@@ -119,4 +119,3 @@ class InvitationAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change): def save_model(self, request, obj, form, change):
obj.issuer = request.user obj.issuer = request.user
obj.save() obj.save()

View File

@@ -1,5 +1,4 @@
"""API endpoints""" """API endpoints"""
import json
from io import BytesIO from io import BytesIO
from django.contrib.postgres.aggregates import ArrayAgg from django.contrib.postgres.aggregates import ArrayAgg
@@ -368,15 +367,15 @@ class DocumentAccessViewSet(
POST /api/v1.0/documents/<resource_id>/accesses/ with expected data: POST /api/v1.0/documents/<resource_id>/accesses/ with expected data:
- user: str - user: str
- role: str [owner|admin|member] - role: str [administrator|editor|reader]
Return newly created document access Return newly created document access
PUT /api/v1.0/documents/<resource_id>/accesses/<document_access_id>/ with expected data: PUT /api/v1.0/documents/<resource_id>/accesses/<document_access_id>/ with expected data:
- role: str [owner|admin|member] - role: str [owner|admin|editor|reader]
Return updated document access Return updated document access
PATCH /api/v1.0/documents/<resource_id>/accesses/<document_access_id>/ with expected data: PATCH /api/v1.0/documents/<resource_id>/accesses/<document_access_id>/ with expected data:
- role: str [owner|admin|member] - role: str [owner|admin|editor|reader]
Return partially updated document access Return partially updated document access
DELETE /api/v1.0/documents/<resource_id>/accesses/<document_access_id>/ DELETE /api/v1.0/documents/<resource_id>/accesses/<document_access_id>/
@@ -458,15 +457,15 @@ class TemplateAccessViewSet(
POST /api/v1.0/templates/<template_id>/accesses/ with expected data: POST /api/v1.0/templates/<template_id>/accesses/ with expected data:
- user: str - user: str
- role: str [owner|admin|member] - role: str [administrator|editor|reader]
Return newly created template access Return newly created template access
PUT /api/v1.0/templates/<template_id>/accesses/<template_access_id>/ with expected data: PUT /api/v1.0/templates/<template_id>/accesses/<template_access_id>/ with expected data:
- role: str [owner|admin|member] - role: str [owner|admin|editor|reader]
Return updated template access Return updated template access
PATCH /api/v1.0/templates/<template_id>/accesses/<template_access_id>/ with expected data: PATCH /api/v1.0/templates/<template_id>/accesses/<template_access_id>/ with expected data:
- role: str [owner|admin|member] - role: str [owner|admin|editor|reader]
Return partially updated template access Return partially updated template access
DELETE /api/v1.0/templates/<template_id>/accesses/<template_access_id>/ DELETE /api/v1.0/templates/<template_id>/accesses/<template_access_id>/
@@ -496,7 +495,7 @@ class InvitationViewset(
POST /api/v1.0/documents/<document_id>/invitations/ with expected data: POST /api/v1.0/documents/<document_id>/invitations/ with expected data:
- email: str - email: str
- role: str [owner|admin|member] - role: str [administrator|editor|reader]
Return newly created invitation (issuer and document are automatically set) Return newly created invitation (issuer and document are automatically set)
PUT / PATCH : Not permitted. Instead of updating your invitation, PUT / PATCH : Not permitted. Instead of updating your invitation,

View File

@@ -1,4 +1,4 @@
# Generated by Django 5.0.3 on 2024-04-19 11:38 # Generated by Django 5.0.3 on 2024-05-28 20:29
import django.contrib.auth.models import django.contrib.auth.models
import django.core.validators import django.core.validators
@@ -89,7 +89,7 @@ class Migration(migrations.Migration):
('created_at', models.DateTimeField(auto_now_add=True, help_text='date and time at which a record was created', verbose_name='created on')), ('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')), ('updated_at', models.DateTimeField(auto_now=True, help_text='date and time at which a record was last updated', verbose_name='updated on')),
('team', models.CharField(blank=True, max_length=100)), ('team', models.CharField(blank=True, max_length=100)),
('role', models.CharField(choices=[('member', 'Member'), ('administrator', 'Administrator'), ('owner', 'Owner')], default='member', max_length=20)), ('role', models.CharField(choices=[('reader', 'Reader'), ('editor', 'Editor'), ('administrator', 'Administrator'), ('owner', 'Owner')], default='reader', max_length=20)),
('document', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='accesses', to='core.document')), ('document', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='accesses', to='core.document')),
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
], ],
@@ -100,6 +100,23 @@ class Migration(migrations.Migration):
'ordering': ('-created_at',), 'ordering': ('-created_at',),
}, },
), ),
migrations.CreateModel(
name='Invitation',
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')),
('email', models.EmailField(max_length=254, verbose_name='email address')),
('role', models.CharField(choices=[('reader', 'Reader'), ('editor', 'Editor'), ('administrator', 'Administrator'), ('owner', 'Owner')], default='reader', max_length=20)),
('document', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='invitations', to='core.document')),
('issuer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='invitations', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'Document invitation',
'verbose_name_plural': 'Document invitations',
'db_table': 'impress_invitation',
},
),
migrations.CreateModel( migrations.CreateModel(
name='TemplateAccess', name='TemplateAccess',
fields=[ fields=[
@@ -107,7 +124,7 @@ class Migration(migrations.Migration):
('created_at', models.DateTimeField(auto_now_add=True, help_text='date and time at which a record was created', verbose_name='created on')), ('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')), ('updated_at', models.DateTimeField(auto_now=True, help_text='date and time at which a record was last updated', verbose_name='updated on')),
('team', models.CharField(blank=True, max_length=100)), ('team', models.CharField(blank=True, max_length=100)),
('role', models.CharField(choices=[('member', 'Member'), ('administrator', 'Administrator'), ('owner', 'Owner')], default='member', max_length=20)), ('role', models.CharField(choices=[('reader', 'Reader'), ('editor', 'Editor'), ('administrator', 'Administrator'), ('owner', 'Owner')], default='reader', max_length=20)),
('template', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='accesses', to='core.template')), ('template', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='accesses', to='core.template')),
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
], ],
@@ -130,6 +147,10 @@ class Migration(migrations.Migration):
model_name='documentaccess', model_name='documentaccess',
constraint=models.CheckConstraint(check=models.Q(models.Q(('team', ''), ('user__isnull', False)), models.Q(('team__gt', ''), ('user__isnull', True)), _connector='OR'), name='check_document_access_either_user_or_team', violation_error_message='Either user or team must be set, not both.'), constraint=models.CheckConstraint(check=models.Q(models.Q(('team', ''), ('user__isnull', False)), models.Q(('team__gt', ''), ('user__isnull', True)), _connector='OR'), name='check_document_access_either_user_or_team', violation_error_message='Either user or team must be set, not both.'),
), ),
migrations.AddConstraint(
model_name='invitation',
constraint=models.UniqueConstraint(fields=('email', 'document'), name='email_and_document_unique_together'),
),
migrations.AddConstraint( migrations.AddConstraint(
model_name='templateaccess', model_name='templateaccess',
constraint=models.UniqueConstraint(condition=models.Q(('user__isnull', False)), fields=('user', 'template'), name='unique_template_user', violation_error_message='This user is already in this template.'), constraint=models.UniqueConstraint(condition=models.Q(('user__isnull', False)), fields=('user', 'template'), name='unique_template_user', violation_error_message='This user is already in this template.'),

View File

@@ -1,42 +0,0 @@
# Generated by Django 5.0.3 on 2024-05-12 19:02
import django.db.models.deletion
import uuid
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='user',
name='language',
field=models.CharField(choices="(('en-us', 'English'), ('fr-fr', 'French'))", default='en-us', help_text='The language in which the user wants to see the interface.', max_length=10, verbose_name='language'),
),
migrations.CreateModel(
name='Invitation',
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')),
('email', models.EmailField(max_length=254, verbose_name='email address')),
('role', models.CharField(choices=[('member', 'Member'), ('administrator', 'Administrator'), ('owner', 'Owner')], default='member', max_length=20)),
('document', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='invitations', to='core.document')),
('issuer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='invitations', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'Document invitation',
'verbose_name_plural': 'Document invitations',
'db_table': 'impress_invitation',
},
),
migrations.AddConstraint(
model_name='invitation',
constraint=models.UniqueConstraint(fields=('email', 'document'), name='email_and_document_unique_together'),
),
]

View File

@@ -2,7 +2,6 @@
Declare and configure the models for the impress core application Declare and configure the models for the impress core application
""" """
import hashlib import hashlib
import json
import smtplib import smtplib
import textwrap import textwrap
import uuid import uuid
@@ -55,8 +54,9 @@ def get_resource_roles(resource, user):
class RoleChoices(models.TextChoices): class RoleChoices(models.TextChoices):
"""Defines the possible roles a user can have in a template.""" """Defines the possible roles a user can have in a template."""
MEMBER = "member", _("Member") READER = "reader", _("Reader") # Can read
ADMIN = "administrator", _("Administrator") EDITOR = "editor", _("Editor") # Can read and edit
ADMIN = "administrator", _("Administrator") # Can read, edit, delete and share
OWNER = "owner", _("Owner") OWNER = "owner", _("Owner")
@@ -233,7 +233,7 @@ class BaseAccess(BaseModel):
) )
team = models.CharField(max_length=100, blank=True) team = models.CharField(max_length=100, blank=True)
role = models.CharField( role = models.CharField(
max_length=20, choices=RoleChoices.choices, default=RoleChoices.MEMBER max_length=20, choices=RoleChoices.choices, default=RoleChoices.READER
) )
class Meta: class Meta:
@@ -265,14 +265,20 @@ class BaseAccess(BaseModel):
RoleChoices.OWNER in roles RoleChoices.OWNER in roles
and resource.accesses.filter(role=RoleChoices.OWNER).count() > 1 and resource.accesses.filter(role=RoleChoices.OWNER).count() > 1
) )
set_role_to = [RoleChoices.ADMIN, RoleChoices.MEMBER] if can_delete else [] set_role_to = (
[RoleChoices.ADMIN, RoleChoices.EDITOR, RoleChoices.READER]
if can_delete
else []
)
else: else:
can_delete = is_owner_or_admin can_delete = is_owner_or_admin
set_role_to = [] set_role_to = []
if RoleChoices.OWNER in roles: if RoleChoices.OWNER in roles:
set_role_to.append(RoleChoices.OWNER) set_role_to.append(RoleChoices.OWNER)
if is_owner_or_admin: if is_owner_or_admin:
set_role_to.extend([RoleChoices.ADMIN, RoleChoices.MEMBER]) set_role_to.extend(
[RoleChoices.ADMIN, RoleChoices.EDITOR, RoleChoices.READER]
)
# Remove the current role as we don't want to propose it as an option # Remove the current role as we don't want to propose it as an option
try: try:
@@ -325,7 +331,7 @@ class Document(BaseModel):
except (FileNotFoundError, ClientError): except (FileNotFoundError, ClientError):
pass pass
else: else:
self._content = response["Body"].read().decode('utf-8') self._content = response["Body"].read().decode("utf-8")
return self._content return self._content
@content.setter @content.setter
@@ -333,7 +339,7 @@ class Document(BaseModel):
"""Cache the content, don't write to object storage yet""" """Cache the content, don't write to object storage yet"""
if not isinstance(content, str): if not isinstance(content, str):
raise ValueError("content should be a string.") raise ValueError("content should be a string.")
self._content = content self._content = content
def get_content_response(self, version_id=""): def get_content_response(self, version_id=""):
@@ -447,6 +453,7 @@ class Document(BaseModel):
is_owner_or_admin = bool( is_owner_or_admin = bool(
set(roles).intersection({RoleChoices.OWNER, RoleChoices.ADMIN}) set(roles).intersection({RoleChoices.OWNER, RoleChoices.ADMIN})
) )
is_editor = bool(RoleChoices.EDITOR in roles)
can_get = self.is_public or bool(roles) can_get = self.is_public or bool(roles)
can_get_versions = bool(roles) can_get_versions = bool(roles)
@@ -456,8 +463,8 @@ class Document(BaseModel):
"versions_list": can_get_versions, "versions_list": can_get_versions,
"versions_retrieve": can_get_versions, "versions_retrieve": can_get_versions,
"manage_accesses": is_owner_or_admin, "manage_accesses": is_owner_or_admin,
"update": is_owner_or_admin, "update": is_owner_or_admin or is_editor,
"partial_update": is_owner_or_admin, "partial_update": is_owner_or_admin or is_editor,
"retrieve": can_get, "retrieve": can_get,
} }
@@ -537,14 +544,15 @@ class Template(BaseModel):
is_owner_or_admin = bool( is_owner_or_admin = bool(
set(roles).intersection({RoleChoices.OWNER, RoleChoices.ADMIN}) set(roles).intersection({RoleChoices.OWNER, RoleChoices.ADMIN})
) )
is_editor = bool(RoleChoices.EDITOR in roles)
can_get = self.is_public or bool(roles) can_get = self.is_public or bool(roles)
return { return {
"destroy": RoleChoices.OWNER in roles, "destroy": RoleChoices.OWNER in roles,
"generate_document": can_get, "generate_document": can_get,
"manage_accesses": is_owner_or_admin, "manage_accesses": is_owner_or_admin,
"update": is_owner_or_admin, "update": is_owner_or_admin or is_editor,
"partial_update": is_owner_or_admin, "partial_update": is_owner_or_admin or is_editor,
"retrieve": can_get, "retrieve": can_get,
} }
@@ -631,7 +639,7 @@ class Invitation(BaseModel):
related_name="invitations", related_name="invitations",
) )
role = models.CharField( role = models.CharField(
max_length=20, choices=RoleChoices.choices, default=RoleChoices.MEMBER max_length=20, choices=RoleChoices.choices, default=RoleChoices.READER
) )
issuer = models.ForeignKey( issuer = models.ForeignKey(
User, User,

View File

@@ -45,14 +45,12 @@ def test_api_documents_delete_authenticated_unrelated():
assert models.Document.objects.count() == 1 assert models.Document.objects.count() == 1
@pytest.mark.parametrize("role", ["member", "administrator"]) @pytest.mark.parametrize("role", ["reader", "editor", "administrator"])
@pytest.mark.parametrize("via", VIA) @pytest.mark.parametrize("via", VIA)
def test_api_documents_delete_authenticated_member_or_administrator( def test_api_documents_delete_authenticated_not_owner(via, role, mock_user_get_teams):
via, role, mock_user_get_teams
):
""" """
Authenticated users should not be allowed to delete a document for which they are Authenticated users should not be allowed to delete a document for which they are
only a member or administrator. only a reader, editor or administrator.
""" """
user = factories.UserFactory() user = factories.UserFactory()

View File

@@ -161,7 +161,10 @@ def test_api_documents_retrieve_authenticated_related_team_none(mock_user_get_te
document = factories.DocumentFactory(is_public=False) document = factories.DocumentFactory(is_public=False)
factories.TeamDocumentAccessFactory( factories.TeamDocumentAccessFactory(
document=document, team="members", role="member" document=document, team="readers", role="reader"
)
factories.TeamDocumentAccessFactory(
document=document, team="editors", role="editor"
) )
factories.TeamDocumentAccessFactory( factories.TeamDocumentAccessFactory(
document=document, team="administrators", role="administrator" document=document, team="administrators", role="administrator"
@@ -178,8 +181,10 @@ def test_api_documents_retrieve_authenticated_related_team_none(mock_user_get_te
@pytest.mark.parametrize( @pytest.mark.parametrize(
"teams", "teams",
[ [
["members"], ["readers"],
["unknown", "members"], ["unknown", "readers"],
["editors"],
["unknown", "editors"],
], ],
) )
def test_api_documents_retrieve_authenticated_related_team_members( def test_api_documents_retrieve_authenticated_related_team_members(
@@ -198,8 +203,11 @@ def test_api_documents_retrieve_authenticated_related_team_members(
document = factories.DocumentFactory(is_public=False) document = factories.DocumentFactory(is_public=False)
access_member = factories.TeamDocumentAccessFactory( access_reader = factories.TeamDocumentAccessFactory(
document=document, team="members", role="member" document=document, team="readers", role="reader"
)
access_editor = factories.TeamDocumentAccessFactory(
document=document, team="editors", role="editor"
) )
access_administrator = factories.TeamDocumentAccessFactory( access_administrator = factories.TeamDocumentAccessFactory(
document=document, team="administrators", role="administrator" document=document, team="administrators", role="administrator"
@@ -222,10 +230,17 @@ def test_api_documents_retrieve_authenticated_related_team_members(
assert sorted(content.pop("accesses"), key=lambda x: x["id"]) == sorted( assert sorted(content.pop("accesses"), key=lambda x: x["id"]) == sorted(
[ [
{ {
"id": str(access_member.id), "id": str(access_reader.id),
"user": None, "user": None,
"team": "members", "team": "readers",
"role": access_member.role, "role": access_reader.role,
"abilities": expected_abilities,
},
{
"id": str(access_editor.id),
"user": None,
"team": "editors",
"role": access_editor.role,
"abilities": expected_abilities, "abilities": expected_abilities,
}, },
{ {
@@ -265,7 +280,7 @@ def test_api_documents_retrieve_authenticated_related_team_members(
"teams", "teams",
[ [
["administrators"], ["administrators"],
["members", "administrators"], ["editors", "administrators"],
["unknown", "administrators"], ["unknown", "administrators"],
], ],
) )
@@ -285,8 +300,11 @@ def test_api_documents_retrieve_authenticated_related_team_administrators(
document = factories.DocumentFactory(is_public=False) document = factories.DocumentFactory(is_public=False)
access_member = factories.TeamDocumentAccessFactory( access_reader = factories.TeamDocumentAccessFactory(
document=document, team="members", role="member" document=document, team="readers", role="reader"
)
access_editor = factories.TeamDocumentAccessFactory(
document=document, team="editors", role="editor"
) )
access_administrator = factories.TeamDocumentAccessFactory( access_administrator = factories.TeamDocumentAccessFactory(
document=document, team="administrators", role="administrator" document=document, team="administrators", role="administrator"
@@ -305,14 +323,26 @@ def test_api_documents_retrieve_authenticated_related_team_administrators(
assert sorted(content.pop("accesses"), key=lambda x: x["id"]) == sorted( assert sorted(content.pop("accesses"), key=lambda x: x["id"]) == sorted(
[ [
{ {
"id": str(access_member.id), "id": str(access_reader.id),
"user": None, "user": None,
"team": "members", "team": "readers",
"role": "member", "role": "reader",
"abilities": { "abilities": {
"destroy": True, "destroy": True,
"retrieve": True, "retrieve": True,
"set_role_to": ["administrator"], "set_role_to": ["administrator", "editor"],
"update": True,
},
},
{
"id": str(access_editor.id),
"user": None,
"team": "editors",
"role": "editor",
"abilities": {
"destroy": True,
"retrieve": True,
"set_role_to": ["administrator", "reader"],
"update": True, "update": True,
}, },
}, },
@@ -324,7 +354,7 @@ def test_api_documents_retrieve_authenticated_related_team_administrators(
"abilities": { "abilities": {
"destroy": True, "destroy": True,
"retrieve": True, "retrieve": True,
"set_role_to": ["member"], "set_role_to": ["editor", "reader"],
"update": True, "update": True,
}, },
}, },
@@ -384,8 +414,11 @@ def test_api_documents_retrieve_authenticated_related_team_owners(
document = factories.DocumentFactory(is_public=False) document = factories.DocumentFactory(is_public=False)
access_member = factories.TeamDocumentAccessFactory( access_reader = factories.TeamDocumentAccessFactory(
document=document, team="members", role="member" document=document, team="readers", role="reader"
)
access_editor = factories.TeamDocumentAccessFactory(
document=document, team="editors", role="editor"
) )
access_administrator = factories.TeamDocumentAccessFactory( access_administrator = factories.TeamDocumentAccessFactory(
document=document, team="administrators", role="administrator" document=document, team="administrators", role="administrator"
@@ -404,14 +437,26 @@ def test_api_documents_retrieve_authenticated_related_team_owners(
assert sorted(content.pop("accesses"), key=lambda x: x["id"]) == sorted( assert sorted(content.pop("accesses"), key=lambda x: x["id"]) == sorted(
[ [
{ {
"id": str(access_member.id), "id": str(access_reader.id),
"user": None, "user": None,
"team": "members", "team": "readers",
"role": "member", "role": "reader",
"abilities": { "abilities": {
"destroy": True, "destroy": True,
"retrieve": True, "retrieve": True,
"set_role_to": ["owner", "administrator"], "set_role_to": ["owner", "administrator", "editor"],
"update": True,
},
},
{
"id": str(access_editor.id),
"user": None,
"team": "editors",
"role": "editor",
"abilities": {
"destroy": True,
"retrieve": True,
"set_role_to": ["owner", "administrator", "reader"],
"update": True, "update": True,
}, },
}, },
@@ -423,7 +468,7 @@ def test_api_documents_retrieve_authenticated_related_team_owners(
"abilities": { "abilities": {
"destroy": True, "destroy": True,
"retrieve": True, "retrieve": True,
"set_role_to": ["owner", "member"], "set_role_to": ["owner", "editor", "reader"],
"update": True, "update": True,
}, },
}, },
@@ -436,7 +481,7 @@ def test_api_documents_retrieve_authenticated_related_team_owners(
# editable only if there is another owner role than the user's team... # editable only if there is another owner role than the user's team...
"destroy": other_access.role == "owner", "destroy": other_access.role == "owner",
"retrieve": True, "retrieve": True,
"set_role_to": ["administrator", "member"] "set_role_to": ["administrator", "editor", "reader"]
if other_access.role == "owner" if other_access.role == "owner"
else [], else [],
"update": other_access.role == "owner", "update": other_access.role == "owner",

View File

@@ -66,9 +66,9 @@ def test_api_documents_update_authenticated_unrelated():
@pytest.mark.parametrize("via", VIA) @pytest.mark.parametrize("via", VIA)
def test_api_documents_update_authenticated_members(via, mock_user_get_teams): def test_api_documents_update_authenticated_reader(via, mock_user_get_teams):
""" """
Users who are members of a document but not administrators should Users who are editors or reader of a document but not administrators should
not be allowed to update it. not be allowed to update it.
""" """
user = factories.UserFactory() user = factories.UserFactory()
@@ -78,11 +78,11 @@ def test_api_documents_update_authenticated_members(via, mock_user_get_teams):
document = factories.DocumentFactory() document = factories.DocumentFactory()
if via == USER: if via == USER:
factories.UserDocumentAccessFactory(document=document, user=user, role="member") factories.UserDocumentAccessFactory(document=document, user=user, role="reader")
elif via == TEAM: elif via == TEAM:
mock_user_get_teams.return_value = ["lasuite", "unknown"] mock_user_get_teams.return_value = ["lasuite", "unknown"]
factories.TeamDocumentAccessFactory( factories.TeamDocumentAccessFactory(
document=document, team="lasuite", role="member" document=document, team="lasuite", role="reader"
) )
old_document_values = serializers.DocumentSerializer(instance=document).data old_document_values = serializers.DocumentSerializer(instance=document).data
@@ -106,12 +106,12 @@ def test_api_documents_update_authenticated_members(via, mock_user_get_teams):
assert document_values == old_document_values assert document_values == old_document_values
@pytest.mark.parametrize("role", ["administrator", "owner"]) @pytest.mark.parametrize("role", ["editor", "administrator", "owner"])
@pytest.mark.parametrize("via", VIA) @pytest.mark.parametrize("via", VIA)
def test_api_documents_update_authenticated_administrator_or_owner( def test_api_documents_update_authenticated_editor_administrator_or_owner(
via, role, mock_user_get_teams via, role, mock_user_get_teams
): ):
"""Administrator or owner of a document should be allowed to update it.""" """A user who is editor, administrator or owner of a document should be allowed to update it."""
user = factories.UserFactory() user = factories.UserFactory()
client = APIClient() client = APIClient()

View File

@@ -45,7 +45,7 @@ def test_api_templates_delete_authenticated_unrelated():
assert models.Template.objects.count() == 1 assert models.Template.objects.count() == 1
@pytest.mark.parametrize("role", ["member", "administrator"]) @pytest.mark.parametrize("role", ["reader", "editor", "administrator"])
@pytest.mark.parametrize("via", VIA) @pytest.mark.parametrize("via", VIA)
def test_api_templates_delete_authenticated_member_or_administrator( def test_api_templates_delete_authenticated_member_or_administrator(
via, role, mock_user_get_teams via, role, mock_user_get_teams

View File

@@ -160,7 +160,10 @@ def test_api_templates_retrieve_authenticated_related_team_none(mock_user_get_te
template = factories.TemplateFactory(is_public=False) template = factories.TemplateFactory(is_public=False)
factories.TeamTemplateAccessFactory( factories.TeamTemplateAccessFactory(
template=template, team="members", role="member" template=template, team="readers", role="reader"
)
factories.TeamTemplateAccessFactory(
template=template, team="editors", role="editor"
) )
factories.TeamTemplateAccessFactory( factories.TeamTemplateAccessFactory(
template=template, team="administrators", role="administrator" template=template, team="administrators", role="administrator"
@@ -177,11 +180,13 @@ def test_api_templates_retrieve_authenticated_related_team_none(mock_user_get_te
@pytest.mark.parametrize( @pytest.mark.parametrize(
"teams", "teams",
[ [
["members"], ["readers"],
["unknown", "members"], ["unknown", "readers"],
["editors"],
["unknown", "editors"],
], ],
) )
def test_api_templates_retrieve_authenticated_related_team_members( def test_api_templates_retrieve_authenticated_related_team_readers_or_editors(
teams, mock_user_get_teams teams, mock_user_get_teams
): ):
""" """
@@ -197,8 +202,11 @@ def test_api_templates_retrieve_authenticated_related_team_members(
template = factories.TemplateFactory(is_public=False) template = factories.TemplateFactory(is_public=False)
access_member = factories.TeamTemplateAccessFactory( access_reader = factories.TeamTemplateAccessFactory(
template=template, team="members", role="member" template=template, team="readers", role="reader"
)
access_editor = factories.TeamTemplateAccessFactory(
template=template, team="editors", role="editor"
) )
access_administrator = factories.TeamTemplateAccessFactory( access_administrator = factories.TeamTemplateAccessFactory(
template=template, team="administrators", role="administrator" template=template, team="administrators", role="administrator"
@@ -221,10 +229,17 @@ def test_api_templates_retrieve_authenticated_related_team_members(
assert sorted(content.pop("accesses"), key=lambda x: x["id"]) == sorted( assert sorted(content.pop("accesses"), key=lambda x: x["id"]) == sorted(
[ [
{ {
"id": str(access_member.id), "id": str(access_reader.id),
"user": None, "user": None,
"team": "members", "team": "readers",
"role": access_member.role, "role": access_reader.role,
"abilities": expected_abilities,
},
{
"id": str(access_editor.id),
"user": None,
"team": "editors",
"role": access_editor.role,
"abilities": expected_abilities, "abilities": expected_abilities,
}, },
{ {
@@ -285,8 +300,11 @@ def test_api_templates_retrieve_authenticated_related_team_administrators(
template = factories.TemplateFactory(is_public=False) template = factories.TemplateFactory(is_public=False)
access_member = factories.TeamTemplateAccessFactory( access_reader = factories.TeamTemplateAccessFactory(
template=template, team="members", role="member" template=template, team="readers", role="reader"
)
access_editor = factories.TeamTemplateAccessFactory(
template=template, team="editors", role="editor"
) )
access_administrator = factories.TeamTemplateAccessFactory( access_administrator = factories.TeamTemplateAccessFactory(
template=template, team="administrators", role="administrator" template=template, team="administrators", role="administrator"
@@ -304,14 +322,26 @@ def test_api_templates_retrieve_authenticated_related_team_administrators(
assert sorted(content.pop("accesses"), key=lambda x: x["id"]) == sorted( assert sorted(content.pop("accesses"), key=lambda x: x["id"]) == sorted(
[ [
{ {
"id": str(access_member.id), "id": str(access_reader.id),
"user": None, "user": None,
"team": "members", "team": "readers",
"role": "member", "role": "reader",
"abilities": { "abilities": {
"destroy": True, "destroy": True,
"retrieve": True, "retrieve": True,
"set_role_to": ["administrator"], "set_role_to": ["administrator", "editor"],
"update": True,
},
},
{
"id": str(access_editor.id),
"user": None,
"team": "editors",
"role": "editor",
"abilities": {
"destroy": True,
"retrieve": True,
"set_role_to": ["administrator", "reader"],
"update": True, "update": True,
}, },
}, },
@@ -323,7 +353,7 @@ def test_api_templates_retrieve_authenticated_related_team_administrators(
"abilities": { "abilities": {
"destroy": True, "destroy": True,
"retrieve": True, "retrieve": True,
"set_role_to": ["member"], "set_role_to": ["editor", "reader"],
"update": True, "update": True,
}, },
}, },
@@ -384,8 +414,11 @@ def test_api_templates_retrieve_authenticated_related_team_owners(
template = factories.TemplateFactory(is_public=False) template = factories.TemplateFactory(is_public=False)
access_member = factories.TeamTemplateAccessFactory( access_reader = factories.TeamTemplateAccessFactory(
template=template, team="members", role="member" template=template, team="readers", role="reader"
)
access_editor = factories.TeamTemplateAccessFactory(
template=template, team="editors", role="editor"
) )
access_administrator = factories.TeamTemplateAccessFactory( access_administrator = factories.TeamTemplateAccessFactory(
template=template, team="administrators", role="administrator" template=template, team="administrators", role="administrator"
@@ -403,14 +436,26 @@ def test_api_templates_retrieve_authenticated_related_team_owners(
assert sorted(content.pop("accesses"), key=lambda x: x["id"]) == sorted( assert sorted(content.pop("accesses"), key=lambda x: x["id"]) == sorted(
[ [
{ {
"id": str(access_member.id), "id": str(access_reader.id),
"user": None, "user": None,
"team": "members", "team": "readers",
"role": "member", "role": "reader",
"abilities": { "abilities": {
"destroy": True, "destroy": True,
"retrieve": True, "retrieve": True,
"set_role_to": ["owner", "administrator"], "set_role_to": ["owner", "administrator", "editor"],
"update": True,
},
},
{
"id": str(access_editor.id),
"user": None,
"team": "editors",
"role": "editor",
"abilities": {
"destroy": True,
"retrieve": True,
"set_role_to": ["owner", "administrator", "reader"],
"update": True, "update": True,
}, },
}, },
@@ -422,7 +467,7 @@ def test_api_templates_retrieve_authenticated_related_team_owners(
"abilities": { "abilities": {
"destroy": True, "destroy": True,
"retrieve": True, "retrieve": True,
"set_role_to": ["owner", "member"], "set_role_to": ["owner", "editor", "reader"],
"update": True, "update": True,
}, },
}, },
@@ -435,7 +480,7 @@ def test_api_templates_retrieve_authenticated_related_team_owners(
# editable only if there is another owner role than the user's team... # editable only if there is another owner role than the user's team...
"destroy": other_access.role == "owner", "destroy": other_access.role == "owner",
"retrieve": True, "retrieve": True,
"set_role_to": ["administrator", "member"] "set_role_to": ["administrator", "editor", "reader"]
if other_access.role == "owner" if other_access.role == "owner"
else [], else [],
"update": other_access.role == "owner", "update": other_access.role == "owner",

View File

@@ -66,10 +66,9 @@ def test_api_templates_update_authenticated_unrelated():
@pytest.mark.parametrize("via", VIA) @pytest.mark.parametrize("via", VIA)
def test_api_templates_update_authenticated_members(via, mock_user_get_teams): def test_api_templates_update_authenticated_readers(via, mock_user_get_teams):
""" """
Users who are members of a template but not administrators should Users who are readers of a template should not be allowed to update it.
not be allowed to update it.
""" """
user = factories.UserFactory() user = factories.UserFactory()
@@ -78,11 +77,11 @@ def test_api_templates_update_authenticated_members(via, mock_user_get_teams):
template = factories.TemplateFactory() template = factories.TemplateFactory()
if via == USER: if via == USER:
factories.UserTemplateAccessFactory(template=template, user=user, role="member") factories.UserTemplateAccessFactory(template=template, user=user, role="reader")
elif via == TEAM: elif via == TEAM:
mock_user_get_teams.return_value = ["lasuite", "unknown"] mock_user_get_teams.return_value = ["lasuite", "unknown"]
factories.TeamTemplateAccessFactory( factories.TeamTemplateAccessFactory(
template=template, team="lasuite", role="member" template=template, team="lasuite", role="reader"
) )
old_template_values = serializers.TemplateSerializer(instance=template).data old_template_values = serializers.TemplateSerializer(instance=template).data
@@ -106,9 +105,9 @@ def test_api_templates_update_authenticated_members(via, mock_user_get_teams):
assert template_values == old_template_values assert template_values == old_template_values
@pytest.mark.parametrize("role", ["administrator", "owner"]) @pytest.mark.parametrize("role", ["editor", "administrator", "owner"])
@pytest.mark.parametrize("via", VIA) @pytest.mark.parametrize("via", VIA)
def test_api_templates_update_authenticated_administrator_or_owner( def test_api_templates_update_authenticated_editor_or_administrator_or_owner(
via, role, mock_user_get_teams via, role, mock_user_get_teams
): ):
"""Administrator or owner of a template should be allowed to update it.""" """Administrator or owner of a template should be allowed to update it."""

View File

@@ -254,9 +254,12 @@ def test_api_document_accesses_create_authenticated_unrelated():
assert not models.DocumentAccess.objects.filter(user=other_user).exists() assert not models.DocumentAccess.objects.filter(user=other_user).exists()
@pytest.mark.parametrize("role", ["reader", "editor"])
@pytest.mark.parametrize("via", VIA) @pytest.mark.parametrize("via", VIA)
def test_api_document_accesses_create_authenticated_member(via, mock_user_get_teams): def test_api_document_accesses_create_authenticated_reader_or_editor(
"""Members of a document should not be allowed to create document accesses.""" via, role, mock_user_get_teams
):
"""Readers or editors of a document should not be allowed to create document accesses."""
user = factories.UserFactory() user = factories.UserFactory()
client = APIClient() client = APIClient()
@@ -264,21 +267,21 @@ def test_api_document_accesses_create_authenticated_member(via, mock_user_get_te
document = factories.DocumentFactory() document = factories.DocumentFactory()
if via == USER: if via == USER:
factories.UserDocumentAccessFactory(document=document, user=user, role="member") factories.UserDocumentAccessFactory(document=document, user=user, role=role)
elif via == TEAM: elif via == TEAM:
mock_user_get_teams.return_value = ["lasuite", "unknown"] mock_user_get_teams.return_value = ["lasuite", "unknown"]
factories.TeamDocumentAccessFactory( factories.TeamDocumentAccessFactory(
document=document, team="lasuite", role="member" document=document, team="lasuite", role=role
) )
other_user = factories.UserFactory() other_user = factories.UserFactory()
for role in [role[0] for role in models.RoleChoices.choices]: for new_role in [role[0] for role in models.RoleChoices.choices]:
response = client.post( response = client.post(
f"/api/v1.0/documents/{document.id!s}/accesses/", f"/api/v1.0/documents/{document.id!s}/accesses/",
{ {
"user": str(other_user.id), "user": str(other_user.id),
"role": role, "role": new_role,
}, },
format="json", format="json",
) )
@@ -456,9 +459,12 @@ def test_api_document_accesses_update_authenticated_unrelated():
assert updated_values == old_values assert updated_values == old_values
@pytest.mark.parametrize("role", ["reader", "editor"])
@pytest.mark.parametrize("via", VIA) @pytest.mark.parametrize("via", VIA)
def test_api_document_accesses_update_authenticated_member(via, mock_user_get_teams): def test_api_document_accesses_update_authenticated_reader_or_editor(
"""Members of a document should not be allowed to update its accesses.""" via, role, mock_user_get_teams
):
"""Readers or editors of a document should not be allowed to update its accesses."""
user = factories.UserFactory() user = factories.UserFactory()
client = APIClient() client = APIClient()
@@ -466,11 +472,11 @@ def test_api_document_accesses_update_authenticated_member(via, mock_user_get_te
document = factories.DocumentFactory() document = factories.DocumentFactory()
if via == USER: if via == USER:
factories.UserDocumentAccessFactory(document=document, user=user, role="member") factories.UserDocumentAccessFactory(document=document, user=user, role=role)
elif via == TEAM: elif via == TEAM:
mock_user_get_teams.return_value = ["lasuite", "unknown"] mock_user_get_teams.return_value = ["lasuite", "unknown"]
factories.TeamDocumentAccessFactory( factories.TeamDocumentAccessFactory(
document=document, team="lasuite", role="member" document=document, team="lasuite", role=role
) )
access = factories.UserDocumentAccessFactory(document=document) access = factories.UserDocumentAccessFactory(document=document)
@@ -521,14 +527,14 @@ def test_api_document_accesses_update_administrator_except_owner(
access = factories.UserDocumentAccessFactory( access = factories.UserDocumentAccessFactory(
document=document, document=document,
role=random.choice(["administrator", "member"]), role=random.choice(["administrator", "editor", "reader"]),
) )
old_values = serializers.DocumentAccessSerializer(instance=access).data old_values = serializers.DocumentAccessSerializer(instance=access).data
new_values = { new_values = {
"id": uuid4(), "id": uuid4(),
"user_id": factories.UserFactory().id, "user_id": factories.UserFactory().id,
"role": random.choice(["administrator", "member"]), "role": random.choice(["administrator", "editor", "reader"]),
} }
for field, value in new_values.items(): for field, value in new_values.items():
@@ -629,7 +635,7 @@ def test_api_document_accesses_update_administrator_to_owner(via, mock_user_get_
access = factories.UserDocumentAccessFactory( access = factories.UserDocumentAccessFactory(
document=document, document=document,
user=other_user, user=other_user,
role=random.choice(["administrator", "member"]), role=random.choice(["administrator", "editor", "reader"]),
) )
old_values = serializers.DocumentAccessSerializer(instance=access).data old_values = serializers.DocumentAccessSerializer(instance=access).data
@@ -736,7 +742,7 @@ def test_api_document_accesses_update_owner_self(via, mock_user_get_teams):
) )
old_values = serializers.DocumentAccessSerializer(instance=access).data old_values = serializers.DocumentAccessSerializer(instance=access).data
new_role = random.choice(["administrator", "member"]) new_role = random.choice(["administrator", "editor", "reader"])
response = client.put( response = client.put(
f"/api/v1.0/documents/{document.id!s}/accesses/{access.id!s}/", f"/api/v1.0/documents/{document.id!s}/accesses/{access.id!s}/",
@@ -797,11 +803,12 @@ def test_api_document_accesses_delete_authenticated():
assert models.DocumentAccess.objects.count() == 1 assert models.DocumentAccess.objects.count() == 1
@pytest.mark.parametrize("role", ["reader", "editor"])
@pytest.mark.parametrize("via", VIA) @pytest.mark.parametrize("via", VIA)
def test_api_document_accesses_delete_member(via, mock_user_get_teams): def test_api_document_accesses_delete_reader_or_editor(via, role, mock_user_get_teams):
""" """
Authenticated users should not be allowed to delete a document access for a Authenticated users should not be allowed to delete a document access for a
document in which they are a simple member. document in which they are a simple reader or editor.
""" """
user = factories.UserFactory() user = factories.UserFactory()
@@ -810,11 +817,11 @@ def test_api_document_accesses_delete_member(via, mock_user_get_teams):
document = factories.DocumentFactory() document = factories.DocumentFactory()
if via == USER: if via == USER:
factories.UserDocumentAccessFactory(document=document, user=user, role="member") factories.UserDocumentAccessFactory(document=document, user=user, role=role)
elif via == TEAM: elif via == TEAM:
mock_user_get_teams.return_value = ["lasuite", "unknown"] mock_user_get_teams.return_value = ["lasuite", "unknown"]
factories.TeamDocumentAccessFactory( factories.TeamDocumentAccessFactory(
document=document, team="lasuite", role="member" document=document, team="lasuite", role=role
) )
access = factories.UserDocumentAccessFactory(document=document) access = factories.UserDocumentAccessFactory(document=document)
@@ -855,7 +862,7 @@ def test_api_document_accesses_delete_administrators_except_owners(
) )
access = factories.UserDocumentAccessFactory( access = factories.UserDocumentAccessFactory(
document=document, role=random.choice(["member", "administrator"]) document=document, role=random.choice(["reader", "editor", "administrator"])
) )
assert models.DocumentAccess.objects.count() == 2 assert models.DocumentAccess.objects.count() == 2

View File

@@ -57,13 +57,20 @@ def test_api_document_invitations__create__authenticated_outsider():
@pytest.mark.parametrize( @pytest.mark.parametrize(
"inviting,invited,is_allowed", "inviting,invited,is_allowed",
( (
["member", "member", False], ["reader", "reader", False],
["member", "administrator", False], ["reader", "editor", False],
["member", "owner", False], ["reader", "administrator", False],
["administrator", "member", True], ["reader", "owner", False],
["editor", "reader", False],
["editor", "editor", False],
["editor", "administrator", False],
["editor", "owner", False],
["administrator", "reader", True],
["administrator", "editor", True],
["administrator", "administrator", True], ["administrator", "administrator", True],
["administrator", "owner", False], ["administrator", "owner", False],
["owner", "member", True], ["owner", "reader", True],
["owner", "editor", True],
["owner", "administrator", True], ["owner", "administrator", True],
["owner", "owner", True], ["owner", "owner", True],
), ),
@@ -146,7 +153,7 @@ def test_api_document_invitations__create__cannot_duplicate_invitation():
# Create a new invitation to the same document with the exact same email address # Create a new invitation to the same document with the exact same email address
invitation_values = { invitation_values = {
"email": existing_invitation.email, "email": existing_invitation.email,
"role": random.choice(["administrator", "member"]), "role": random.choice(["administrator", "editor", "reader"]),
} }
client = APIClient() client = APIClient()
@@ -222,12 +229,12 @@ def test_api_document_invitations__list__authenticated(
document=document, role="administrator", issuer=user document=document, role="administrator", issuer=user
) )
other_invitations = factories.InvitationFactory.create_batch( other_invitations = factories.InvitationFactory.create_batch(
2, document=document, role="member", issuer=other_user 2, document=document, role="reader", issuer=other_user
) )
# invitations from other documents should not be listed # invitations from other documents should not be listed
other_document = factories.DocumentFactory() other_document = factories.DocumentFactory()
factories.InvitationFactory.create_batch(2, document=other_document, role="member") factories.InvitationFactory.create_batch(2, document=other_document, role="reader")
client = APIClient() client = APIClient()
client.force_login(user) client.force_login(user)
@@ -275,7 +282,7 @@ def test_api_document_invitations__list__expired_invitations_still_listed(settin
settings.INVITATION_VALIDITY_DURATION = 1 # second settings.INVITATION_VALIDITY_DURATION = 1 # second
expired_invitation = factories.InvitationFactory( expired_invitation = factories.InvitationFactory(
document=document, document=document,
role="member", role="reader",
issuer=user, issuer=user,
) )
time.sleep(1) time.sleep(1)
@@ -467,17 +474,20 @@ def test_api_document_invitations__delete__privileged_members(
assert response.status_code == status.HTTP_204_NO_CONTENT assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.parametrize("role", ["reader", "editor"])
@pytest.mark.parametrize("via", VIA) @pytest.mark.parametrize("via", VIA)
def test_api_document_invitations__delete__members(via, mock_user_get_teams): def test_api_document_invitations_delete_readers_or_editors(
"""Member should not be able to cancel invitation.""" via, role, mock_user_get_teams
):
"""Readers or editors should not be able to cancel invitation."""
user = factories.UserFactory() user = factories.UserFactory()
document = factories.DocumentFactory() document = factories.DocumentFactory()
if via == USER: if via == USER:
factories.UserDocumentAccessFactory(document=document, user=user, role="member") factories.UserDocumentAccessFactory(document=document, user=user, role=role)
elif via == TEAM: elif via == TEAM:
mock_user_get_teams.return_value = ["lasuite", "unknown"] mock_user_get_teams.return_value = ["lasuite", "unknown"]
factories.TeamDocumentAccessFactory( factories.TeamDocumentAccessFactory(
document=document, team="lasuite", role="member" document=document, team="lasuite", role=role
) )
invitation = factories.InvitationFactory(document=document) invitation = factories.InvitationFactory(document=document)

View File

@@ -437,11 +437,12 @@ def test_api_document_versions_delete_authenticated_private():
assert response.json() == {"detail": "Not found."} assert response.json() == {"detail": "Not found."}
@pytest.mark.parametrize("role", ["reader", "editor"])
@pytest.mark.parametrize("via", VIA) @pytest.mark.parametrize("via", VIA)
def test_api_document_versions_delete_member(via, mock_user_get_teams): def test_api_document_versions_delete_reader_or_editor(via, role, mock_user_get_teams):
""" """
Authenticated users should not be allowed to delete a document version for a Authenticated users should not be allowed to delete a document version for a
document in which they are a simple member. document in which they are a simple reader or editor.
""" """
user = factories.UserFactory() user = factories.UserFactory()
@@ -450,11 +451,11 @@ def test_api_document_versions_delete_member(via, mock_user_get_teams):
document = factories.DocumentFactory() document = factories.DocumentFactory()
if via == USER: if via == USER:
factories.UserDocumentAccessFactory(document=document, user=user, role="member") factories.UserDocumentAccessFactory(document=document, user=user, role=role)
elif via == TEAM: elif via == TEAM:
mock_user_get_teams.return_value = ["lasuite", "unknown"] mock_user_get_teams.return_value = ["lasuite", "unknown"]
factories.TeamDocumentAccessFactory( factories.TeamDocumentAccessFactory(
document=document, team="lasuite", role="member" document=document, team="lasuite", role=role
) )
# Create a new version should make it available to the user # Create a new version should make it available to the user

View File

@@ -254,9 +254,12 @@ def test_api_template_accesses_create_authenticated_unrelated():
assert not models.TemplateAccess.objects.filter(user=other_user).exists() assert not models.TemplateAccess.objects.filter(user=other_user).exists()
@pytest.mark.parametrize("role", ["reader", "editor"])
@pytest.mark.parametrize("via", VIA) @pytest.mark.parametrize("via", VIA)
def test_api_template_accesses_create_authenticated_member(via, mock_user_get_teams): def test_api_template_accesses_create_authenticated_editor_or_reader(
"""Members of a template should not be allowed to create template accesses.""" via, role, mock_user_get_teams
):
"""Editors or readers of a template should not be allowed to create template accesses."""
user = factories.UserFactory() user = factories.UserFactory()
client = APIClient() client = APIClient()
@@ -264,21 +267,21 @@ def test_api_template_accesses_create_authenticated_member(via, mock_user_get_te
template = factories.TemplateFactory() template = factories.TemplateFactory()
if via == USER: if via == USER:
factories.UserTemplateAccessFactory(template=template, user=user, role="member") factories.UserTemplateAccessFactory(template=template, user=user, role=role)
elif via == TEAM: elif via == TEAM:
mock_user_get_teams.return_value = ["lasuite", "unknown"] mock_user_get_teams.return_value = ["lasuite", "unknown"]
factories.TeamTemplateAccessFactory( factories.TeamTemplateAccessFactory(
template=template, team="lasuite", role="member" template=template, team="lasuite", role=role
) )
other_user = factories.UserFactory() other_user = factories.UserFactory()
for role in [role[0] for role in models.RoleChoices.choices]: for new_role in [role[0] for role in models.RoleChoices.choices]:
response = client.post( response = client.post(
f"/api/v1.0/templates/{template.id!s}/accesses/", f"/api/v1.0/templates/{template.id!s}/accesses/",
{ {
"user": str(other_user.id), "user": str(other_user.id),
"role": role, "role": new_role,
}, },
format="json", format="json",
) )
@@ -456,9 +459,12 @@ def test_api_template_accesses_update_authenticated_unrelated():
assert updated_values == old_values assert updated_values == old_values
@pytest.mark.parametrize("role", ["reader", "editor"])
@pytest.mark.parametrize("via", VIA) @pytest.mark.parametrize("via", VIA)
def test_api_template_accesses_update_authenticated_member(via, mock_user_get_teams): def test_api_template_accesses_update_authenticated_editor_or_reader(
"""Members of a template should not be allowed to update its accesses.""" via, role, mock_user_get_teams
):
"""Editors or readers of a template should not be allowed to update its accesses."""
user = factories.UserFactory() user = factories.UserFactory()
client = APIClient() client = APIClient()
@@ -466,11 +472,11 @@ def test_api_template_accesses_update_authenticated_member(via, mock_user_get_te
template = factories.TemplateFactory() template = factories.TemplateFactory()
if via == USER: if via == USER:
factories.UserTemplateAccessFactory(template=template, user=user, role="member") factories.UserTemplateAccessFactory(template=template, user=user, role=role)
elif via == TEAM: elif via == TEAM:
mock_user_get_teams.return_value = ["lasuite", "unknown"] mock_user_get_teams.return_value = ["lasuite", "unknown"]
factories.TeamTemplateAccessFactory( factories.TeamTemplateAccessFactory(
template=template, team="lasuite", role="member" template=template, team="lasuite", role=role
) )
access = factories.UserTemplateAccessFactory(template=template) access = factories.UserTemplateAccessFactory(template=template)
@@ -521,14 +527,14 @@ def test_api_template_accesses_update_administrator_except_owner(
access = factories.UserTemplateAccessFactory( access = factories.UserTemplateAccessFactory(
template=template, template=template,
role=random.choice(["administrator", "member"]), role=random.choice(["administrator", "editor", "reader"]),
) )
old_values = serializers.TemplateAccessSerializer(instance=access).data old_values = serializers.TemplateAccessSerializer(instance=access).data
new_values = { new_values = {
"id": uuid4(), "id": uuid4(),
"user_id": factories.UserFactory().id, "user_id": factories.UserFactory().id,
"role": random.choice(["administrator", "member"]), "role": random.choice(["administrator", "editor", "reader"]),
} }
for field, value in new_values.items(): for field, value in new_values.items():
@@ -629,7 +635,7 @@ def test_api_template_accesses_update_administrator_to_owner(via, mock_user_get_
access = factories.UserTemplateAccessFactory( access = factories.UserTemplateAccessFactory(
template=template, template=template,
user=other_user, user=other_user,
role=random.choice(["administrator", "member"]), role=random.choice(["administrator", "editor", "reader"]),
) )
old_values = serializers.TemplateAccessSerializer(instance=access).data old_values = serializers.TemplateAccessSerializer(instance=access).data
@@ -736,7 +742,7 @@ def test_api_template_accesses_update_owner_self(via, mock_user_get_teams):
) )
old_values = serializers.TemplateAccessSerializer(instance=access).data old_values = serializers.TemplateAccessSerializer(instance=access).data
new_role = random.choice(["administrator", "member"]) new_role = random.choice(["administrator", "editor", "reader"])
response = client.put( response = client.put(
f"/api/v1.0/templates/{template.id!s}/accesses/{access.id!s}/", f"/api/v1.0/templates/{template.id!s}/accesses/{access.id!s}/",
@@ -797,11 +803,12 @@ def test_api_template_accesses_delete_authenticated():
assert models.TemplateAccess.objects.count() == 1 assert models.TemplateAccess.objects.count() == 1
@pytest.mark.parametrize("role", ["reader", "editor"])
@pytest.mark.parametrize("via", VIA) @pytest.mark.parametrize("via", VIA)
def test_api_template_accesses_delete_member(via, mock_user_get_teams): def test_api_template_accesses_delete_editor_or_reader(via, role, mock_user_get_teams):
""" """
Authenticated users should not be allowed to delete a template access for a Authenticated users should not be allowed to delete a template access for a
template in which they are a simple member. template in which they are a simple editor or reader.
""" """
user = factories.UserFactory() user = factories.UserFactory()
@@ -810,11 +817,11 @@ def test_api_template_accesses_delete_member(via, mock_user_get_teams):
template = factories.TemplateFactory() template = factories.TemplateFactory()
if via == USER: if via == USER:
factories.UserTemplateAccessFactory(template=template, user=user, role="member") factories.UserTemplateAccessFactory(template=template, user=user, role=role)
elif via == TEAM: elif via == TEAM:
mock_user_get_teams.return_value = ["lasuite", "unknown"] mock_user_get_teams.return_value = ["lasuite", "unknown"]
factories.TeamTemplateAccessFactory( factories.TeamTemplateAccessFactory(
template=template, team="lasuite", role="member" template=template, team="lasuite", role=role
) )
access = factories.UserTemplateAccessFactory(template=template) access = factories.UserTemplateAccessFactory(template=template)
@@ -855,7 +862,7 @@ def test_api_template_accesses_delete_administrators_except_owners(
) )
access = factories.UserTemplateAccessFactory( access = factories.UserTemplateAccessFactory(
template=template, role=random.choice(["member", "administrator"]) template=template, role=random.choice(["reader", "editor", "administrator"])
) )
assert models.TemplateAccess.objects.count() == 2 assert models.TemplateAccess.objects.count() == 2

View File

@@ -17,11 +17,11 @@ def test_models_document_accesses_str():
""" """
user = factories.UserFactory(email="david.bowman@example.com") user = factories.UserFactory(email="david.bowman@example.com")
access = factories.UserDocumentAccessFactory( access = factories.UserDocumentAccessFactory(
role="member", role="reader",
user=user, user=user,
document__title="admins", document__title="admins",
) )
assert str(access) == "david.bowman@example.com is member in document admins" assert str(access) == "david.bowman@example.com is reader in document admins"
def test_models_document_accesses_unique_user(): def test_models_document_accesses_unique_user():
@@ -119,7 +119,7 @@ def test_models_document_access_get_abilities_for_owner_of_self_allowed():
"destroy": True, "destroy": True,
"retrieve": True, "retrieve": True,
"update": True, "update": True,
"set_role_to": ["administrator", "member"], "set_role_to": ["administrator", "editor", "reader"],
} }
@@ -149,7 +149,7 @@ def test_models_document_access_get_abilities_for_owner_of_owner():
"destroy": True, "destroy": True,
"retrieve": True, "retrieve": True,
"update": True, "update": True,
"set_role_to": ["administrator", "member"], "set_role_to": ["administrator", "editor", "reader"],
} }
@@ -165,13 +165,13 @@ def test_models_document_access_get_abilities_for_owner_of_administrator():
"destroy": True, "destroy": True,
"retrieve": True, "retrieve": True,
"update": True, "update": True,
"set_role_to": ["owner", "member"], "set_role_to": ["owner", "editor", "reader"],
} }
def test_models_document_access_get_abilities_for_owner_of_member(): def test_models_document_access_get_abilities_for_owner_of_editor():
"""Check abilities of member access for the owner of a document.""" """Check abilities of editor access for the owner of a document."""
access = factories.UserDocumentAccessFactory(role="member") access = factories.UserDocumentAccessFactory(role="editor")
factories.UserDocumentAccessFactory(document=access.document) # another one factories.UserDocumentAccessFactory(document=access.document) # another one
user = factories.UserDocumentAccessFactory( user = factories.UserDocumentAccessFactory(
document=access.document, role="owner" document=access.document, role="owner"
@@ -181,7 +181,23 @@ def test_models_document_access_get_abilities_for_owner_of_member():
"destroy": True, "destroy": True,
"retrieve": True, "retrieve": True,
"update": True, "update": True,
"set_role_to": ["owner", "administrator"], "set_role_to": ["owner", "administrator", "reader"],
}
def test_models_document_access_get_abilities_for_owner_of_reader():
"""Check abilities of reader access for the owner of a document."""
access = factories.UserDocumentAccessFactory(role="reader")
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", "editor"],
} }
@@ -216,13 +232,13 @@ def test_models_document_access_get_abilities_for_administrator_of_administrator
"destroy": True, "destroy": True,
"retrieve": True, "retrieve": True,
"update": True, "update": True,
"set_role_to": ["member"], "set_role_to": ["editor", "reader"],
} }
def test_models_document_access_get_abilities_for_administrator_of_member(): def test_models_document_access_get_abilities_for_administrator_of_editor():
"""Check abilities of member access for the administrator of a document.""" """Check abilities of editor access for the administrator of a document."""
access = factories.UserDocumentAccessFactory(role="member") access = factories.UserDocumentAccessFactory(role="editor")
factories.UserDocumentAccessFactory(document=access.document) # another one factories.UserDocumentAccessFactory(document=access.document) # another one
user = factories.UserDocumentAccessFactory( user = factories.UserDocumentAccessFactory(
document=access.document, role="administrator" document=access.document, role="administrator"
@@ -232,19 +248,35 @@ def test_models_document_access_get_abilities_for_administrator_of_member():
"destroy": True, "destroy": True,
"retrieve": True, "retrieve": True,
"update": True, "update": True,
"set_role_to": ["administrator"], "set_role_to": ["administrator", "reader"],
} }
# - for member def test_models_document_access_get_abilities_for_administrator_of_reader():
"""Check abilities of reader access for the administrator of a document."""
access = factories.UserDocumentAccessFactory(role="reader")
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", "editor"],
}
def test_models_document_access_get_abilities_for_member_of_owner(): # - for editor
"""Check abilities of owner access for the member of a document."""
def test_models_document_access_get_abilities_for_editor_of_owner():
"""Check abilities of owner access for the editor of a document."""
access = factories.UserDocumentAccessFactory(role="owner") access = factories.UserDocumentAccessFactory(role="owner")
factories.UserDocumentAccessFactory(document=access.document) # another one factories.UserDocumentAccessFactory(document=access.document) # another one
user = factories.UserDocumentAccessFactory( user = factories.UserDocumentAccessFactory(
document=access.document, role="member" document=access.document, role="editor"
).user ).user
abilities = access.get_abilities(user) abilities = access.get_abilities(user)
assert abilities == { assert abilities == {
@@ -255,12 +287,12 @@ def test_models_document_access_get_abilities_for_member_of_owner():
} }
def test_models_document_access_get_abilities_for_member_of_administrator(): def test_models_document_access_get_abilities_for_editor_of_administrator():
"""Check abilities of administrator access for the member of a document.""" """Check abilities of administrator access for the editor of a document."""
access = factories.UserDocumentAccessFactory(role="administrator") access = factories.UserDocumentAccessFactory(role="administrator")
factories.UserDocumentAccessFactory(document=access.document) # another one factories.UserDocumentAccessFactory(document=access.document) # another one
user = factories.UserDocumentAccessFactory( user = factories.UserDocumentAccessFactory(
document=access.document, role="member" document=access.document, role="editor"
).user ).user
abilities = access.get_abilities(user) abilities = access.get_abilities(user)
assert abilities == { assert abilities == {
@@ -271,14 +303,70 @@ def test_models_document_access_get_abilities_for_member_of_administrator():
} }
def test_models_document_access_get_abilities_for_member_of_member_user( def test_models_document_access_get_abilities_for_editor_of_editor_user(
django_assert_num_queries django_assert_num_queries
): ):
"""Check abilities of member access for the member of a document.""" """Check abilities of editor access for the editor of a document."""
access = factories.UserDocumentAccessFactory(role="member") access = factories.UserDocumentAccessFactory(role="editor")
factories.UserDocumentAccessFactory(document=access.document) # another one factories.UserDocumentAccessFactory(document=access.document) # another one
user = factories.UserDocumentAccessFactory( user = factories.UserDocumentAccessFactory(
document=access.document, role="member" document=access.document, role="editor"
).user
with django_assert_num_queries(1):
abilities = access.get_abilities(user)
assert abilities == {
"destroy": False,
"retrieve": True,
"update": False,
"set_role_to": [],
}
# - for reader
def test_models_document_access_get_abilities_for_reader_of_owner():
"""Check abilities of owner access for the reader of a document."""
access = factories.UserDocumentAccessFactory(role="owner")
factories.UserDocumentAccessFactory(document=access.document) # another one
user = factories.UserDocumentAccessFactory(
document=access.document, role="reader"
).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_reader_of_administrator():
"""Check abilities of administrator access for the reader of a document."""
access = factories.UserDocumentAccessFactory(role="administrator")
factories.UserDocumentAccessFactory(document=access.document) # another one
user = factories.UserDocumentAccessFactory(
document=access.document, role="reader"
).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_reader_of_reader_user(
django_assert_num_queries
):
"""Check abilities of reader access for the reader of a document."""
access = factories.UserDocumentAccessFactory(role="reader")
factories.UserDocumentAccessFactory(document=access.document) # another one
user = factories.UserDocumentAccessFactory(
document=access.document, role="reader"
).user ).user
with django_assert_num_queries(1): with django_assert_num_queries(1):
@@ -294,11 +382,11 @@ def test_models_document_access_get_abilities_for_member_of_member_user(
def test_models_document_access_get_abilities_preset_role(django_assert_num_queries): 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.""" """No query is done if the role is preset, e.g., with a query annotation."""
access = factories.UserDocumentAccessFactory(role="member") access = factories.UserDocumentAccessFactory(role="reader")
user = factories.UserDocumentAccessFactory( user = factories.UserDocumentAccessFactory(
document=access.document, role="member" document=access.document, role="reader"
).user ).user
access.user_roles = ["member"] access.user_roles = ["reader"]
with django_assert_num_queries(0): with django_assert_num_queries(0):
abilities = access.get_abilities(user) abilities = access.get_abilities(user)

View File

@@ -147,9 +147,28 @@ def test_models_documents_get_abilities_administrator():
} }
def test_models_documents_get_abilities_member_user(django_assert_num_queries): def test_models_documents_get_abilities_editor_user(django_assert_num_queries):
"""Check abilities returned for the member of a document.""" """Check abilities returned for the editor of a document."""
access = factories.UserDocumentAccessFactory(role="member") access = factories.UserDocumentAccessFactory(role="editor")
with django_assert_num_queries(1):
abilities = access.document.get_abilities(access.user)
assert abilities == {
"destroy": False,
"retrieve": True,
"update": True,
"manage_accesses": False,
"partial_update": True,
"versions_destroy": False,
"versions_list": True,
"versions_retrieve": True,
}
def test_models_documents_get_abilities_reader_user(django_assert_num_queries):
"""Check abilities returned for the reader of a document."""
access = factories.UserDocumentAccessFactory(role="reader")
with django_assert_num_queries(1): with django_assert_num_queries(1):
abilities = access.document.get_abilities(access.user) abilities = access.document.get_abilities(access.user)
@@ -168,8 +187,8 @@ def test_models_documents_get_abilities_member_user(django_assert_num_queries):
def test_models_documents_get_abilities_preset_role(django_assert_num_queries): 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.""" """No query is done if the role is preset e.g. with query annotation."""
access = factories.UserDocumentAccessFactory(role="member") access = factories.UserDocumentAccessFactory(role="reader")
access.document.user_roles = ["member"] access.document.user_roles = ["reader"]
with django_assert_num_queries(0): with django_assert_num_queries(0):
abilities = access.document.get_abilities(access.user) abilities = access.document.get_abilities(access.user)

View File

@@ -170,7 +170,7 @@ def test_models_invitation__new_user__user_creation_constant_num_queries(
def test_models_document_invitations_email(): def test_models_document_invitations_email():
"""Check email invitation during invitation creation.""" """Check email invitation during invitation creation."""
member_access = factories.UserDocumentAccessFactory(role="member") member_access = factories.UserDocumentAccessFactory(role="reader")
document = member_access.document document = member_access.document
# pylint: disable-next=no-member # pylint: disable-next=no-member
@@ -201,7 +201,7 @@ def test_models_document_invitations_email():
def test_models_document_invitations_email_failed(mock_logger, _mock_send_mail): def test_models_document_invitations_email_failed(mock_logger, _mock_send_mail):
"""Check invitation behavior when an SMTP error occurs during invitation creation.""" """Check invitation behavior when an SMTP error occurs during invitation creation."""
member_access = factories.UserDocumentAccessFactory(role="member") member_access = factories.UserDocumentAccessFactory(role="reader")
document = member_access.document document = member_access.document
# pylint: disable-next=no-member # pylint: disable-next=no-member
@@ -288,17 +288,42 @@ def test_models_document_invitations_get_abilities_privileged_member(
@pytest.mark.parametrize("via", VIA) @pytest.mark.parametrize("via", VIA)
def test_models_document_invitations_get_abilities_member(via, mock_user_get_teams): def test_models_document_invitations_get_abilities_reader(via, mock_user_get_teams):
"""Check abilities for a document member with 'member' role.""" """Check abilities for a document reader with 'reader' role."""
user = factories.UserFactory() user = factories.UserFactory()
document = factories.DocumentFactory() document = factories.DocumentFactory()
if via == USER: if via == USER:
factories.UserDocumentAccessFactory(document=document, user=user, role="member") factories.UserDocumentAccessFactory(document=document, user=user, role="reader")
elif via == TEAM: elif via == TEAM:
mock_user_get_teams.return_value = ["lasuite", "unknown"] mock_user_get_teams.return_value = ["lasuite", "unknown"]
factories.TeamDocumentAccessFactory( factories.TeamDocumentAccessFactory(
document=document, team="lasuite", role="member" document=document, team="lasuite", role="reader"
)
invitation = factories.InvitationFactory(document=document)
abilities = invitation.get_abilities(user)
assert abilities == {
"destroy": False,
"retrieve": True,
"partial_update": False,
"update": False,
}
@pytest.mark.parametrize("via", VIA)
def test_models_document_invitations_get_abilities_editor(via, mock_user_get_teams):
"""Check abilities for a document editor with 'editor' role."""
user = factories.UserFactory()
document = factories.DocumentFactory()
if via == USER:
factories.UserDocumentAccessFactory(document=document, user=user, role="editor")
elif via == TEAM:
mock_user_get_teams.return_value = ["lasuite", "unknown"]
factories.TeamDocumentAccessFactory(
document=document, team="lasuite", role="editor"
) )
invitation = factories.InvitationFactory(document=document) invitation = factories.InvitationFactory(document=document)

View File

@@ -17,11 +17,11 @@ def test_models_template_accesses_str():
""" """
user = factories.UserFactory(email="david.bowman@example.com") user = factories.UserFactory(email="david.bowman@example.com")
access = factories.UserTemplateAccessFactory( access = factories.UserTemplateAccessFactory(
role="member", role="reader",
user=user, user=user,
template__title="admins", template__title="admins",
) )
assert str(access) == "david.bowman@example.com is member in template admins" assert str(access) == "david.bowman@example.com is reader in template admins"
def test_models_template_accesses_unique_user(): def test_models_template_accesses_unique_user():
@@ -119,7 +119,7 @@ def test_models_template_access_get_abilities_for_owner_of_self_allowed():
"destroy": True, "destroy": True,
"retrieve": True, "retrieve": True,
"update": True, "update": True,
"set_role_to": ["administrator", "member"], "set_role_to": ["administrator", "editor", "reader"],
} }
@@ -149,7 +149,7 @@ def test_models_template_access_get_abilities_for_owner_of_owner():
"destroy": True, "destroy": True,
"retrieve": True, "retrieve": True,
"update": True, "update": True,
"set_role_to": ["administrator", "member"], "set_role_to": ["administrator", "editor", "reader"],
} }
@@ -165,13 +165,13 @@ def test_models_template_access_get_abilities_for_owner_of_administrator():
"destroy": True, "destroy": True,
"retrieve": True, "retrieve": True,
"update": True, "update": True,
"set_role_to": ["owner", "member"], "set_role_to": ["owner", "editor", "reader"],
} }
def test_models_template_access_get_abilities_for_owner_of_member(): def test_models_template_access_get_abilities_for_owner_of_editor():
"""Check abilities of member access for the owner of a template.""" """Check abilities of editor access for the owner of a template."""
access = factories.UserTemplateAccessFactory(role="member") access = factories.UserTemplateAccessFactory(role="editor")
factories.UserTemplateAccessFactory(template=access.template) # another one factories.UserTemplateAccessFactory(template=access.template) # another one
user = factories.UserTemplateAccessFactory( user = factories.UserTemplateAccessFactory(
template=access.template, role="owner" template=access.template, role="owner"
@@ -181,7 +181,23 @@ def test_models_template_access_get_abilities_for_owner_of_member():
"destroy": True, "destroy": True,
"retrieve": True, "retrieve": True,
"update": True, "update": True,
"set_role_to": ["owner", "administrator"], "set_role_to": ["owner", "administrator", "reader"],
}
def test_models_template_access_get_abilities_for_owner_of_reader():
"""Check abilities of reader access for the owner of a template."""
access = factories.UserTemplateAccessFactory(role="reader")
factories.UserTemplateAccessFactory(template=access.template) # another one
user = factories.UserTemplateAccessFactory(
template=access.template, role="owner"
).user
abilities = access.get_abilities(user)
assert abilities == {
"destroy": True,
"retrieve": True,
"update": True,
"set_role_to": ["owner", "administrator", "editor"],
} }
@@ -216,13 +232,13 @@ def test_models_template_access_get_abilities_for_administrator_of_administrator
"destroy": True, "destroy": True,
"retrieve": True, "retrieve": True,
"update": True, "update": True,
"set_role_to": ["member"], "set_role_to": ["editor", "reader"],
} }
def test_models_template_access_get_abilities_for_administrator_of_member(): def test_models_template_access_get_abilities_for_administrator_of_editor():
"""Check abilities of member access for the administrator of a template.""" """Check abilities of editor access for the administrator of a template."""
access = factories.UserTemplateAccessFactory(role="member") access = factories.UserTemplateAccessFactory(role="editor")
factories.UserTemplateAccessFactory(template=access.template) # another one factories.UserTemplateAccessFactory(template=access.template) # another one
user = factories.UserTemplateAccessFactory( user = factories.UserTemplateAccessFactory(
template=access.template, role="administrator" template=access.template, role="administrator"
@@ -232,19 +248,35 @@ def test_models_template_access_get_abilities_for_administrator_of_member():
"destroy": True, "destroy": True,
"retrieve": True, "retrieve": True,
"update": True, "update": True,
"set_role_to": ["administrator"], "set_role_to": ["administrator", "reader"],
} }
# - for member def test_models_template_access_get_abilities_for_administrator_of_reader():
"""Check abilities of reader access for the administrator of a template."""
access = factories.UserTemplateAccessFactory(role="reader")
factories.UserTemplateAccessFactory(template=access.template) # another one
user = factories.UserTemplateAccessFactory(
template=access.template, role="administrator"
).user
abilities = access.get_abilities(user)
assert abilities == {
"destroy": True,
"retrieve": True,
"update": True,
"set_role_to": ["administrator", "editor"],
}
def test_models_template_access_get_abilities_for_member_of_owner(): # - For editor
"""Check abilities of owner access for the member of a template."""
def test_models_template_access_get_abilities_for_editor_of_owner():
"""Check abilities of owner access for the editor of a template."""
access = factories.UserTemplateAccessFactory(role="owner") access = factories.UserTemplateAccessFactory(role="owner")
factories.UserTemplateAccessFactory(template=access.template) # another one factories.UserTemplateAccessFactory(template=access.template) # another one
user = factories.UserTemplateAccessFactory( user = factories.UserTemplateAccessFactory(
template=access.template, role="member" template=access.template, role="editor"
).user ).user
abilities = access.get_abilities(user) abilities = access.get_abilities(user)
assert abilities == { assert abilities == {
@@ -255,12 +287,12 @@ def test_models_template_access_get_abilities_for_member_of_owner():
} }
def test_models_template_access_get_abilities_for_member_of_administrator(): def test_models_template_access_get_abilities_for_editor_of_administrator():
"""Check abilities of administrator access for the member of a template.""" """Check abilities of administrator access for the editor of a template."""
access = factories.UserTemplateAccessFactory(role="administrator") access = factories.UserTemplateAccessFactory(role="administrator")
factories.UserTemplateAccessFactory(template=access.template) # another one factories.UserTemplateAccessFactory(template=access.template) # another one
user = factories.UserTemplateAccessFactory( user = factories.UserTemplateAccessFactory(
template=access.template, role="member" template=access.template, role="editor"
).user ).user
abilities = access.get_abilities(user) abilities = access.get_abilities(user)
assert abilities == { assert abilities == {
@@ -271,14 +303,70 @@ def test_models_template_access_get_abilities_for_member_of_administrator():
} }
def test_models_template_access_get_abilities_for_member_of_member_user( def test_models_template_access_get_abilities_for_editor_of_editor_user(
django_assert_num_queries django_assert_num_queries
): ):
"""Check abilities of member access for the member of a template.""" """Check abilities of editor access for the editor of a template."""
access = factories.UserTemplateAccessFactory(role="member") access = factories.UserTemplateAccessFactory(role="editor")
factories.UserTemplateAccessFactory(template=access.template) # another one factories.UserTemplateAccessFactory(template=access.template) # another one
user = factories.UserTemplateAccessFactory( user = factories.UserTemplateAccessFactory(
template=access.template, role="member" template=access.template, role="editor"
).user
with django_assert_num_queries(1):
abilities = access.get_abilities(user)
assert abilities == {
"destroy": False,
"retrieve": True,
"update": False,
"set_role_to": [],
}
# - For reader
def test_models_template_access_get_abilities_for_reader_of_owner():
"""Check abilities of owner access for the reader of a template."""
access = factories.UserTemplateAccessFactory(role="owner")
factories.UserTemplateAccessFactory(template=access.template) # another one
user = factories.UserTemplateAccessFactory(
template=access.template, role="reader"
).user
abilities = access.get_abilities(user)
assert abilities == {
"destroy": False,
"retrieve": True,
"update": False,
"set_role_to": [],
}
def test_models_template_access_get_abilities_for_reader_of_administrator():
"""Check abilities of administrator access for the reader of a template."""
access = factories.UserTemplateAccessFactory(role="administrator")
factories.UserTemplateAccessFactory(template=access.template) # another one
user = factories.UserTemplateAccessFactory(
template=access.template, role="reader"
).user
abilities = access.get_abilities(user)
assert abilities == {
"destroy": False,
"retrieve": True,
"update": False,
"set_role_to": [],
}
def test_models_template_access_get_abilities_for_reader_of_reader_user(
django_assert_num_queries
):
"""Check abilities of reader access for the reader of a template."""
access = factories.UserTemplateAccessFactory(role="reader")
factories.UserTemplateAccessFactory(template=access.template) # another one
user = factories.UserTemplateAccessFactory(
template=access.template, role="reader"
).user ).user
with django_assert_num_queries(1): with django_assert_num_queries(1):
@@ -294,11 +382,11 @@ def test_models_template_access_get_abilities_for_member_of_member_user(
def test_models_template_access_get_abilities_preset_role(django_assert_num_queries): def test_models_template_access_get_abilities_preset_role(django_assert_num_queries):
"""No query is done if the role is preset, e.g., with a query annotation.""" """No query is done if the role is preset, e.g., with a query annotation."""
access = factories.UserTemplateAccessFactory(role="member") access = factories.UserTemplateAccessFactory(role="reader")
user = factories.UserTemplateAccessFactory( user = factories.UserTemplateAccessFactory(
template=access.template, role="member" template=access.template, role="reader"
).user ).user
access.user_roles = ["member"] access.user_roles = ["reader"]
with django_assert_num_queries(0): with django_assert_num_queries(0):
abilities = access.get_abilities(user) abilities = access.get_abilities(user)

View File

@@ -134,9 +134,26 @@ def test_models_templates_get_abilities_administrator():
} }
def test_models_templates_get_abilities_member_user(django_assert_num_queries): def test_models_templates_get_abilities_editor_user(django_assert_num_queries):
"""Check abilities returned for the member of a template.""" """Check abilities returned for the editor of a template."""
access = factories.UserTemplateAccessFactory(role="member") access = factories.UserTemplateAccessFactory(role="editor")
with django_assert_num_queries(1):
abilities = access.template.get_abilities(access.user)
assert abilities == {
"destroy": False,
"retrieve": True,
"update": True,
"manage_accesses": False,
"partial_update": True,
"generate_document": True,
}
def test_models_templates_get_abilities_reader_user(django_assert_num_queries):
"""Check abilities returned for the reader of a template."""
access = factories.UserTemplateAccessFactory(role="reader")
with django_assert_num_queries(1): with django_assert_num_queries(1):
abilities = access.template.get_abilities(access.user) abilities = access.template.get_abilities(access.user)
@@ -153,8 +170,8 @@ def test_models_templates_get_abilities_member_user(django_assert_num_queries):
def test_models_templates_get_abilities_preset_role(django_assert_num_queries): def test_models_templates_get_abilities_preset_role(django_assert_num_queries):
"""No query is done if the role is preset e.g. with query annotation.""" """No query is done if the role is preset e.g. with query annotation."""
access = factories.UserTemplateAccessFactory(role="member") access = factories.UserTemplateAccessFactory(role="reader")
access.template.user_roles = ["member"] access.template.user_roles = ["reader"]
with django_assert_num_queries(0): with django_assert_num_queries(0):
abilities = access.template.get_abilities(access.user) abilities = access.template.get_abilities(access.user)

View File

@@ -12,7 +12,8 @@ export interface Access {
} }
export enum Role { export enum Role {
MEMBER = 'member', READER = 'reader',
EDITOR = 'editor',
ADMIN = 'administrator', ADMIN = 'administrator',
OWNER = 'owner', OWNER = 'owner',
} }

View File

@@ -1,5 +1,6 @@
export enum Role { export enum Role {
MEMBER = 'member', READER = 'reader',
EDITOR = 'editor',
ADMIN = 'administrator', ADMIN = 'administrator',
OWNER = 'owner', OWNER = 'owner',
} }