feat(auth): akamai auth integration (#8199)

This commit is contained in:
shubh-bruno
2026-07-03 13:31:28 +05:30
committed by GitHub
parent 876a4c4cd3
commit efd61bf682
58 changed files with 2782 additions and 30 deletions

View File

@@ -0,0 +1,154 @@
import { expect, test } from '../../../playwright';
import {
closeAllCollections,
createCollection,
createFolder,
createRequest,
getResponseBody,
openFolderRequest,
openRequest,
selectAuthMode,
selectRequestPaneTab,
sendRequestAndWaitForResponse
} from '../../utils/page';
/**
* EdgeGrid is configurable at collection settings, folder settings, and request level. These
* tests verify each renders without crashing (collection-level was a `null` crash regression),
* persists all 8 fields across a save, and that a request which inherits the auth actually
* signs and is accepted by the local simulator — asserting the echoed effective values.
*/
const { TEST_EDGEGRID } = require('../../../packages/bruno-tests/src/auth/edgegrid');
const SIMULATOR_URL = 'http://localhost:8081/api/auth/edgegrid/resource';
const label = (page, text: string) => page.locator('label').filter({ hasText: text });
const fieldRow = (page, text: string) => label(page, text).locator('..');
const editorIn = (row) => row.locator('.single-line-editor-wrapper .CodeMirror');
const advancedHeader = (page) => page.locator('.advanced-settings-header').filter({ hasText: 'Advanced Settings' });
const fieldValue = (page, fieldName: string): Promise<string> =>
editorIn(fieldRow(page, fieldName)).evaluate((el: any) => el.CodeMirror.getValue());
const expectFieldValue = async (page, fieldName: string, expected: string) => {
await expect.poll(() => fieldValue(page, fieldName)).toBe(expected);
};
const setField = async (page, fieldName: string, value: string) => {
await editorIn(fieldRow(page, fieldName)).click();
await page.keyboard.press('ControlOrMeta+a');
await page.keyboard.type(value);
};
const saveSettings = async (page) => {
await page.getByRole('button', { name: 'Save' }).click();
};
const BASIC_FIELDS = ['Access Token', 'Client Token', 'Client Secret'];
const ADVANCED_FIELDS = ['Base URL', 'Nonce', 'Timestamp', 'Headers to Sign', 'Max Body Size'];
// Valid creds + simulator host so an inheriting request validates; a distinct nonce per level
// so the echoed response proves which level's auth was used.
const valuesFor = (nonce: string): Record<string, string> => ({
'Access Token': TEST_EDGEGRID.accessToken,
'Client Token': TEST_EDGEGRID.clientToken,
'Client Secret': TEST_EDGEGRID.clientSecret,
'Base URL': 'http://localhost:8081',
'Nonce': nonce,
'Timestamp': '20240101T00:00:00+0000',
'Headers to Sign': 'X-Sign',
'Max Body Size': '4096'
});
// Advanced Settings is always open, so all fields are visible without expanding.
const fillAllFields = async (page, values: Record<string, string>) => {
for (const name of [...BASIC_FIELDS, ...ADVANCED_FIELDS]) await setField(page, name, values[name]);
};
const verifyAllFields = async (page, values: Record<string, string>) => {
for (const name of [...BASIC_FIELDS, ...ADVANCED_FIELDS]) await expectFieldValue(page, name, values[name]);
};
const expectInheritedEdgeGridSignature = async (page, expectedNonce: string) => {
await sendRequestAndWaitForResponse(page, 200);
const body = await getResponseBody(page);
expect(body).toContain(expectedNonce); // the inherited level's configured nonce was used
expect(body).toContain('20240101T00:00:00+0000');
expect(body).toContain('localhost:8081');
};
test.describe('Akamai EdgeGrid Authentication — collection, folder & inherit', () => {
test.afterEach(async ({ page }) => {
await closeAllCollections(page);
});
test('Collection level: renders, persists, and an inheriting request signs', async ({ page, createTmpDir }) => {
test.setTimeout(120_000);
const values = valuesFor('collection-nonce');
await createCollection(page, 'edgegrid-collection', await createTmpDir());
// Collection settings open automatically on creation.
await page.locator('.tab.auth').click();
await selectAuthMode(page, 'Akamai EdgeGrid');
await test.step('EdgeGrid renders without crashing (regression: null edgegrid)', async () => {
for (const name of BASIC_FIELDS) await expect(label(page, name)).toBeVisible();
await expect(advancedHeader(page)).toBeVisible();
});
await test.step('Fill all fields and save', async () => {
await fillAllFields(page, values);
await saveSettings(page);
});
await test.step('Switching away and back restores the saved EdgeGrid credentials', async () => {
await selectAuthMode(page, 'Basic Auth');
await selectAuthMode(page, 'Akamai EdgeGrid');
await verifyAllFields(page, values);
});
await test.step('A request set to Inherit shows + signs with the collection EdgeGrid auth', async () => {
await createRequest(page, 'inherit-request', 'edgegrid-collection', { url: SIMULATOR_URL });
await openRequest(page, 'edgegrid-collection', 'inherit-request');
await selectRequestPaneTab(page, 'Auth');
await selectAuthMode(page, 'Inherit');
await expect(page.locator('.inherit-mode-text')).toHaveText('Akamai EdgeGrid');
await expectInheritedEdgeGridSignature(page, 'collection-nonce');
});
});
test('Folder level: renders, persists, and an inheriting request signs', async ({ page, createTmpDir }) => {
test.setTimeout(120_000);
const values = valuesFor('folder-nonce');
await createCollection(page, 'edgegrid-folder', await createTmpDir());
await createFolder(page, 'folder-1', 'edgegrid-folder', true);
await page.locator('.collection-item-name').filter({ hasText: 'folder-1' }).dblclick();
await page.locator('.tab.auth').click();
await selectAuthMode(page, 'Akamai EdgeGrid');
await test.step('EdgeGrid renders at folder level', async () => {
for (const name of BASIC_FIELDS) await expect(label(page, name)).toBeVisible();
await expect(advancedHeader(page)).toBeVisible();
});
await test.step('Fill all fields and save', async () => {
await fillAllFields(page, values);
await saveSettings(page);
});
await test.step('Switching away and back restores the saved folder EdgeGrid credentials', async () => {
await selectAuthMode(page, 'Basic Auth');
await selectAuthMode(page, 'Akamai EdgeGrid');
await verifyAllFields(page, values);
});
await test.step('A request inside the folder set to Inherit signs with the folder EdgeGrid auth', async () => {
await createRequest(page, 'folder-inherit-request', 'folder-1', { url: SIMULATOR_URL, inFolder: true });
await openFolderRequest(page, 'folder-1', 'folder-inherit-request');
await selectRequestPaneTab(page, 'Auth');
await selectAuthMode(page, 'Inherit');
await expect(page.locator('.inherit-mode-text')).toHaveText('Akamai EdgeGrid');
await expectInheritedEdgeGridSignature(page, 'folder-nonce');
});
});
});

View File

@@ -0,0 +1,60 @@
import { test } from '../../../playwright';
import {
closeAllCollections,
openCollection,
openRequest,
selectEnvironment,
sendRequestAndWaitForResponse
} from '../../utils/page';
/**
* End-to-end EdgeGrid (EG1-HMAC-SHA256) auth against the local simulator
* (packages/bruno-tests/src/auth/edgegrid.js, mounted at /api/auth/edgegrid). The simulator
* independently re-derives the signature and returns 200/401 — so a green run proves the full
* UI → prepare-request → interceptor → wire → server-validation path, with no real Akamai API.
*/
const BRU_COLLECTION = 'edgegrid-testbench-bru';
const YML_COLLECTION = 'edgegrid-testbench-yml';
const requests = [
{ name: 'EdgeGrid GET 200', status: 200 },
{ name: 'EdgeGrid GET 401', status: 401 },
{ name: 'EdgeGrid POST JSON 200', status: 200 },
{ name: 'EdgeGrid POST Pretty JSON 200', status: 200 }, // regression: body hashed as-sent
{ name: 'EdgeGrid Signed Headers 200', status: 200 }, // regression: headers_to_sign canonicalization
{ name: 'EdgeGrid Base URL Override 200', status: 200 },
{ name: 'EdgeGrid Inherit 200', status: 200 } // request inherits collection-level EdgeGrid auth
];
const sendAllRequests = async (page, collectionName: string) => {
await openCollection(page, collectionName);
await selectEnvironment(page, 'Local', 'collection');
for (const { name, status } of requests) {
await test.step(name, async () => {
await openRequest(page, collectionName, name);
await sendRequestAndWaitForResponse(page, status);
});
}
};
test.describe('Akamai EdgeGrid Runner', () => {
test.afterAll(async ({ pageWithUserData: page }) => {
await closeAllCollections(page);
});
test.describe('[bru]', () => {
test('Send individual requests', async ({ pageWithUserData: page }) => {
test.setTimeout(3 * 60 * 1000);
await sendAllRequests(page, BRU_COLLECTION);
});
});
test.describe('[yml]', () => {
test('Send individual requests', async ({ pageWithUserData: page }) => {
test.setTimeout(3 * 60 * 1000);
await sendAllRequests(page, YML_COLLECTION);
});
});
});

View File

@@ -0,0 +1,136 @@
import { expect, test } from '../../../playwright';
import {
closeAllCollections,
closeAllTabs,
createCollection,
createRequest,
getResponseBody,
openRequest,
saveRequest,
selectRequestPaneTab,
sendRequestAndWaitForResponse
} from '../../utils/page';
// Shared test creds — the local simulator (packages/bruno-tests/src/auth/edgegrid.js) knows
// this secret, so signed requests validate (200) and the response echoes the effective values.
const { TEST_EDGEGRID } = require('../../../packages/bruno-tests/src/auth/edgegrid');
const SIMULATOR_URL = 'http://localhost:8081/api/auth/edgegrid/resource';
const label = (page, text: string) => page.locator('label').filter({ hasText: text });
const fieldRow = (page, text: string) => label(page, text).locator('..');
const editorIn = (row) => row.locator('.single-line-editor-wrapper .CodeMirror');
const advancedHeader = (page) => page.locator('.advanced-settings-header').filter({ hasText: 'Advanced Settings' });
const fieldValue = (page, fieldName: string): Promise<string> =>
editorIn(fieldRow(page, fieldName)).evaluate((el: any) => el.CodeMirror.getValue());
const expectFieldValue = async (page, fieldName: string, expected: string) => {
await expect.poll(() => fieldValue(page, fieldName)).toBe(expected);
};
// Select-all then type so it works for empty fields and the prefilled Base URL alike.
const setField = async (page, fieldName: string, value: string) => {
await editorIn(fieldRow(page, fieldName)).click();
await page.keyboard.press('ControlOrMeta+a');
await page.keyboard.type(value);
};
const selectEdgeGridMode = async (page) => {
await page.locator('.auth-mode-label').click();
await page.locator('.dropdown-item').filter({ hasText: 'Akamai EdgeGrid' }).click();
};
const BASIC_FIELDS = ['Access Token', 'Client Token', 'Client Secret'];
const ADVANCED_FIELDS = ['Base URL', 'Nonce', 'Timestamp', 'Headers to Sign', 'Max Body Size'];
// Exact values for the configure → save → send → persist round-trip. Base URL is the simulator
// host (so the signature validates), and a fixed nonce/timestamp so we can assert the echo.
const VALUES: Record<string, string> = {
'Access Token': TEST_EDGEGRID.accessToken,
'Client Token': TEST_EDGEGRID.clientToken,
'Client Secret': TEST_EDGEGRID.clientSecret,
'Base URL': 'http://localhost:8081',
'Nonce': 'ui-nonce-123',
'Timestamp': '20240101T00:00:00+0000',
'Headers to Sign': 'X-Sign-A',
'Max Body Size': '4096'
};
test.describe('Akamai EdgeGrid Authentication (request level)', () => {
test.afterAll(async ({ page }) => {
await closeAllCollections(page);
});
test('Configure, sign against the simulator, and persist all fields', async ({ page, createTmpDir }) => {
test.setTimeout(90_000);
await createCollection(page, 'edgegrid-ui-test', await createTmpDir());
await createRequest(page, 'edgegrid-request', 'edgegrid-ui-test', { url: SIMULATOR_URL });
await openRequest(page, 'edgegrid-ui-test', 'edgegrid-request');
await selectRequestPaneTab(page, 'Auth');
await selectEdgeGridMode(page);
await test.step('Basic fields render after selecting EdgeGrid (regression: request-level render)', async () => {
for (const name of BASIC_FIELDS) {
await expect(label(page, name)).toBeVisible();
}
await expect(advancedHeader(page)).toBeVisible();
});
await test.step('Base URL editor prefills with the request URL when empty', async () => {
await expectFieldValue(page, 'Base URL', SIMULATOR_URL);
});
await test.step('Fill all basic + advanced fields and save', async () => {
for (const name of BASIC_FIELDS) await setField(page, name, VALUES[name]);
for (const name of ADVANCED_FIELDS) await setField(page, name, VALUES[name]);
await saveRequest(page);
});
await test.step('Send the request — simulator validates and echoes the configured values', async () => {
await sendRequestAndWaitForResponse(page, 200);
const body = await getResponseBody(page);
expect(body).toContain('ui-nonce-123'); // configured nonce was signed & sent
expect(body).toContain('20240101T00:00:00+0000'); // configured timestamp
expect(body).toContain('localhost:8081'); // base_url host the signature validated against
});
await test.step('All fields persist across reopen (regression: draft-path save)', async () => {
await closeAllTabs(page);
await openRequest(page, 'edgegrid-ui-test', 'edgegrid-request');
await selectRequestPaneTab(page, 'Auth');
for (const name of BASIC_FIELDS) await expectFieldValue(page, name, VALUES[name]);
for (const name of ADVANCED_FIELDS) await expectFieldValue(page, name, VALUES[name]);
});
});
test('Empty Advanced Settings fall back to auto-generated defaults', async ({ page, createTmpDir }) => {
test.setTimeout(90_000);
await createCollection(page, 'edgegrid-defaults-test', await createTmpDir());
await createRequest(page, 'edgegrid-defaults', 'edgegrid-defaults-test', { url: SIMULATOR_URL });
await openRequest(page, 'edgegrid-defaults-test', 'edgegrid-defaults');
await selectRequestPaneTab(page, 'Auth');
await selectEdgeGridMode(page);
await test.step('Fill only the required credentials, leave Advanced Settings empty', async () => {
await setField(page, 'Access Token', TEST_EDGEGRID.accessToken);
await setField(page, 'Client Token', TEST_EDGEGRID.clientToken);
await setField(page, 'Client Secret', TEST_EDGEGRID.clientSecret);
await saveRequest(page);
});
await test.step('Send request — simulator echoes the auto-generated defaults', async () => {
await sendRequestAndWaitForResponse(page, 200);
const body = await getResponseBody(page);
// nonce auto-generated as a UUID v4
expect(body).toMatch(/[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/i);
// timestamp auto-generated in EdgeGrid format YYYYMMDDTHH:MM:SS+0000
expect(body).toMatch(/\d{8}T\d{2}:\d{2}:\d{2}\+0000/);
// base_url defaulted to the request host
expect(body).toContain('localhost:8081');
// max_body_size defaulted to 131072
expect(body).toContain('131072');
});
});
});

View File

@@ -0,0 +1,27 @@
meta {
name: EdgeGrid Base URL Override 200
type: http
seq: 6
}
get {
url: {{localhost}}/api/auth/edgegrid/resource
body: none
auth: akamai-edgegrid
}
auth:akamai-edgegrid {
accessToken: akab-access-token-bruno-test
clientToken: akab-client-token-bruno-test
clientSecret: dGVzdC1jbGllbnQtc2VjcmV0LWZvci1icnVuby1lZGdlZ3JpZA==
nonce:
timestamp:
baseURL: http://localhost:8081
headersToSign:
maxBodySize:
}
assert {
res.status: eq 200
res.body.authenticated: eq true
}

View File

@@ -0,0 +1,27 @@
meta {
name: EdgeGrid GET 200
type: http
seq: 1
}
get {
url: {{localhost}}/api/auth/edgegrid/resource
body: none
auth: akamai-edgegrid
}
auth:akamai-edgegrid {
accessToken: akab-access-token-bruno-test
clientToken: akab-client-token-bruno-test
clientSecret: dGVzdC1jbGllbnQtc2VjcmV0LWZvci1icnVuby1lZGdlZ3JpZA==
nonce:
timestamp:
baseURL:
headersToSign:
maxBodySize:
}
assert {
res.status: eq 200
res.body.authenticated: eq true
}

View File

@@ -0,0 +1,26 @@
meta {
name: EdgeGrid GET 401
type: http
seq: 2
}
get {
url: {{localhost}}/api/auth/edgegrid/resource
body: none
auth: akamai-edgegrid
}
auth:akamai-edgegrid {
accessToken: akab-access-token-bruno-test
clientToken: akab-client-token-bruno-test
clientSecret: this-is-the-wrong-secret=
nonce:
timestamp:
baseURL:
headersToSign:
maxBodySize:
}
assert {
res.status: eq 401
}

View File

@@ -0,0 +1,16 @@
meta {
name: EdgeGrid Inherit 200
type: http
seq: 7
}
get {
url: {{localhost}}/api/auth/edgegrid/resource
body: none
auth: inherit
}
assert {
res.status: eq 200
res.body.authenticated: eq true
}

View File

@@ -0,0 +1,31 @@
meta {
name: EdgeGrid POST JSON 200
type: http
seq: 3
}
post {
url: {{localhost}}/api/auth/edgegrid/resource
body: json
auth: akamai-edgegrid
}
auth:akamai-edgegrid {
accessToken: akab-access-token-bruno-test
clientToken: akab-client-token-bruno-test
clientSecret: dGVzdC1jbGllbnQtc2VjcmV0LWZvci1icnVuby1lZGdlZ3JpZA==
nonce:
timestamp:
baseURL:
headersToSign:
maxBodySize:
}
body:json {
{"name":"bruno","kind":"compact"}
}
assert {
res.status: eq 200
res.body.authenticated: eq true
}

View File

@@ -0,0 +1,34 @@
meta {
name: EdgeGrid POST Pretty JSON 200
type: http
seq: 4
}
post {
url: {{localhost}}/api/auth/edgegrid/resource
body: json
auth: akamai-edgegrid
}
auth:akamai-edgegrid {
accessToken: akab-access-token-bruno-test
clientToken: akab-client-token-bruno-test
clientSecret: dGVzdC1jbGllbnQtc2VjcmV0LWZvci1icnVuby1lZGdlZ3JpZA==
nonce:
timestamp:
baseURL:
headersToSign:
maxBodySize:
}
body:json {
{
"name": "bruno",
"kind": "pretty"
}
}
assert {
res.status: eq 200
res.body.authenticated: eq true
}

View File

@@ -0,0 +1,32 @@
meta {
name: EdgeGrid Signed Headers 200
type: http
seq: 5
}
get {
url: {{localhost}}/api/auth/edgegrid/resource
body: none
auth: akamai-edgegrid
}
headers {
X-Test1: my-signed-value
x-eg-headers-to-sign: X-Test1
}
auth:akamai-edgegrid {
accessToken: akab-access-token-bruno-test
clientToken: akab-client-token-bruno-test
clientSecret: dGVzdC1jbGllbnQtc2VjcmV0LWZvci1icnVuby1lZGdlZ3JpZA==
nonce:
timestamp:
baseURL:
headersToSign: X-Test1
maxBodySize:
}
assert {
res.status: eq 200
res.body.authenticated: eq true
}

View File

@@ -0,0 +1,5 @@
{
"version": "1",
"name": "edgegrid-testbench-bru",
"type": "collection"
}

View File

@@ -0,0 +1,14 @@
auth {
mode: akamai-edgegrid
}
auth:akamai-edgegrid {
accessToken: akab-access-token-bruno-test
clientToken: akab-client-token-bruno-test
clientSecret: dGVzdC1jbGllbnQtc2VjcmV0LWZvci1icnVuby1lZGdlZ3JpZA==
nonce:
timestamp:
baseURL:
headersToSign:
maxBodySize:
}

View File

@@ -0,0 +1,3 @@
vars {
localhost: http://localhost:8081
}

View File

@@ -0,0 +1,29 @@
info:
name: EdgeGrid Base URL Override 200
type: http
seq: 6
http:
method: GET
url: "{{localhost}}/api/auth/edgegrid/resource"
auth:
type: akamai-edgegrid
accessToken: akab-access-token-bruno-test
clientToken: akab-client-token-bruno-test
clientSecret: dGVzdC1jbGllbnQtc2VjcmV0LWZvci1icnVuby1lZGdlZ3JpZA==
baseURL: http://localhost:8081
runtime:
assertions:
- expression: res.status
operator: eq
value: "200"
- expression: res.body.authenticated
operator: eq
value: "true"
settings:
encodeUrl: true
timeout: 0
followRedirects: true
maxRedirects: 5

View File

@@ -0,0 +1,28 @@
info:
name: EdgeGrid GET 200
type: http
seq: 1
http:
method: GET
url: "{{localhost}}/api/auth/edgegrid/resource"
auth:
type: akamai-edgegrid
accessToken: akab-access-token-bruno-test
clientToken: akab-client-token-bruno-test
clientSecret: dGVzdC1jbGllbnQtc2VjcmV0LWZvci1icnVuby1lZGdlZ3JpZA==
runtime:
assertions:
- expression: res.status
operator: eq
value: "200"
- expression: res.body.authenticated
operator: eq
value: "true"
settings:
encodeUrl: true
timeout: 0
followRedirects: true
maxRedirects: 5

View File

@@ -0,0 +1,25 @@
info:
name: EdgeGrid GET 401
type: http
seq: 2
http:
method: GET
url: "{{localhost}}/api/auth/edgegrid/resource"
auth:
type: akamai-edgegrid
accessToken: akab-access-token-bruno-test
clientToken: akab-client-token-bruno-test
clientSecret: this-is-the-wrong-secret=
runtime:
assertions:
- expression: res.status
operator: eq
value: "401"
settings:
encodeUrl: true
timeout: 0
followRedirects: true
maxRedirects: 5

View File

@@ -0,0 +1,24 @@
info:
name: EdgeGrid Inherit 200
type: http
seq: 7
http:
method: GET
url: "{{localhost}}/api/auth/edgegrid/resource"
auth: inherit
runtime:
assertions:
- expression: res.status
operator: eq
value: "200"
- expression: res.body.authenticated
operator: eq
value: "true"
settings:
encodeUrl: true
timeout: 0
followRedirects: true
maxRedirects: 5

View File

@@ -0,0 +1,32 @@
info:
name: EdgeGrid POST JSON 200
type: http
seq: 3
http:
method: POST
url: "{{localhost}}/api/auth/edgegrid/resource"
auth:
type: akamai-edgegrid
accessToken: akab-access-token-bruno-test
clientToken: akab-client-token-bruno-test
clientSecret: dGVzdC1jbGllbnQtc2VjcmV0LWZvci1icnVuby1lZGdlZ3JpZA==
body:
type: json
data: |-
{"name":"bruno","kind":"compact"}
runtime:
assertions:
- expression: res.status
operator: eq
value: "200"
- expression: res.body.authenticated
operator: eq
value: "true"
settings:
encodeUrl: true
timeout: 0
followRedirects: true
maxRedirects: 5

View File

@@ -0,0 +1,35 @@
info:
name: EdgeGrid POST Pretty JSON 200
type: http
seq: 4
http:
method: POST
url: "{{localhost}}/api/auth/edgegrid/resource"
auth:
type: akamai-edgegrid
accessToken: akab-access-token-bruno-test
clientToken: akab-client-token-bruno-test
clientSecret: dGVzdC1jbGllbnQtc2VjcmV0LWZvci1icnVuby1lZGdlZ3JpZA==
body:
type: json
data: |-
{
"name": "bruno",
"kind": "pretty"
}
runtime:
assertions:
- expression: res.status
operator: eq
value: "200"
- expression: res.body.authenticated
operator: eq
value: "true"
settings:
encodeUrl: true
timeout: 0
followRedirects: true
maxRedirects: 5

View File

@@ -0,0 +1,34 @@
info:
name: EdgeGrid Signed Headers 200
type: http
seq: 5
http:
method: GET
url: "{{localhost}}/api/auth/edgegrid/resource"
headers:
- name: X-Test1
value: my-signed-value
- name: x-eg-headers-to-sign
value: X-Test1
auth:
type: akamai-edgegrid
accessToken: akab-access-token-bruno-test
clientToken: akab-client-token-bruno-test
clientSecret: dGVzdC1jbGllbnQtc2VjcmV0LWZvci1icnVuby1lZGdlZ3JpZA==
headersToSign: X-Test1
runtime:
assertions:
- expression: res.status
operator: eq
value: "200"
- expression: res.body.authenticated
operator: eq
value: "true"
settings:
encodeUrl: true
timeout: 0
followRedirects: true
maxRedirects: 5

View File

@@ -0,0 +1,5 @@
name: Local
variables:
- name: localhost
value: http://localhost:8081

View File

@@ -0,0 +1,13 @@
opencollection: '1.0.0'
info:
name: edgegrid-testbench-yml
request:
auth:
type: akamai-edgegrid
accessToken: akab-access-token-bruno-test
clientToken: akab-client-token-bruno-test
clientSecret: dGVzdC1jbGllbnQtc2VjcmV0LWZvci1icnVuby1lZGdlZ3JpZA==
bundled: false

View File

@@ -0,0 +1,10 @@
{
"maximized": false,
"lastOpenedCollections": ["{{collectionPath}}/bru", "{{collectionPath}}/yml"],
"preferences": {
"onboarding": {
"hasLaunchedBefore": true,
"hasSeenWelcomeModal": true
}
}
}

View File

@@ -0,0 +1,155 @@
{
"info": {
"_postman_id": "5dfec9be-beb9-45f2-b5b1-0ecf5c3d3e2b",
"name": "EgdeGrid Auth",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
"_exporter_id": "36465539"
},
"item": [
{
"name": "Edgegrid Auth - inherit",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "http://localhost:6000/edgegrid/version",
"protocol": "http",
"host": [
"localhost"
],
"port": "6000",
"path": [
"edgegrid",
"version"
]
}
},
"response": []
},
{
"name": "Edgegrid auth - request level",
"request": {
"auth": {
"type": "edgegrid",
"edgegrid": [
{
"key": "maxBodySize",
"value": 131072,
"type": "number"
},
{
"key": "baseURL",
"value": "https://akab-h05tnam3wl42son7nktnlnnx-kbob3i3v.luna.akamaiapis.request.net",
"type": "string"
},
{
"key": "timestamp",
"value": "20160804T07:00:00+0000",
"type": "string"
},
{
"key": "nonce",
"value": "ec9d20ee-1e9b-4c1f-925a-request",
"type": "string"
},
{
"key": "clientSecret",
"value": "C113nt53KR3TN6N90yVuAgICxIRwsObLi0E67/request=",
"type": "string"
},
{
"key": "clientToken",
"value": "akab-c113ntt0k3n4qtari252bfxxbsl-request",
"type": "string"
},
{
"key": "accessToken",
"value": "akab-acc35t0k3nodujqunph3w7hzp7-request",
"type": "string"
}
]
},
"method": "POST",
"header": [],
"url": {
"raw": "http://localhost:6000/edgegrid/version/2",
"protocol": "http",
"host": [
"localhost"
],
"port": "6000",
"path": [
"edgegrid",
"version",
"2"
]
}
},
"response": []
}
],
"auth": {
"type": "edgegrid",
"edgegrid": [
{
"key": "maxBodySize",
"value": 131072,
"type": "number"
},
{
"key": "baseURL",
"value": "https://akab-h05tnam3wl42son7nktnlnnx-kbob3i3v.luna.akamaiapis.net",
"type": "string"
},
{
"key": "timestamp",
"value": "20160804T07:00:00+0000",
"type": "string"
},
{
"key": "nonce",
"value": "ec9d20ee-1e9b-4c1f-925a-f0017754f86c",
"type": "string"
},
{
"key": "clientSecret",
"value": "C113nt53KR3TN6N90yVuAgICxIRwsObLi0E67/N8eRN=",
"type": "string"
},
{
"key": "clientToken",
"value": "akab-c113ntt0k3n4qtari252bfxxbsl-yvsdj",
"type": "string"
},
{
"key": "accessToken",
"value": "akab-acc35t0k3nodujqunph3w7hzp7-gtm6ij",
"type": "string"
}
]
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"packages": {},
"requests": {},
"exec": [
""
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"packages": {},
"requests": {},
"exec": [
""
]
}
}
]
}

View File

@@ -0,0 +1,158 @@
import { test, expect } from '../../../playwright';
import * as path from 'path';
import * as fs from 'fs';
import { closeAllCollections, openCollection, selectRequestPaneTab } from '../../utils/page';
import { buildCommonLocators } from '../../utils/page/locators';
/**
* Imports the real Postman EdgeGrid collection and asserts every credential value lands in the
* right SingleLineEditor at every level (collection settings, request level, inherit). Expected
* values are read straight from the fixture JSON so assertions can never drift from the source.
*
* Locators are scoped to the VISIBLE pane: when the collection-settings tab and a request tab
* are both open, the field labels/editors exist twice in the DOM (one hidden), so we must only
* read the active pane.
*/
const FIXTURE = path.resolve(__dirname, 'fixtures', 'postman-edgegrid-collection.json');
const COLLECTION_NAME = 'EgdeGrid Auth';
const REQUEST_LEVEL = 'Edgegrid auth - request level';
const INHERIT_REQUEST = 'Edgegrid Auth - inherit';
// UI label → Postman/Bruno camelCase key (1:1). Order = display order (3 basic, then advanced).
const FIELDS: Array<{ label: string; key: string; advanced?: boolean }> = [
{ label: 'Access Token', key: 'accessToken' },
{ label: 'Client Token', key: 'clientToken' },
{ label: 'Client Secret', key: 'clientSecret' },
{ label: 'Base URL', key: 'baseURL', advanced: true },
{ label: 'Nonce', key: 'nonce', advanced: true },
{ label: 'Timestamp', key: 'timestamp', advanced: true },
{ label: 'Headers to Sign', key: 'headersToSign', advanced: true },
{ label: 'Max Body Size', key: 'maxBodySize', advanced: true }
];
// Flatten a Postman edgegrid auth array ([{key,value}]) into { key: stringValue }.
const egMap = (arr: any[] = []): Record<string, string> =>
Object.fromEntries((arr || []).map((p) => [p.key, p.value == null ? '' : String(p.value)]));
const pm = JSON.parse(fs.readFileSync(FIXTURE, 'utf8'));
const collectionExpected = egMap(pm.auth?.edgegrid);
const requestExpected = egMap(pm.item.find((i: any) => i.name === REQUEST_LEVEL)?.request?.auth?.edgegrid);
// The exact key/value pairs the fixture is expected to carry. Asserting the parsed maps equal
// these (below) guarantees the UI value checks are matching against real, exact values — not
// silently degenerating to '' === '' if the fixture/mapping ever changes.
const EXPECTED_COLLECTION = {
accessToken: 'akab-acc35t0k3nodujqunph3w7hzp7-gtm6ij',
clientToken: 'akab-c113ntt0k3n4qtari252bfxxbsl-yvsdj',
clientSecret: 'C113nt53KR3TN6N90yVuAgICxIRwsObLi0E67/N8eRN=',
nonce: 'ec9d20ee-1e9b-4c1f-925a-f0017754f86c',
timestamp: '20160804T07:00:00+0000',
baseURL: 'https://akab-h05tnam3wl42son7nktnlnnx-kbob3i3v.luna.akamaiapis.net',
maxBodySize: '131072'
};
const EXPECTED_REQUEST = {
accessToken: 'akab-acc35t0k3nodujqunph3w7hzp7-request',
clientToken: 'akab-c113ntt0k3n4qtari252bfxxbsl-request',
clientSecret: 'C113nt53KR3TN6N90yVuAgICxIRwsObLi0E67/request=',
nonce: 'ec9d20ee-1e9b-4c1f-925a-request',
timestamp: '20160804T07:00:00+0000',
baseURL: 'https://akab-h05tnam3wl42son7nktnlnnx-kbob3i3v.luna.akamaiapis.request.net',
maxBodySize: '131072'
};
// --- visible-scoped locators (active pane only) ---
const visible = (loc) => loc.filter({ visible: true });
const visibleLabel = (page, labelText: string) => visible(page.locator('label').filter({ hasText: labelText }));
const visibleAuthModeLabel = (page) => visible(page.locator('.auth-mode-label'));
const fieldEditorValue = (page, labelText: string): Promise<string> =>
visibleLabel(page, labelText)
.locator('..')
.locator('.single-line-editor-wrapper .CodeMirror')
.evaluate((el: any) => el.CodeMirror.getValue());
// Assert mode = EdgeGrid and EVERY field's editor value matches the expected JSON value.
const assertEdgeGridValues = async (page, expected: Record<string, string>) => {
await expect(visibleAuthModeLabel(page)).toHaveText(/Akamai EdgeGrid/);
// Advanced Settings is always open, so all fields are visible — assert them all.
for (const f of FIELDS) {
await expect.poll(() => fieldEditorValue(page, f.label), { message: `${f.label} (${f.key})` }).toBe(
expected[f.key] ?? ''
);
}
};
test.describe('Import Postman Collection with Akamai EdgeGrid auth', () => {
let originalShowOpenDialog;
test.beforeAll(async ({ electronApp }) => {
await electronApp.evaluate(({ dialog }) => {
originalShowOpenDialog = dialog.showOpenDialog;
});
// Pin the fixture to the exact key/value pairs we assert in the UI (exact, not a count).
expect(collectionExpected).toEqual(EXPECTED_COLLECTION);
expect(requestExpected).toEqual(EXPECTED_REQUEST);
});
test.afterAll(async ({ electronApp, page }) => {
await closeAllCollections(page);
await electronApp.evaluate(({ dialog }) => {
dialog.showOpenDialog = originalShowOpenDialog;
});
});
test('imports EdgeGrid auth with exact values at collection, request and inherit levels', async ({
page,
electronApp,
createTmpDir
}) => {
test.setTimeout(120_000);
const locators = buildCommonLocators(page);
const importDir = await createTmpDir('imported-edgegrid');
await electronApp.evaluate(({ dialog }, { importDir }) => {
dialog.showOpenDialog = async () => ({ canceled: false, filePaths: [importDir] });
}, { importDir });
await test.step('Import the Postman collection', async () => {
await locators.plusMenu.button().click();
await locators.plusMenu.importCollection().click();
await page.getByRole('dialog').waitFor({ state: 'visible' });
await locators.import.fileInput().setInputFiles(FIXTURE);
await locators.import.locationModal().waitFor({ state: 'visible', timeout: 10000 });
const hasError = await locators.import.parsingError().isVisible().catch(() => false);
expect(hasError).toBeFalsy();
await expect(locators.import.locationModal().getByText(COLLECTION_NAME)).toBeVisible();
await locators.import.browseLink(locators.import.locationModal()).click();
const locationModal = locators.import.locationModal();
await locators.import.importButton(locationModal).click();
await locationModal.waitFor({ state: 'hidden' });
});
await openCollection(page, COLLECTION_NAME);
await test.step('Collection-level: every EdgeGrid field value matches the JSON', async () => {
await locators.actions.collectionActions(COLLECTION_NAME).click();
await locators.dropdown.item('Settings').click();
await page.getByTestId('collection-settings-tab-auth').click();
await assertEdgeGridValues(page, collectionExpected);
});
await test.step('Request-level ("Edgegrid auth - request level"): every field value matches the JSON', async () => {
await locators.sidebar.request(REQUEST_LEVEL).click();
await selectRequestPaneTab(page, 'Auth');
await assertEdgeGridValues(page, requestExpected);
});
await test.step('Inherit request inherits EdgeGrid from the collection', async () => {
await locators.sidebar.request(INHERIT_REQUEST).click();
await selectRequestPaneTab(page, 'Auth');
await expect(visibleAuthModeLabel(page)).toHaveText(/Inherit/);
await expect(visible(page.locator('.inherit-mode-text'))).toHaveText('Akamai EdgeGrid');
});
});
});