Skip to content

Commit 70aa160

Browse files
committed
test(e2e): add personas permissions scenario for policy workbench
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
1 parent 85cc5f7 commit 70aa160

1 file changed

Lines changed: 210 additions & 0 deletions

File tree

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
/**
2+
* SPDX-FileCopyrightText: 2026 LibreCode coop and contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
import { expect, request, test, type APIRequestContext } from '@playwright/test'
7+
import {
8+
ensureGroupExists,
9+
ensureSubadminOfGroup,
10+
ensureUserExists,
11+
ensureUserInGroup,
12+
} from '../support/nc-provisioning'
13+
14+
test.describe.configure({ retries: 0, timeout: 90000 })
15+
16+
const ADMIN_USER = process.env.NEXTCLOUD_ADMIN_USER ?? 'admin'
17+
const ADMIN_PASSWORD = process.env.NEXTCLOUD_ADMIN_PASSWORD ?? 'admin'
18+
const DEFAULT_TEST_PASSWORD = '123456'
19+
20+
const GROUP_ID = 'policy-e2e-group'
21+
const GROUP_ADMIN_USER = 'policy-e2e-group-admin'
22+
const END_USER = 'policy-e2e-end-user'
23+
const POLICY_KEY = 'signature_flow'
24+
25+
type OcsPolicyResponse = {
26+
ocs?: {
27+
meta?: {
28+
statuscode?: number
29+
message?: string
30+
}
31+
data?: Record<string, unknown>
32+
}
33+
}
34+
35+
async function policyRequest(
36+
requestContext: APIRequestContext,
37+
method: 'GET' | 'POST' | 'PUT' | 'DELETE',
38+
path: string,
39+
body?: Record<string, unknown>,
40+
) {
41+
const requestUrl = `./ocs/v2.php${path}`
42+
const requestOptions = {
43+
data: body,
44+
failOnStatusCode: false,
45+
}
46+
47+
const response = method === 'GET'
48+
? await requestContext.get(requestUrl, requestOptions)
49+
: method === 'POST'
50+
? await requestContext.post(requestUrl, requestOptions)
51+
: method === 'PUT'
52+
? await requestContext.put(requestUrl, requestOptions)
53+
: await requestContext.delete(requestUrl, requestOptions)
54+
55+
const text = await response.text()
56+
const parsed = text ? JSON.parse(text) as OcsPolicyResponse : { ocs: { data: {} } }
57+
58+
return {
59+
httpStatus: response.status(),
60+
statusCode: parsed.ocs?.meta?.statuscode ?? response.status(),
61+
message: parsed.ocs?.meta?.message ?? '',
62+
data: parsed.ocs?.data ?? {},
63+
}
64+
}
65+
66+
async function getEffectivePolicy(
67+
requestContext: APIRequestContext,
68+
) {
69+
const result = await policyRequest(requestContext, 'GET', `/apps/libresign/api/v1/policies/effective`)
70+
const policies = (result.data.policies ?? {}) as Record<string, {
71+
effectiveValue?: unknown
72+
editableByCurrentActor?: boolean
73+
canSaveAsUserDefault?: boolean
74+
sourceScope?: string
75+
allowedValues?: unknown[]
76+
}>
77+
78+
return policies[POLICY_KEY] ?? null
79+
}
80+
81+
async function clearOwnUserPreference(
82+
requestContext: APIRequestContext,
83+
) {
84+
const result = await policyRequest(requestContext, 'DELETE', `/apps/libresign/api/v1/policies/user/${POLICY_KEY}`)
85+
expect([200, 500]).toContain(result.httpStatus)
86+
}
87+
88+
async function createAuthenticatedRequestContext(authUser: string, authPassword: string): Promise<APIRequestContext> {
89+
const auth = 'Basic ' + Buffer.from(`${authUser}:${authPassword}`).toString('base64')
90+
91+
return request.newContext({
92+
baseURL: process.env.PLAYWRIGHT_BASE_URL ?? 'https://localhost',
93+
ignoreHTTPSErrors: true,
94+
extraHTTPHeaders: {
95+
'OCS-ApiRequest': 'true',
96+
Accept: 'application/json',
97+
Authorization: auth,
98+
'Content-Type': 'application/json',
99+
},
100+
})
101+
}
102+
103+
test('personas can manage policies according to permissions and override toggles', async ({ page }) => {
104+
await ensureUserExists(page.request, GROUP_ADMIN_USER, DEFAULT_TEST_PASSWORD)
105+
await ensureUserExists(page.request, END_USER, DEFAULT_TEST_PASSWORD)
106+
await ensureGroupExists(page.request, GROUP_ID)
107+
await ensureUserInGroup(page.request, GROUP_ADMIN_USER, GROUP_ID)
108+
await ensureUserInGroup(page.request, END_USER, GROUP_ID)
109+
await ensureSubadminOfGroup(page.request, GROUP_ADMIN_USER, GROUP_ID)
110+
111+
const adminRequest = await createAuthenticatedRequestContext(ADMIN_USER, ADMIN_PASSWORD)
112+
const groupAdminRequest = await createAuthenticatedRequestContext(GROUP_ADMIN_USER, DEFAULT_TEST_PASSWORD)
113+
const endUserRequest = await createAuthenticatedRequestContext(END_USER, DEFAULT_TEST_PASSWORD)
114+
115+
try {
116+
117+
// Normalize user-level state before assertions.
118+
await clearOwnUserPreference(groupAdminRequest)
119+
await clearOwnUserPreference(endUserRequest)
120+
121+
// Global admin defines baseline and group policy with override enabled.
122+
let result = await policyRequest(
123+
adminRequest,
124+
'POST',
125+
`/apps/libresign/api/v1/policies/system/${POLICY_KEY}`,
126+
{ value: 'parallel', allowChildOverride: true },
127+
)
128+
expect(result.httpStatus).toBe(200)
129+
130+
result = await policyRequest(
131+
adminRequest,
132+
'PUT',
133+
`/apps/libresign/api/v1/policies/group/${GROUP_ID}/${POLICY_KEY}`,
134+
{ value: 'ordered_numeric', allowChildOverride: true },
135+
)
136+
expect(result.httpStatus).toBe(200)
137+
138+
// Group admin can edit own group rule.
139+
result = await policyRequest(
140+
groupAdminRequest,
141+
'PUT',
142+
`/apps/libresign/api/v1/policies/group/${GROUP_ID}/${POLICY_KEY}`,
143+
{ value: 'ordered_numeric', allowChildOverride: false },
144+
)
145+
expect(result.httpStatus).toBe(200)
146+
147+
const groupPolicyReadback = await policyRequest(
148+
groupAdminRequest,
149+
'GET',
150+
`/apps/libresign/api/v1/policies/group/${GROUP_ID}/${POLICY_KEY}`,
151+
)
152+
expect(groupPolicyReadback.httpStatus).toBe(200)
153+
expect(groupPolicyReadback.data?.policy).toMatchObject({
154+
targetId: GROUP_ID,
155+
policyKey: POLICY_KEY,
156+
value: 'ordered_numeric',
157+
allowChildOverride: false,
158+
})
159+
160+
// End user cannot manage group policy and cannot save user preference while group blocks lower layers.
161+
result = await policyRequest(
162+
endUserRequest,
163+
'PUT',
164+
`/apps/libresign/api/v1/policies/group/${GROUP_ID}/${POLICY_KEY}`,
165+
{ value: 'parallel', allowChildOverride: true },
166+
)
167+
expect(result.httpStatus).toBe(403)
168+
169+
result = await policyRequest(
170+
endUserRequest,
171+
'PUT',
172+
`/apps/libresign/api/v1/policies/user/${POLICY_KEY}`,
173+
{ value: 'parallel' },
174+
)
175+
expect(result.httpStatus).toBe(400)
176+
177+
let endUserEffective = await getEffectivePolicy(endUserRequest)
178+
expect(endUserEffective?.effectiveValue).toBe('ordered_numeric')
179+
expect(endUserEffective?.canSaveAsUserDefault).toBe(false)
180+
181+
// Group admin enables lower-layer overrides again.
182+
result = await policyRequest(
183+
groupAdminRequest,
184+
'PUT',
185+
`/apps/libresign/api/v1/policies/group/${GROUP_ID}/${POLICY_KEY}`,
186+
{ value: 'ordered_numeric', allowChildOverride: true },
187+
)
188+
expect(result.httpStatus).toBe(200)
189+
190+
// End user can now save personal preference and it becomes effective.
191+
result = await policyRequest(
192+
endUserRequest,
193+
'PUT',
194+
`/apps/libresign/api/v1/policies/user/${POLICY_KEY}`,
195+
{ value: 'parallel' },
196+
)
197+
expect(result.httpStatus).toBe(200)
198+
199+
endUserEffective = await getEffectivePolicy(endUserRequest)
200+
expect(endUserEffective?.effectiveValue).toBe('parallel')
201+
expect(endUserEffective?.sourceScope).toBe('user')
202+
expect(endUserEffective?.canSaveAsUserDefault).toBe(true)
203+
} finally {
204+
await Promise.all([
205+
adminRequest.dispose(),
206+
groupAdminRequest.dispose(),
207+
endUserRequest.dispose(),
208+
])
209+
}
210+
})

0 commit comments

Comments
 (0)