"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; Object.defineProperty(exports, "__esModule", { value: true }); exports.UsersManagementClient = void 0; var utils_1 = require("../utils"); var graphqlapi_1 = require("../graphqlapi"); var graphql_v2_1 = require("../../types/graphql.v2"); var utils_2 = require("../utils"); /** * @name UsersManagementClient * @description Authing 用户管理模块。 * * 此模块可以进行用户目录增删改查、搜索用户、刷新用户 token、管理用户分组、管理用户角色、管理用户策略授权等操作。 * * 该模块发出的所有操作都将以管理员身份进行,所以不需要进行建议短信验证码等操作,如果你希望以普通用户的身份进行操作,请使用 AuthenticationClient 。 * * @example * * 请使用以下方式使用该模块: * \`\`\`javascript * import { ManagementClient } from "authing-js-sdk" * const managementClient = new ManagementClient({ * userPoolId: "YOUR_USERPOOL_ID", * secret: "YOUR_USERPOOL_SECRET", * }) * * managementClient.users.list // 获取用户列表 * managementClient.users.create // 创建用户 * managementClient.users.listRoles // 获取用户角色列表 * managementClient.users.search // 搜索用户 * \`\`\` * * @class UsersManagementClient 管理用户 */ var UsersManagementClient = /** @class */ (function () { function UsersManagementClient(options, graphqlClient, httpClient, tokenProvider, publickKeyManager) { this.options = options; this.graphqlClient = graphqlClient; this.tokenProvider = tokenProvider; this.httpClient = httpClient; this.publickKeyManager = publickKeyManager; } /** * @name create * @name_zh 创建用户 * @description 此接口将以管理员身份创建用户,不需要进行手机号验证码检验等安全检测。 * * @param {CreateUserInput} userInfo 用户资料 * @param {string} userInfo.email 邮箱,用户池内唯一 * @param {boolean} userInfo.emailVerified 邮箱是否已验证 * @param {string} userInfo.phone 手机号 * @param {boolean} userInfo.phoneVerified 手机号是否验证 * @param {string} userInfo.unionid 以社会化登录的用户该字段为用户在第三方社会化登录服务商中的唯一 ID * @param {string} userInfo.openid 微信登录返回的 openid * @param {string} userInfo.password 密码 * @param {string} userInfo.registerSource 注册来源,可以多选 * @param {string} userInfo.username 用户名 * @param {string} userInfo.nickname 昵称 * @param {string} userInfo.photo 头像 * @param {string} userInfo.company 公司 * @param {string} userInfo.browser 浏览器 * @param {number} userInfo.loginsCount 登录次数,当你从原有用户系统迁移到 Authing 时可以设置该字段。 * @param {string} userInfo.lastLogin 上次登录时间, 符合 ISO8601 格式的时间字符串。(如 "2017-06-07T14:34:08.700Z", "2017-06-07T14:34:08.700 or "2017-06-07T14:34:08+04:00") * @param {string} userInfo.lastIP 用户最近一次登录(或其他活动)的 IP * @param {string} userInfo.signedUp 注册时间,符合 ISO8601 格式的时间字符串。(如 "2017-06-07T14:34:08.700Z", "2017-06-07T14:34:08.700 or "2017-06-07T14:34:08+04:00") * @param {boolean} userInfo.blocked 账号是否被禁用 * @param {boolean} userInfo.isDeleted 标记账号是否被删除 * @param {string} userInfo.device 设备 * @param {string} userInfo.lastIP 最近登录的 IP * @param {string} userInfo.name Name * @param {string} userInfo.givenName Given Name * @param {string} userInfo.familyName Family Name * @param {string} userInfo.middleName Middle Name * @param {string} userInfo.profile Profile Url * @param {string} userInfo.preferredUsername Preferred Name * @param {string} userInfo.website 个人网站 * @param {string} userInfo.gender 性别, F 表示男性、W 表示女性、未知表示 U * @param {string} userInfo.birthdate 生日 * @param {string} userInfo.zoneinfo 时区 * @param {string} userInfo.locale 语言 * @param {string} userInfo.address 地址 * @param {string} userInfo.streetAddress 街道地址 * @param {string} userInfo.locality * @param {string} userInfo.region 地域 * @param {string} userInfo.postalCode 邮编 * @param {string} userInfo.city 城市 * @param {string} userInfo.province 省份 * @param {string} userInfo.country 国家 * * @example * * const user = await managementClient.users.create({ * username: 'bob', * password: 'passw0rd' * }) * * @example * * const user = await managementClient.users.create({ * nickname: 'Nick', * phone: '176xxxx7041', // 由于是管理员操作,所以检验手机号验证码, 如果你需要检验,请使用 AuthenticationClient * loginsCount: 2 // 原有用户系统记录的用户登录次数 * signedUp: '2020-10-15T17:55:37+08:00' // 原有用户系统记录的用户注册时间 * }) * * @returns {Promise} * @memberof UsersManagementClient */ UsersManagementClient.prototype.create = function (userInfo, options) { return __awaiter(this, void 0, void 0, function () { var _a, _b, keepPassword, _c, resetPasswordOnFirstLogin, _d, _e, _f, _g, user; return __generator(this, function (_h) { switch (_h.label) { case 0: _a = options || {}, _b = _a.keepPassword, keepPassword = _b === void 0 ? false : _b, _c = _a.resetPasswordOnFirstLogin, resetPasswordOnFirstLogin = _c === void 0 ? false : _c; if (!(userInfo === null || userInfo === void 0 ? void 0 : userInfo.password)) return [3 /*break*/, 3]; _d = userInfo; _f = (_e = this.options).encryptFunction; _g = [userInfo.password]; return [4 /*yield*/, this.publickKeyManager.getPublicKey()]; case 1: return [4 /*yield*/, _f.apply(_e, _g.concat([_h.sent()]))]; case 2: _d.password = _h.sent(); _h.label = 3; case 3: return [4 /*yield*/, graphqlapi_1.createUser(this.graphqlClient, this.tokenProvider, { userInfo: userInfo, keepPassword: keepPassword, resetPasswordOnFirstLogin: resetPasswordOnFirstLogin })]; case 4: user = (_h.sent()).createUser; return [2 /*return*/, user]; } }); }); }; /** * @name update * @name_zh 修改用户资料 * @description 修改用户资料 * * @param {string} id 用户 ID * @param {UpdateUserInput} updates 修改的用户资料 * @param {string} updates.email 邮箱 * @param {boolean} updates.emailVerified 邮箱是否已验证 * @param {string} updates.phone 手机号 * @param {boolean} updates.phoneVerified 手机号是否验证 * @param {string} updates.unionid 以社会化登录的用户该字段为用户在第三方社会化登录服务商中的唯一 ID * @param {string} updates.openid 微信登录返回的 openid * @param {string} updates.password 密码 * @param {string} updates.registerSource 注册来源,可以多选 * @param {string} updates.tokenExpiredAt token 过期时间,符合 ISO8601 格式的时间字符串。(如 "2017-06-07T14:34:08.700Z", "2017-06-07T14:34:08.700 or "2017-06-07T14:34:08+04:00")。 * 将该字段设置为小于当前时间可以让用户的 token 失效。 * @param {string} updates.username 用户名 * @param {string} updates.nickname 昵称 * @param {string} updates.photo 头像 * @param {string} updates.company 公司 * @param {string} updates.browser 浏览器 * @param {number} updates.loginsCount 登录次数,当你从原有用户系统迁移到 Authing 时可以设置该字段。 * @param {string} updates.lastLogin 上次登录时间, 符合 ISO8601 格式的时间字符串。(如 "2017-06-07T14:34:08.700Z", "2017-06-07T14:34:08.700 or "2017-06-07T14:34:08+04:00") * @param {string} updates.lastIP 用户最近一次登录(或其他活动)的 IP * @param {string} updates.signedUp 注册时间,符合 ISO8601 格式的时间字符串。(如 "2017-06-07T14:34:08.700Z", "2017-06-07T14:34:08.700 or "2017-06-07T14:34:08+04:00") * @param {boolean} updates.blocked 账号是否被禁用 * @param {string} updates.device 设备 * @param {string} updates.lastIP 最近登录的 IP * @param {string} updates.name Name * @param {string} updates.givenName Given Name * @param {string} updates.familyName Family Name * @param {string} updates.middleName Middle Name * @param {string} updates.profile Profile Url * @param {string} updates.preferredUsername Preferred Name * @param {string} updates.website 个人网站 * @param {string} updates.gender 性别, F 表示男性、W 表示女性、未知表示 U * @param {string} updates.birthdate 生日 * @param {string} updates.zoneinfo 时区 * @param {string} updates.locale 语言 * @param {string} updates.address 地址 * @param {string} updates.streetAddress 街道地址 * @param {string} updates.locality * @param {string} updates.region 地域 * @param {string} updates.postalCode 邮编 * @param {string} updates.city 城市 * @param {string} updates.province 省份 * @param {string} updates.country 国家 * * @example * * const user = await managementClient.users.update("USERID", { * nickname: "Nick" * }) * * @example * * const user = await managementClient.users.update("USERID" ,{ * nickname: 'Nick', * phone: '176xxxx7041', // 由于是管理员操作,所以检验手机号验证码, 如果你需要检验,请使用 AuthenticationClient * tokenExpiredAt: '2020-10-15T17:55:37+08:00' * }) * * @returns {Promise} * @memberof UsersManagementClient * */ UsersManagementClient.prototype.update = function (id, updates) { return __awaiter(this, void 0, void 0, function () { var _a, _b, _c, _d, user; return __generator(this, function (_e) { switch (_e.label) { case 0: if (!(updates && updates.password)) return [3 /*break*/, 3]; _a = updates; _c = (_b = this.options).encryptFunction; _d = [updates.password]; return [4 /*yield*/, this.publickKeyManager.getPublicKey()]; case 1: return [4 /*yield*/, _c.apply(_b, _d.concat([_e.sent()]))]; case 2: _a.password = _e.sent(); _e.label = 3; case 3: return [4 /*yield*/, graphqlapi_1.updateUser(this.graphqlClient, this.tokenProvider, { id: id, input: updates })]; case 4: user = (_e.sent()).updateUser; return [2 /*return*/, user]; } }); }); }; /** * @name detail * @name_zh 获取用户详情 * @description 通过用户 ID 获取用户详情,如果你想通过 token 获取用户详情,请使用 AuthenticationClient SDK 。 * * @param {string} userId 用户 ID * * @example * * const user = await managementClient.users.detail('USERID'); * * @returns {Promise} * @memberof UsersManagementClient */ UsersManagementClient.prototype.detail = function (userId, options) { return __awaiter(this, void 0, void 0, function () { var _a, withCustomData, data, data; return __generator(this, function (_b) { switch (_b.label) { case 0: _a = (options || {}).withCustomData, withCustomData = _a === void 0 ? false : _a; if (!withCustomData) return [3 /*break*/, 2]; return [4 /*yield*/, graphqlapi_1.userWithCustomData(this.graphqlClient, this.tokenProvider, { id: userId })]; case 1: data = (_b.sent()).user; // @ts-ignore data.customData = utils_2.convertUdvToKeyValuePair(data.customData); return [2 /*return*/, data]; case 2: return [4 /*yield*/, graphqlapi_1.user(this.graphqlClient, this.tokenProvider, { id: userId })]; case 3: data = (_b.sent()).user; return [2 /*return*/, data]; } }); }); }; /** * @name delete * @name_zh 删除用户 * @description 删除用户 * * @param {string} userId 用户 ID * * @example * * const user = await managementClient.users.delete('USERID'); * * @returns {Promise} * @memberof UsersManagementClient */ UsersManagementClient.prototype.delete = function (userId) { return __awaiter(this, void 0, void 0, function () { var data; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, graphqlapi_1.deleteUser(this.graphqlClient, this.tokenProvider, { id: userId })]; case 1: data = (_a.sent()).deleteUser; return [2 /*return*/, data]; } }); }); }; /** * @name deleteMany * @name_zh 批量删除用户 * @description 批量删除用户 * * @param {string[]} userIds 用户 ID 列表 * * @example * * const user = await managementClient.users.deleteMany(['USERID']); * * @returns {Promise} * @memberof UsersManagementClient */ UsersManagementClient.prototype.deleteMany = function (userIds) { return __awaiter(this, void 0, void 0, function () { var data; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, graphqlapi_1.deleteUsers(this.graphqlClient, this.tokenProvider, { ids: userIds })]; case 1: data = (_a.sent()).deleteUsers; return [2 /*return*/, data]; } }); }); }; /** * @name batch * @name_zh 批量获取用户 * @description 通过 ID、username、email、phone、email、externalId 批量获取用户详情 * * @param {string[]} identifiers 需要查询的数据列表,如 用户 ID 列表 * @param {string} [type] 列表类型,可选值为 'id' ,'username' ,'phone' ,'email', 'externalId',默认为 'id' * * @example * * const users = await managementClient.users.batch(['USERID'], options); * * @returns {Promise} * @memberof UsersManagementClient */ UsersManagementClient.prototype.batch = function (ids, options) { return __awaiter(this, void 0, void 0, function () { var _a, _b, queryField, _c, withCustomData, list, list; return __generator(this, function (_d) { switch (_d.label) { case 0: _a = options || {}, _b = _a.queryField, queryField = _b === void 0 ? 'id' : _b, _c = _a.withCustomData, withCustomData = _c === void 0 ? false : _c; if (!withCustomData) return [3 /*break*/, 2]; return [4 /*yield*/, graphqlapi_1.userBatchWithCustomData(this.graphqlClient, this.tokenProvider, { ids: ids, type: queryField })]; case 1: list = (_d.sent()).userBatch; list = list.map(function (user) { // @ts-ignore user.customData = utils_2.convertUdvToKeyValuePair(user.customData); return user; }); return [2 /*return*/, list]; case 2: return [4 /*yield*/, graphqlapi_1.userBatch(this.graphqlClient, this.tokenProvider, { ids: ids, type: queryField })]; case 3: list = (_d.sent()).userBatch; return [2 /*return*/, list]; } }); }); }; /** * @name list * @name_zh 获取用户列表 * @description 获取用户池用户列表 * * @param {number} [page=1] 页码数, 从 1 开始 * @param {number} [limit=10] 每页包含的用户数 * * @example * * const user = await managementClient.users.list(); * * @returns * @memberof UsersManagementClient */ UsersManagementClient.prototype.list = function (page, limit, options) { if (page === void 0) { page = 1; } if (limit === void 0) { limit = 10; } return __awaiter(this, void 0, void 0, function () { var _a, withCustomData, data, totalCount, list, data; return __generator(this, function (_b) { switch (_b.label) { case 0: _a = (options || {}).withCustomData, withCustomData = _a === void 0 ? false : _a; if (!withCustomData) return [3 /*break*/, 2]; return [4 /*yield*/, graphqlapi_1.usersWithCustomData(this.graphqlClient, this.tokenProvider, { page: page, limit: limit })]; case 1: data = (_b.sent()).users; totalCount = data.totalCount, list = data.list; list = list.map(function (user) { // @ts-ignore user.customData = utils_2.convertUdvToKeyValuePair(user.customData); return user; }); return [2 /*return*/, { totalCount: totalCount, list: list }]; case 2: return [4 /*yield*/, graphqlapi_1.users(this.graphqlClient, this.tokenProvider, { page: page, limit: limit })]; case 3: data = (_b.sent()).users; return [2 /*return*/, data]; } }); }); }; /** * @name listArchivedUsers * @name_zh 获取已归档用户列表 * @description 获取已归档用户列表 * * @param {number} [page=1] 页码数, 从 1 开始 * @param {number} [limit=10] 每页包含的用户数 * * @example * * const user = await managementClient.users.listArchivedUsers(); * * @returns * @memberof UsersManagementClient */ UsersManagementClient.prototype.listArchivedUsers = function (page, limit) { if (page === void 0) { page = 1; } if (limit === void 0) { limit = 10; } return __awaiter(this, void 0, void 0, function () { var data; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, graphqlapi_1.archivedUsers(this.graphqlClient, this.tokenProvider, { page: page, limit: limit })]; case 1: data = (_a.sent()).archivedUsers; return [2 /*return*/, data]; } }); }); }; /** * @name exists * @name_zh 检查用户是否存在 * @description 检查用户是否存在,目前可检测的字段有用户名、邮箱、手机号。 * * * @param {Object} options * @param {string} [options.username] 用户名,区分大小写。 * @param {string} [options.email] 邮箱,邮箱不区分大小写。 * @param {string} [options.phone] 手机号 * * @example * * const exists = await managementClient.users.exists({ * username: "bob" * }); * * @returns {Promise} * @memberof UsersManagementClient */ UsersManagementClient.prototype.exists = function (options) { return __awaiter(this, void 0, void 0, function () { var username, email, phone, externalId, data; return __generator(this, function (_a) { switch (_a.label) { case 0: username = options.username, email = options.email, phone = options.phone, externalId = options.externalId; return [4 /*yield*/, graphqlapi_1.isUserExists(this.graphqlClient, this.tokenProvider, { username: username, email: email, phone: phone, externalId: externalId })]; case 1: data = (_a.sent()).isUserExists; return [2 /*return*/, data]; } }); }); }; /** * @name find * @name_zh 查找用户 * @description 通过用户名、邮箱、手机号查找用户 * * @param {Object} options * @param {string} [options.username] 用户名,区分大小写。 * @param {string} [options.email] 邮箱,邮箱不区分大小写。 * @param {string} [options.phone] 手机号 * @param {string} [options.externalId] externalId * * @memberof UsersManagementClient */ UsersManagementClient.prototype.find = function (options) { return __awaiter(this, void 0, void 0, function () { var username, email, phone, externalId, _a, withCustomData, user_1, user_2; return __generator(this, function (_b) { switch (_b.label) { case 0: username = options.username, email = options.email, phone = options.phone, externalId = options.externalId, _a = options.withCustomData, withCustomData = _a === void 0 ? false : _a; if (!withCustomData) return [3 /*break*/, 2]; return [4 /*yield*/, graphqlapi_1.findUserWithCustomData(this.graphqlClient, this.tokenProvider, { username: username, email: email, phone: phone, externalId: externalId })]; case 1: user_1 = (_b.sent()).findUser; // @ts-ignore user_1.customData = utils_2.convertUdvToKeyValuePair(user_1.customData); return [2 /*return*/, user_1]; case 2: return [4 /*yield*/, graphqlapi_1.findUser(this.graphqlClient, this.tokenProvider, { username: username, email: email, phone: phone, externalId: externalId })]; case 3: user_2 = (_b.sent()).findUser; return [2 /*return*/, user_2]; } }); }); }; /** * @name search * @name_zh 搜索用户 * @description 根据关键字搜索用户 * * @param query 搜索内容 * @param options 选项 * @param {string[]} [options.fields] 搜索用户字段,如果不指定,默认会从 username、nickname、email、phone、company、name、givenName、familyName、middleName、profile、preferredUsername 这些字段进行模糊搜索。 * 如果你需要精确查找,请使用 find 方法。 * @param {number} [options.page=1] * @param {number} [options.limit=10] * @param {Object} [options.departmentOpts] 限制条件,用户所在的部门 * @param {string} [options.departmentOpts.departmentId] 部门 ID * @param {string} [options.departmentOpts.includeChildrenDepartments] 是否包含此部门的子部门 * * @example * * const { totalCount, list } = await managementClient.users.search("Bob"); * * @returns {Promise} * @memberof UsersManagementClient */ UsersManagementClient.prototype.search = function (query, options) { return __awaiter(this, void 0, void 0, function () { var fields, _a, page, _b, limit, departmentOpts, groupOpts, roleOpts, _c, withCustomData, data, data, totalCount, list; return __generator(this, function (_d) { switch (_d.label) { case 0: options = options || {}; fields = options.fields, _a = options.page, page = _a === void 0 ? 1 : _a, _b = options.limit, limit = _b === void 0 ? 10 : _b, departmentOpts = options.departmentOpts, groupOpts = options.groupOpts, roleOpts = options.roleOpts, _c = options.withCustomData, withCustomData = _c === void 0 ? false : _c; if (!!withCustomData) return [3 /*break*/, 2]; return [4 /*yield*/, graphqlapi_1.searchUser(this.graphqlClient, this.tokenProvider, { query: query, fields: fields, page: page, limit: limit, departmentOpts: departmentOpts, groupOpts: groupOpts, roleOpts: roleOpts })]; case 1: data = (_d.sent()).searchUser; return [2 /*return*/, data]; case 2: return [4 /*yield*/, graphqlapi_1.searchUserWithCustomData(this.graphqlClient, this.tokenProvider, { query: query, fields: fields, page: page, limit: limit, departmentOpts: departmentOpts, groupOpts: groupOpts, roleOpts: roleOpts })]; case 3: data = (_d.sent()).searchUser; totalCount = data.totalCount, list = data.list; list = list.map(function (user) { // @ts-ignore user.customData = utils_2.convertUdvToKeyValuePair(user.customData); return user; }); return [2 /*return*/, { totalCount: totalCount, list: list }]; } }); }); }; /** * @name refreshToken * @name_zh 刷新用户 token * @description 刷新用户 token * * @param {string} id 用户 ID * * @example * * const { token } = await managementClient.users.refreshToken("USERID"); * * // 检测 token 的最新状态,能够获取到该用户对应的 token * * const data = await managementClient.checkLoginStatus(token, { * fetchUserDetail: true * }); * * @returns {Promise} * @memberof UsersManagementClient */ UsersManagementClient.prototype.refreshToken = function (id) { return __awaiter(this, void 0, void 0, function () { var data; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, graphqlapi_1.refreshToken(this.graphqlClient, this.tokenProvider, { id: id })]; case 1: data = (_a.sent()).refreshToken; return [2 /*return*/, data]; } }); }); }; /** * @name listGroups * @name_zh 获取用户分组列表 * @description 获取用户的分组列表 * * @param {string} userId 用户 ID * * @example * * const { list, totalCount} = await managementClient.users.listGroups("USERID"); * * @returns {Promise>} * @memberof UsersManagementClient */ UsersManagementClient.prototype.listGroups = function (userId) { return __awaiter(this, void 0, void 0, function () { var user; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, graphqlapi_1.getUserGroups(this.graphqlClient, this.tokenProvider, { id: userId })]; case 1: user = (_a.sent()).user; return [2 /*return*/, user.groups]; } }); }); }; /** * @name addGroup * @name_zh 加入分组 * @description 将用户加入分组 * * @param {string} userId 用户 ID * @param {string} group 分组 code * * @example * * const { code, message } = await managementClient.users.addGroup("USERID", "GROUP_CODE"); * * @returns {Promise} * @memberof UsersManagementClient */ UsersManagementClient.prototype.addGroup = function (userId, group) { return __awaiter(this, void 0, void 0, function () { var res; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, graphqlapi_1.addUserToGroup(this.graphqlClient, this.tokenProvider, { userIds: [userId], code: group })]; case 1: res = _a.sent(); return [2 /*return*/, res.addUserToGroup]; } }); }); }; /** * @name removeGroup * @name_zh 退出分组 * @description 退出分组 * * @param {string} userId 用户 ID * @param {string} group 分组 code * * @example * * const { code, message } = await managementClient.users.removeGroup("USERID", "GROUP_CODE"); * * @returns {Promise} * @memberof UsersManagementClient */ UsersManagementClient.prototype.removeGroup = function (userId, group) { return __awaiter(this, void 0, void 0, function () { var res; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, graphqlapi_1.removeUserFromGroup(this.graphqlClient, this.tokenProvider, { code: group, userIds: [userId] })]; case 1: res = _a.sent(); return [2 /*return*/, res.removeUserFromGroup]; } }); }); }; /** * @name listRoles * @name_zh 获取用户角色列表 * @description 获取用户的角色列表 * * @param {string} userId 用户 ID * @param {string} namespace 权限组命名空间 * * @example * * const { list, totalCount} = await managementClient.users.listRoles("USERID"); * * @returns {Promise>} * @memberof UsersManagementClient */ UsersManagementClient.prototype.listRoles = function (userId, namespace) { return __awaiter(this, void 0, void 0, function () { var user; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, graphqlapi_1.getUserRoles(this.graphqlClient, this.tokenProvider, { id: userId, namespace: namespace })]; case 1: user = (_a.sent()).user; if (!user) { throw new Error('用户不存在!'); } return [2 /*return*/, user.roles]; } }); }); }; /** * @name addRoles * @name_zh 添加角色 * @description 将用户加入角色 * * @param {string} userId 用户 ID * @param {string} roles 角色 code 列表 * @param {string} namespace 权限组命名空间 * * @example * * const { code, message } = await managementClient.users.addRoles("USERID", ["ROLEA"]); * * @returns {Promise} * @memberof UsersManagementClient */ UsersManagementClient.prototype.addRoles = function (userId, roles, namespace) { return __awaiter(this, void 0, void 0, function () { var data; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, graphqlapi_1.assignRole(this.graphqlClient, this.tokenProvider, { roleCodes: roles, userIds: [userId], namespace: namespace })]; case 1: data = (_a.sent()).assignRole; return [2 /*return*/, data]; } }); }); }; /** * @name removeRoles * @name_zh 移除角色 * @description 将用户从角色中移除 * * @param {string} userId 用户 ID * @param {string} roles 角色 code 列表 * @param {string} namespace 权限分组 code * * @example * * const { code, message } = await managementClient.users.removeRoles("USERID", ["ROLEA"]); * * @returns {Promise} * @memberof UsersManagementClient */ UsersManagementClient.prototype.removeRoles = function (userId, roles, namespace) { return __awaiter(this, void 0, void 0, function () { var data; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, graphqlapi_1.revokeRole(this.graphqlClient, this.tokenProvider, { roleCodes: roles, userIds: [userId], namespace: namespace })]; case 1: data = (_a.sent()).revokeRole; return [2 /*return*/, data]; } }); }); }; /** * @name listOrg * @name_zh 获取用户所在组织机构 * @description 获取用户所在组织机构,以及他在该组织机构内的的节点路径。 * * @param {string} userId 用户 ID * * @example * * const data = await managementClient.users.listOrgs("USERID"); * * @returns {Promise} * * @memberof UsersManagementClient */ UsersManagementClient.prototype.listOrgs = function (userId) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.httpClient.request({ method: 'GET', url: this.options.host + "/api/v2/users/" + userId + "/orgs" })]; case 1: return [2 /*return*/, _a.sent()]; } }); }); }; /** * @name listDepartment * @name_zh 获取用户所在部门 * @description 获取用户所在部门列表 * * @param {string} userId 用户 ID * * @example * * const data = await managementClient.users.listDepartment("USERID"); * * @returns {Promise} * * @memberof UsersManagementClient */ UsersManagementClient.prototype.listDepartment = function (userId) { return __awaiter(this, void 0, void 0, function () { var departments; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, graphqlapi_1.getUserDepartments(this.graphqlClient, this.tokenProvider, { id: userId })]; case 1: departments = (_a.sent()).user.departments; return [2 /*return*/, departments]; } }); }); }; /** * @description 获取用户被授权的所有资源 * * @param userId * @param namespace */ UsersManagementClient.prototype.listAuthorizedResources = function (userId, namespace, options) { return __awaiter(this, void 0, void 0, function () { var resourceType, user, _a, list, totalCount; return __generator(this, function (_b) { switch (_b.label) { case 0: resourceType = (options || {}).resourceType; return [4 /*yield*/, graphqlapi_1.listUserAuthorizedResources(this.graphqlClient, this.tokenProvider, { id: userId, namespace: namespace, resourceType: resourceType })]; case 1: user = (_b.sent()).user; if (!user) { throw new Error('用户不存在'); } _a = user.authorizedResources, list = _a.list, totalCount = _a.totalCount; list = utils_2.formatAuthorizedResources(list); return [2 /*return*/, { list: list, totalCount: totalCount }]; } }); }); }; /** * @description 获取某个用户的所有自定义数据 * @param userId: 用户 ID * */ UsersManagementClient.prototype.getUdfValue = function (userId) { return __awaiter(this, void 0, void 0, function () { var list; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, graphqlapi_1.udv(this.graphqlClient, this.tokenProvider, { targetType: graphql_v2_1.UdfTargetType.User, targetId: userId })]; case 1: list = (_a.sent()).udv; return [2 /*return*/, utils_2.convertUdvToKeyValuePair(list)]; } }); }); }; /** * @description 批量获取多个用户的自定义数据 * * @param userIds: 用户 ID 列表 */ UsersManagementClient.prototype.getUdfValueBatch = function (userIds) { return __awaiter(this, void 0, void 0, function () { var result, ret, _i, result_1, _a, targetId, data; return __generator(this, function (_b) { switch (_b.label) { case 0: if (userIds.length === 0) { throw new Error('empty user id list'); } return [4 /*yield*/, graphqlapi_1.udfValueBatch(this.graphqlClient, this.tokenProvider, { targetType: graphql_v2_1.UdfTargetType.User, targetIds: userIds })]; case 1: result = (_b.sent()).udfValueBatch; ret = {}; for (_i = 0, result_1 = result; _i < result_1.length; _i++) { _a = result_1[_i], targetId = _a.targetId, data = _a.data; ret[targetId] = utils_2.convertUdvToKeyValuePair(data); } return [2 /*return*/, ret]; } }); }); }; /** * @description 设置某个用户的自定义数据 * * @param userId * @param data */ UsersManagementClient.prototype.setUdfValue = function (userId, data) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: if (Object.keys(data).length === 0) { throw new Error('empty udf value list'); } return [4 /*yield*/, graphqlapi_1.setUdvBatch(this.graphqlClient, this.tokenProvider, { targetType: graphql_v2_1.UdfTargetType.User, targetId: userId, udvList: Object.keys(data).map(function (key) { return ({ key: key, value: JSON.stringify(data[key]) }); }) })]; case 1: _a.sent(); return [2 /*return*/]; } }); }); }; /** * @description 批量设置自定义数据 * */ UsersManagementClient.prototype.setUdfValueBatch = function (input) { return __awaiter(this, void 0, void 0, function () { var params; return __generator(this, function (_a) { switch (_a.label) { case 0: if (input.length === 0) { throw new Error('empty input list'); } params = []; input.forEach(function (_a) { var userId = _a.userId, data = _a.data; for (var _i = 0, _b = Object.keys(data); _i < _b.length; _i++) { var key = _b[_i]; params.push({ targetId: userId, key: key, value: JSON.stringify(data[key]) }); } }); return [4 /*yield*/, graphqlapi_1.setUdfValueBatch(this.graphqlClient, this.tokenProvider, { targetType: graphql_v2_1.UdfTargetType.User, input: params })]; case 1: _a.sent(); return [2 /*return*/]; } }); }); }; /** * @description 清除用户的自定义数据 * */ UsersManagementClient.prototype.removeUdfValue = function (userId, key) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, graphqlapi_1.removeUdv(this.graphqlClient, this.tokenProvider, { targetType: graphql_v2_1.UdfTargetType.User, targetId: userId, key: key })]; case 1: _a.sent(); return [2 /*return*/]; } }); }); }; /** * 判断用户是否有某个角色 * @param userId 用户 ID * @param roleCode 角色 Code * @param namespace 权限分组 ID */ UsersManagementClient.prototype.hasRole = function (userId, roleCode, namespace) { return __awaiter(this, void 0, void 0, function () { var roleList, hasRole; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.listRoles(userId, namespace)]; case 1: roleList = _a.sent(); if (roleList.totalCount < 1) { return [2 /*return*/, false]; } hasRole = false; roleList.list.forEach(function (item) { if (item.code === roleCode) { hasRole = true; } }); return [2 /*return*/, hasRole]; } }); }); }; /** * @description 强制一批用户下线 */ UsersManagementClient.prototype.kick = function (userIds) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.httpClient.request({ url: this.options.host + "/api/v2/users/kick", method: 'POST', data: { userIds: userIds } })]; case 1: _a.sent(); return [2 /*return*/, { code: 200, message: '强制下线成功' }]; } }); }); }; UsersManagementClient.prototype.logout = function (options) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: if (!options || !options.userId) { throw new Error('请传入 options.userId,内容为要下线的用户 ID'); } return [4 /*yield*/, this.httpClient.request({ url: this.options.host + "/logout", method: 'GET', params: { appId: options.appId, userId: options.userId } })]; case 1: _a.sent(); return [2 /*return*/, { code: 200, message: '强制下线成功' }]; } }); }); }; /** * @description 查询用户的登录状态 */ UsersManagementClient.prototype.checkLoginStatus = function (userId, appId, deviceId) { return __awaiter(this, void 0, void 0, function () { var result; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.httpClient.request({ method: 'GET', url: this.options.host + "/api/v2/users/login-status", params: { userId: userId, appId: appId, deviceId: deviceId } })]; case 1: result = _a.sent(); return [2 /*return*/, result]; } }); }); }; /** * 审计日志列表 * @param options.page 当前页数 * @param options.limit 每页显示条数 * @param options.clientIp 客户端 IP 地址 * @param options.operationName 操作类型 * @param options.operatoArn 用户 Arn 通过 searchUser 方法获得 * @returns Promise */ UsersManagementClient.prototype.listUserActions = function (options) { var _a, _b; if (options === void 0) { options = { page: 1, limit: 10 }; } return __awaiter(this, void 0, void 0, function () { var result; return __generator(this, function (_c) { switch (_c.label) { case 0: return [4 /*yield*/, this.httpClient.request({ method: 'GET', url: this.options.host + "/api/v2/analysis/user-action" + utils_1.objectToQueryString({ page: (_a = options.page) === null || _a === void 0 ? void 0 : _a.toString(), limit: (_b = options.limit) === null || _b === void 0 ? void 0 : _b.toString(), clientip: options.clientIp, operation_name: options.operationName, operator_arn: options.operatoArn }) })]; case 1: result = _c.sent(); return [2 /*return*/, result]; } }); }); }; /** * @description 发送首次登录验证邮件 * */ UsersManagementClient.prototype.sendFirstLoginVerifyEmail = function (options) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, graphqlapi_1.sendFirstLoginVerifyEmail(this.graphqlClient, this.tokenProvider, options)]; case 1: _a.sent(); return [2 /*return*/, true]; } }); }); }; return UsersManagementClient; }()); exports.UsersManagementClient = UsersManagementClient; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiVXNlcnNNYW5hZ2VtZW50Q2xpZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2xpYi9tYW5hZ2VtZW50L1VzZXJzTWFuYWdlbWVudENsaWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFRQSxrQ0FBK0M7QUFDL0MsNENBZ0N1QjtBQUN2QixxREFjZ0M7QUFJaEMsa0NBQStFO0FBRS9FOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBeUJHO0FBQ0g7SUFPRSwrQkFDRSxPQUFnQyxFQUNoQyxhQUE0QixFQUM1QixVQUFzQixFQUN0QixhQUFzQyxFQUN0QyxpQkFBbUM7UUFFbkMsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUM7UUFDdkIsSUFBSSxDQUFDLGFBQWEsR0FBRyxhQUFhLENBQUM7UUFDbkMsSUFBSSxDQUFDLGFBQWEsR0FBRyxhQUFhLENBQUM7UUFDbkMsSUFBSSxDQUFDLFVBQVUsR0FBRyxVQUFVLENBQUM7UUFDN0IsSUFBSSxDQUFDLGlCQUFpQixHQUFHLGlCQUFpQixDQUFDO0lBQzdDLENBQUM7SUFFRDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7T0FpRUc7SUFDRyxzQ0FBTSxHQUFaLFVBQ0UsUUFBeUIsRUFDekIsT0FHQzs7Ozs7O3dCQUVLLEtBQ0osT0FBTyxJQUFJLEVBQUUsRUFEUCxvQkFBb0IsRUFBcEIsWUFBWSxtQkFBRyxLQUFLLEtBQUEsRUFBRSxpQ0FBaUMsRUFBakMseUJBQXlCLG1CQUFHLEtBQUssS0FBQSxDQUMvQzs4QkFDWixRQUFRLGFBQVIsUUFBUSx1QkFBUixRQUFRLENBQUUsUUFBUTt3QkFDcEIsS0FBQSxRQUFRLENBQUE7d0JBQWtCLEtBQUEsQ0FBQSxLQUFBLElBQUksQ0FBQyxPQUFPLENBQUEsQ0FBQyxlQUFlLENBQUE7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