fix: use verbatim resource slugs in group names (matches access-request tests + live convention)
The group naming inserts a kind segment (resourceGroupCns(site, kind, slug, level)),
but the access-request tests + the live directory convention are verbatim
({site}_{slug}_{level} -- the kind is carried in the resource slug, e.g. host_theta-env).
For bare test slugs this produced site_x_host_artest-host_x_access instead of the
expected site_x_artest-host_x_access, so the requester was never removed from the
auto-provisioned access group and every request 409'd. resourceGroupCns is now
(site, slug, level) with the verbatim slug; the kind is used only to pick the
aggregate the group nests into.
This commit is contained in:
+20
-25
@@ -12,13 +12,12 @@ const {
|
||||
GOD_ADMIN,
|
||||
} = require('../utils/groups');
|
||||
|
||||
// Resource fixtures use already-normalized slugs (the group-model builders treat
|
||||
// the site + resource slugs as opaque, not re-slugified -- see groups.js). Raw
|
||||
// display names like "Main Office" are normalized by the resource model before
|
||||
// they reach the resolver.
|
||||
const HOST = { site: 'main-office', kind: 'host', slug: 'web-01' };
|
||||
// Resource fixtures mirror the directory's real slugs: hosts carry a `host_`
|
||||
// prefix, services/apps are stored bare. The group-model builders use these
|
||||
// verbatim (no re-slugifying, no kind insertion) -- see groups.js.
|
||||
const HOST = { site: 'main-office', kind: 'host', slug: 'host_web-01' };
|
||||
const APP = { site: 'main-office', kind: 'app', slug: 'emby' };
|
||||
const OTHER_SITE_HOST = { site: 'branch-office', kind: 'host', slug: 'db' };
|
||||
const OTHER_SITE_HOST = { site: 'branch-office', kind: 'host', slug: 'host_db' };
|
||||
|
||||
describe('slugify', () => {
|
||||
test('lowercases, spaces and underscores become hyphens, no leading/trailing dash', () => {
|
||||
@@ -28,18 +27,14 @@ describe('slugify', () => {
|
||||
expect(slugify(' Mixed CASE--name ')).toBe('mixed-case-name');
|
||||
expect(slugify('')).toBe('');
|
||||
});
|
||||
test('never contains an underscore (the structural delimiter)', () => {
|
||||
expect(slugify('a_b_c')).not.toContain('_');
|
||||
expect(resourceGroupCns('Main Office', 'host', 'Web 01', 'access')).not.toContain('__');
|
||||
});
|
||||
});
|
||||
|
||||
describe('group cn builders', () => {
|
||||
test('per-resource uses singular kind', () => {
|
||||
expect(resourceGroupCns('main-office', 'host', 'web-01', 'admin')).toBe('main-office_host_web-01_admin');
|
||||
expect(resourceGroupCns('main-office', 'app', 'emby', 'access')).toBe('main-office_app_emby_access');
|
||||
test('per-resource uses the resource slug verbatim (kind is carried in the slug)', () => {
|
||||
expect(resourceGroupCns('main-office', 'host_web-01', 'admin')).toBe('main-office_host_web-01_admin');
|
||||
expect(resourceGroupCns('main-office', 'emby', 'access')).toBe('main-office_emby_access');
|
||||
});
|
||||
test('aggregate uses plural kind', () => {
|
||||
test('aggregate uses the plural kind', () => {
|
||||
expect(aggregateGroupCns('main-office', 'host', 'admin')).toBe('main-office_hosts_admin');
|
||||
expect(aggregateGroupCns('main-office', 'app', 'access')).toBe('main-office_apps_access');
|
||||
});
|
||||
@@ -53,10 +48,10 @@ describe('group cn builders', () => {
|
||||
expect(siteSuperAdminCns('site_local')).toBe('site_local_super_admin');
|
||||
expect(siteEveryoneCns('site_local')).toBe('site_local_everyone');
|
||||
expect(aggregateGroupCns('site_local', 'host', 'admin')).toBe('site_local_hosts_admin');
|
||||
expect(resourceGroupCns('site_local', 'host', 'theta-env', 'access')).toBe('site_local_host_theta-env_access');
|
||||
expect(resourceGroupCns('site_local', 'host_theta-env', 'access')).toBe('site_local_host_theta-env_access');
|
||||
});
|
||||
test('invalid kind throws', () => {
|
||||
expect(() => resourceGroupCns('s', 'service', 'x', 'admin')).toThrow();
|
||||
test('invalid kind throws (aggregates only — per-resource has no kind arg)', () => {
|
||||
expect(() => aggregateGroupCns('s', 'service', 'admin')).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -94,32 +89,32 @@ describe('hasPermission — inheritance', () => {
|
||||
});
|
||||
|
||||
test('specific host group grants only that host', () => {
|
||||
const cn = resourceGroupCns('main-office', 'host', 'web-01', 'admin');
|
||||
const cn = resourceGroupCns('main-office', 'host_web-01', 'admin');
|
||||
expect(hasPermission([cn], HOST, 'admin')).toBe(true);
|
||||
expect(hasPermission([cn], OTHER_SITE_HOST, 'admin')).toBe(false);
|
||||
});
|
||||
|
||||
test('admin implies access; access does not imply admin', () => {
|
||||
expect(hasPermission([resourceGroupCns('main-office', 'host', 'web-01', 'admin')], HOST, 'access')).toBe(true);
|
||||
expect(hasPermission([resourceGroupCns('main-office', 'host', 'web-01', 'access')], HOST, 'admin')).toBe(false);
|
||||
expect(hasPermission([resourceGroupCns('main-office', 'host_web-01', 'admin')], HOST, 'access')).toBe(true);
|
||||
expect(hasPermission([resourceGroupCns('main-office', 'host_web-01', 'access')], HOST, 'admin')).toBe(false);
|
||||
});
|
||||
|
||||
test('capabilities are exact — admin does not grant a capability', () => {
|
||||
expect(hasPermission([resourceGroupCns('main-office', 'host', 'web-01', 'reboot')], HOST, 'reboot')).toBe(true);
|
||||
expect(hasPermission([resourceGroupCns('main-office', 'host', 'web-01', 'admin')], HOST, 'reboot')).toBe(false);
|
||||
expect(hasPermission([resourceGroupCns('main-office', 'host_web-01', 'reboot')], HOST, 'reboot')).toBe(true);
|
||||
expect(hasPermission([resourceGroupCns('main-office', 'host_web-01', 'admin')], HOST, 'reboot')).toBe(false);
|
||||
// aggregate capability
|
||||
expect(hasPermission(['main-office_hosts_reboot'], HOST, 'reboot')).toBe(true);
|
||||
});
|
||||
|
||||
test('hosts and apps are orthogonal namespaces', () => {
|
||||
const hostAdmin = resourceGroupCns('main-office', 'host', 'web-01', 'admin');
|
||||
const hostAdmin = resourceGroupCns('main-office', 'host_web-01', 'admin');
|
||||
expect(hasPermission([hostAdmin], APP, 'access')).toBe(false);
|
||||
const appAdmin = resourceGroupCns('main-office', 'app', 'emby', 'admin');
|
||||
const appAdmin = resourceGroupCns('main-office', 'emby', 'admin');
|
||||
expect(hasPermission([appAdmin], APP, 'access')).toBe(true);
|
||||
});
|
||||
|
||||
test('cross-site isolation', () => {
|
||||
const mainHostAdmin = resourceGroupCns('main-office', 'host', 'web-01', 'admin');
|
||||
const mainHostAdmin = resourceGroupCns('main-office', 'host_web-01', 'admin');
|
||||
expect(hasPermission([mainHostAdmin], OTHER_SITE_HOST, 'access')).toBe(false);
|
||||
expect(hasPermission(['branch-office_hosts_admin'], OTHER_SITE_HOST, 'admin')).toBe(true);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user