🔥(backend) remove all code related to template

The template feature is removed.
Migration created to drop related tables.
Files modified:
- viewsets
- serializers
- models
- admin
- factories
- urls
- tests
- demo data
This commit is contained in:
Anthony LC
2026-01-09 16:00:10 +01:00
parent 548f32bf4e
commit 61dbda0bf6
24 changed files with 39 additions and 1925 deletions

View File

@@ -10,6 +10,12 @@ and this project adheres to
- ✨(frontend) integrate configurable Waffle #1795
### Changed
- ♿(frontend) improve accessibility:
- ♿️(frontend) fix subdoc opening and emoji pick focus #1745
- ✨(backend) add field for button label in email template #1817
### Fixed
- ✅(e2e) fix e2e test for other browsers #1799
@@ -17,17 +23,14 @@ and this project adheres to
- 🐛(frontend) fix emojipicker closing in tree #1808
- 🐛(frontend) display children in favorite #1782
### Changed
### Removed
- ♿(frontend) improve accessibility:
- ♿️(frontend) fix subdoc opening and emoji pick focus #1745
- ✨(backend) add field for button label in email template #1817
- 🔥(project) remove all code related to template #1780
### Security
- 🔒️(trivy) fix vulnerability about jaraco.context #1806
## [4.4.0] - 2026-01-13
### Added

View File

@@ -9,14 +9,6 @@ from treebeard.admin import TreeAdmin
from . import models
class TemplateAccessInline(admin.TabularInline):
"""Inline admin class for template accesses."""
autocomplete_fields = ["user"]
model = models.TemplateAccess
extra = 0
@admin.register(models.User)
class UserAdmin(auth_admin.UserAdmin):
"""Admin class for the User model"""
@@ -69,7 +61,6 @@ class UserAdmin(auth_admin.UserAdmin):
},
),
)
inlines = (TemplateAccessInline,)
list_display = (
"id",
"sub",
@@ -104,15 +95,8 @@ class UserAdmin(auth_admin.UserAdmin):
search_fields = ("id", "sub", "admin_email", "email", "full_name")
@admin.register(models.Template)
class TemplateAdmin(admin.ModelAdmin):
"""Template admin interface declaration."""
inlines = (TemplateAccessInline,)
class DocumentAccessInline(admin.TabularInline):
"""Inline admin class for template accesses."""
"""Inline admin class for document accesses."""
autocomplete_fields = ["user"]
model = models.DocumentAccess

View File

@@ -98,10 +98,10 @@ class CanCreateInvitationPermission(permissions.BasePermission):
class ResourceWithAccessPermission(permissions.BasePermission):
"""A permission class for templates and invitations."""
"""A permission class for invitations."""
def has_permission(self, request, view):
"""check create permission for templates."""
"""check create permission."""
return request.user.is_authenticated or view.action != "create"
def has_object_permission(self, request, view, obj):

View File

@@ -59,30 +59,6 @@ class UserLightSerializer(UserSerializer):
read_only_fields = ["full_name", "short_name"]
class TemplateAccessSerializer(serializers.ModelSerializer):
"""Serialize template accesses."""
abilities = serializers.SerializerMethodField(read_only=True)
class Meta:
model = models.TemplateAccess
resource_field_name = "template"
fields = ["id", "user", "team", "role", "abilities"]
read_only_fields = ["id", "abilities"]
def get_abilities(self, instance) -> dict:
"""Return abilities of the logged-in user on the instance."""
request = self.context.get("request")
if request:
return instance.get_abilities(request.user)
return {}
def update(self, instance, validated_data):
"""Make "user" field is readonly but only on update."""
validated_data.pop("user", None)
return super().update(instance, validated_data)
class ListDocumentSerializer(serializers.ModelSerializer):
"""Serialize documents with limited fields for display in lists."""
@@ -660,52 +636,6 @@ class FileUploadSerializer(serializers.Serializer):
return attrs
class TemplateSerializer(serializers.ModelSerializer):
"""Serialize templates."""
abilities = serializers.SerializerMethodField(read_only=True)
accesses = TemplateAccessSerializer(many=True, read_only=True)
class Meta:
model = models.Template
fields = [
"id",
"title",
"accesses",
"abilities",
"css",
"code",
"is_public",
]
read_only_fields = ["id", "accesses", "abilities"]
def get_abilities(self, document) -> dict:
"""Return abilities of the logged-in user on the instance."""
request = self.context.get("request")
if request:
return document.get_abilities(request.user)
return {}
# pylint: disable=abstract-method
class DocumentGenerationSerializer(serializers.Serializer):
"""Serializer to receive a request to generate a document on a template."""
body = serializers.CharField(label=_("Body"))
body_type = serializers.ChoiceField(
choices=["html", "markdown"],
label=_("Body type"),
required=False,
default="html",
)
format = serializers.ChoiceField(
choices=["pdf", "docx"],
label=_("Format"),
required=False,
default="pdf",
)
class InvitationSerializer(serializers.ModelSerializer):
"""Serialize invitations."""

View File

@@ -2109,64 +2109,6 @@ class DocumentAccessViewSet(
)
class TemplateViewSet(
drf.mixins.RetrieveModelMixin,
viewsets.GenericViewSet,
):
"""Template ViewSet"""
filter_backends = [drf.filters.OrderingFilter]
permission_classes = [
permissions.IsAuthenticatedOrSafe,
permissions.ResourceWithAccessPermission,
]
throttle_scope = "template"
ordering = ["-created_at"]
ordering_fields = ["created_at", "updated_at", "title"]
serializer_class = serializers.TemplateSerializer
queryset = models.Template.objects.all()
def get_queryset(self):
"""Custom queryset to get user related templates."""
queryset = super().get_queryset()
user = self.request.user
if not user.is_authenticated:
return queryset
user_roles_query = (
models.TemplateAccess.objects.filter(
db.Q(user=user) | db.Q(team__in=user.teams),
template_id=db.OuterRef("pk"),
)
.values("template")
.annotate(roles_array=ArrayAgg("role"))
.values("roles_array")
)
return queryset.annotate(user_roles=db.Subquery(user_roles_query)).distinct()
def list(self, request, *args, **kwargs):
"""Restrict templates returned by the list endpoint"""
queryset = self.filter_queryset(self.get_queryset())
user = self.request.user
if user.is_authenticated:
queryset = queryset.filter(
db.Q(accesses__user=user)
| db.Q(accesses__team__in=user.teams)
| db.Q(is_public=True)
)
else:
queryset = queryset.filter(is_public=True)
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return drf.response.Response(serializer.data)
class InvitationViewset(
drf.mixins.CreateModelMixin,
drf.mixins.ListModelMixin,

View File

@@ -53,15 +53,6 @@ class UserFactory(factory.django.DjangoModelFactory):
if create and (extracted is True):
UserDocumentAccessFactory(user=self, role="owner")
@factory.post_generation
def with_owned_template(self, create, extracted, **kwargs):
"""
Create a template for which the user is owner to check
that there is no interference
"""
if create and (extracted is True):
UserTemplateAccessFactory(user=self, role="owner")
class ParentNodeFactory(factory.declarations.ParameteredAttribute):
"""Custom factory attribute for setting the parent node."""
@@ -202,50 +193,6 @@ class DocumentAskForAccessFactory(factory.django.DjangoModelFactory):
role = factory.fuzzy.FuzzyChoice([r[0] for r in models.RoleChoices.choices])
class TemplateFactory(factory.django.DjangoModelFactory):
"""A factory to create templates"""
class Meta:
model = models.Template
django_get_or_create = ("title",)
skip_postgeneration_save = True
title = factory.Sequence(lambda n: f"template{n}")
is_public = factory.Faker("boolean")
@factory.post_generation
def users(self, create, extracted, **kwargs):
"""Add users to template from a given list of users with or without roles."""
if create and extracted:
for item in extracted:
if isinstance(item, models.User):
UserTemplateAccessFactory(template=self, user=item)
else:
UserTemplateAccessFactory(template=self, user=item[0], role=item[1])
class UserTemplateAccessFactory(factory.django.DjangoModelFactory):
"""Create fake template user accesses for testing."""
class Meta:
model = models.TemplateAccess
template = factory.SubFactory(TemplateFactory)
user = factory.SubFactory(UserFactory)
role = factory.fuzzy.FuzzyChoice([r[0] for r in models.RoleChoices.choices])
class TeamTemplateAccessFactory(factory.django.DjangoModelFactory):
"""Create fake template team accesses for testing."""
class Meta:
model = models.TemplateAccess
template = factory.SubFactory(TemplateFactory)
team = factory.Sequence(lambda n: f"team{n}")
role = factory.fuzzy.FuzzyChoice([r[0] for r in models.RoleChoices.choices])
class InvitationFactory(factory.django.DjangoModelFactory):
"""A factory to create invitations for a user"""

View File

@@ -0,0 +1,26 @@
# Generated by Django 5.2.9 on 2026-01-09 14:18
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("core", "0027_auto_20251120_0956"),
]
operations = [
migrations.RemoveField(
model_name="templateaccess",
name="template",
),
migrations.RemoveField(
model_name="templateaccess",
name="user",
),
migrations.DeleteModel(
name="Template",
),
migrations.DeleteModel(
name="TemplateAccess",
),
]

View File

@@ -1430,163 +1430,6 @@ class Reaction(BaseModel):
return f"Reaction {self.emoji} on comment {self.comment.id}"
class Template(BaseModel):
"""HTML and CSS code used for formatting the print around the MarkDown body."""
title = models.CharField(_("title"), max_length=255)
description = models.TextField(_("description"), blank=True)
code = models.TextField(_("code"), blank=True)
css = models.TextField(_("css"), blank=True)
is_public = models.BooleanField(
_("public"),
default=False,
help_text=_("Whether this template is public for anyone to use."),
)
class Meta:
db_table = "impress_template"
ordering = ("title",)
verbose_name = _("Template")
verbose_name_plural = _("Templates")
def __str__(self):
return self.title
def get_role(self, user):
"""Return the roles a user has on a resource as an iterable."""
if not user.is_authenticated:
return None
try:
roles = self.user_roles or []
except AttributeError:
try:
roles = self.accesses.filter(
models.Q(user=user) | models.Q(team__in=user.teams),
).values_list("role", flat=True)
except (models.ObjectDoesNotExist, IndexError):
roles = []
return RoleChoices.max(*roles)
def get_abilities(self, user):
"""
Compute and return abilities for a given user on the template.
"""
role = self.get_role(user)
is_owner_or_admin = role in PRIVILEGED_ROLES
can_get = self.is_public or bool(role)
can_update = is_owner_or_admin or role == RoleChoices.EDITOR
return {
"destroy": role == RoleChoices.OWNER,
"generate_document": can_get,
"accesses_manage": is_owner_or_admin,
"update": can_update,
"partial_update": can_update,
"retrieve": can_get,
}
class TemplateAccess(BaseAccess):
"""Relation model to give access to a template for a user or a team with a role."""
template = models.ForeignKey(
Template,
on_delete=models.CASCADE,
related_name="accesses",
)
class Meta:
db_table = "impress_template_access"
ordering = ("-created_at",)
verbose_name = _("Template/user relation")
verbose_name_plural = _("Template/user relations")
constraints = [
models.UniqueConstraint(
fields=["user", "template"],
condition=models.Q(user__isnull=False), # Exclude null users
name="unique_template_user",
violation_error_message=_("This user is already in this template."),
),
models.UniqueConstraint(
fields=["team", "template"],
condition=models.Q(team__gt=""), # Exclude empty string teams
name="unique_template_team",
violation_error_message=_("This team is already in this template."),
),
models.CheckConstraint(
condition=models.Q(user__isnull=False, team="")
| models.Q(user__isnull=True, team__gt=""),
name="check_template_access_either_user_or_team",
violation_error_message=_("Either user or team must be set, not both."),
),
]
def __str__(self):
return f"{self.user!s} is {self.role:s} in template {self.template!s}"
def get_role(self, user):
"""
Get the role a user has on a resource.
"""
if not user.is_authenticated:
return None
try:
roles = self.user_roles or []
except AttributeError:
teams = user.teams
try:
roles = self.template.accesses.filter(
models.Q(user=user) | models.Q(team__in=teams),
).values_list("role", flat=True)
except (Template.DoesNotExist, IndexError):
roles = []
return RoleChoices.max(*roles)
def get_abilities(self, user):
"""
Compute and return abilities for a given user on the template access.
"""
role = self.get_role(user)
is_owner_or_admin = role in PRIVILEGED_ROLES
if self.role == RoleChoices.OWNER:
can_delete = (role == RoleChoices.OWNER) and self.template.accesses.filter(
role=RoleChoices.OWNER
).count() > 1
set_role_to = (
[RoleChoices.ADMIN, RoleChoices.EDITOR, RoleChoices.READER]
if can_delete
else []
)
else:
can_delete = is_owner_or_admin
set_role_to = []
if role == RoleChoices.OWNER:
set_role_to.append(RoleChoices.OWNER)
if is_owner_or_admin:
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
try:
set_role_to.remove(self.role)
except ValueError:
pass
return {
"destroy": can_delete,
"update": bool(set_role_to),
"partial_update": bool(set_role_to),
"retrieve": bool(role),
"set_role_to": set_role_to,
}
class Invitation(BaseModel):
"""User invitation to a document."""

View File

@@ -1,14 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Generate Document</title>
</head>
<body>
<h2>Generate Document</h2>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Generate PDF</button>
</form>
</body>
</html>

View File

@@ -1,4 +1,4 @@
"""Custom template tags for the core application of People."""
"""Custom template tags for the core application of Docs."""
import base64

View File

@@ -1,46 +0,0 @@
"""
Tests for Templates API endpoint in impress's core app: create
"""
import pytest
from rest_framework.test import APIClient
from core import factories
from core.models import Template
pytestmark = pytest.mark.django_db
def test_api_templates_create_anonymous():
"""Anonymous users should not be allowed to create templates."""
response = APIClient().post(
"/api/v1.0/templates/",
{
"title": "my template",
},
)
assert response.status_code == 401
assert not Template.objects.exists()
def test_api_templates_create_authenticated():
"""
Authenticated users should be able to create templates and should automatically be declared
as the owner of the newly created template.
"""
user = factories.UserFactory()
client = APIClient()
client.force_login(user)
response = client.post(
"/api/v1.0/templates/",
{
"title": "my template",
},
format="json",
)
assert response.status_code == 405
assert not Template.objects.exists()

View File

@@ -1,45 +0,0 @@
"""
Tests for Templates API endpoint in impress's core app: delete
"""
import random
import pytest
from rest_framework.test import APIClient
from core import factories, models
pytestmark = pytest.mark.django_db
def test_api_templates_delete_anonymous():
"""Anonymous users should not be allowed to destroy a template."""
template = factories.TemplateFactory()
response = APIClient().delete(
f"/api/v1.0/templates/{template.id!s}/",
)
assert response.status_code == 401
assert models.Template.objects.count() == 1
def test_api_templates_delete_not_implemented():
"""
Authenticated users should not be allowed to delete a template to which they are not
related.
"""
user = factories.UserFactory()
client = APIClient()
client.force_login(user)
is_public = random.choice([True, False])
template = factories.TemplateFactory(is_public=is_public, users=[(user, "owner")])
response = client.delete(
f"/api/v1.0/templates/{template.id!s}/",
)
assert response.status_code == 405
assert models.Template.objects.count() == 1

View File

@@ -1,237 +0,0 @@
"""
Tests for Templates API endpoint in impress's core app: list
"""
from unittest import mock
import pytest
from rest_framework.pagination import PageNumberPagination
from rest_framework.test import APIClient
from core import factories
pytestmark = pytest.mark.django_db
def test_api_templates_list_anonymous():
"""Anonymous users should only be able to list public templates."""
factories.TemplateFactory.create_batch(2, is_public=False)
public_templates = factories.TemplateFactory.create_batch(2, is_public=True)
expected_ids = {str(template.id) for template in public_templates}
response = APIClient().get("/api/v1.0/templates/")
assert response.status_code == 200
results = response.json()["results"]
assert len(results) == 2
results_id = {result["id"] for result in results}
assert expected_ids == results_id
def test_api_templates_list_authenticated_direct():
"""
Authenticated users should be able to list templates they are a direct
owner/administrator/member of or that are public.
"""
user = factories.UserFactory()
client = APIClient()
client.force_login(user)
related_templates = [
access.template
for access in factories.UserTemplateAccessFactory.create_batch(5, user=user)
]
public_templates = factories.TemplateFactory.create_batch(2, is_public=True)
factories.TemplateFactory.create_batch(2, is_public=False)
expected_ids = {
str(template.id) for template in related_templates + public_templates
}
response = client.get(
"/api/v1.0/templates/",
)
assert response.status_code == 200
results = response.json()["results"]
assert len(results) == 7
results_id = {result["id"] for result in results}
assert expected_ids == results_id
def test_api_templates_list_authenticated_via_team(mock_user_teams):
"""
Authenticated users should be able to list templates they are a
owner/administrator/member of via a team or that are public.
"""
user = factories.UserFactory()
client = APIClient()
client.force_login(user)
mock_user_teams.return_value = ["team1", "team2", "unknown"]
templates_team1 = [
access.template
for access in factories.TeamTemplateAccessFactory.create_batch(2, team="team1")
]
templates_team2 = [
access.template
for access in factories.TeamTemplateAccessFactory.create_batch(3, team="team2")
]
public_templates = factories.TemplateFactory.create_batch(2, is_public=True)
factories.TemplateFactory.create_batch(2, is_public=False)
expected_ids = {
str(template.id)
for template in templates_team1 + templates_team2 + public_templates
}
response = client.get("/api/v1.0/templates/")
assert response.status_code == 200
results = response.json()["results"]
assert len(results) == 7
results_id = {result["id"] for result in results}
assert expected_ids == results_id
@mock.patch.object(PageNumberPagination, "get_page_size", return_value=2)
def test_api_templates_list_pagination(
_mock_page_size,
):
"""Pagination should work as expected."""
user = factories.UserFactory()
client = APIClient()
client.force_login(user)
template_ids = [
str(access.template_id)
for access in factories.UserTemplateAccessFactory.create_batch(3, user=user)
]
# Get page 1
response = client.get(
"/api/v1.0/templates/",
)
assert response.status_code == 200
content = response.json()
assert content["count"] == 3
assert content["next"] == "http://testserver/api/v1.0/templates/?page=2"
assert content["previous"] is None
assert len(content["results"]) == 2
for item in content["results"]:
template_ids.remove(item["id"])
# Get page 2
response = client.get(
"/api/v1.0/templates/?page=2",
)
assert response.status_code == 200
content = response.json()
assert content["count"] == 3
assert content["next"] is None
assert content["previous"] == "http://testserver/api/v1.0/templates/"
assert len(content["results"]) == 1
template_ids.remove(content["results"][0]["id"])
assert template_ids == []
def test_api_templates_list_authenticated_distinct():
"""A template with several related users should only be listed once."""
user = factories.UserFactory()
client = APIClient()
client.force_login(user)
other_user = factories.UserFactory()
template = factories.TemplateFactory(users=[user, other_user], is_public=True)
response = client.get(
"/api/v1.0/templates/",
)
assert response.status_code == 200
content = response.json()
assert len(content["results"]) == 1
assert content["results"][0]["id"] == str(template.id)
def test_api_templates_list_order_default():
"""The templates list should be sorted by 'created_at' in descending order by default."""
user = factories.UserFactory()
client = APIClient()
client.force_login(user)
template_ids = [
str(access.template.id)
for access in factories.UserTemplateAccessFactory.create_batch(5, user=user)
]
response = client.get(
"/api/v1.0/templates/",
)
assert response.status_code == 200
response_data = response.json()
response_template_ids = [template["id"] for template in response_data["results"]]
template_ids.reverse()
assert response_template_ids == template_ids, (
"created_at values are not sorted from newest to oldest"
)
def test_api_templates_list_order_param():
"""
The templates list is sorted by 'created_at' in ascending order when setting
the "ordering" query parameter.
"""
user = factories.UserFactory()
client = APIClient()
client.force_login(user)
templates_ids = [
str(access.template.id)
for access in factories.UserTemplateAccessFactory.create_batch(5, user=user)
]
response = client.get(
"/api/v1.0/templates/?ordering=created_at",
)
assert response.status_code == 200
response_data = response.json()
response_template_ids = [template["id"] for template in response_data["results"]]
assert response_template_ids == templates_ids, (
"created_at values are not sorted from oldest to newest"
)
def test_api_template_throttling(settings):
"""Test api template throttling."""
current_rate = settings.REST_FRAMEWORK["DEFAULT_THROTTLE_RATES"]["template"]
settings.REST_FRAMEWORK["DEFAULT_THROTTLE_RATES"]["template"] = "2/minute"
client = APIClient()
for _i in range(2):
response = client.get("/api/v1.0/templates/")
assert response.status_code == 200
with mock.patch("core.api.throttling.capture_message") as mock_capture_message:
response = client.get("/api/v1.0/templates/")
assert response.status_code == 429
mock_capture_message.assert_called_once_with(
"Rate limit exceeded for scope template", "warning"
)
settings.REST_FRAMEWORK["DEFAULT_THROTTLE_RATES"]["template"] = current_rate

View File

@@ -1,522 +0,0 @@
"""
Tests for Templates API endpoint in impress's core app: retrieve
"""
import pytest
from rest_framework.test import APIClient
from core import factories
pytestmark = pytest.mark.django_db
def test_api_templates_retrieve_anonymous_public():
"""Anonymous users should be allowed to retrieve public templates."""
template = factories.TemplateFactory(is_public=True)
response = APIClient().get(f"/api/v1.0/templates/{template.id!s}/")
assert response.status_code == 200
assert response.json() == {
"id": str(template.id),
"abilities": {
"destroy": False,
"generate_document": True,
"accesses_manage": False,
"partial_update": False,
"retrieve": True,
"update": False,
},
"accesses": [],
"title": template.title,
"is_public": True,
"code": template.code,
"css": template.css,
}
def test_api_templates_retrieve_anonymous_not_public():
"""Anonymous users should not be able to retrieve a template that is not public."""
template = factories.TemplateFactory(is_public=False)
response = APIClient().get(f"/api/v1.0/templates/{template.id!s}/")
assert response.status_code == 401
assert response.json() == {
"detail": "Authentication credentials were not provided."
}
def test_api_templates_retrieve_authenticated_unrelated_public():
"""
Authenticated users should be able to retrieve a public template to which they are
not related.
"""
user = factories.UserFactory()
client = APIClient()
client.force_login(user)
template = factories.TemplateFactory(is_public=True)
response = client.get(
f"/api/v1.0/templates/{template.id!s}/",
)
assert response.status_code == 200
assert response.json() == {
"id": str(template.id),
"abilities": {
"destroy": False,
"generate_document": True,
"accesses_manage": False,
"partial_update": False,
"retrieve": True,
"update": False,
},
"accesses": [],
"title": template.title,
"is_public": True,
"code": template.code,
"css": template.css,
}
def test_api_templates_retrieve_authenticated_unrelated_not_public():
"""
Authenticated users should not be allowed to retrieve a template that is not public and
to which they are not related.
"""
user = factories.UserFactory()
client = APIClient()
client.force_login(user)
template = factories.TemplateFactory(is_public=False)
response = client.get(
f"/api/v1.0/templates/{template.id!s}/",
)
assert response.status_code == 403
assert response.json() == {
"detail": "You do not have permission to perform this action."
}
def test_api_templates_retrieve_authenticated_related_direct():
"""
Authenticated users should be allowed to retrieve a template to which they
are directly related whatever the role.
"""
user = factories.UserFactory()
client = APIClient()
client.force_login(user)
template = factories.TemplateFactory()
access1 = factories.UserTemplateAccessFactory(template=template, user=user)
access2 = factories.UserTemplateAccessFactory(template=template)
response = client.get(
f"/api/v1.0/templates/{template.id!s}/",
)
assert response.status_code == 200
content = response.json()
assert sorted(content.pop("accesses"), key=lambda x: x["user"]) == sorted(
[
{
"id": str(access1.id),
"user": str(user.id),
"team": "",
"role": access1.role,
"abilities": access1.get_abilities(user),
},
{
"id": str(access2.id),
"user": str(access2.user.id),
"team": "",
"role": access2.role,
"abilities": access2.get_abilities(user),
},
],
key=lambda x: x["user"],
)
assert response.json() == {
"id": str(template.id),
"title": template.title,
"abilities": template.get_abilities(user),
"is_public": template.is_public,
"code": template.code,
"css": template.css,
}
def test_api_templates_retrieve_authenticated_related_team_none(mock_user_teams):
"""
Authenticated users should not be able to retrieve a template related to teams in
which the user is not.
"""
mock_user_teams.return_value = []
user = factories.UserFactory()
client = APIClient()
client.force_login(user)
template = factories.TemplateFactory(is_public=False)
factories.TeamTemplateAccessFactory(
template=template, team="readers", role="reader"
)
factories.TeamTemplateAccessFactory(
template=template, team="editors", role="editor"
)
factories.TeamTemplateAccessFactory(
template=template, team="administrators", role="administrator"
)
factories.TeamTemplateAccessFactory(template=template, team="owners", role="owner")
factories.TeamTemplateAccessFactory(template=template)
factories.TeamTemplateAccessFactory()
response = client.get(f"/api/v1.0/templates/{template.id!s}/")
assert response.status_code == 403
assert response.json() == {
"detail": "You do not have permission to perform this action."
}
@pytest.mark.parametrize(
"teams",
[
["readers"],
["unknown", "readers"],
["editors"],
["unknown", "editors"],
],
)
def test_api_templates_retrieve_authenticated_related_team_readers_or_editors(
teams, mock_user_teams
):
"""
Authenticated users should be allowed to retrieve a template to which they
are related via a team whatever the role and see all its accesses.
"""
mock_user_teams.return_value = teams
user = factories.UserFactory()
client = APIClient()
client.force_login(user)
template = factories.TemplateFactory(is_public=False)
access_reader = factories.TeamTemplateAccessFactory(
template=template, team="readers", role="reader"
)
access_editor = factories.TeamTemplateAccessFactory(
template=template, team="editors", role="editor"
)
access_administrator = factories.TeamTemplateAccessFactory(
template=template, team="administrators", role="administrator"
)
access_owner = factories.TeamTemplateAccessFactory(
template=template, team="owners", role="owner"
)
other_access = factories.TeamTemplateAccessFactory(template=template)
factories.TeamTemplateAccessFactory()
response = client.get(f"/api/v1.0/templates/{template.id!s}/")
assert response.status_code == 200
content = response.json()
expected_abilities = {
"destroy": False,
"retrieve": True,
"set_role_to": [],
"update": False,
"partial_update": False,
}
assert sorted(content.pop("accesses"), key=lambda x: x["id"]) == sorted(
[
{
"id": str(access_reader.id),
"user": None,
"team": "readers",
"role": access_reader.role,
"abilities": expected_abilities,
},
{
"id": str(access_editor.id),
"user": None,
"team": "editors",
"role": access_editor.role,
"abilities": expected_abilities,
},
{
"id": str(access_administrator.id),
"user": None,
"team": "administrators",
"role": access_administrator.role,
"abilities": expected_abilities,
},
{
"id": str(access_owner.id),
"user": None,
"team": "owners",
"role": access_owner.role,
"abilities": expected_abilities,
},
{
"id": str(other_access.id),
"user": None,
"team": other_access.team,
"role": other_access.role,
"abilities": expected_abilities,
},
],
key=lambda x: x["id"],
)
assert response.json() == {
"id": str(template.id),
"title": template.title,
"abilities": template.get_abilities(user),
"is_public": False,
"code": template.code,
"css": template.css,
}
@pytest.mark.parametrize(
"teams",
[
["administrators"],
["members", "administrators"],
["unknown", "administrators"],
],
)
def test_api_templates_retrieve_authenticated_related_team_administrators(
teams, mock_user_teams
):
"""
Authenticated users should be allowed to retrieve a template to which they
are related via a team whatever the role and see all its accesses.
"""
mock_user_teams.return_value = teams
user = factories.UserFactory()
client = APIClient()
client.force_login(user)
template = factories.TemplateFactory(is_public=False)
access_reader = factories.TeamTemplateAccessFactory(
template=template, team="readers", role="reader"
)
access_editor = factories.TeamTemplateAccessFactory(
template=template, team="editors", role="editor"
)
access_administrator = factories.TeamTemplateAccessFactory(
template=template, team="administrators", role="administrator"
)
access_owner = factories.TeamTemplateAccessFactory(
template=template, team="owners", role="owner"
)
other_access = factories.TeamTemplateAccessFactory(template=template)
factories.TeamTemplateAccessFactory()
response = client.get(f"/api/v1.0/templates/{template.id!s}/")
assert response.status_code == 200
content = response.json()
assert sorted(content.pop("accesses"), key=lambda x: x["id"]) == sorted(
[
{
"id": str(access_reader.id),
"user": None,
"team": "readers",
"role": "reader",
"abilities": {
"destroy": True,
"retrieve": True,
"set_role_to": ["administrator", "editor"],
"update": True,
"partial_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,
"partial_update": True,
},
},
{
"id": str(access_administrator.id),
"user": None,
"team": "administrators",
"role": "administrator",
"abilities": {
"destroy": True,
"retrieve": True,
"set_role_to": ["editor", "reader"],
"update": True,
"partial_update": True,
},
},
{
"id": str(access_owner.id),
"user": None,
"team": "owners",
"role": "owner",
"abilities": {
"destroy": False,
"retrieve": True,
"set_role_to": [],
"update": False,
"partial_update": False,
},
},
{
"id": str(other_access.id),
"user": None,
"team": other_access.team,
"role": other_access.role,
"abilities": other_access.get_abilities(user),
},
],
key=lambda x: x["id"],
)
assert response.json() == {
"id": str(template.id),
"title": template.title,
"abilities": template.get_abilities(user),
"is_public": False,
"code": template.code,
"css": template.css,
}
@pytest.mark.parametrize(
"teams",
[
["owners"],
["owners", "administrators"],
["members", "administrators", "owners"],
["unknown", "owners"],
],
)
def test_api_templates_retrieve_authenticated_related_team_owners(
teams, mock_user_teams
):
"""
Authenticated users should be allowed to retrieve a template to which they
are related via a team whatever the role and see all its accesses.
"""
mock_user_teams.return_value = teams
user = factories.UserFactory()
client = APIClient()
client.force_login(user)
template = factories.TemplateFactory(is_public=False)
access_reader = factories.TeamTemplateAccessFactory(
template=template, team="readers", role="reader"
)
access_editor = factories.TeamTemplateAccessFactory(
template=template, team="editors", role="editor"
)
access_administrator = factories.TeamTemplateAccessFactory(
template=template, team="administrators", role="administrator"
)
access_owner = factories.TeamTemplateAccessFactory(
template=template, team="owners", role="owner"
)
other_access = factories.TeamTemplateAccessFactory(template=template)
factories.TeamTemplateAccessFactory()
response = client.get(f"/api/v1.0/templates/{template.id!s}/")
assert response.status_code == 200
content = response.json()
assert sorted(content.pop("accesses"), key=lambda x: x["id"]) == sorted(
[
{
"id": str(access_reader.id),
"user": None,
"team": "readers",
"role": "reader",
"abilities": {
"destroy": True,
"retrieve": True,
"set_role_to": ["owner", "administrator", "editor"],
"update": True,
"partial_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,
"partial_update": True,
},
},
{
"id": str(access_administrator.id),
"user": None,
"team": "administrators",
"role": "administrator",
"abilities": {
"destroy": True,
"retrieve": True,
"set_role_to": ["owner", "editor", "reader"],
"update": True,
"partial_update": True,
},
},
{
"id": str(access_owner.id),
"user": None,
"team": "owners",
"role": "owner",
"abilities": {
# editable only if there is another owner role than the user's team...
"destroy": other_access.role == "owner",
"retrieve": True,
"set_role_to": ["administrator", "editor", "reader"]
if other_access.role == "owner"
else [],
"update": other_access.role == "owner",
"partial_update": other_access.role == "owner",
},
},
{
"id": str(other_access.id),
"user": None,
"team": other_access.team,
"role": other_access.role,
"abilities": other_access.get_abilities(user),
},
],
key=lambda x: x["id"],
)
assert response.json() == {
"id": str(template.id),
"title": template.title,
"abilities": template.get_abilities(user),
"is_public": False,
"code": template.code,
"css": template.css,
}

View File

@@ -1,54 +0,0 @@
"""
Tests for Templates API endpoint in impress's core app: update
"""
import pytest
from rest_framework.test import APIClient
from core import factories
from core.api import serializers
pytestmark = pytest.mark.django_db
def test_api_templates_update_anonymous():
"""Anonymous users should not be allowed to update a template."""
template = factories.TemplateFactory()
new_template_values = serializers.TemplateSerializer(
instance=factories.TemplateFactory()
).data
response = APIClient().put(
f"/api/v1.0/templates/{template.id!s}/",
new_template_values,
format="json",
)
assert response.status_code == 401
def test_api_templates_update_not_implemented():
"""
Authenticated users should not be allowed to update a template.
"""
user = factories.UserFactory()
client = APIClient()
client.force_login(user)
template = factories.TemplateFactory(users=[(user, "owner")])
new_template_values = serializers.TemplateSerializer(
instance=factories.TemplateFactory()
).data
response = client.put(
f"/api/v1.0/templates/{template.id!s}/", new_template_values, format="json"
)
assert response.status_code == 405
response = client.patch(
f"/api/v1.0/templates/{template.id!s}/", new_template_values, format="json"
)
assert response.status_code == 405

View File

@@ -1,419 +0,0 @@
"""
Unit tests for the TemplateAccess model
"""
from django.contrib.auth.models import AnonymousUser
from django.core.exceptions import ValidationError
import pytest
from core import factories
pytestmark = pytest.mark.django_db
def test_models_template_accesses_str():
"""
The str representation should include user email, template title and role.
"""
user = factories.UserFactory(email="david.bowman@example.com")
access = factories.UserTemplateAccessFactory(
role="reader",
user=user,
template__title="admins",
)
assert str(access) == "david.bowman@example.com is reader in template admins"
def test_models_template_accesses_unique_user():
"""Template accesses should be unique for a given couple of user and template."""
access = factories.UserTemplateAccessFactory()
with pytest.raises(
ValidationError,
match="This user is already in this template.",
):
factories.UserTemplateAccessFactory(user=access.user, template=access.template)
def test_models_template_accesses_several_empty_teams():
"""A template can have several template accesses with an empty team."""
access = factories.UserTemplateAccessFactory()
factories.UserTemplateAccessFactory(template=access.template)
def test_models_template_accesses_unique_team():
"""Template accesses should be unique for a given couple of team and template."""
access = factories.TeamTemplateAccessFactory()
with pytest.raises(
ValidationError,
match="This team is already in this template.",
):
factories.TeamTemplateAccessFactory(team=access.team, template=access.template)
def test_models_template_accesses_several_null_users():
"""A template can have several template accesses with a null user."""
access = factories.TeamTemplateAccessFactory()
factories.TeamTemplateAccessFactory(template=access.template)
def test_models_template_accesses_user_and_team_set():
"""User and team can't both be set on a template access."""
with pytest.raises(
ValidationError,
match="Either user or team must be set, not both.",
):
factories.UserTemplateAccessFactory(team="my-team")
def test_models_template_accesses_user_and_team_empty():
"""User and team can't both be empty on a template access."""
with pytest.raises(
ValidationError,
match="Either user or team must be set, not both.",
):
factories.UserTemplateAccessFactory(user=None)
# get_abilities
def test_models_template_access_get_abilities_anonymous():
"""Check abilities returned for an anonymous user."""
access = factories.UserTemplateAccessFactory()
abilities = access.get_abilities(AnonymousUser())
assert abilities == {
"destroy": False,
"retrieve": False,
"update": False,
"partial_update": False,
"set_role_to": [],
}
def test_models_template_access_get_abilities_authenticated():
"""Check abilities returned for an authenticated user."""
access = factories.UserTemplateAccessFactory()
user = factories.UserFactory()
abilities = access.get_abilities(user)
assert abilities == {
"destroy": False,
"retrieve": False,
"update": False,
"partial_update": False,
"set_role_to": [],
}
# - for owner
def test_models_template_access_get_abilities_for_owner_of_self_allowed():
"""
Check abilities of self access for the owner of a template when
there is more than one owner left.
"""
access = factories.UserTemplateAccessFactory(role="owner")
factories.UserTemplateAccessFactory(template=access.template, role="owner")
abilities = access.get_abilities(access.user)
assert abilities == {
"destroy": True,
"retrieve": True,
"update": True,
"partial_update": True,
"set_role_to": ["administrator", "editor", "reader"],
}
def test_models_template_access_get_abilities_for_owner_of_self_last():
"""
Check abilities of self access for the owner of a template when there is only one owner left.
"""
access = factories.UserTemplateAccessFactory(role="owner")
abilities = access.get_abilities(access.user)
assert abilities == {
"destroy": False,
"retrieve": True,
"update": False,
"partial_update": False,
"set_role_to": [],
}
def test_models_template_access_get_abilities_for_owner_of_owner():
"""Check abilities of owner access for the owner of a template."""
access = factories.UserTemplateAccessFactory(role="owner")
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,
"partial_update": True,
"set_role_to": ["administrator", "editor", "reader"],
}
def test_models_template_access_get_abilities_for_owner_of_administrator():
"""Check abilities of administrator access for the owner of a template."""
access = factories.UserTemplateAccessFactory(role="administrator")
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,
"partial_update": True,
"set_role_to": ["owner", "editor", "reader"],
}
def test_models_template_access_get_abilities_for_owner_of_editor():
"""Check abilities of editor access for the owner of a template."""
access = factories.UserTemplateAccessFactory(role="editor")
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,
"partial_update": True,
"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,
"partial_update": True,
"set_role_to": ["owner", "administrator", "editor"],
}
# - for administrator
def test_models_template_access_get_abilities_for_administrator_of_owner():
"""Check abilities of owner access for the administrator of a template."""
access = factories.UserTemplateAccessFactory(role="owner")
factories.UserTemplateAccessFactory(template=access.template) # another one
user = factories.UserTemplateAccessFactory(
template=access.template, role="administrator"
).user
abilities = access.get_abilities(user)
assert abilities == {
"destroy": False,
"retrieve": True,
"update": False,
"partial_update": False,
"set_role_to": [],
}
def test_models_template_access_get_abilities_for_administrator_of_administrator():
"""Check abilities of administrator access for the administrator of a template."""
access = factories.UserTemplateAccessFactory(role="administrator")
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,
"partial_update": True,
"set_role_to": ["editor", "reader"],
}
def test_models_template_access_get_abilities_for_administrator_of_editor():
"""Check abilities of editor access for the administrator of a template."""
access = factories.UserTemplateAccessFactory(role="editor")
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,
"partial_update": True,
"set_role_to": ["administrator", "reader"],
}
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,
"partial_update": True,
"set_role_to": ["administrator", "editor"],
}
# - For editor
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")
factories.UserTemplateAccessFactory(template=access.template) # another one
user = factories.UserTemplateAccessFactory(
template=access.template, role="editor"
).user
abilities = access.get_abilities(user)
assert abilities == {
"destroy": False,
"retrieve": True,
"update": False,
"partial_update": False,
"set_role_to": [],
}
def test_models_template_access_get_abilities_for_editor_of_administrator():
"""Check abilities of administrator access for the editor of a template."""
access = factories.UserTemplateAccessFactory(role="administrator")
factories.UserTemplateAccessFactory(template=access.template) # another one
user = factories.UserTemplateAccessFactory(
template=access.template, role="editor"
).user
abilities = access.get_abilities(user)
assert abilities == {
"destroy": False,
"retrieve": True,
"update": False,
"partial_update": False,
"set_role_to": [],
}
def test_models_template_access_get_abilities_for_editor_of_editor_user(
django_assert_num_queries,
):
"""Check abilities of editor access for the editor of a template."""
access = factories.UserTemplateAccessFactory(role="editor")
factories.UserTemplateAccessFactory(template=access.template) # another one
user = factories.UserTemplateAccessFactory(
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,
"partial_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,
"partial_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,
"partial_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
with django_assert_num_queries(1):
abilities = access.get_abilities(user)
assert abilities == {
"destroy": False,
"retrieve": True,
"update": False,
"partial_update": False,
"set_role_to": [],
}
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."""
access = factories.UserTemplateAccessFactory(role="reader")
user = factories.UserTemplateAccessFactory(
template=access.template, role="reader"
).user
access.user_roles = ["reader"]
with django_assert_num_queries(0):
abilities = access.get_abilities(user)
assert abilities == {
"destroy": False,
"retrieve": True,
"update": False,
"partial_update": False,
"set_role_to": [],
}

View File

@@ -1,187 +0,0 @@
"""
Unit tests for the Template model
"""
from django.contrib.auth.models import AnonymousUser
from django.core.exceptions import ValidationError
import pytest
from core import factories, models
pytestmark = pytest.mark.django_db
def test_models_templates_str():
"""The str representation should be the title of the template."""
template = factories.TemplateFactory(title="admins")
assert str(template) == "admins"
def test_models_templates_id_unique():
"""The "id" field should be unique."""
template = factories.TemplateFactory()
with pytest.raises(ValidationError, match="Template with this Id already exists."):
factories.TemplateFactory(id=template.id)
def test_models_templates_title_null():
"""The "title" field should not be null."""
with pytest.raises(ValidationError, match="This field cannot be null."):
models.Template.objects.create(title=None)
def test_models_templates_title_empty():
"""The "title" field should not be empty."""
with pytest.raises(ValidationError, match="This field cannot be blank."):
models.Template.objects.create(title="")
def test_models_templates_title_max_length():
"""The "title" field should be 100 characters maximum."""
factories.TemplateFactory(title="a" * 255)
with pytest.raises(
ValidationError,
match=r"Ensure this value has at most 255 characters \(it has 256\)\.",
):
factories.TemplateFactory(title="a" * 256)
# get_abilities
def test_models_templates_get_abilities_anonymous_public():
"""Check abilities returned for an anonymous user if the template is public."""
template = factories.TemplateFactory(is_public=True)
abilities = template.get_abilities(AnonymousUser())
assert abilities == {
"destroy": False,
"retrieve": True,
"update": False,
"accesses_manage": False,
"partial_update": False,
"generate_document": True,
}
def test_models_templates_get_abilities_anonymous_not_public():
"""Check abilities returned for an anonymous user if the template is private."""
template = factories.TemplateFactory(is_public=False)
abilities = template.get_abilities(AnonymousUser())
assert abilities == {
"destroy": False,
"retrieve": False,
"update": False,
"accesses_manage": False,
"partial_update": False,
"generate_document": False,
}
def test_models_templates_get_abilities_authenticated_public():
"""Check abilities returned for an authenticated user if the user is public."""
template = factories.TemplateFactory(is_public=True)
abilities = template.get_abilities(factories.UserFactory())
assert abilities == {
"destroy": False,
"retrieve": True,
"update": False,
"accesses_manage": False,
"partial_update": False,
"generate_document": True,
}
def test_models_templates_get_abilities_authenticated_not_public():
"""Check abilities returned for an authenticated user if the template is private."""
template = factories.TemplateFactory(is_public=False)
abilities = template.get_abilities(factories.UserFactory())
assert abilities == {
"destroy": False,
"retrieve": False,
"update": False,
"accesses_manage": False,
"partial_update": False,
"generate_document": False,
}
def test_models_templates_get_abilities_owner():
"""Check abilities returned for the owner of a template."""
user = factories.UserFactory()
access = factories.UserTemplateAccessFactory(role="owner", user=user)
abilities = access.template.get_abilities(access.user)
assert abilities == {
"destroy": True,
"retrieve": True,
"update": True,
"accesses_manage": True,
"partial_update": True,
"generate_document": True,
}
def test_models_templates_get_abilities_administrator():
"""Check abilities returned for the administrator of a template."""
access = factories.UserTemplateAccessFactory(role="administrator")
abilities = access.template.get_abilities(access.user)
assert abilities == {
"destroy": False,
"retrieve": True,
"update": True,
"accesses_manage": True,
"partial_update": True,
"generate_document": True,
}
def test_models_templates_get_abilities_editor_user(django_assert_num_queries):
"""Check abilities returned for the editor of a template."""
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,
"accesses_manage": 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):
abilities = access.template.get_abilities(access.user)
assert abilities == {
"destroy": False,
"retrieve": True,
"update": False,
"accesses_manage": False,
"partial_update": False,
"generate_document": True,
}
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."""
access = factories.UserTemplateAccessFactory(role="reader")
access.template.user_roles = ["reader"]
with django_assert_num_queries(0):
abilities = access.template.get_abilities(access.user)
assert abilities == {
"destroy": False,
"retrieve": True,
"update": False,
"accesses_manage": False,
"partial_update": False,
"generate_document": True,
}

View File

@@ -10,7 +10,6 @@ from core.api import viewsets
# - Main endpoints
router = DefaultRouter()
router.register("templates", viewsets.TemplateViewSet, basename="templates")
router.register("documents", viewsets.DocumentViewSet, basename="documents")
router.register("users", viewsets.UserViewSet, basename="users")

View File

@@ -1,2 +0,0 @@
<img width="200" src="http://localhost:3000/assets/logo-gouv.png" />
<br/>

View File

@@ -216,29 +216,6 @@ def create_demo(stdout):
queue.flush()
with Timeit(stdout, "Creating Template"):
with open(
file="demo/data/template/code.txt", mode="r", encoding="utf-8"
) as text_file:
code_data = text_file.read()
with open(
file="demo/data/template/css.txt", mode="r", encoding="utf-8"
) as text_file:
css_data = text_file.read()
queue.push(
models.Template(
id="baca9e2a-59fb-42ef-b5c6-6f6b05637111",
title="Demo Template",
description="This is the demo template",
code=code_data,
css=css_data,
is_public=True,
)
)
queue.flush()
class Command(BaseCommand):
"""A management command to create a demo database."""

View File

@@ -25,7 +25,6 @@ def test_commands_create_demo():
"""The create_demo management command should create objects as expected."""
call_command("create_demo")
assert models.Template.objects.count() == 1
assert models.User.objects.count() >= 10
assert models.Document.objects.count() >= 10
assert models.DocumentAccess.objects.count() > 10

View File

@@ -406,16 +406,6 @@ class Base(Configuration):
environ_name="API_DOCUMENT_ACCESS_THROTTLE_RATE",
environ_prefix=None,
),
"template": values.Value(
default="30/minute",
environ_name="API_TEMPLATE_THROTTLE_RATE",
environ_prefix=None,
),
"template_access": values.Value(
default="30/minute",
environ_name="API_TEMPLATE_ACCESS_THROTTLE_RATE",
environ_prefix=None,
),
"invitation": values.Value(
default="60/minute",
environ_name="API_INVITATION_THROTTLE_RATE",

View File

@@ -19,7 +19,7 @@ classifiers = [
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.12",
]
description = "An application to print markdown to pdf from a set of managed templates."
description = "Docs is a collaborative text editor designed to address common challenges in knowledge building and sharing."
keywords = ["Django", "Contacts", "Templates", "RBAC"]
license = { file = "LICENSE" }
readme = "README.md"