Skip to content

Fix link reach/role and add ancestors link access info #846

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 23 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
1a085a4
🐛(backend) fix link definition select options linked to ancestors
sampaccoud Apr 6, 2025
ea5e7be
✨(backend) add ancestors links definitions to document abilities
sampaccoud Apr 6, 2025
e419064
♻️(backend) remove different reach for authenticated and anonymous
sampaccoud Apr 11, 2025
6e7fe57
♻️(backend) refactor resource access viewset
sampaccoud Apr 12, 2025
54fb9fe
♻️(backend) factorize document query set annotation
sampaccoud Apr 12, 2025
83ff572
✨(backend) we want to display ancestors accesses on a document share
sampaccoud Apr 12, 2025
7e17656
✨(backend) give an order to choices
sampaccoud Apr 23, 2025
b4df306
♻️(backend) refactor get_select_options to take definitions dict
sampaccoud Apr 24, 2025
050cfa7
✅(backend) fix randomly failing test on user search
sampaccoud May 2, 2025
c29a65a
♻️(backend) simplify roles by returning only the max role
sampaccoud Apr 25, 2025
7b8e853
✨(backend) add ancestors link reach and role to document API
sampaccoud Apr 28, 2025
67cee4b
✨(backend) add computed link reach and role to document API
sampaccoud Apr 28, 2025
d8ab990
♻️(backend) optimize refactoring access abilities and fix inheritance
sampaccoud May 2, 2025
016de21
✨(backend) add max ancestors role field to document access endpoint
sampaccoud May 2, 2025
70f1602
♻️(backend) stop requiring owner for non-root documents
sampaccoud May 4, 2025
8b78f4d
✅(backend) fix randomly failing test due to delay before check
sampaccoud May 4, 2025
22d9757
🐛(backend) allow creating accesses when privileged by heritage
sampaccoud May 6, 2025
b242f53
✨(backend) add document path and depth to accesses endpoint
sampaccoud May 7, 2025
e08245d
🐛(backend) fix creating/updating document accesses for teams
sampaccoud May 7, 2025
d8f1d87
♻️(backend) simplify further select options on link reach/role
sampaccoud May 9, 2025
2733c13
✨(backend) add max_role field to the document access API endpoint
sampaccoud May 13, 2025
09757e2
🚸(backend) validate document access roles on submit
sampaccoud May 26, 2025
5546f86
✨(backend) add child_set_role_to field to document access abilities
sampaccoud May 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ and this project adheres to

## Added

- ✨(backend) include ancestors accesses on document accesses list view #846
- ✨(backend) add ancestors links reach and role to document API #846
- 🚸(backend) make document search on title accent-insensitive #874
- 🚩 add homepage feature flag #861
- 📝(doc) update contributing policy (commit signatures are now mandatory) #895
Expand All @@ -84,12 +86,15 @@ and this project adheres to

## Changed

- ♻️(backend) stop requiring owner for non-root documents #846
- ♻️(backend) simplify roles by ranking them and return only the max role #846
- ⚡️(frontend) reduce unblocking time for config #867
- ♻️(frontend) bind UI with ability access #900
- ♻️(frontend) use built-in Quote block #908

## Fixed

- 🐛(backend) fix link definition select options linked to ancestors #846
- 🐛(nginx) fix 404 when accessing a doc #866
- 🔒️(drf) disable browsable HTML API renderer #919
- 🔒(frontend) enhance file download security #889
Expand Down
78 changes: 54 additions & 24 deletions src/backend/core/api/permissions.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
"""Permission handlers for the impress core app."""

from django.core import exceptions
from django.db.models import Q
from django.http import Http404

from rest_framework import permissions

from core.models import DocumentAccess, RoleChoices, get_trashbin_cutoff
from core import choices
from core.models import RoleChoices, get_trashbin_cutoff

ACTION_FOR_METHOD_TO_PERMISSION = {
"versions_detail": {"DELETE": "versions_destroy", "GET": "versions_retrieve"},
Expand Down Expand Up @@ -80,42 +80,37 @@ def has_permission(self, request, view):
if view.action != "create":
return True

# Check if resource_id is passed in the context
try:
document_id = view.kwargs["resource_id"]
except KeyError as exc:
raise exceptions.ValidationError(
"You must set a document ID in kwargs to manage document invitations."
) from exc

# Check if the user has access to manage invitations (Owner/Admin roles)
return DocumentAccess.objects.filter(
Q(user=user) | Q(team__in=user.teams),
document=document_id,
role__in=[RoleChoices.OWNER, RoleChoices.ADMIN],
).exists()
role = view.document.get_role(user)
if role not in choices.PRIVILEGED_ROLES:
raise exceptions.PermissionDenied(
"You are not allowed to create invitations for this document."
)

return True


class AccessPermission(permissions.BasePermission):
"""Permission class for access objects."""
class ResourceWithAccessPermission(permissions.BasePermission):
"""A permission class for templates and invitations."""

def has_permission(self, request, view):
"""check create permission for templates."""
return request.user.is_authenticated or view.action != "create"

def has_object_permission(self, request, view, obj):
"""Check permission for a given object."""
abilities = obj.get_abilities(request.user)
action = view.action
try:
action = ACTION_FOR_METHOD_TO_PERMISSION[view.action][request.method]
except KeyError:
pass
return abilities.get(action, False)


class DocumentAccessPermission(AccessPermission):
class DocumentPermission(permissions.BasePermission):
"""Subclass to handle soft deletion specificities."""

def has_permission(self, request, view):
"""check create permission for documents."""
return request.user.is_authenticated or view.action != "create"

def has_object_permission(self, request, view, obj):
"""
Return a 404 on deleted documents
Expand All @@ -127,10 +122,45 @@ def has_object_permission(self, request, view, obj):
) and deleted_at < get_trashbin_cutoff():
raise Http404

# Compute permission first to ensure the "user_roles" attribute is set
has_permission = super().has_object_permission(request, view, obj)
abilities = obj.get_abilities(request.user)
action = view.action
try:
action = ACTION_FOR_METHOD_TO_PERMISSION[view.action][request.method]
except KeyError:
pass

has_permission = abilities.get(action, False)

if obj.ancestors_deleted_at and not RoleChoices.OWNER in obj.user_roles:
raise Http404

return has_permission


class ResourceAccessPermission(IsAuthenticated):
"""Permission class for document access objects."""

def has_permission(self, request, view):
"""check create permission for accesses in documents tree."""
if super().has_permission(request, view) is False:
return False

if view.action == "create":
role = getattr(view, view.resource_field_name).get_role(request.user)
if role not in choices.PRIVILEGED_ROLES:
raise exceptions.PermissionDenied(
"You are not allowed to manage accesses for this resource."
)

return True

def has_object_permission(self, request, view, obj):
"""Check permission for a given object."""
abilities = obj.get_abilities(request.user)

requested_role = request.data.get("role")
if requested_role and requested_role not in abilities.get("set_role_to", []):
return False

action = view.action
return abilities.get(action, False)
Loading
Loading