@hualinge/relay-green-package 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +100 -0
- package/dist/auth/huawei-cas.d.ts +17 -0
- package/dist/auth/huawei-cas.d.ts.map +1 -0
- package/dist/auth/huawei-cas.js +428 -0
- package/dist/auth/huawei-cas.js.map +1 -0
- package/dist/auth/huawei-iam.d.ts +7 -0
- package/dist/auth/huawei-iam.d.ts.map +1 -0
- package/dist/auth/huawei-iam.js +177 -0
- package/dist/auth/huawei-iam.js.map +1 -0
- package/dist/auth/index.d.ts +3 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +3 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/huawei-maas.d.ts +21 -0
- package/dist/integrations/huawei-maas.d.ts.map +1 -0
- package/dist/integrations/huawei-maas.js +44 -0
- package/dist/integrations/huawei-maas.js.map +1 -0
- package/dist/metrics/aom-access-code-client.d.ts +52 -0
- package/dist/metrics/aom-access-code-client.d.ts.map +1 -0
- package/dist/metrics/aom-access-code-client.js +170 -0
- package/dist/metrics/aom-access-code-client.js.map +1 -0
- package/dist/metrics-plugin.d.ts +3 -0
- package/dist/metrics-plugin.d.ts.map +1 -0
- package/dist/metrics-plugin.js +45 -0
- package/dist/metrics-plugin.js.map +1 -0
- package/dist/providers-plugin.d.ts +3 -0
- package/dist/providers-plugin.d.ts.map +1 -0
- package/dist/providers-plugin.js +4 -0
- package/dist/providers-plugin.js.map +1 -0
- package/dist/types.d.ts +9 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/signer.d.ts +27 -0
- package/dist/utils/signer.d.ts.map +1 -0
- package/dist/utils/signer.js +240 -0
- package/dist/utils/signer.js.map +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
import { createCipheriv, createDecipheriv, createHash, randomBytes } from 'node:crypto';
|
|
2
|
+
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
|
+
import { resolveHuaweiMaaSRuntimeConfig } from '../integrations/huawei-maas.js';
|
|
6
|
+
import { HttpRequest, Signer } from '../utils/signer.js';
|
|
7
|
+
const SERVER_STARTUP_TOKEN = randomBytes(16).toString('hex');
|
|
8
|
+
const CAS_AUTH_UI_LOGIN_URL = 'https://auth.huaweicloud.com/authui/login.html';
|
|
9
|
+
const CAS_AUTH_UI_LOGOUT_URL = 'https://auth.huaweicloud.com/authui/logout';
|
|
10
|
+
const DEFAULT_CAS_BASE_URL = CAS_AUTH_UI_LOGIN_URL;
|
|
11
|
+
const DEFAULT_CAS_SERVICE_CALLBACK_URL = 'https://versatile.cn-north-4.myhuaweicloud.com/v1/claw/cas/login/callback';
|
|
12
|
+
const DEFAULT_CAS_BACKGROUND_IMAGE_URL = 'https://res.hc-cdn.com/AgentArts-Console/26.3.2/hws/assets/login/bg2.png';
|
|
13
|
+
const DEFAULT_CAS_PORTAL_IMAGE_URL = 'https://res.hc-cdn.com/AgentArts-Console/26.3.2/hws/assets/login/ad2.png';
|
|
14
|
+
const DEFAULT_TICKET_VALIDATE_URL = 'https://versatile.cn-north-4.myhuaweicloud.com/v1/claw/cas/login/ticket-validate';
|
|
15
|
+
const DEFAULT_SUBSCRIPTION_URL = 'https://versatile.cn-north-4.myhuaweicloud.com/v1/claw/client-subscription';
|
|
16
|
+
const DEFAULT_SESSION_TTL_MS = 12 * 60 * 60 * 1000;
|
|
17
|
+
const DEFAULT_PENDING_TTL_MS = 10 * 60 * 1000;
|
|
18
|
+
const INVITATION_HTML_ERROR_CODE = 'common.01010004';
|
|
19
|
+
const INVITATION_HTML_ERROR_MESSAGE = '请确认账号状态,是否已<a href="https://support.huaweicloud.com/usermanual-account/account_auth_00001.html" target="_blank" rel="noreferrer">实名认证</a>或<a href="https://support.huaweicloud.com/billing_faq/billing_faq_1000000.html" target="_blank" rel="noreferrer">非欠费</a>状态。';
|
|
20
|
+
const PROMOTION_CODE_ERROR_CODES = new Set(['AgentArts.11000008', 'AgentArts.11000009', INVITATION_HTML_ERROR_CODE]);
|
|
21
|
+
const PROMOTION_CODE_ERROR_MESSAGES = {
|
|
22
|
+
'AgentArts.11000008': '邀请码无效,请重新输入',
|
|
23
|
+
'AgentArts.11000009': '邀请码不存在或者今日邀请码配额已用完',
|
|
24
|
+
[INVITATION_HTML_ERROR_CODE]: INVITATION_HTML_ERROR_MESSAGE,
|
|
25
|
+
};
|
|
26
|
+
function getSecureConfigDir(env) {
|
|
27
|
+
return readEnv(env, 'OFFICE_CLAW_SECURE_CONFIG_DIR') ?? join(env.HOME ?? process.cwd(), '.config', 'secure-config-nodejs');
|
|
28
|
+
}
|
|
29
|
+
function getSecureConfigPath(env, userId) {
|
|
30
|
+
return join(getSecureConfigDir(env), `${encodeURIComponent(userId)}.json`);
|
|
31
|
+
}
|
|
32
|
+
function hashPendingToken(token) {
|
|
33
|
+
return createHash('sha256').update(token).digest('hex');
|
|
34
|
+
}
|
|
35
|
+
function getEncryptionKey(env) {
|
|
36
|
+
const configured = readEnv(env, 'OFFICE_CLAW_SECURE_CONFIG_ENCRYPTION_KEY') ?? readEnv(env, 'OFFICE_CLAW_SESSION_SECRET') ?? readInstallSecret(env);
|
|
37
|
+
return createHash('sha256').update(configured).digest();
|
|
38
|
+
}
|
|
39
|
+
function isNoEntryError(error) {
|
|
40
|
+
return Boolean(error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT');
|
|
41
|
+
}
|
|
42
|
+
function readInstallSecret(env) {
|
|
43
|
+
const filePath = join(getSecureConfigDir(env), '.cas-profile-encryption-key');
|
|
44
|
+
try {
|
|
45
|
+
const existing = readFileSync(filePath, 'utf8').trim();
|
|
46
|
+
if (existing)
|
|
47
|
+
return existing;
|
|
48
|
+
throw new Error(`Failed to read CAS profile encryption key file: ${filePath}; file is empty`);
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
if (!isNoEntryError(error)) {
|
|
52
|
+
throw error;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const secret = randomBytes(32).toString('base64url');
|
|
56
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
57
|
+
writeFileSync(filePath, `${secret}\n`, { encoding: 'utf8', mode: 0o600 });
|
|
58
|
+
return secret;
|
|
59
|
+
}
|
|
60
|
+
function encryptJson(env, value) {
|
|
61
|
+
const iv = randomBytes(12);
|
|
62
|
+
const cipher = createCipheriv('aes-256-gcm', getEncryptionKey(env), iv);
|
|
63
|
+
const encrypted = Buffer.concat([cipher.update(JSON.stringify(value), 'utf8'), cipher.final()]);
|
|
64
|
+
const payload = {
|
|
65
|
+
version: 1,
|
|
66
|
+
alg: 'aes-256-gcm',
|
|
67
|
+
iv: iv.toString('base64url'),
|
|
68
|
+
tag: cipher.getAuthTag().toString('base64url'),
|
|
69
|
+
data: encrypted.toString('base64url'),
|
|
70
|
+
};
|
|
71
|
+
return JSON.stringify(payload);
|
|
72
|
+
}
|
|
73
|
+
function decryptJson(env, raw) {
|
|
74
|
+
const payload = JSON.parse(raw);
|
|
75
|
+
if (payload.version !== 1 || payload.alg !== 'aes-256-gcm')
|
|
76
|
+
throw new Error('Unsupported encrypted payload');
|
|
77
|
+
const decipher = createDecipheriv('aes-256-gcm', getEncryptionKey(env), Buffer.from(payload.iv, 'base64url'));
|
|
78
|
+
decipher.setAuthTag(Buffer.from(payload.tag, 'base64url'));
|
|
79
|
+
const decrypted = Buffer.concat([decipher.update(Buffer.from(payload.data, 'base64url')), decipher.final()]);
|
|
80
|
+
return JSON.parse(decrypted.toString('utf8'));
|
|
81
|
+
}
|
|
82
|
+
function getPendingConfigPath(env, pendingTokenHash) {
|
|
83
|
+
return join(getSecureConfigDir(env), `pending-${pendingTokenHash}.json`);
|
|
84
|
+
}
|
|
85
|
+
function readEnv(env, key) {
|
|
86
|
+
const value = env[key]?.trim();
|
|
87
|
+
return value || undefined;
|
|
88
|
+
}
|
|
89
|
+
function isRecord(value) {
|
|
90
|
+
return typeof value === 'object' && value !== null;
|
|
91
|
+
}
|
|
92
|
+
function unwrapPayload(value) {
|
|
93
|
+
if (!isRecord(value))
|
|
94
|
+
return null;
|
|
95
|
+
if (isRecord(value.data))
|
|
96
|
+
return value.data;
|
|
97
|
+
if (isRecord(value.result))
|
|
98
|
+
return value.result;
|
|
99
|
+
return value;
|
|
100
|
+
}
|
|
101
|
+
function extractModelInfo(payload) {
|
|
102
|
+
if (!payload)
|
|
103
|
+
return undefined;
|
|
104
|
+
if (isRecord(payload.model_info))
|
|
105
|
+
return payload.model_info;
|
|
106
|
+
const subscriptionPayload = unwrapPayload(payload.subscription);
|
|
107
|
+
if (isRecord(subscriptionPayload?.model_info))
|
|
108
|
+
return subscriptionPayload.model_info;
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
function hasActiveSubscription(payload) {
|
|
112
|
+
if (!payload)
|
|
113
|
+
return false;
|
|
114
|
+
return payload.subscription !== null && payload.subscription !== undefined;
|
|
115
|
+
}
|
|
116
|
+
function resolveCasServiceCallbackUrl(options) {
|
|
117
|
+
const env = options.env ?? process.env;
|
|
118
|
+
return (options.serviceCallbackUrl ??
|
|
119
|
+
readEnv(env, 'OFFICE_CLAW_CAS_SERVICE_CALLBACK_URL') ??
|
|
120
|
+
readEnv(env, 'CAS_CALLBACK_SERVICE_URL') ??
|
|
121
|
+
DEFAULT_CAS_SERVICE_CALLBACK_URL);
|
|
122
|
+
}
|
|
123
|
+
function resolveCasVisualAssets(options) {
|
|
124
|
+
const env = options.env ?? process.env;
|
|
125
|
+
const backgroundImageUrl = options.backgroundImageUrl ??
|
|
126
|
+
readEnv(env, 'OFFICE_CLAW_CAS_BACKGROUND_IMAGE_URL') ??
|
|
127
|
+
readEnv(env, 'CAS_BACKGROUND_IMAGE_URL') ??
|
|
128
|
+
DEFAULT_CAS_BACKGROUND_IMAGE_URL;
|
|
129
|
+
const portalImageUrl = options.portalImageUrl ??
|
|
130
|
+
readEnv(env, 'OFFICE_CLAW_CAS_PORTAL_IMAGE_URL') ??
|
|
131
|
+
readEnv(env, 'CAS_PORTAL_IMAGE_URL') ??
|
|
132
|
+
DEFAULT_CAS_PORTAL_IMAGE_URL;
|
|
133
|
+
return { backgroundImageUrl, portalImageUrl };
|
|
134
|
+
}
|
|
135
|
+
function buildCasLoginUrl(options) {
|
|
136
|
+
const env = options.env ?? process.env;
|
|
137
|
+
const configuredLoginUrl = readEnv(env, 'OFFICE_CLAW_CAS_LOGIN_URL') ?? readEnv(env, 'CAS_LOGIN_URL');
|
|
138
|
+
if (configuredLoginUrl)
|
|
139
|
+
return configuredLoginUrl;
|
|
140
|
+
const casBaseUrl = options.casBaseUrl ?? readEnv(env, 'OFFICE_CLAW_CAS_BASE_URL') ?? DEFAULT_CAS_BASE_URL;
|
|
141
|
+
const serviceCallbackUrl = resolveCasServiceCallbackUrl(options);
|
|
142
|
+
const { backgroundImageUrl, portalImageUrl } = resolveCasVisualAssets(options);
|
|
143
|
+
const url = new URL(casBaseUrl);
|
|
144
|
+
if (!url.searchParams.has('locale'))
|
|
145
|
+
url.searchParams.set('locale', 'zh-cn');
|
|
146
|
+
if (!url.searchParams.has('hide_banner'))
|
|
147
|
+
url.searchParams.set('hide_banner', 'true');
|
|
148
|
+
if (!url.searchParams.has('hide_foot'))
|
|
149
|
+
url.searchParams.set('hide_foot', 'true');
|
|
150
|
+
url.searchParams.set('background_img_url', backgroundImageUrl);
|
|
151
|
+
url.searchParams.set('portal_img_url', portalImageUrl);
|
|
152
|
+
url.searchParams.set('service', serviceCallbackUrl);
|
|
153
|
+
if (!url.hash)
|
|
154
|
+
url.hash = '/login';
|
|
155
|
+
return url.toString();
|
|
156
|
+
}
|
|
157
|
+
function buildCasLogoutUrl(options) {
|
|
158
|
+
const env = options.env ?? process.env;
|
|
159
|
+
const configuredLogoutUrl = readEnv(env, 'OFFICE_CLAW_CAS_LOGOUT_URL') ?? readEnv(env, 'CAS_LOGOUT_URL');
|
|
160
|
+
if (configuredLogoutUrl)
|
|
161
|
+
return configuredLogoutUrl;
|
|
162
|
+
const serviceCallbackUrl = resolveCasServiceCallbackUrl(options);
|
|
163
|
+
const { backgroundImageUrl, portalImageUrl } = resolveCasVisualAssets(options);
|
|
164
|
+
const loginService = `${CAS_AUTH_UI_LOGIN_URL}?locale=zh-cn&hide_banner=true&hide_foot=true` +
|
|
165
|
+
`&background_img_url=${backgroundImageUrl}&portal_img_url=${portalImageUrl}&service=${serviceCallbackUrl}#/login`;
|
|
166
|
+
return `${CAS_AUTH_UI_LOGOUT_URL}?service=${encodeURIComponent(loginService)}`;
|
|
167
|
+
}
|
|
168
|
+
function normalizeProfile(raw, expiresAt) {
|
|
169
|
+
if (!raw || typeof raw !== 'object')
|
|
170
|
+
return null;
|
|
171
|
+
const record = raw;
|
|
172
|
+
const userId = typeof record.user_id === 'string' ? record.user_id : typeof record.userId === 'string' ? record.userId : '';
|
|
173
|
+
if (!userId.trim())
|
|
174
|
+
return null;
|
|
175
|
+
const userName = typeof record.user_name === 'string' ? record.user_name : typeof record.userName === 'string' ? record.userName : userId;
|
|
176
|
+
return {
|
|
177
|
+
...record,
|
|
178
|
+
user_id: userId,
|
|
179
|
+
user_name: userName,
|
|
180
|
+
expiresAt,
|
|
181
|
+
serverStartId: SERVER_STARTUP_TOKEN,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
function getStableUserId(profile) {
|
|
185
|
+
const domain = profile.domain_id || profile.domain_name;
|
|
186
|
+
const user = profile.user_name || profile.user_id;
|
|
187
|
+
return domain ? `${domain}:${user}` : profile.user_id;
|
|
188
|
+
}
|
|
189
|
+
async function readStoredProfile(env, userId) {
|
|
190
|
+
try {
|
|
191
|
+
const raw = await readFile(getSecureConfigPath(env, userId), 'utf8');
|
|
192
|
+
return decryptJson(env, raw);
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
async function readPendingProfile(env, pendingToken) {
|
|
199
|
+
try {
|
|
200
|
+
const raw = await readFile(getPendingConfigPath(env, hashPendingToken(pendingToken)), 'utf8');
|
|
201
|
+
return decryptJson(env, raw);
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
async function writeStoredProfile(env, profile) {
|
|
208
|
+
const filePath = getSecureConfigPath(env, getStableUserId(profile));
|
|
209
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
210
|
+
await writeFile(filePath, encryptJson(env, profile), { encoding: 'utf8', mode: 0o600 });
|
|
211
|
+
}
|
|
212
|
+
async function writePendingProfile(env, pendingToken, profile, pendingTtlMs) {
|
|
213
|
+
const filePath = getPendingConfigPath(env, hashPendingToken(pendingToken));
|
|
214
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
215
|
+
await writeFile(filePath, encryptJson(env, { profile, expiresAt: Date.now() + pendingTtlMs, attempts: 0 }), { encoding: 'utf8', mode: 0o600 });
|
|
216
|
+
}
|
|
217
|
+
async function deleteStoredProfile(env, userId) {
|
|
218
|
+
await rm(getSecureConfigPath(env, userId), { force: true });
|
|
219
|
+
}
|
|
220
|
+
async function deletePendingProfile(env, pendingToken) {
|
|
221
|
+
await rm(getPendingConfigPath(env, hashPendingToken(pendingToken)), { force: true });
|
|
222
|
+
}
|
|
223
|
+
function createPendingFailure(message, userId, pendingToken, errorCode) {
|
|
224
|
+
return { success: false, needCode: true, message, userId, pendingToken, ...(errorCode ? { errorCode } : {}) };
|
|
225
|
+
}
|
|
226
|
+
function getSubscriptionErrorCode(value) {
|
|
227
|
+
const payload = unwrapPayload(value);
|
|
228
|
+
const raw = payload?.error_code ?? payload?.errorCode ?? payload?.code;
|
|
229
|
+
return typeof raw === 'string' ? raw : '';
|
|
230
|
+
}
|
|
231
|
+
function getPromotionCodeErrorMessage(errorCode) {
|
|
232
|
+
return PROMOTION_CODE_ERROR_MESSAGES[errorCode];
|
|
233
|
+
}
|
|
234
|
+
async function validateCasTicket(fetchImpl, ticketValidateUrl, ticket, serviceCallbackUrl, expiresAt) {
|
|
235
|
+
const url = new URL(ticketValidateUrl);
|
|
236
|
+
url.searchParams.set('ticket', ticket);
|
|
237
|
+
if (serviceCallbackUrl)
|
|
238
|
+
url.searchParams.set('service', serviceCallbackUrl);
|
|
239
|
+
const response = await fetchImpl(url.toString(), { method: 'GET' });
|
|
240
|
+
if (!response.ok)
|
|
241
|
+
return null;
|
|
242
|
+
const data = (await response.json());
|
|
243
|
+
const payload = unwrapPayload(data);
|
|
244
|
+
const profilePayload = payload && 'user' in payload ? payload.user : payload;
|
|
245
|
+
const profile = normalizeProfile(profilePayload, expiresAt);
|
|
246
|
+
if (!profile)
|
|
247
|
+
return null;
|
|
248
|
+
const profileRecord = unwrapPayload(profilePayload);
|
|
249
|
+
const modelInfo = extractModelInfo(payload) ?? extractModelInfo(profileRecord);
|
|
250
|
+
if (modelInfo || hasActiveSubscription(payload) || hasActiveSubscription(profileRecord)) {
|
|
251
|
+
profile.model_info = modelInfo ?? {};
|
|
252
|
+
}
|
|
253
|
+
return profile;
|
|
254
|
+
}
|
|
255
|
+
function toPrincipal(profile) {
|
|
256
|
+
const { model_info: modelInfo = {}, ...providerStateRest } = profile;
|
|
257
|
+
const providerState = {
|
|
258
|
+
...providerStateRest,
|
|
259
|
+
modelInfo,
|
|
260
|
+
};
|
|
261
|
+
return {
|
|
262
|
+
userId: getStableUserId(profile),
|
|
263
|
+
displayName: profile.user_name,
|
|
264
|
+
expiresAt: new Date(profile.expiresAt),
|
|
265
|
+
providerState,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
function hasModelInfo(profile) {
|
|
269
|
+
return Boolean(profile.model_info && typeof profile.model_info === 'object');
|
|
270
|
+
}
|
|
271
|
+
async function subscribeWithPromotionCode(fetchImpl, subscriptionUrl, profile, promotionCode) {
|
|
272
|
+
if (!profile.access || !profile.secret)
|
|
273
|
+
return null;
|
|
274
|
+
const body = JSON.stringify({ promotion_code: promotionCode });
|
|
275
|
+
const headers = {
|
|
276
|
+
'Content-Type': 'application/json;charset=utf8',
|
|
277
|
+
...(profile.sts_token ? { 'X-Security-Token': profile.sts_token } : {}),
|
|
278
|
+
};
|
|
279
|
+
const signer = new Signer();
|
|
280
|
+
signer.Key = profile.access;
|
|
281
|
+
signer.Secret = profile.secret;
|
|
282
|
+
const signed = signer.Sign(new HttpRequest('POST', subscriptionUrl, headers, body));
|
|
283
|
+
const response = await fetchImpl(subscriptionUrl, {
|
|
284
|
+
method: 'POST',
|
|
285
|
+
headers: signed.headers,
|
|
286
|
+
body,
|
|
287
|
+
});
|
|
288
|
+
if (!response.ok) {
|
|
289
|
+
try {
|
|
290
|
+
const errorBody = await response.json();
|
|
291
|
+
const errorCode = getSubscriptionErrorCode(errorBody);
|
|
292
|
+
const errorMessage = getPromotionCodeErrorMessage(errorCode);
|
|
293
|
+
console.warn('[Huawei CAS] subscribeWithPromotionCode failed', {
|
|
294
|
+
subscriptionUrl,
|
|
295
|
+
status: response.status,
|
|
296
|
+
errorCode,
|
|
297
|
+
errorMessage,
|
|
298
|
+
errorBody,
|
|
299
|
+
});
|
|
300
|
+
return { errorCode, errorMessage };
|
|
301
|
+
}
|
|
302
|
+
catch (err) {
|
|
303
|
+
console.warn('[Huawei CAS] subscribeWithPromotionCode failed to parse error response', err);
|
|
304
|
+
return { errorCode: '' };
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
const data = (await response.json());
|
|
308
|
+
const subscription = data.subscription && typeof data.subscription === 'object' ? data.subscription : null;
|
|
309
|
+
const modelInfo = data.model_info ?? subscription?.model_info;
|
|
310
|
+
console.debug('[Huawei CAS] subscribeWithPromotionCode response', {
|
|
311
|
+
subscriptionUrl,
|
|
312
|
+
subscription,
|
|
313
|
+
modelInfoPresent: Boolean(modelInfo && typeof modelInfo === 'object'),
|
|
314
|
+
});
|
|
315
|
+
return modelInfo && typeof modelInfo === 'object' ? { modelInfo: modelInfo } : null;
|
|
316
|
+
}
|
|
317
|
+
export function createHuaweiCasAuthProvider(options = {}) {
|
|
318
|
+
const env = options.env ?? process.env;
|
|
319
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
320
|
+
const ticketValidateUrl = options.ticketValidateUrl ?? readEnv(env, 'OFFICE_CLAW_CAS_TICKET_VALIDATE_URL') ?? DEFAULT_TICKET_VALIDATE_URL;
|
|
321
|
+
const subscriptionUrl = options.subscriptionUrl ?? readEnv(env, 'OFFICE_CLAW_CAS_SUBSCRIPTION_URL') ?? DEFAULT_SUBSCRIPTION_URL;
|
|
322
|
+
const serviceCallbackUrl = options.serviceCallbackUrl ?? readEnv(env, 'OFFICE_CLAW_CAS_SERVICE_CALLBACK_URL');
|
|
323
|
+
const configuredSessionTtlMs = Number(readEnv(env, 'OFFICE_CLAW_CAS_SESSION_TTL_MS'));
|
|
324
|
+
const sessionTtlMs = options.sessionTtlMs ?? (Number.isFinite(configuredSessionTtlMs) && configuredSessionTtlMs > 0 ? configuredSessionTtlMs : DEFAULT_SESSION_TTL_MS);
|
|
325
|
+
const configuredPendingTtlMs = Number(readEnv(env, 'OFFICE_CLAW_CAS_PENDING_TTL_MS'));
|
|
326
|
+
const pendingTtlMs = options.pendingTtlMs ?? (Number.isFinite(configuredPendingTtlMs) && configuredPendingTtlMs > 0 ? configuredPendingTtlMs : DEFAULT_PENDING_TTL_MS);
|
|
327
|
+
const canCreateModel = options.canCreateModel ?? readEnv(env, 'CAN_CREATE_MODEL') === '1';
|
|
328
|
+
const loginUrl = buildCasLoginUrl(options);
|
|
329
|
+
const logoutUrl = buildCasLogoutUrl(options);
|
|
330
|
+
return {
|
|
331
|
+
id: 'huawei-cas',
|
|
332
|
+
displayName: 'Huawei Cloud (CAS)',
|
|
333
|
+
presentation: {
|
|
334
|
+
mode: 'redirect',
|
|
335
|
+
redirectUrl: loginUrl,
|
|
336
|
+
fields: [],
|
|
337
|
+
description: 'Sign in with your Huawei Cloud account',
|
|
338
|
+
},
|
|
339
|
+
async authenticate() {
|
|
340
|
+
return { success: false, message: 'Use callback flow for CAS provider' };
|
|
341
|
+
},
|
|
342
|
+
async handleCallback(params) {
|
|
343
|
+
const ticket = params.ticket?.trim();
|
|
344
|
+
const promotionCode = params.promotionCode?.trim();
|
|
345
|
+
const pendingToken = params.pendingToken?.trim();
|
|
346
|
+
if (!ticket && promotionCode && pendingToken) {
|
|
347
|
+
const pending = await readPendingProfile(env, pendingToken);
|
|
348
|
+
if (!pending || pending.expiresAt < Date.now()) {
|
|
349
|
+
await deletePendingProfile(env, pendingToken);
|
|
350
|
+
return { success: false, needCode: true, message: '登录状态已失效,请重新登录' };
|
|
351
|
+
}
|
|
352
|
+
const userId = getStableUserId(pending.profile);
|
|
353
|
+
const subscription = await subscribeWithPromotionCode(fetchImpl, subscriptionUrl, pending.profile, promotionCode);
|
|
354
|
+
if (!subscription?.modelInfo) {
|
|
355
|
+
pending.attempts += 1;
|
|
356
|
+
if (pending.attempts >= 5) {
|
|
357
|
+
await deletePendingProfile(env, pendingToken);
|
|
358
|
+
return { success: false, needCode: true, message: '邀请码错误次数过多,请重新登录' };
|
|
359
|
+
}
|
|
360
|
+
await writeFile(getPendingConfigPath(env, hashPendingToken(pendingToken)), encryptJson(env, pending), { encoding: 'utf8', mode: 0o600 });
|
|
361
|
+
if (subscription?.errorCode === INVITATION_HTML_ERROR_CODE) {
|
|
362
|
+
// Trusted static backend text for common.01010004 only; no external input is interpolated.
|
|
363
|
+
return createPendingFailure(INVITATION_HTML_ERROR_MESSAGE, userId, pendingToken, INVITATION_HTML_ERROR_CODE);
|
|
364
|
+
}
|
|
365
|
+
return createPendingFailure(subscription?.errorMessage ?? '邀请码无效,请重新输入', userId, pendingToken, subscription?.errorCode);
|
|
366
|
+
}
|
|
367
|
+
pending.profile.model_info = subscription.modelInfo;
|
|
368
|
+
await writeStoredProfile(env, pending.profile);
|
|
369
|
+
await deletePendingProfile(env, pendingToken);
|
|
370
|
+
return { success: true, principal: toPrincipal(pending.profile) };
|
|
371
|
+
}
|
|
372
|
+
if (!ticket)
|
|
373
|
+
return { success: false, message: 'Missing CAS ticket' };
|
|
374
|
+
const profile = await validateCasTicket(fetchImpl, ticketValidateUrl, ticket, serviceCallbackUrl, Date.now() + sessionTtlMs);
|
|
375
|
+
if (!profile)
|
|
376
|
+
return { success: false, message: 'Invalid CAS ticket' };
|
|
377
|
+
const userId = getStableUserId(profile);
|
|
378
|
+
const nextPendingToken = randomBytes(32).toString('base64url');
|
|
379
|
+
if (!hasModelInfo(profile) && promotionCode) {
|
|
380
|
+
const subscription = await subscribeWithPromotionCode(fetchImpl, subscriptionUrl, profile, promotionCode);
|
|
381
|
+
if (!subscription?.modelInfo) {
|
|
382
|
+
await writePendingProfile(env, nextPendingToken, profile, pendingTtlMs);
|
|
383
|
+
if (subscription?.errorCode === INVITATION_HTML_ERROR_CODE) {
|
|
384
|
+
// Trusted static backend text for common.01010004 only; no external input is interpolated.
|
|
385
|
+
return createPendingFailure(INVITATION_HTML_ERROR_MESSAGE, userId, nextPendingToken, INVITATION_HTML_ERROR_CODE);
|
|
386
|
+
}
|
|
387
|
+
return createPendingFailure(subscription?.errorMessage ?? '邀请码无效,请重新输入', userId, nextPendingToken, subscription?.errorCode);
|
|
388
|
+
}
|
|
389
|
+
profile.model_info = subscription.modelInfo;
|
|
390
|
+
}
|
|
391
|
+
if (!hasModelInfo(profile)) {
|
|
392
|
+
await writePendingProfile(env, nextPendingToken, profile, pendingTtlMs);
|
|
393
|
+
return createPendingFailure('请输入邀请码后再登录', userId, nextPendingToken);
|
|
394
|
+
}
|
|
395
|
+
await writeStoredProfile(env, profile);
|
|
396
|
+
return { success: true, principal: toPrincipal(profile) };
|
|
397
|
+
},
|
|
398
|
+
async restoreSession(userId) {
|
|
399
|
+
const stored = await readStoredProfile(env, userId);
|
|
400
|
+
if (!stored)
|
|
401
|
+
return null;
|
|
402
|
+
if (stored.serverStartId !== SERVER_STARTUP_TOKEN || stored.expiresAt < Date.now()) {
|
|
403
|
+
await deleteStoredProfile(env, userId);
|
|
404
|
+
return null;
|
|
405
|
+
}
|
|
406
|
+
return toPrincipal(stored);
|
|
407
|
+
},
|
|
408
|
+
async logout(session) {
|
|
409
|
+
await deleteStoredProfile(env, session.userId);
|
|
410
|
+
},
|
|
411
|
+
async getPublicConfig() {
|
|
412
|
+
return { hascode: true, canCreateModel, loginUrl, logoutUrl };
|
|
413
|
+
},
|
|
414
|
+
resolveProtocolCredential(protocol, session) {
|
|
415
|
+
if (protocol !== 'huawei_maas')
|
|
416
|
+
return null;
|
|
417
|
+
try {
|
|
418
|
+
return resolveHuaweiMaaSRuntimeConfig(session.userId, () => ({
|
|
419
|
+
providerState: session.providerState,
|
|
420
|
+
}));
|
|
421
|
+
}
|
|
422
|
+
catch {
|
|
423
|
+
return null;
|
|
424
|
+
}
|
|
425
|
+
},
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
//# sourceMappingURL=huawei-cas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"huawei-cas.js","sourceRoot":"","sources":["../../src/auth/huawei-cas.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { AuthProvider } from '@openjiuwen/relay-api-server-contracts/auth';
|
|
2
|
+
interface HuaweiIamAuthProviderOptions {
|
|
3
|
+
fetchImpl?: typeof fetch;
|
|
4
|
+
}
|
|
5
|
+
export declare function createHuaweiIamAuthProvider(options?: HuaweiIamAuthProviderOptions): AuthProvider;
|
|
6
|
+
export {};
|
|
7
|
+
//# sourceMappingURL=huawei-iam.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"huawei-iam.d.ts","sourceRoot":"","sources":["../../src/auth/huawei-iam.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAuB,YAAY,EAAmB,MAAM,6CAA6C,CAAC;AA6BtH,UAAU,4BAA4B;IACpC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AA0GD,wBAAgB,2BAA2B,CAAC,OAAO,GAAE,4BAAiC,GAAG,YAAY,CAsGpG"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { resolveHuaweiMaaSRuntimeConfig } from '../integrations/huawei-maas.js';
|
|
2
|
+
const IAM_URL = 'https://iam.myhuaweicloud.com';
|
|
3
|
+
const DEFAULT_PROMOTION_CODE = 'huawei_dev_blue';
|
|
4
|
+
async function getErrorMessage(response) {
|
|
5
|
+
const data = (await response.json());
|
|
6
|
+
return {
|
|
7
|
+
error_code: typeof data.error_code === 'string' ? data.error_code : String(response.status),
|
|
8
|
+
error_message: typeof data.error_message === 'string'
|
|
9
|
+
? data.error_message
|
|
10
|
+
: typeof data.error_msg === 'string'
|
|
11
|
+
? data.error_msg
|
|
12
|
+
: response.statusText,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
async function getTokens(fetchImpl, domainName, userName, password) {
|
|
16
|
+
try {
|
|
17
|
+
const authResponse = await fetchImpl(`${IAM_URL}/v3/auth/tokens`, {
|
|
18
|
+
method: 'POST',
|
|
19
|
+
headers: { 'Content-Type': 'application/json;charset=utf8' },
|
|
20
|
+
body: JSON.stringify({
|
|
21
|
+
auth: {
|
|
22
|
+
identity: {
|
|
23
|
+
methods: ['password'],
|
|
24
|
+
password: {
|
|
25
|
+
user: {
|
|
26
|
+
domain: { name: domainName },
|
|
27
|
+
name: userName,
|
|
28
|
+
password,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
scope: { project: { name: 'cn-north-4' } },
|
|
33
|
+
},
|
|
34
|
+
}),
|
|
35
|
+
});
|
|
36
|
+
if (!authResponse.ok) {
|
|
37
|
+
const { error_code, error_message } = await getErrorMessage(authResponse);
|
|
38
|
+
throw new Error(`认证失败,错误码: ${error_code}, 错误信息: ${error_message}`);
|
|
39
|
+
}
|
|
40
|
+
const data = (await authResponse.json());
|
|
41
|
+
const tokenObj = data.token;
|
|
42
|
+
const userObj = tokenObj?.user;
|
|
43
|
+
const domainObj = userObj?.domain;
|
|
44
|
+
const domainId = domainObj?.id || domainName;
|
|
45
|
+
const expiresAt = tokenObj?.expires_at || new Date().toISOString();
|
|
46
|
+
return {
|
|
47
|
+
success: true,
|
|
48
|
+
token: authResponse.headers.get('x-subject-token') ?? '',
|
|
49
|
+
expiresAt,
|
|
50
|
+
domainId,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
console.error('获取IAM Token失败:', error);
|
|
55
|
+
return { success: false, message: '登录失败' };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async function subscriptionClaw(fetchImpl, token, rememberedPromotionCode, promotionCode) {
|
|
59
|
+
try {
|
|
60
|
+
const subResponse = await fetchImpl(`https://versatile.cn-north-4.myhuaweicloud.com/v1/claw/client-subscription`, {
|
|
61
|
+
method: 'POST',
|
|
62
|
+
headers: {
|
|
63
|
+
'Content-Type': 'application/json;charset=utf8',
|
|
64
|
+
'X-Auth-Token': token,
|
|
65
|
+
},
|
|
66
|
+
body: JSON.stringify({
|
|
67
|
+
promotion_code: promotionCode || rememberedPromotionCode || DEFAULT_PROMOTION_CODE,
|
|
68
|
+
}),
|
|
69
|
+
});
|
|
70
|
+
if (!subResponse.ok) {
|
|
71
|
+
const { error_code, error_message } = await getErrorMessage(subResponse);
|
|
72
|
+
const needCode = ['AgentArts.11000008', 'AgentArts.11000009'].includes(error_code);
|
|
73
|
+
console.error(`开通客户端失败,错误码: ${error_code}, 错误信息: ${error_message}`);
|
|
74
|
+
return { success: false, message: needCode ? '邀请码无效,请重新输入' : '开通失败', needCode };
|
|
75
|
+
}
|
|
76
|
+
const data = (await subResponse.json());
|
|
77
|
+
return { success: true, modelInfo: data.model_info ?? {} };
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
console.error('开通客户端claw失败:', error);
|
|
81
|
+
return { success: false, message: '开通失败' };
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
export function createHuaweiIamAuthProvider(options = {}) {
|
|
85
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
86
|
+
let lastPromotionCode = null;
|
|
87
|
+
return {
|
|
88
|
+
id: 'huawei-iam',
|
|
89
|
+
displayName: 'Huawei IAM',
|
|
90
|
+
presentation: {
|
|
91
|
+
mode: 'form',
|
|
92
|
+
submitLabel: '登录',
|
|
93
|
+
fields: [
|
|
94
|
+
{
|
|
95
|
+
name: 'userType',
|
|
96
|
+
label: '账号类型',
|
|
97
|
+
type: 'select',
|
|
98
|
+
required: true,
|
|
99
|
+
options: [
|
|
100
|
+
{ value: 'huawei', label: '华为云账号' },
|
|
101
|
+
{ value: 'iam', label: 'IAM 用户' },
|
|
102
|
+
],
|
|
103
|
+
},
|
|
104
|
+
{ name: 'domainName', label: '租户 / 域名', type: 'text', required: true },
|
|
105
|
+
{ name: 'userName', label: 'IAM 用户名', type: 'text', required: false },
|
|
106
|
+
{ name: 'password', label: '密码', type: 'password', required: true },
|
|
107
|
+
{ name: 'promotionCode', label: '邀请码', type: 'text', required: false },
|
|
108
|
+
],
|
|
109
|
+
description: 'Authenticate with Huawei IAM.',
|
|
110
|
+
},
|
|
111
|
+
async getPublicConfig() {
|
|
112
|
+
return { hascode: Boolean(lastPromotionCode || DEFAULT_PROMOTION_CODE) };
|
|
113
|
+
},
|
|
114
|
+
/**
|
|
115
|
+
* Core authentication: credentials → identity.
|
|
116
|
+
* Also performs MaaS subscription check — if an invite code is required,
|
|
117
|
+
* authentication fails with `needCode: true` BEFORE session creation.
|
|
118
|
+
*/
|
|
119
|
+
async authenticate(input) {
|
|
120
|
+
const raw = input.credentials;
|
|
121
|
+
const domainName = typeof raw.domainName === 'string' ? raw.domainName.trim() : '';
|
|
122
|
+
const password = typeof raw.password === 'string' ? raw.password : '';
|
|
123
|
+
const userType = raw.userType === 'iam' ? 'iam' : 'huawei';
|
|
124
|
+
const userName = typeof raw.userName === 'string' ? raw.userName.trim() : '';
|
|
125
|
+
const name = userType === 'huawei' ? domainName : userName;
|
|
126
|
+
if (!domainName || !password || !name) {
|
|
127
|
+
return { success: false, message: '用户名或密码错误' };
|
|
128
|
+
}
|
|
129
|
+
const tokenResult = await getTokens(fetchImpl, domainName, name, password);
|
|
130
|
+
if (!tokenResult.success || !tokenResult.token) {
|
|
131
|
+
return { success: false, message: tokenResult.message || '登录失败' };
|
|
132
|
+
}
|
|
133
|
+
const promotionCode = typeof raw.promotionCode === 'string' && raw.promotionCode.trim() ? raw.promotionCode.trim() : undefined;
|
|
134
|
+
// Subscription check — must happen before session creation so needCode
|
|
135
|
+
// can surface to the frontend as an auth failure.
|
|
136
|
+
const subResult = await subscriptionClaw(fetchImpl, tokenResult.token, lastPromotionCode, promotionCode);
|
|
137
|
+
if (!subResult.success) {
|
|
138
|
+
if (subResult.needCode) {
|
|
139
|
+
lastPromotionCode = null;
|
|
140
|
+
return { success: false, message: subResult.message || '邀请码无效,请重新输入', needCode: true };
|
|
141
|
+
}
|
|
142
|
+
return { success: false, message: subResult.message || '开通失败' };
|
|
143
|
+
}
|
|
144
|
+
if (promotionCode) {
|
|
145
|
+
lastPromotionCode = promotionCode;
|
|
146
|
+
}
|
|
147
|
+
const providerState = {
|
|
148
|
+
token: tokenResult.token,
|
|
149
|
+
modelInfo: subResult.modelInfo ?? {},
|
|
150
|
+
domainId: tokenResult.domainId || domainName,
|
|
151
|
+
promotionCode,
|
|
152
|
+
};
|
|
153
|
+
return {
|
|
154
|
+
success: true,
|
|
155
|
+
principal: {
|
|
156
|
+
userId: `${domainName}:${name}`,
|
|
157
|
+
displayName: name,
|
|
158
|
+
expiresAt: tokenResult.expiresAt ? new Date(tokenResult.expiresAt) : null,
|
|
159
|
+
providerState,
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
},
|
|
163
|
+
resolveProtocolCredential(protocol, session) {
|
|
164
|
+
if (protocol !== 'huawei_maas')
|
|
165
|
+
return null;
|
|
166
|
+
try {
|
|
167
|
+
return resolveHuaweiMaaSRuntimeConfig(session.userId, () => ({
|
|
168
|
+
providerState: session.providerState,
|
|
169
|
+
}));
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=huawei-iam.js.map
|