cangku/backend/test/externalAutomationSharedAuth.test.js

198 lines
6.8 KiB
JavaScript

'use strict';
const test = require('node:test');
const assert = require('node:assert/strict');
const jwt = require('jsonwebtoken');
const presaleRouter = require('../src/api/presaleExternal');
const autoListingRouter = require('../src/api/planningSelectionFissionExternal');
const pricingRouter = require('../src/api/pricingCalculatorExternal');
const alibaba1688Router = require('../src/api/alibaba1688ProductExternal');
const {
requireDesktopPageAccess,
requireExternalAutomationApiKey,
} = require('../src/services/externalAutomationAuth');
function getRouteMiddleware(router, path) {
const layer = router.stack.find((item) => item.route?.path === path);
return layer?.route?.stack?.[0]?.handle;
}
function createResponseRecorder() {
return {
statusCode: 200,
payload: null,
status(code) {
this.statusCode = code;
return this;
},
json(payload) {
this.payload = payload;
return this;
},
};
}
test('presale and auto-listing external APIs use the same request key middleware', () => {
assert.equal(getRouteMiddleware(presaleRouter, '/tasks'), requireExternalAutomationApiKey);
assert.equal(getRouteMiddleware(autoListingRouter, '/tasks/claim'), requireExternalAutomationApiKey);
assert.equal(getRouteMiddleware(alibaba1688Router, '/:productId'), requireExternalAutomationApiKey);
});
test('desktop pricing uses the shared request key middleware', () => {
const middlewareLayer = pricingRouter.stack.find((item) => item.name === 'requireExternalAutomationApiKey');
assert.equal(middlewareLayer?.handle, requireExternalAutomationApiKey);
});
test('desktop pricing identity is stable, isolated and uses negative safe integers', () => {
const first = pricingRouter.desktopPricingUserId('香茗');
assert.equal(first, pricingRouter.desktopPricingUserId(' 香茗 '));
assert.notEqual(first, pricingRouter.desktopPricingUserId('江陵'));
assert.equal(Number.isSafeInteger(first), true);
assert.equal(first < 0, true);
});
test('desktop pricing accepts a UTF-8 base64url nickname header', () => {
const nickname = '胖大海';
const req = {
get(name) {
return name.toLowerCase() === 'x-operator-name-encoded'
? Buffer.from(nickname, 'utf8').toString('base64url')
: '';
},
query: {},
body: {},
};
let nextCalled = false;
pricingRouter.requireDesktopPricingOperator(req, {}, () => { nextCalled = true; });
assert.equal(nextCalled, true);
assert.equal(req.user.name, nickname);
assert.equal(req.user.id, req.user.sub);
assert.ok(req.user.id < 0);
});
test('shared external auth accepts a valid desktop JWT without an API key', async () => {
const previousSecret = process.env.CUBEJS_SECRET;
process.env.CUBEJS_SECRET = 'desktop-session-test-secret';
try {
const accessToken = jwt.sign({ sub: '42', name: '陆南亭', authSource: 'dingtalk' }, process.env.CUBEJS_SECRET);
const req = {
ip: '127.0.0.1',
query: {},
get(name) {
return name.toLowerCase() === 'authorization' ? `Bearer ${accessToken}` : '';
},
};
const res = createResponseRecorder();
let nextCalled = false;
await requireExternalAutomationApiKey(req, res, () => { nextCalled = true; });
assert.equal(nextCalled, true);
assert.equal(req.user.name, '陆南亭');
assert.equal(req.externalApiKey.authType, 'jwt');
assert.equal(req.externalApiKey.name, '桌面端登录会话');
} finally {
if (previousSecret === undefined) delete process.env.CUBEJS_SECRET;
else process.env.CUBEJS_SECRET = previousSecret;
}
});
test('shared external auth rejects an invalid desktop JWT', async () => {
const previousSecret = process.env.CUBEJS_SECRET;
process.env.CUBEJS_SECRET = 'desktop-session-test-secret';
try {
const accessToken = jwt.sign({ sub: '42', name: '陆南亭' }, 'wrong-secret');
const req = {
ip: '127.0.0.1',
query: {},
get(name) {
return name.toLowerCase() === 'authorization' ? `Bearer ${accessToken}` : '';
},
};
const res = createResponseRecorder();
let nextCalled = false;
await requireExternalAutomationApiKey(req, res, () => { nextCalled = true; });
assert.equal(nextCalled, false);
assert.equal(res.statusCode, 401);
assert.equal(res.payload.code, 'AUTH_TOKEN_INVALID');
} finally {
if (previousSecret === undefined) delete process.env.CUBEJS_SECRET;
else process.env.CUBEJS_SECRET = previousSecret;
}
});
test('shared external auth rejects a desktop SSO session token on business APIs', async () => {
const previousSecret = process.env.CUBEJS_SECRET;
process.env.CUBEJS_SECRET = 'desktop-session-test-secret';
try {
const accessToken = jwt.sign(
{ sub: '42', name: '陆南亭', tokenType: 'desktop-session' },
process.env.CUBEJS_SECRET
);
const req = {
ip: '127.0.0.1',
query: {},
get(name) {
return name.toLowerCase() === 'authorization' ? `Bearer ${accessToken}` : '';
},
};
const res = createResponseRecorder();
let nextCalled = false;
await requireExternalAutomationApiKey(req, res, () => { nextCalled = true; });
assert.equal(nextCalled, false);
assert.equal(res.statusCode, 401);
assert.equal(res.payload.code, 'AUTH_TOKEN_SCOPE_INVALID');
} finally {
if (previousSecret === undefined) delete process.env.CUBEJS_SECRET;
else process.env.CUBEJS_SECRET = previousSecret;
}
});
test('desktop page access enforces JWT permissions and preserves API key compatibility', () => {
const middleware = requireDesktopPageAccess('planning_upload', 'market_insight');
const deniedResponse = createResponseRecorder();
let deniedNextCalled = false;
middleware({
externalApiKey: { authType: 'jwt' },
user: { role: 'user', allowedPageKeys: ['dashboard'] },
}, deniedResponse, () => { deniedNextCalled = true; });
assert.equal(deniedNextCalled, false);
assert.equal(deniedResponse.statusCode, 403);
assert.equal(deniedResponse.payload.code, 'DESKTOP_PAGE_ACCESS_DENIED');
for (const req of [
{ externalApiKey: { authType: 'jwt' }, user: { role: 'user', allowedPageKeys: ['market_insight'] } },
{ externalApiKey: { authType: 'jwt' }, user: { role: 'admin', allowedPageKeys: [] } },
{ externalApiKey: { authType: 'api-key' }, user: null },
]) {
let nextCalled = false;
middleware(req, createResponseRecorder(), () => { nextCalled = true; });
assert.equal(nextCalled, true);
}
});
test('desktop pricing keeps the verified JWT user identity', () => {
const req = {
externalApiKey: { authType: 'jwt' },
user: { sub: '42', id: 42, name: '陆南亭' },
get() { return ''; },
query: {},
body: {},
};
let nextCalled = false;
pricingRouter.requireDesktopPricingOperator(req, {}, () => { nextCalled = true; });
assert.equal(nextCalled, true);
assert.equal(req.user.id, 42);
assert.equal(req.user.name, '陆南亭');
});