fix(harness): add workspace authorization input

This commit is contained in:
YBF
2026-09-15 12:07:33 +08:00
parent c168d83d97
commit aea6085d73
8 changed files with 35 additions and 2 deletions
@@ -13,6 +13,7 @@ import { hasExactKeys, isJsonRequest, isNonEmptyString, readBody } from "../requ
import { writeAuthorization, writePlainResponse, writeRejection } from "../response.ts";
export const MIND_SESSION_AUTHORIZATION_PATH = "/internal/bright/onetalk/authorize-session";
const MIND_WORKSPACE_HEADER = "sinan-pilot-workspace-id";
const readSessionRequest = (body: unknown): { channelAccountId: string } | null => {
if (
@@ -45,6 +46,7 @@ export const handleSessionAuthorization = async (
authorization: MindMockAuthorization,
channelAccountIds: readonly string[],
cookie: string,
workspaceAuthorization: string,
result: MindMockSessionResult,
): Promise<void> => {
if (!isJsonRequest(request)) {
@@ -68,6 +70,10 @@ export const handleSessionAuthorization = async (
writeRejection(response, 401, "auth_required");
return;
}
if (request.headers[MIND_WORKSPACE_HEADER] !== workspaceAuthorization) {
writeRejection(response, 403, "scope_mismatch");
return;
}
if (!channelAccountIds.includes(input.channelAccountId)) {
writeRejection(response, 403, "scope_mismatch");
return;
+6
View File
@@ -28,6 +28,7 @@ export type MindMockConfig = {
host: string;
port: number;
cookie: string;
workspaceAuthorization: string;
authorization: MindMockAuthorization;
channelAccountIds: readonly string[];
bindingResult?: MindMockBindingResult;
@@ -167,6 +168,11 @@ export const parseMindMockConfig = (
"MIND_MOCK_PORT",
),
cookie: parseFixtureString(environment, "MIND_MOCK_COOKIE", DEFAULT_COOKIE),
workspaceAuthorization: parseFixtureString(
environment,
"MIND_MOCK_WORKSPACE_AUTHORIZATION",
authorization.mindScope.workspaceId,
),
authorization,
channelAccountIds,
bindingResult: parseResult(environment, "MIND_MOCK_BINDING_RESULT", BINDING_MOCK_RESULTS),
@@ -17,6 +17,7 @@ export const harnessEventsScript = String.raw` const writeCookie = ()
fields.uploadFileButton.addEventListener('click', uploadFile);
fields.copyUploadUrl.addEventListener('click', copyUploadUrl);
fields.account.addEventListener('input', resetForAccountOrQueryChange);
fields.workspaceAuthorization.addEventListener('input', resetForAccountOrQueryChange);
fields.conversationQuery.addEventListener('input', resetForAccountOrQueryChange);
fields.wsFrameFilter.addEventListener('input', () => {
state.wsFrameFilterKeyword = fields.wsFrameFilter.value.trim().toLowerCase();
@@ -26,6 +26,7 @@ export const harnessPageMarkup = String.raw` <main>
<h2 id="connection-heading">账号与连接</h2>
<div class="field-grid">
<label>OneTalk 账号 ID<input id="account-id" autocomplete="off" placeholder="onetalk-account-1" required></label>
<label>Workspace Authorization<input id="workspace-authorization" autocomplete="off" placeholder="workspace-1" required></label>
<label>会话查询<input id="conversation-query" autocomplete="off" placeholder="名称或会话 ID"></label>
<label class="span-2">会话<select id="conversation-id" disabled><option value="">先加载会话</option></select></label>
<label class="span-2">开发 Cookie<input id="mind-cookie" autocomplete="off" placeholder="mind_session=opaque"></label>
+18 -2
View File
@@ -1,8 +1,13 @@
// 页面脚本分片:scope、URL 构造与会话/消息读取(由 harness/index.ts 组装进主 IIFE
export const harnessReadingScript = String.raw` const requestedChannelAccountId = () => fields.account.value.trim();
const requestedWorkspaceAuthorization = () => fields.workspaceAuthorization.value.trim();
const requestedQuery = () => fields.conversationQuery.value.trim();
const currentScope = () => state.scope && state.scope.channelAccountId === requestedChannelAccountId() ? state.scope : null;
const currentScope = () => state.scope
&& state.scope.channelAccountId === requestedChannelAccountId()
&& state.scope.workspaceId === requestedWorkspaceAuthorization()
? state.scope
: null;
const matchesCurrentScope = (scope) => {
const activeScope = currentScope();
return activeScope
@@ -48,7 +53,13 @@ export const harnessReadingScript = String.raw` const requestedChanne
};
const readResponse = async (url, requestHeaders = {}) => {
return fetchWithDeadline(url, { headers: requestHeaders, credentials: 'include' }, async (response) => {
return fetchWithDeadline(url, {
headers: {
...requestHeaders,
'Sinan-Pilot-Workspace-Id': requestedWorkspaceAuthorization()
},
credentials: 'include'
}, async (response) => {
let body = null;
try { body = await response.json(); } catch (_) { body = null; }
if (!response.ok) {
@@ -141,11 +152,16 @@ export const harnessReadingScript = String.raw` const requestedChanne
const loadConversations = async (append = false) => {
const channelAccountId = requestedChannelAccountId();
const workspaceAuthorization = requestedWorkspaceAuthorization();
const queryValue = requestedQuery();
if (!channelAccountId) {
setStatus(fields.historyStatus, '请输入 OneTalk 账号 ID', 'error');
return;
}
if (!workspaceAuthorization) {
setStatus(fields.historyStatus, '请输入 Workspace Authorization', 'error');
return;
}
if (append) {
if (!state.scope || state.scope.channelAccountId !== channelAccountId || state.listQuery !== queryValue || !state.listCursor) {
return;
@@ -33,6 +33,7 @@ export const harnessRuntimeScript = String.raw` const heartbeatInterv
const element = (id) => document.getElementById(id);
const fields = {
account: element('account-id'),
workspaceAuthorization: element('workspace-authorization'),
conversationQuery: element('conversation-query'),
conversation: element('conversation-id'),
cookie: element('mind-cookie'),
@@ -150,6 +150,7 @@ export const harnessWebsocketScript = String.raw` const frameMatchesF
const wsUrl = () => {
const url = new URL('/ws/mind', brightBaseUrl);
url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:';
url.searchParams.set('workspaceId', requestedWorkspaceAuthorization());
return url.toString();
};
+1
View File
@@ -50,6 +50,7 @@ const handleRequest = async (
config.authorization,
config.channelAccountIds,
config.cookie,
config.workspaceAuthorization,
config.sessionResult ?? "allow",
);
return;