Files
Oxicloud/tests/load/scenarios/subject_group_nested.js
T

74 lines
3.2 KiB
JavaScript
Raw Normal View History

// subject_group_nested.js — measures the worst-case AuthZ path: a user who
// is a direct member of the *innermost* group of a depth-N nested chain, and
// the grant is on the *outermost* group. Every AuthZ check has to expand the
// chain transitively. Pairs with share_cascade_rebac.js to attribute regressions
// to either the resource side (folder cascade) or the subject side (group
// expansion).
//
// Same depth-gradient idea as share_cascade_rebac: measure AuthZ cost at
// depth 1 / 4 / 8 / deep so we can see WHERE in the chain a regression
// lands. The subject-side expansion (group_member → leaf → mid → root group
// → grant) runs once per request regardless of folder depth, but the
// resource-side cascade (folder ancestors walked to find the grant) grows
// linearly with depth — so depth4/depth8/deep moving together while depth1
// stays flat would point at the resource side; all four moving together
// would point at the group-expansion path.
import { check } from 'k6';
import http from 'k6/http';
import { BASE, authParams } from '../lib/http.js';
import { login } from '../lib/auth.js';
import { thresholdsFromBaseline, loadManifest } from '../lib/metrics.js';
const manifest = loadManifest();
export const options = {
vus: 1,
// See folder_cascade.js for the iteration-count rationale: N=100 makes
// p99 a real percentile instead of the worst-of-the-batch.
iterations: 100,
thresholds: thresholdsFromBaseline('subject_group_nested'),
};
export function setup() {
const memberToken = login(manifest.group_member.username, manifest.group_member.password);
return { memberToken };
}
export default function (data) {
const { memberToken } = data;
const t = manifest.group_subtree;
// Baseline: group member fetches the folder the grant is directly on.
// Subject-side expansion runs (user → leaf → mid → root group → grant);
// resource-side walk is zero ancestors. Captures the constant subject-
// expansion overhead.
const d1 = http.get(
`${BASE}/api/folders/${t.root}/resources?resource_types=folder`,
authParams(memberToken, 'subject_group_nested.fetch_as_member_depth1'),
);
check(d1, { 'fetch root 200': (r) => r.status === 200 });
// Mid-tree: 4 folder ancestors walked + subject expansion.
const d4 = http.get(
`${BASE}/api/folders/${t.depth4}/resources?resource_types=folder`,
authParams(memberToken, 'subject_group_nested.fetch_as_member_depth4'),
);
check(d4, { 'fetch depth4 200': (r) => r.status === 200 });
// 8 folder ancestors walked + subject expansion.
const d8 = http.get(
`${BASE}/api/folders/${t.depth8}/resources?resource_types=folder`,
authParams(memberToken, 'subject_group_nested.fetch_as_member_depth8'),
);
check(d8, { 'fetch depth8 200': (r) => r.status === 200 });
// Worst case: full-depth folder cascade × full-chain subject expansion.
// group_member → leaf group → mid group → root group → grant → folder root → deepest.
const dD = http.get(
`${BASE}/api/folders/${t.deepest}/resources?resource_types=folder`,
authParams(memberToken, 'subject_group_nested.fetch_as_member_depth_deep'),
);
check(dD, { 'fetch deepest 200': (r) => r.status === 200 });
}