@cloudbase/manager-node 5.6.0-beta.1 → 5.6.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/lib/integration/constants.js +1 -3
- package/lib/integration/index.js +35 -174
- package/package.json +1 -1
- package/types/integration/constants.d.ts +0 -2
- package/types/integration/index.d.ts +15 -34
- package/types/integration/types.d.ts +10 -24
- package/lib/integration/crypto.js +0 -102
- package/lib/integration/fields.js +0 -69
- package/lib/integration/secretKey.js +0 -92
- package/types/integration/crypto.d.ts +0 -16
- package/types/integration/fields.d.ts +0 -17
- package/types/integration/secretKey.d.ts +0 -15
|
@@ -3,8 +3,6 @@
|
|
|
3
3
|
* 集成中心常量定义(与 dev-platform 对齐)
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.
|
|
6
|
+
exports.USER_KEY_BUSINESS = void 0;
|
|
7
7
|
/** 业务类型标识:集成中心来源的密钥对 */
|
|
8
8
|
exports.USER_KEY_BUSINESS = 'integration';
|
|
9
|
-
/** 密文最小长度(小于该长度视为非密文,解密时原样返回) */
|
|
10
|
-
exports.MIN_AES_CIPHERTEXT_LENGTH = 24;
|
package/lib/integration/index.js
CHANGED
|
@@ -23,14 +23,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
23
23
|
exports.IntegrationService = void 0;
|
|
24
24
|
const error_1 = require("../error");
|
|
25
25
|
const utils_1 = require("../utils");
|
|
26
|
-
const
|
|
27
|
-
const fields_1 = require("./fields");
|
|
26
|
+
const constants_1 = require("./constants");
|
|
28
27
|
__exportStar(require("./types"), exports);
|
|
29
28
|
/**
|
|
30
29
|
* 集成中心(UserKey)服务
|
|
31
30
|
*
|
|
32
31
|
* 复用现有 tcb 云 API(version 2018-06-08),统一携带 Business='integration'。
|
|
33
|
-
*
|
|
32
|
+
*
|
|
33
|
+
* 本服务只做云 API 的纯封装:不识别模板字段、不做敏感字段加解密、不做
|
|
34
|
+
* "绑定资源"这类编排逻辑——这些依赖模板结构 / 七彩石密钥 / 上层交互,
|
|
35
|
+
* 由调用方(CLI / 控制台)按各自场景自行编排后,通过 `ext` 传入最终 JSON 字符串。
|
|
34
36
|
*/
|
|
35
37
|
class IntegrationService {
|
|
36
38
|
constructor(environment) {
|
|
@@ -40,19 +42,20 @@ class IntegrationService {
|
|
|
40
42
|
// =========== 只读 API ===========
|
|
41
43
|
/** 列出集成模板(密钥对模版列表) */
|
|
42
44
|
async listTemplates(params = {}) {
|
|
43
|
-
var _a, _b;
|
|
45
|
+
var _a, _b, _c;
|
|
44
46
|
const { envId } = this.getEnvInfo();
|
|
45
47
|
return this.tcbService.request('GetUserKeyTemplateList', {
|
|
46
48
|
EnvId: envId,
|
|
47
49
|
Offset: (_a = params.offset) !== null && _a !== void 0 ? _a : 0,
|
|
48
50
|
Limit: (_b = params.limit) !== null && _b !== void 0 ? _b : 100,
|
|
49
51
|
AuthTypeCode: params.authTypeCode,
|
|
50
|
-
AuthTypeName: params.authTypeName
|
|
52
|
+
AuthTypeName: params.authTypeName,
|
|
53
|
+
Business: (_c = params.business) !== null && _c !== void 0 ? _c : constants_1.USER_KEY_BUSINESS // 默认 integration
|
|
51
54
|
});
|
|
52
55
|
}
|
|
53
56
|
/** 列出集成实例(密钥对列表) */
|
|
54
57
|
async listUserKeys(params = {}) {
|
|
55
|
-
var _a, _b;
|
|
58
|
+
var _a, _b, _c;
|
|
56
59
|
const { envId } = this.getEnvInfo();
|
|
57
60
|
return this.tcbService.request('GetUserKeyList', {
|
|
58
61
|
EnvId: envId,
|
|
@@ -60,77 +63,66 @@ class IntegrationService {
|
|
|
60
63
|
Limit: (_b = params.limit) !== null && _b !== void 0 ? _b : 100,
|
|
61
64
|
Name: params.name,
|
|
62
65
|
AuthTypeCode: params.authTypeCode,
|
|
63
|
-
Business: params.business //
|
|
66
|
+
Business: (_c = params.business) !== null && _c !== void 0 ? _c : constants_1.USER_KEY_BUSINESS // 默认 integration
|
|
64
67
|
});
|
|
65
68
|
}
|
|
66
69
|
/** 查询单个集成实例详情 */
|
|
67
70
|
async getUserKey(params) {
|
|
68
|
-
var _a;
|
|
71
|
+
var _a, _b;
|
|
69
72
|
this.validateRequiredParams(params, ['keyId']);
|
|
70
73
|
const { envId } = this.getEnvInfo();
|
|
71
74
|
return this.tcbService.request('GetOneUserKey', {
|
|
72
75
|
EnvId: envId,
|
|
73
76
|
KeyID: params.keyId,
|
|
74
77
|
NeedPwd: (_a = params.needPwd) !== null && _a !== void 0 ? _a : false,
|
|
75
|
-
Business: params.business //
|
|
78
|
+
Business: (_b = params.business) !== null && _b !== void 0 ? _b : constants_1.USER_KEY_BUSINESS // 默认 integration
|
|
76
79
|
});
|
|
77
80
|
}
|
|
78
81
|
// =========== 写 API ===========
|
|
79
|
-
/**
|
|
82
|
+
/**
|
|
83
|
+
* 创建集成实例。
|
|
84
|
+
*
|
|
85
|
+
* `Ext` 为最终写入的 JSON 字符串,由调用方按模板字段自行拼装(含敏感字段
|
|
86
|
+
* 加密),本方法不做任何解析或加工,原样传递给云 API。
|
|
87
|
+
*/
|
|
80
88
|
async createUserKey(params) {
|
|
89
|
+
var _a;
|
|
81
90
|
this.validateRequiredParams(params, ['name', 'authTypeCode']);
|
|
82
91
|
const { envId } = this.getEnvInfo();
|
|
83
|
-
const ext = await this.buildExt({
|
|
84
|
-
authTypeCode: params.authTypeCode,
|
|
85
|
-
envVariables: params.envVariables,
|
|
86
|
-
ext: params.ext
|
|
87
|
-
});
|
|
88
92
|
return this.tcbService.request('CreateUserKey', {
|
|
89
93
|
EnvId: envId,
|
|
90
94
|
Name: params.name,
|
|
91
95
|
KeyID: params.keyId,
|
|
92
96
|
AuthTypeCode: params.authTypeCode,
|
|
93
97
|
Description: params.description,
|
|
94
|
-
Ext: ext,
|
|
98
|
+
Ext: params.ext,
|
|
95
99
|
TicketID: params.ticketId,
|
|
96
|
-
Business: params.business, //
|
|
97
|
-
DemoCodeFunctionName: params.demoCodeFunctionName,
|
|
98
|
-
RuntimeConfig: params.runtimeConfig
|
|
100
|
+
Business: (_a = params.business) !== null && _a !== void 0 ? _a : constants_1.USER_KEY_BUSINESS, // 默认 integration
|
|
101
|
+
DemoCodeFunctionName: params.demoCodeFunctionName,
|
|
102
|
+
RuntimeConfig: params.runtimeConfig
|
|
99
103
|
});
|
|
100
104
|
}
|
|
101
|
-
/**
|
|
105
|
+
/**
|
|
106
|
+
* 更新集成实例。
|
|
107
|
+
*
|
|
108
|
+
* `Ext` 为最终写入的 JSON 字符串,由调用方自行完成"与现有 Ext 合并"
|
|
109
|
+
* "敏感字段加密"等编排后传入;未传 `ext` 时后端保留原有 Ext 不变。
|
|
110
|
+
*/
|
|
102
111
|
async updateUserKey(params) {
|
|
103
|
-
var _a
|
|
112
|
+
var _a;
|
|
104
113
|
this.validateRequiredParams(params, ['keyId']);
|
|
105
114
|
const { envId } = this.getEnvInfo();
|
|
106
|
-
|
|
107
|
-
let authTypeCode = params.authTypeCode;
|
|
108
|
-
let existingExt;
|
|
109
|
-
if (params.envVariables) {
|
|
110
|
-
const existing = await this.getUserKey({ keyId: params.keyId });
|
|
111
|
-
existingExt = (_a = existing.UserKeyInfo) === null || _a === void 0 ? void 0 : _a.Ext;
|
|
112
|
-
if (!authTypeCode) {
|
|
113
|
-
authTypeCode = (_b = existing.UserKeyInfo) === null || _b === void 0 ? void 0 : _b.AuthTypeCode;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
const ext = await this.buildExt({
|
|
117
|
-
authTypeCode,
|
|
118
|
-
envVariables: params.envVariables,
|
|
119
|
-
ext: params.ext,
|
|
120
|
-
existingExt
|
|
121
|
-
});
|
|
122
|
-
const requestParams = {
|
|
115
|
+
return this.tcbService.request('UpdateUserKey', {
|
|
123
116
|
EnvId: envId,
|
|
124
117
|
KeyID: params.keyId,
|
|
125
118
|
Name: params.name,
|
|
126
|
-
AuthTypeCode: authTypeCode,
|
|
119
|
+
AuthTypeCode: params.authTypeCode,
|
|
127
120
|
Description: params.description,
|
|
128
|
-
Ext: ext,
|
|
121
|
+
Ext: params.ext,
|
|
129
122
|
DemoCodeFunctionName: params.demoCodeFunctionName,
|
|
130
|
-
Business: params.business,
|
|
123
|
+
Business: (_a = params.business) !== null && _a !== void 0 ? _a : constants_1.USER_KEY_BUSINESS, // 默认 integration
|
|
131
124
|
RuntimeConfig: params.runtimeConfig
|
|
132
|
-
};
|
|
133
|
-
return this.tcbService.request('UpdateUserKey', requestParams);
|
|
125
|
+
});
|
|
134
126
|
}
|
|
135
127
|
/** 删除集成实例 */
|
|
136
128
|
async deleteUserKey(params) {
|
|
@@ -141,135 +133,7 @@ class IntegrationService {
|
|
|
141
133
|
KeyID: params.keyId
|
|
142
134
|
});
|
|
143
135
|
}
|
|
144
|
-
// =========== 编排 API ===========
|
|
145
|
-
/**
|
|
146
|
-
* 将集成实例绑定到云函数(口径 A)。
|
|
147
|
-
*
|
|
148
|
-
* 绑定载体为 `DemoCodeFunctionName`:调 `UpdateUserKey` 设置该字段,
|
|
149
|
-
* 后端负责把集成配置注入函数运行时;CLI 不读写函数环境变量。
|
|
150
|
-
*
|
|
151
|
-
* - 集成与云函数一对一:已绑定到不同函数且未传 `force` 时报错;
|
|
152
|
-
* - 幂等:已绑定到同一函数时直接返回,不发送请求;
|
|
153
|
-
* - 不携带 `Ext`,保留集成原有配置。
|
|
154
|
-
*/
|
|
155
|
-
async bindFunction(params) {
|
|
156
|
-
this.validateRequiredParams(params, ['keyId', 'functionName']);
|
|
157
|
-
const { keyId, functionName, force } = params;
|
|
158
|
-
const { UserKeyInfo } = await this.getUserKey({ keyId, business: 'integration' });
|
|
159
|
-
if (!UserKeyInfo) {
|
|
160
|
-
throw new error_1.CloudBaseError(`集成实例不存在: ${keyId}`);
|
|
161
|
-
}
|
|
162
|
-
const previousFunctionName = UserKeyInfo.DemoCodeFunctionName;
|
|
163
|
-
// 已绑定到同一函数:幂等返回
|
|
164
|
-
if (previousFunctionName === functionName) {
|
|
165
|
-
return {
|
|
166
|
-
keyId,
|
|
167
|
-
functionName,
|
|
168
|
-
previousFunctionName,
|
|
169
|
-
overwritten: false,
|
|
170
|
-
alreadyBound: true
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
// 已绑定到不同函数:需 force 覆盖
|
|
174
|
-
const overwritten = Boolean(previousFunctionName);
|
|
175
|
-
if (overwritten && !force) {
|
|
176
|
-
throw new error_1.CloudBaseError(`集成实例 ${keyId} 已绑定云函数 ${previousFunctionName},如需改绑请使用 force 覆盖`);
|
|
177
|
-
}
|
|
178
|
-
// 与控制台对齐:传递完整参数,business: 'integration' 标记来源
|
|
179
|
-
const result = await this.updateUserKey({
|
|
180
|
-
keyId,
|
|
181
|
-
name: UserKeyInfo.Name,
|
|
182
|
-
authTypeCode: UserKeyInfo.AuthTypeCode,
|
|
183
|
-
description: UserKeyInfo.Description,
|
|
184
|
-
ext: UserKeyInfo.Ext,
|
|
185
|
-
demoCodeFunctionName: functionName,
|
|
186
|
-
business: 'integration'
|
|
187
|
-
});
|
|
188
|
-
return {
|
|
189
|
-
keyId,
|
|
190
|
-
functionName,
|
|
191
|
-
previousFunctionName,
|
|
192
|
-
overwritten,
|
|
193
|
-
alreadyBound: false
|
|
194
|
-
};
|
|
195
|
-
}
|
|
196
|
-
/**
|
|
197
|
-
* 解密集成实例 Ext 中的 password 字段,返回扁平明文对象。
|
|
198
|
-
* 供 bind-resource / 详情展示使用;解密失败的字段保留原值。
|
|
199
|
-
*/
|
|
200
|
-
async decryptUserKeyExt(userKeyInfo) {
|
|
201
|
-
const extObj = this.safeParseExt(userKeyInfo.Ext);
|
|
202
|
-
const passwordFieldNames = await this.resolvePasswordFieldNames(userKeyInfo.AuthTypeCode);
|
|
203
|
-
if (!passwordFieldNames.length) {
|
|
204
|
-
return extObj;
|
|
205
|
-
}
|
|
206
|
-
const { envId } = this.getEnvInfo();
|
|
207
|
-
const aesKey = await (0, secretKey_1.getAesKey)({ envId });
|
|
208
|
-
return (0, fields_1.decryptPasswordFields)(extObj, passwordFieldNames, aesKey);
|
|
209
|
-
}
|
|
210
136
|
// =========== 辅助方法 ===========
|
|
211
|
-
/**
|
|
212
|
-
* 构建最终写入的 Ext(JSON 字符串)。
|
|
213
|
-
*
|
|
214
|
-
* - 显式传入 `ext` 时优先使用(不做额外处理);
|
|
215
|
-
* - 传入 `envVariables` 时:
|
|
216
|
-
* - 提供 `existingExt`(update 场景)时与现有 Ext **合并**,仅加密本次传入(变更)
|
|
217
|
-
* 的 password 字段,未传入的 password 字段保留现有密文、非 password 字段保留原值;
|
|
218
|
-
* - **编辑模式下空值删除字段**(与控制台对齐:空值表示不更新);
|
|
219
|
-
* - 未提供 `existingExt`(create 场景)时按模板识别 password 字段并加密有值字段;
|
|
220
|
-
* - 两者均未传时返回 undefined(保持原有 Ext 不变)。
|
|
221
|
-
*/
|
|
222
|
-
async buildExt(params) {
|
|
223
|
-
if (params.ext !== undefined) {
|
|
224
|
-
return params.ext;
|
|
225
|
-
}
|
|
226
|
-
if (!params.envVariables) {
|
|
227
|
-
return undefined;
|
|
228
|
-
}
|
|
229
|
-
const passwordFieldNames = await this.resolvePasswordFieldNames(params.authTypeCode);
|
|
230
|
-
let changedObj = Object.assign({}, params.envVariables);
|
|
231
|
-
// 编辑模式(existingExt 存在):删除空值字段(表示不更新,与控制台对齐)
|
|
232
|
-
if (params.existingExt) {
|
|
233
|
-
for (const [name, value] of Object.entries(changedObj)) {
|
|
234
|
-
if (value === '' || value === null || value === undefined) {
|
|
235
|
-
delete changedObj[name];
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
// 仅加密本次传入(变更)的 password 字段,避免对现有密文二次加密
|
|
240
|
-
const changedPasswordNames = passwordFieldNames.filter(name => name in changedObj);
|
|
241
|
-
if (changedPasswordNames.length) {
|
|
242
|
-
const { envId } = this.getEnvInfo();
|
|
243
|
-
// 注:manager-node 暂以国内七彩石为默认;国际站由上层透传后扩展
|
|
244
|
-
const aesKey = await (0, secretKey_1.getAesKey)({ envId });
|
|
245
|
-
changedObj = (0, fields_1.encryptPasswordFields)(changedObj, changedPasswordNames, aesKey);
|
|
246
|
-
}
|
|
247
|
-
// 合并现有 Ext(update):现有字段打底,变更字段覆盖
|
|
248
|
-
const existingObj = params.existingExt ? this.safeParseExt(params.existingExt) : {};
|
|
249
|
-
const merged = Object.assign(Object.assign({}, existingObj), changedObj);
|
|
250
|
-
return JSON.stringify(merged);
|
|
251
|
-
}
|
|
252
|
-
/** 按 authTypeCode 拉取模板并解析 password 字段名集合 */
|
|
253
|
-
async resolvePasswordFieldNames(authTypeCode) {
|
|
254
|
-
var _a, _b;
|
|
255
|
-
if (!authTypeCode)
|
|
256
|
-
return [];
|
|
257
|
-
const res = await this.listTemplates({ authTypeCode, limit: 1 });
|
|
258
|
-
const templateExt = (_b = (_a = res.KeyTemplateList) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.Ext;
|
|
259
|
-
return (0, fields_1.parsePasswordFieldNames)(templateExt);
|
|
260
|
-
}
|
|
261
|
-
/** 安全解析实例 Ext(扁平对象),失败返回空对象 */
|
|
262
|
-
safeParseExt(ext) {
|
|
263
|
-
if (!ext)
|
|
264
|
-
return {};
|
|
265
|
-
try {
|
|
266
|
-
const parsed = JSON.parse(ext);
|
|
267
|
-
return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
|
|
268
|
-
}
|
|
269
|
-
catch (_a) {
|
|
270
|
-
return {};
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
137
|
/** 获取环境信息 */
|
|
274
138
|
getEnvInfo() {
|
|
275
139
|
const envConfig = this.environment.lazyEnvironmentConfig;
|
|
@@ -305,6 +169,3 @@ __decorate([
|
|
|
305
169
|
__decorate([
|
|
306
170
|
(0, utils_1.preLazy)()
|
|
307
171
|
], IntegrationService.prototype, "deleteUserKey", null);
|
|
308
|
-
__decorate([
|
|
309
|
-
(0, utils_1.preLazy)()
|
|
310
|
-
], IntegrationService.prototype, "bindFunction", null);
|
package/package.json
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { Environment } from '../environment';
|
|
2
|
-
import { IListTemplatesParams, IListUserKeysParams, IGetUserKeyParams, ICreateUserKeyParams, IUpdateUserKeyParams, IDeleteUserKeyParams, IGetUserKeyTemplateListResult, IGetUserKeyListResult, IGetOneUserKeyResult, ICreateUserKeyResult, IUpdateUserKeyResult, IDeleteUserKeyResult
|
|
2
|
+
import { IListTemplatesParams, IListUserKeysParams, IGetUserKeyParams, ICreateUserKeyParams, IUpdateUserKeyParams, IDeleteUserKeyParams, IGetUserKeyTemplateListResult, IGetUserKeyListResult, IGetOneUserKeyResult, ICreateUserKeyResult, IUpdateUserKeyResult, IDeleteUserKeyResult } from './types';
|
|
3
3
|
export * from './types';
|
|
4
4
|
/**
|
|
5
5
|
* 集成中心(UserKey)服务
|
|
6
6
|
*
|
|
7
7
|
* 复用现有 tcb 云 API(version 2018-06-08),统一携带 Business='integration'。
|
|
8
|
-
*
|
|
8
|
+
*
|
|
9
|
+
* 本服务只做云 API 的纯封装:不识别模板字段、不做敏感字段加解密、不做
|
|
10
|
+
* "绑定资源"这类编排逻辑——这些依赖模板结构 / 七彩石密钥 / 上层交互,
|
|
11
|
+
* 由调用方(CLI / 控制台)按各自场景自行编排后,通过 `ext` 传入最终 JSON 字符串。
|
|
9
12
|
*/
|
|
10
13
|
export declare class IntegrationService {
|
|
11
14
|
private tcbService;
|
|
@@ -17,44 +20,22 @@ export declare class IntegrationService {
|
|
|
17
20
|
listUserKeys(params?: IListUserKeysParams): Promise<IGetUserKeyListResult>;
|
|
18
21
|
/** 查询单个集成实例详情 */
|
|
19
22
|
getUserKey(params: IGetUserKeyParams): Promise<IGetOneUserKeyResult>;
|
|
20
|
-
/** 创建集成实例 */
|
|
21
|
-
createUserKey(params: ICreateUserKeyParams): Promise<ICreateUserKeyResult>;
|
|
22
|
-
/** 更新集成实例 */
|
|
23
|
-
updateUserKey(params: IUpdateUserKeyParams): Promise<IUpdateUserKeyResult>;
|
|
24
|
-
/** 删除集成实例 */
|
|
25
|
-
deleteUserKey(params: IDeleteUserKeyParams): Promise<IDeleteUserKeyResult>;
|
|
26
23
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* 绑定载体为 `DemoCodeFunctionName`:调 `UpdateUserKey` 设置该字段,
|
|
30
|
-
* 后端负责把集成配置注入函数运行时;CLI 不读写函数环境变量。
|
|
24
|
+
* 创建集成实例。
|
|
31
25
|
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* - 不携带 `Ext`,保留集成原有配置。
|
|
26
|
+
* `Ext` 为最终写入的 JSON 字符串,由调用方按模板字段自行拼装(含敏感字段
|
|
27
|
+
* 加密),本方法不做任何解析或加工,原样传递给云 API。
|
|
35
28
|
*/
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* 解密集成实例 Ext 中的 password 字段,返回扁平明文对象。
|
|
39
|
-
* 供 bind-resource / 详情展示使用;解密失败的字段保留原值。
|
|
40
|
-
*/
|
|
41
|
-
decryptUserKeyExt(userKeyInfo: IUserKeyInfo): Promise<Record<string, string>>;
|
|
29
|
+
createUserKey(params: ICreateUserKeyParams): Promise<ICreateUserKeyResult>;
|
|
42
30
|
/**
|
|
43
|
-
*
|
|
31
|
+
* 更新集成实例。
|
|
44
32
|
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* - 提供 `existingExt`(update 场景)时与现有 Ext **合并**,仅加密本次传入(变更)
|
|
48
|
-
* 的 password 字段,未传入的 password 字段保留现有密文、非 password 字段保留原值;
|
|
49
|
-
* - **编辑模式下空值删除字段**(与控制台对齐:空值表示不更新);
|
|
50
|
-
* - 未提供 `existingExt`(create 场景)时按模板识别 password 字段并加密有值字段;
|
|
51
|
-
* - 两者均未传时返回 undefined(保持原有 Ext 不变)。
|
|
33
|
+
* `Ext` 为最终写入的 JSON 字符串,由调用方自行完成"与现有 Ext 合并"
|
|
34
|
+
* "敏感字段加密"等编排后传入;未传 `ext` 时后端保留原有 Ext 不变。
|
|
52
35
|
*/
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
|
|
56
|
-
/** 安全解析实例 Ext(扁平对象),失败返回空对象 */
|
|
57
|
-
private safeParseExt;
|
|
36
|
+
updateUserKey(params: IUpdateUserKeyParams): Promise<IUpdateUserKeyResult>;
|
|
37
|
+
/** 删除集成实例 */
|
|
38
|
+
deleteUserKey(params: IDeleteUserKeyParams): Promise<IDeleteUserKeyResult>;
|
|
58
39
|
/** 获取环境信息 */
|
|
59
40
|
private getEnvInfo;
|
|
60
41
|
/** 校验必填参数 */
|
|
@@ -96,6 +96,8 @@ export interface IListTemplatesParams {
|
|
|
96
96
|
limit?: number;
|
|
97
97
|
authTypeCode?: string;
|
|
98
98
|
authTypeName?: string;
|
|
99
|
+
/** 业务类型,integration:从集成中心来的,默认为空,表示是从授权管理中心来的 */
|
|
100
|
+
business?: string;
|
|
99
101
|
}
|
|
100
102
|
/** 列出集成实例入参 */
|
|
101
103
|
export interface IListUserKeysParams {
|
|
@@ -119,9 +121,10 @@ export interface ICreateUserKeyParams {
|
|
|
119
121
|
name: string;
|
|
120
122
|
authTypeCode: string;
|
|
121
123
|
description?: string;
|
|
122
|
-
/**
|
|
123
|
-
|
|
124
|
-
|
|
124
|
+
/**
|
|
125
|
+
* Ext JSON 字符串(模板动态字段的最终取值,含敏感字段密文)。
|
|
126
|
+
* 由调用方按模板字段自行拼装(含加密),本层不做任何解析或加工。
|
|
127
|
+
*/
|
|
125
128
|
ext?: string;
|
|
126
129
|
keyId?: string;
|
|
127
130
|
ticketId?: string;
|
|
@@ -138,9 +141,10 @@ export interface IUpdateUserKeyParams {
|
|
|
138
141
|
name?: string;
|
|
139
142
|
authTypeCode?: string;
|
|
140
143
|
description?: string;
|
|
141
|
-
/**
|
|
142
|
-
|
|
143
|
-
|
|
144
|
+
/**
|
|
145
|
+
* Ext JSON 字符串(模板动态字段的最终取值,含敏感字段密文)。
|
|
146
|
+
* 由调用方自行完成"与现有 Ext 合并"等编排后传入;未传时后端保留原有 Ext 不变。
|
|
147
|
+
*/
|
|
144
148
|
ext?: string;
|
|
145
149
|
/** 绑定的示例代码云函数名称(bind-resource 使用) */
|
|
146
150
|
demoCodeFunctionName?: string;
|
|
@@ -153,24 +157,6 @@ export interface IUpdateUserKeyParams {
|
|
|
153
157
|
export interface IDeleteUserKeyParams {
|
|
154
158
|
keyId: string;
|
|
155
159
|
}
|
|
156
|
-
/** 绑定集成到云函数入参 */
|
|
157
|
-
export interface IBindFunctionParams {
|
|
158
|
-
keyId: string;
|
|
159
|
-
functionName: string;
|
|
160
|
-
/** 已绑定到其它函数时是否强制覆盖 */
|
|
161
|
-
force?: boolean;
|
|
162
|
-
}
|
|
163
|
-
/** 绑定结果摘要 */
|
|
164
|
-
export interface IBindResult {
|
|
165
|
-
keyId: string;
|
|
166
|
-
functionName: string;
|
|
167
|
-
/** 先前绑定的云函数名(无则 undefined) */
|
|
168
|
-
previousFunctionName?: string;
|
|
169
|
-
/** 是否覆盖了已有的不同绑定 */
|
|
170
|
-
overwritten: boolean;
|
|
171
|
-
/** 已绑定到同一函数、无需变更时为 true(幂等) */
|
|
172
|
-
alreadyBound: boolean;
|
|
173
|
-
}
|
|
174
160
|
export interface IGetUserKeyTemplateListResult {
|
|
175
161
|
KeyTemplateList: IUserKeyTemplate[];
|
|
176
162
|
TotalCount: number;
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.AESCrypto = void 0;
|
|
37
|
-
exports.aesEncryptWithIv = aesEncryptWithIv;
|
|
38
|
-
/**
|
|
39
|
-
* 集成中心 AES 加解密
|
|
40
|
-
*
|
|
41
|
-
* 与 dev-platform 的 AESCrypto(crypto-js)完全对齐,保证密文可互相解出:
|
|
42
|
-
* - 算法:AES-256-CBC
|
|
43
|
-
* - key:32 字节(按 UTF-8 解析)
|
|
44
|
-
* - IV:每次随机 16 字节
|
|
45
|
-
* - padding:PKCS7
|
|
46
|
-
* - 密文格式:base64(IV) + base64(ciphertext)
|
|
47
|
-
* (IV 固定 16 字节 → base64 长度恒为 24,前 24 字符即 IV)
|
|
48
|
-
*
|
|
49
|
-
* 使用 Node 内置 crypto 复刻,无需引入额外依赖。
|
|
50
|
-
*/
|
|
51
|
-
const crypto = __importStar(require("crypto"));
|
|
52
|
-
const ALGORITHM = 'aes-256-cbc';
|
|
53
|
-
const KEY_BYTE_LENGTH = 32;
|
|
54
|
-
const IV_BYTE_LENGTH = 16;
|
|
55
|
-
/** base64(16 字节) 的固定长度 */
|
|
56
|
-
const IV_BASE64_LENGTH = 24;
|
|
57
|
-
function assertKey(key) {
|
|
58
|
-
if (Buffer.byteLength(key, 'utf8') !== KEY_BYTE_LENGTH) {
|
|
59
|
-
throw new Error('密钥必须是 32 字节长度');
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* 使用指定 IV 加密(供测试与内部使用,保证可重现)
|
|
64
|
-
*/
|
|
65
|
-
function aesEncryptWithIv(plaintext, key, iv) {
|
|
66
|
-
assertKey(key);
|
|
67
|
-
if (iv.length !== IV_BYTE_LENGTH) {
|
|
68
|
-
throw new Error('IV 必须是 16 字节长度');
|
|
69
|
-
}
|
|
70
|
-
const cipher = crypto.createCipheriv(ALGORITHM, Buffer.from(key, 'utf8'), iv);
|
|
71
|
-
const encrypted = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);
|
|
72
|
-
return iv.toString('base64') + encrypted.toString('base64');
|
|
73
|
-
}
|
|
74
|
-
class AESCryptoImpl {
|
|
75
|
-
/**
|
|
76
|
-
* AES-256-CBC 加密,返回 base64(IV)+base64(密文)
|
|
77
|
-
*/
|
|
78
|
-
encrypt(plaintext, key) {
|
|
79
|
-
assertKey(key);
|
|
80
|
-
const iv = crypto.randomBytes(IV_BYTE_LENGTH);
|
|
81
|
-
return aesEncryptWithIv(plaintext, key, iv);
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* 解密 base64(IV)+base64(密文) 格式的密文
|
|
85
|
-
*/
|
|
86
|
-
decrypt(encryptedData, key) {
|
|
87
|
-
assertKey(key);
|
|
88
|
-
const ivBase64 = encryptedData.slice(0, IV_BASE64_LENGTH);
|
|
89
|
-
const ciphertextBase64 = encryptedData.slice(IV_BASE64_LENGTH);
|
|
90
|
-
const iv = Buffer.from(ivBase64, 'base64');
|
|
91
|
-
if (iv.length !== IV_BYTE_LENGTH) {
|
|
92
|
-
throw new Error('密文格式错误:IV 长度非法');
|
|
93
|
-
}
|
|
94
|
-
const decipher = crypto.createDecipheriv(ALGORITHM, Buffer.from(key, 'utf8'), iv);
|
|
95
|
-
const decrypted = Buffer.concat([
|
|
96
|
-
decipher.update(Buffer.from(ciphertextBase64, 'base64')),
|
|
97
|
-
decipher.final()
|
|
98
|
-
]);
|
|
99
|
-
return decrypted.toString('utf8');
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
exports.AESCrypto = new AESCryptoImpl();
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.parsePasswordFieldNames = parsePasswordFieldNames;
|
|
4
|
-
exports.encryptPasswordFields = encryptPasswordFields;
|
|
5
|
-
exports.decryptPasswordFields = decryptPasswordFields;
|
|
6
|
-
/**
|
|
7
|
-
* 集成中心敏感字段处理(与 dev-platform 对齐)
|
|
8
|
-
*
|
|
9
|
-
* - 实例 Ext 为扁平对象 `{ [fieldName]: value }`
|
|
10
|
-
* - 模板 Ext 为 `{ fields: [{ name, type }] }`,type === 'password' 即敏感字段
|
|
11
|
-
* - 加密:仅加密有值的 password 字段(create / update 同一逻辑,后端对未改动 password 做保留)
|
|
12
|
-
* - 解密:长度 > MIN_AES_CIPHERTEXT_LENGTH 才尝试解密,失败保留原值不中断
|
|
13
|
-
*/
|
|
14
|
-
const crypto_1 = require("./crypto");
|
|
15
|
-
const constants_1 = require("./constants");
|
|
16
|
-
/**
|
|
17
|
-
* 从模板 Ext(JSON:`{ fields: [{ name, type }] }`)解析出 password 字段名集合。
|
|
18
|
-
* 解析失败或无 fields 时返回空数组。
|
|
19
|
-
*/
|
|
20
|
-
function parsePasswordFieldNames(templateExt) {
|
|
21
|
-
if (!templateExt)
|
|
22
|
-
return [];
|
|
23
|
-
try {
|
|
24
|
-
const parsed = JSON.parse(templateExt);
|
|
25
|
-
const fields = (parsed === null || parsed === void 0 ? void 0 : parsed.fields) || [];
|
|
26
|
-
return fields
|
|
27
|
-
.filter(f => (f === null || f === void 0 ? void 0 : f.type) === 'password' && (f === null || f === void 0 ? void 0 : f.name))
|
|
28
|
-
.map(f => f.name);
|
|
29
|
-
}
|
|
30
|
-
catch (_a) {
|
|
31
|
-
return [];
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* 加密 ext 对象中的 password 字段(仅加密有值的字段)。
|
|
36
|
-
* 返回新对象,不修改入参。
|
|
37
|
-
*/
|
|
38
|
-
function encryptPasswordFields(extObj, passwordFieldNames, aesKey) {
|
|
39
|
-
const result = Object.assign({}, extObj);
|
|
40
|
-
passwordFieldNames.forEach(name => {
|
|
41
|
-
const value = result[name];
|
|
42
|
-
if (name && value) {
|
|
43
|
-
result[name] = crypto_1.AESCrypto.encrypt(value, aesKey);
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
return result;
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* 解密 ext 对象中的 password 字段。
|
|
50
|
-
* - 仅处理长度 > MIN_AES_CIPHERTEXT_LENGTH 的字符串(规避明显非密文的噪音)
|
|
51
|
-
* - 解密失败保留原值,不中断流程
|
|
52
|
-
* 返回新对象,不修改入参。
|
|
53
|
-
*/
|
|
54
|
-
function decryptPasswordFields(extObj, passwordFieldNames, aesKey) {
|
|
55
|
-
const result = Object.assign({}, extObj);
|
|
56
|
-
passwordFieldNames.forEach(name => {
|
|
57
|
-
const encrypted = result[name];
|
|
58
|
-
if (typeof encrypted !== 'string' || encrypted.length <= constants_1.MIN_AES_CIPHERTEXT_LENGTH) {
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
try {
|
|
62
|
-
result[name] = crypto_1.AESCrypto.decrypt(encrypted, aesKey);
|
|
63
|
-
}
|
|
64
|
-
catch (_a) {
|
|
65
|
-
// 解密失败保留原值,不打断主流程
|
|
66
|
-
}
|
|
67
|
-
});
|
|
68
|
-
return result;
|
|
69
|
-
}
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getAesKey = getAesKey;
|
|
4
|
-
exports.__clearAesKeyCache = __clearAesKeyCache;
|
|
5
|
-
/**
|
|
6
|
-
* 集成中心 AES key 获取(七彩石)
|
|
7
|
-
*
|
|
8
|
-
* 与 dev-platform 一致:通过 QueryRainbowConfig 拉取 group=tcb-config(国内)/
|
|
9
|
-
* frontend.tcb-config(国际)下字段 dev-platform-apis 的值作为 AES-256-CBC 密钥。
|
|
10
|
-
*
|
|
11
|
-
* 安全约束:
|
|
12
|
-
* - key 仅在进程内存使用,按 group 维度缓存;
|
|
13
|
-
* - 绝不写入任何本地文件、绝不打印日志。
|
|
14
|
-
*/
|
|
15
|
-
const http_request_1 = require("../utils/http-request");
|
|
16
|
-
const error_1 = require("../error");
|
|
17
|
-
/** 七彩石配置(与 dev-platform constants 对齐) */
|
|
18
|
-
const RAINBOW_CONFIG = {
|
|
19
|
-
ENV_TYPE: 'Default',
|
|
20
|
-
GROUP_CN: 'tcb-config',
|
|
21
|
-
GROUP_INTL: 'frontend.tcb-config',
|
|
22
|
-
/** AES key 在 kvs 中的字段名 */
|
|
23
|
-
CRYPTO_KEY_FIELD: 'dev-platform-apis'
|
|
24
|
-
};
|
|
25
|
-
const RAINBOW_HOST_CN = 'wedaapi.tcbautomation.cn';
|
|
26
|
-
const RAINBOW_HOST_INTL = 'gray.decisiontcb.tencentcloud.com';
|
|
27
|
-
/** 无 EnvId 时的占位 Uin(与 dev-platform 行为一致) */
|
|
28
|
-
const FALLBACK_UIN = '3309033';
|
|
29
|
-
/** 进程内缓存(按 group 维度),key 不落盘、不打印 */
|
|
30
|
-
const keyCache = {};
|
|
31
|
-
/**
|
|
32
|
-
* 获取集成中心 AES key(进程内缓存)
|
|
33
|
-
* @throws CloudBaseError 拉取或解析失败
|
|
34
|
-
*/
|
|
35
|
-
async function getAesKey(params = {}) {
|
|
36
|
-
var _a, _b;
|
|
37
|
-
const { envId, isIntl = false, proxy } = params;
|
|
38
|
-
const group = isIntl ? RAINBOW_CONFIG.GROUP_INTL : RAINBOW_CONFIG.GROUP_CN;
|
|
39
|
-
if (keyCache[group]) {
|
|
40
|
-
return keyCache[group];
|
|
41
|
-
}
|
|
42
|
-
const host = isIntl ? RAINBOW_HOST_INTL : RAINBOW_HOST_CN;
|
|
43
|
-
const url = `https://${host}/Graydecision/V1/QueryRainbowConfig?group=${group}`;
|
|
44
|
-
const payload = {
|
|
45
|
-
EnvType: RAINBOW_CONFIG.ENV_TYPE,
|
|
46
|
-
Group: group
|
|
47
|
-
};
|
|
48
|
-
if (envId) {
|
|
49
|
-
payload.EnvId = envId;
|
|
50
|
-
}
|
|
51
|
-
else {
|
|
52
|
-
payload.Uin = FALLBACK_UIN;
|
|
53
|
-
}
|
|
54
|
-
let resJson;
|
|
55
|
-
try {
|
|
56
|
-
resJson = await (0, http_request_1.fetch)(url, {
|
|
57
|
-
method: 'POST',
|
|
58
|
-
headers: { 'content-type': 'application/json' },
|
|
59
|
-
body: JSON.stringify(payload)
|
|
60
|
-
}, proxy);
|
|
61
|
-
}
|
|
62
|
-
catch (e) {
|
|
63
|
-
throw new error_1.CloudBaseError('获取集成加解密密钥失败:七彩石配置拉取请求异常,请检查网络连接或代理设置', {
|
|
64
|
-
original: e
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
const data = (_a = resJson === null || resJson === void 0 ? void 0 : resJson.Response) === null || _a === void 0 ? void 0 : _a.Data;
|
|
68
|
-
if (!data || data.ErrorCode !== '0') {
|
|
69
|
-
throw new error_1.CloudBaseError(`获取集成加解密密钥失败:七彩石返回异常(ErrorCode=${data === null || data === void 0 ? void 0 : data.ErrorCode})。请确认当前环境已开通集成中心能力`);
|
|
70
|
-
}
|
|
71
|
-
let config;
|
|
72
|
-
try {
|
|
73
|
-
config = JSON.parse(data.ConfigValue)[0];
|
|
74
|
-
}
|
|
75
|
-
catch (e) {
|
|
76
|
-
throw new error_1.CloudBaseError('获取集成加解密密钥失败:七彩石配置内容解析失败', {
|
|
77
|
-
original: e
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
const kvs = ((_b = config === null || config === void 0 ? void 0 : config.kvs) === null || _b === void 0 ? void 0 : _b.kvs) || [];
|
|
81
|
-
const target = kvs.find(kv => kv.key === RAINBOW_CONFIG.CRYPTO_KEY_FIELD);
|
|
82
|
-
const key = target === null || target === void 0 ? void 0 : target.value;
|
|
83
|
-
if (!key) {
|
|
84
|
-
throw new error_1.CloudBaseError(`获取集成加解密密钥失败:七彩石配置中缺少 ${RAINBOW_CONFIG.CRYPTO_KEY_FIELD} 字段`);
|
|
85
|
-
}
|
|
86
|
-
keyCache[group] = key;
|
|
87
|
-
return key;
|
|
88
|
-
}
|
|
89
|
-
/** 清空进程内缓存(仅供测试使用) */
|
|
90
|
-
function __clearAesKeyCache() {
|
|
91
|
-
Object.keys(keyCache).forEach(k => delete keyCache[k]);
|
|
92
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 使用指定 IV 加密(供测试与内部使用,保证可重现)
|
|
3
|
-
*/
|
|
4
|
-
export declare function aesEncryptWithIv(plaintext: string, key: string, iv: Buffer): string;
|
|
5
|
-
declare class AESCryptoImpl {
|
|
6
|
-
/**
|
|
7
|
-
* AES-256-CBC 加密,返回 base64(IV)+base64(密文)
|
|
8
|
-
*/
|
|
9
|
-
encrypt(plaintext: string, key: string): string;
|
|
10
|
-
/**
|
|
11
|
-
* 解密 base64(IV)+base64(密文) 格式的密文
|
|
12
|
-
*/
|
|
13
|
-
decrypt(encryptedData: string, key: string): string;
|
|
14
|
-
}
|
|
15
|
-
export declare const AESCrypto: AESCryptoImpl;
|
|
16
|
-
export {};
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 从模板 Ext(JSON:`{ fields: [{ name, type }] }`)解析出 password 字段名集合。
|
|
3
|
-
* 解析失败或无 fields 时返回空数组。
|
|
4
|
-
*/
|
|
5
|
-
export declare function parsePasswordFieldNames(templateExt?: string): string[];
|
|
6
|
-
/**
|
|
7
|
-
* 加密 ext 对象中的 password 字段(仅加密有值的字段)。
|
|
8
|
-
* 返回新对象,不修改入参。
|
|
9
|
-
*/
|
|
10
|
-
export declare function encryptPasswordFields(extObj: Record<string, string>, passwordFieldNames: string[], aesKey: string): Record<string, string>;
|
|
11
|
-
/**
|
|
12
|
-
* 解密 ext 对象中的 password 字段。
|
|
13
|
-
* - 仅处理长度 > MIN_AES_CIPHERTEXT_LENGTH 的字符串(规避明显非密文的噪音)
|
|
14
|
-
* - 解密失败保留原值,不中断流程
|
|
15
|
-
* 返回新对象,不修改入参。
|
|
16
|
-
*/
|
|
17
|
-
export declare function decryptPasswordFields(extObj: Record<string, string>, passwordFieldNames: string[], aesKey: string): Record<string, string>;
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export interface IGetAesKeyParams {
|
|
2
|
-
/** 环境 Id(用于灰度命中,无则用占位 Uin) */
|
|
3
|
-
envId?: string;
|
|
4
|
-
/** 是否国际站 */
|
|
5
|
-
isIntl?: boolean;
|
|
6
|
-
/** 代理 */
|
|
7
|
-
proxy?: string;
|
|
8
|
-
}
|
|
9
|
-
/**
|
|
10
|
-
* 获取集成中心 AES key(进程内缓存)
|
|
11
|
-
* @throws CloudBaseError 拉取或解析失败
|
|
12
|
-
*/
|
|
13
|
-
export declare function getAesKey(params?: IGetAesKeyParams): Promise<string>;
|
|
14
|
-
/** 清空进程内缓存(仅供测试使用) */
|
|
15
|
-
export declare function __clearAesKeyCache(): void;
|