Skip to content

Commit 492b16f

Browse files
committed
🐛(backend) fix computed_link_role computation
By default a document is restricted, but with inheritance it can be authenticated.
1 parent 0cf8b9d commit 492b16f

File tree

4 files changed

+54
-15
lines changed

4 files changed

+54
-15
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ and this project adheres to
2222

2323
- 🐛(makefile) Windows compatibility fix for Docker volume mounting #1264
2424
- 🐛(minio) fix user permission error with Minio and Windows #1264
25+
- 🐛link role update #1287
2526

2627

2728
## [3.5.0] - 2025-07-31

src/backend/core/choices.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ def get_equivalent_link_definition(ancestors_links):
8888
"""
8989
Return the (reach, role) pair with:
9090
1. Highest reach
91-
2. Highest role among links having that reach
91+
2. Highest role among links having that reach, or from restricted links when max_reach is higher
9292
"""
9393
if not ancestors_links:
9494
return {"link_reach": None, "link_role": None}
@@ -99,17 +99,17 @@ def get_equivalent_link_definition(ancestors_links):
9999
key=lambda link: LinkReachChoices.get_priority(link["link_reach"]),
100100
)["link_reach"]
101101

102-
# 2) Among those, find the highest role (ignore role if RESTRICTED)
102+
# 2) Among those, find the highest role
103103
if max_reach == LinkReachChoices.RESTRICTED:
104104
max_role = None
105105
else:
106-
max_role = max(
107-
(
108-
link["link_role"]
109-
for link in ancestors_links
110-
if link["link_reach"] == max_reach
111-
),
112-
key=LinkRoleChoices.get_priority,
113-
)
106+
eligible_roles = [
107+
link["link_role"]
108+
for link in ancestors_links
109+
if (link["link_reach"] == max_reach or
110+
link["link_reach"] == LinkReachChoices.RESTRICTED)
111+
]
112+
113+
max_role = max(eligible_roles, key=LinkRoleChoices.get_priority) if eligible_roles else None
114114

115115
return {"link_reach": max_reach, "link_role": max_role}

src/backend/core/tests/documents/test_api_documents_link_configuration.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,3 +158,39 @@ def test_api_documents_link_configuration_update_authenticated_related_success(
158158
document_values = serializers.LinkDocumentSerializer(instance=document).data
159159
for key, value in document_values.items():
160160
assert value == new_document_values[key]
161+
162+
def test_api_documents_link_configuration_update_role_is_computed():
163+
"""
164+
Test that the computed link_role is updated correctly when the link_role changes.
165+
"""
166+
user = factories.UserFactory()
167+
client = APIClient()
168+
client.force_login(user)
169+
170+
parent_document = factories.DocumentFactory(
171+
link_reach=models.LinkReachChoices.AUTHENTICATED,
172+
link_role=models.LinkRoleChoices.READER,
173+
)
174+
175+
factories.UserDocumentAccessFactory(
176+
document=parent_document, user=user, role=models.RoleChoices.OWNER
177+
)
178+
179+
child_document = factories.DocumentFactory(
180+
parent=parent_document,
181+
link_reach=models.Document._meta.get_field('link_reach').default,
182+
link_role=models.Document._meta.get_field('link_role').default,
183+
)
184+
185+
new_data = {"link_role": models.LinkRoleChoices.EDITOR}
186+
187+
response = client.put(
188+
f"/api/v1.0/documents/{child_document.id!s}/link-configuration/",
189+
new_data,
190+
format="json",
191+
)
192+
193+
assert response.status_code == 200
194+
195+
child_document = models.Document.objects.get(pk=child_document.pk)
196+
assert child_document.computed_link_role == models.LinkRoleChoices.EDITOR

src/frontend/apps/e2e/__tests__/app-impress/doc-inherited-share.spec.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ test.describe('Inherited share accesses', () => {
5252
await expect(docVisibilityCard.getByText('Connected')).toBeVisible();
5353
await expect(docVisibilityCard.getByText('Reading')).toBeVisible();
5454

55+
await docVisibilityCard.getByText('Reading').click();
56+
await page.getByRole('menuitem', { name: 'Editing' }).click();
57+
58+
await expect(docVisibilityCard.getByText('Reading')).toBeHidden();
59+
await expect(docVisibilityCard.getByText('Editing')).toBeVisible();
60+
5561
// Verify inherited link
5662
await docVisibilityCard.getByText('Connected').click();
5763
await expect(
@@ -61,17 +67,13 @@ test.describe('Inherited share accesses', () => {
6167
// Update child link
6268
await page.getByRole('menuitem', { name: 'Public' }).click();
6369

64-
await docVisibilityCard.getByText('Reading').click();
65-
await page.getByRole('menuitem', { name: 'Editing' }).click();
66-
6770
await expect(docVisibilityCard.getByText('Connected')).toBeHidden();
68-
await expect(docVisibilityCard.getByText('Reading')).toBeHidden();
6971
await expect(
7072
docVisibilityCard.getByText('Public', {
7173
exact: true,
7274
}),
7375
).toBeVisible();
74-
await expect(docVisibilityCard.getByText('Editing')).toBeVisible();
76+
7577
await expect(
7678
docVisibilityCard.getByText(
7779
'The link sharing rules differ from the parent document',

0 commit comments

Comments
 (0)