(backend) retrieve & update a document taking into account ancestors

A document should inherit the access rights a user has on any of its
ancestors.
This commit is contained in:
Samuel Paccoud - DINUM
2024-12-17 07:47:23 +01:00
committed by Anthony LC
parent 48662ceecb
commit d073a9c9b3
7 changed files with 544 additions and 124 deletions

View File

@@ -18,6 +18,10 @@ from django.core.files.base import ContentFile
from django.core.files.storage import default_storage
from django.core.mail import send_mail
from django.db import models
from django.db.models.functions import Left, Length
from django.http import FileResponse
from django.template.base import Template as DjangoTemplate
from django.template.context import Context
from django.template.loader import render_to_string
from django.utils import timezone
from django.utils.functional import cached_property, lazy
@@ -31,23 +35,6 @@ from treebeard.mp_tree import MP_Node
logger = getLogger(__name__)
def get_resource_roles(resource, user):
"""Compute the roles a user has on a resource."""
if not user.is_authenticated:
return []
try:
roles = resource.user_roles or []
except AttributeError:
try:
roles = resource.accesses.filter(
models.Q(user=user) | models.Q(team__in=user.teams),
).values_list("role", flat=True)
except (models.ObjectDoesNotExist, IndexError):
roles = []
return roles
class LinkRoleChoices(models.TextChoices):
"""Defines the possible roles a link can offer on a document."""
@@ -539,11 +526,30 @@ class Document(MP_Node, BaseModel):
Bucket=default_storage.bucket_name, Key=self.file_key, VersionId=version_id
)
def get_roles(self, user):
"""Return the roles a user has on a document."""
if not user.is_authenticated:
return []
try:
roles = self.user_roles or []
except AttributeError:
try:
roles = DocumentAccess.objects.filter(
models.Q(user=user) | models.Q(team__in=user.teams),
document__path=Left(
models.Value(self.path), Length("document__path")
),
).values_list("role", flat=True)
except (models.ObjectDoesNotExist, IndexError):
roles = []
return roles
def get_abilities(self, user):
"""
Compute and return abilities for a given user on the document.
"""
roles = set(get_resource_roles(self, user))
roles = set(self.get_roles(user))
# Compute version roles before adding link roles because we don't
# want anonymous users to access versions (we wouldn't know from
@@ -551,11 +557,20 @@ class Document(MP_Node, BaseModel):
# Anonymous users should also not see document accesses
has_role = bool(roles)
# Add role provided by the document link
if self.link_reach == LinkReachChoices.PUBLIC or (
self.link_reach == LinkReachChoices.AUTHENTICATED and user.is_authenticated
):
roles.add(self.link_role)
# Add roles provided by the document link, taking into account its ancestors
link_reaches = list(self.get_ancestors().values("link_reach", "link_role"))
link_reaches.append(
{"link_reach": self.link_reach, "link_role": self.link_role}
)
for lr in link_reaches:
if lr["link_reach"] == LinkReachChoices.PUBLIC:
roles.add(lr["link_role"])
if user.is_authenticated:
for lr in link_reaches:
if lr["link_reach"] == LinkReachChoices.AUTHENTICATED:
roles.add(lr["link_role"])
is_owner_or_admin = bool(
roles.intersection({RoleChoices.OWNER, RoleChoices.ADMIN})
@@ -772,11 +787,27 @@ class Template(BaseModel):
def __str__(self):
return self.title
def get_roles(self, user):
"""Return the roles a user has on a resource."""
if not user.is_authenticated:
return []
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 roles
def get_abilities(self, user):
"""
Compute and return abilities for a given user on the template.
"""
roles = get_resource_roles(self, user)
roles = self.get_roles(user)
is_owner_or_admin = bool(
set(roles).intersection({RoleChoices.OWNER, RoleChoices.ADMIN})
)