Skip to content

Commit 49826e3

Browse files
fix(auth): port testing from helix-admin (#32)
* chore(test): auth (wip) * fix(auth): cover and fix testing
1 parent 34fbf9a commit 49826e3

File tree

9 files changed

+2154
-24
lines changed

9 files changed

+2154
-24
lines changed

src/auth/support.js

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -209,11 +209,11 @@ export async function detectTokenIDP(token) {
209209
* Returns the site auth token if the project is configured as such.
210210
*
211211
* @param {import('../support/AdminContext').AdminContext} context context
212-
* @param {import('../support/RequestInfo').RequestInfo} info request info
212+
* @param {string} partition partition
213213
* @returns {Promise<string|null>} the auth token
214214
*/
215-
export async function getSiteAuthToken(context, info) {
216-
const accessConfig = await context.getSiteAccessConfig(info.partition);
215+
export async function getSiteAuthToken(context, partition) {
216+
const accessConfig = await context.getSiteAccessConfig(partition);
217217

218218
if (!accessConfig.allow.length
219219
&& !accessConfig.apiKeyId.length
@@ -399,12 +399,17 @@ export async function getAuthInfo(context, info) {
399399
// return AuthInfo.Default();
400400
// }
401401

402-
const { attributes: { config } } = context;
403-
let apiKeyId = config?.data?.admin?.apiKeyId || [];
404-
if (!Array.isArray(apiKeyId)) {
405-
apiKeyId = [apiKeyId];
402+
const { attributes: { config, orgConfig } } = context;
403+
let siteApiKeyId = config?.access?.admin?.apiKeyId || [];
404+
if (!Array.isArray(siteApiKeyId)) {
405+
siteApiKeyId = [siteApiKeyId];
406+
}
407+
let orgApiKeyId = orgConfig?.access?.admin?.apiKeyId || [];
408+
if (!Array.isArray(orgApiKeyId)) {
409+
orgApiKeyId = [orgApiKeyId];
406410
}
407-
if (apiKeyId.indexOf(profile.jti) < 0) {
411+
if (siteApiKeyId.indexOf(profile.jti) < 0 && orgApiKeyId.indexOf(profile.jti) < 0) {
412+
const apiKeyId = [...siteApiKeyId, ...orgApiKeyId];
408413
log.warn(`auth: api token invalid: jti ${profile.jti} does not match configured id [${apiKeyId}] in ${info.org}/${info.site || '*'}`);
409414
return AuthInfo.Default();
410415
}

src/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ export const router = new Router(nameSelector)
7070
.add('/:org/profiles/:profile/versions', notImplemented)
7171
.add('/:org/sites', notImplemented)
7272
.add('/:org/sites/:site/status/*', status)
73+
.add('/:org/sites/:site/config', notImplemented)
7374
.add('/:org/sites/:site/config/da', notImplemented)
7475
.add('/:org/sites/:site/config/sidekick', notImplemented)
7576
.add('/:org/sites/:site/config/access', notImplemented)

src/support/AdminContext.js

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,12 @@ export class AdminContext {
7373
}
7474

7575
/**
76-
* Loads the site configuration.
76+
* Loads the site and org configuration.
7777
*
7878
* @param {import('./RequestInfo.js').RequestInfo} info info
7979
* @returns {Promise<object>} configuration
8080
*/
81-
async getConfig(info) {
81+
async loadConfig(info) {
8282
if (this.attributes.config === undefined) {
8383
const { org, site } = info;
8484
if (org && site) {
@@ -91,18 +91,10 @@ export class AdminContext {
9191
this.attributes.config = config;
9292
}
9393
}
94-
return this.attributes.config;
95-
}
96-
97-
async getOrgConfig(info) {
9894
if (this.attributes.orgConfig === undefined) {
9995
const { org } = info;
10096
if (org) {
101-
const config = await loadOrgConfig(this, org);
102-
if (config === null) {
103-
throw new StatusCodeError('', 404);
104-
}
105-
this.attributes.orgConfig = config;
97+
this.attributes.orgConfig = await loadOrgConfig(this, org);
10698
}
10799
}
108100
return this.attributes.orgConfig;
@@ -131,7 +123,7 @@ export class AdminContext {
131123
async authenticate(info) {
132124
const { attributes } = this;
133125

134-
await this.getConfig(info);
126+
await this.loadConfig(info);
135127

136128
if (attributes.authInfo === undefined) {
137129
attributes.authInfo = await getAuthInfo(this, info);
@@ -146,9 +138,6 @@ export class AdminContext {
146138
* @returns {Promise<void>}
147139
*/
148140
async authorize(info) {
149-
// eslint-disable-next-line no-unused-vars
150-
const orgConfig = await this.getOrgConfig(info);
151-
152141
return authorize(this, info);
153142
}
154143

test/auth/auth-info.test.js

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright 2025 Adobe. All rights reserved.
3+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License. You may obtain a copy
5+
* of the License at https://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software distributed under
8+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9+
* OF ANY KIND, either express or implied. See the License for the specific language
10+
* governing permissions and limitations under the License.
11+
*/
12+
13+
/* eslint-env mocha */
14+
import assert from 'assert';
15+
import { AccessDeniedError } from '../../src/auth/AccessDeniedError.js';
16+
import { AuthInfo } from '../../src/auth/auth-info.js';
17+
18+
describe('AuthInfo Test', () => {
19+
it('auth info can assert for permissions', async () => {
20+
const authInfo = new AuthInfo()
21+
.withRole('publish');
22+
authInfo.assertPermissions('live:read');
23+
assert.throws(() => authInfo.assertPermissions('system:exit'), new AccessDeniedError('system:exit'));
24+
25+
authInfo.assertAnyPermission('live:read', 'system:exit');
26+
assert.throws(() => authInfo.assertAnyPermission('system:exit', 'config:read'), new AccessDeniedError('system:exit or config:read'));
27+
});
28+
29+
it('auth info can filter permissions', async () => {
30+
const authInfo = new AuthInfo()
31+
.withRole('admin');
32+
assert.deepStrictEqual(authInfo.getPermissions('live:'), ['delete', 'delete-forced', 'list', 'read', 'write']);
33+
});
34+
35+
it('auth info can remove permissions', async () => {
36+
const authInfo = new AuthInfo()
37+
.withRole('publish')
38+
.removePermissions('live:read', 'preview:read');
39+
40+
assert.deepStrictEqual(authInfo.getPermissions(), [
41+
'cache:write',
42+
'code:delete',
43+
'code:read',
44+
'code:write',
45+
'cron:read',
46+
'cron:write',
47+
'discover:peek',
48+
'edit:list',
49+
'edit:read',
50+
'index:read',
51+
'index:write',
52+
'job:list',
53+
'job:read',
54+
'job:write',
55+
'live:delete',
56+
'live:delete-forced',
57+
'live:list',
58+
'live:write',
59+
'log:read',
60+
'preview:delete',
61+
'preview:delete-forced',
62+
'preview:list',
63+
'preview:write',
64+
'snapshot:delete',
65+
'snapshot:read',
66+
'snapshot:write',
67+
]);
68+
});
69+
});

0 commit comments

Comments
 (0)