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

Closed
wants to merge 23 commits into from
Closed
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
7347329
🐛(backend) fix link definition select options linked to ancestors
sampaccoud Apr 6, 2025
402d007
✨(backend) add ancestors links definitions to document abilities
sampaccoud Apr 6, 2025
a5cb105
♻️(backend) remove different reach for authenticated and anonymous
sampaccoud Apr 11, 2025
c717f7a
♻️(backend) refactor resource access viewset
sampaccoud Apr 12, 2025
5008916
♻️(backend) factorize document query set annotation
sampaccoud Apr 12, 2025
a17332c
✨(backend) we want to display ancestors accesses on a document share
sampaccoud Apr 12, 2025
2baba11
✨(backend) give an order to choices
sampaccoud Apr 23, 2025
1f7a3d8
♻️(backend) refactor get_select_options to take definitions dict
sampaccoud Apr 24, 2025
561398e
✅(backend) fix randomly failing test on user search
sampaccoud May 2, 2025
42197bc
♻️(backend) simplify roles by returning only the max role
sampaccoud Apr 25, 2025
70328a7
✨(backend) add ancestors link reach and role to document API
sampaccoud Apr 28, 2025
fd424a0
✨(backend) add computed link reach and role to document API
sampaccoud Apr 28, 2025
859f4ad
♻️(backend) optimize refactoring access abilities and fix inheritance
sampaccoud May 2, 2025
68b8e52
✨(backend) add max ancestors role field to document access endpoint
sampaccoud May 2, 2025
619ef90
♻️(backend) stop requiring owner for non-root documents
sampaccoud May 4, 2025
5021eac
✅(backend) fix randomly failing test due to delay before check
sampaccoud May 4, 2025
ca0ac7e
🐛(backend) allow creating accesses when privileged by heritage
sampaccoud May 6, 2025
21ef3cd
✨(backend) add document path and depth to accesses endpoint
sampaccoud May 7, 2025
2966323
🐛(backend) fix creating/updating document accesses for teams
sampaccoud May 7, 2025
c24066d
♻️(backend) simplify further select options on link reach/role
sampaccoud May 9, 2025
1c501aa
✨(backend) add max_role field to the document access API endpoint
sampaccoud May 13, 2025
b193a6e
🚸(backend) validate document access roles on submit
sampaccoud May 26, 2025
f3244c3
✨(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 @@ -85,6 +85,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 @@ -96,12 +98,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