@h-ai/iam 1.0.0-beta.2 → 1.0.0-beta.20
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/README.en.md +269 -88
- package/README.ja.md +272 -91
- package/README.md +275 -94
- package/dist/browser.d.ts +2 -1
- package/dist/browser.js +1 -1
- package/dist/{chunk-VR7JW2EW.js → chunk-PDPLBMGN.js} +6 -3
- package/dist/chunk-PDPLBMGN.js.map +1 -0
- package/dist/index.js +1212 -363
- package/dist/index.js.map +1 -1
- package/package.json +14 -14
- package/dist/chunk-VR7JW2EW.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { HaiIamError, IamConfigSchema, SessionConfigSchema, SecurityConfigSchema, LoginConfigSchema, OtpConfigSchema, ApiKeyConfigSchema, AuthUiConfigSchema, PasswordConfigSchema, RbacConfigSchema, AgreementConfigSchema, PasswordResetConfigSchema, RegisterConfigSchema } from './chunk-
|
|
2
|
-
export { AccountConfigSchema, AgreementConfigSchema, ApiKeyConfigSchema, AuthStrategyTypeSchema, AuthUiConfigSchema, CaptchaConfigSchema, HaiIamError, IamConfigSchema, LdapConfigSchema, LoginAccessConfigSchema, LoginConfigSchema, OtpConfigSchema, PasswordConfigSchema, PasswordResetConfigSchema, RbacConfigSchema, RegisterConfigSchema, SecurityConfigSchema, SessionConfigSchema } from './chunk-
|
|
1
|
+
import { HaiIamError, IamConfigSchema, SessionConfigSchema, SecurityConfigSchema, LoginConfigSchema, OtpConfigSchema, ApiKeyConfigSchema, AuthUiConfigSchema, PasswordConfigSchema, RbacConfigSchema, AgreementConfigSchema, PasswordResetConfigSchema, RegisterConfigSchema } from './chunk-PDPLBMGN.js';
|
|
2
|
+
export { AccountConfigSchema, AgreementConfigSchema, ApiKeyConfigSchema, AuthStrategyTypeSchema, AuthUiConfigSchema, CaptchaConfigSchema, HaiIamError, IamConfigSchema, LdapConfigSchema, LoginAccessConfigSchema, LoginConfigSchema, OtpConfigSchema, PasswordConfigSchema, PasswordResetConfigSchema, RbacConfigSchema, RegisterConfigSchema, SecurityConfigSchema, SessionConfigSchema } from './chunk-PDPLBMGN.js';
|
|
3
3
|
import { cache } from '@h-ai/cache';
|
|
4
4
|
import { core, err, ok } from '@h-ai/core';
|
|
5
5
|
import { crypto as crypto$1 } from '@h-ai/crypto';
|
|
6
|
-
import {
|
|
6
|
+
import { reldb, BaseReldbCrudRepository, HaiReldbError } from '@h-ai/reldb';
|
|
7
7
|
import { randomUUID, createHash, randomBytes, randomInt } from 'crypto';
|
|
8
8
|
import { verifySolution, pbkdf2, createChallenge } from 'altcha/lib';
|
|
9
9
|
import { z } from 'zod';
|
|
@@ -654,7 +654,10 @@ async function revision() {
|
|
|
654
654
|
return current;
|
|
655
655
|
if (current.data)
|
|
656
656
|
return ok(current.data);
|
|
657
|
-
const created = await cache.kv.set(revisionKey, {
|
|
657
|
+
const created = await cache.kv.set(revisionKey, {
|
|
658
|
+
epoch: randomUUID(),
|
|
659
|
+
writers: 0
|
|
660
|
+
}, { nx: true });
|
|
658
661
|
if (!created.success)
|
|
659
662
|
return created;
|
|
660
663
|
const loaded = await cache.kv.get(revisionKey);
|
|
@@ -665,7 +668,10 @@ async function change(delta) {
|
|
|
665
668
|
const current = await revision();
|
|
666
669
|
if (!current.success)
|
|
667
670
|
return current;
|
|
668
|
-
const written = await cache.kv.compareAndSwap(revisionKey, current.data, {
|
|
671
|
+
const written = await cache.kv.compareAndSwap(revisionKey, current.data, {
|
|
672
|
+
epoch: randomUUID(),
|
|
673
|
+
writers: Math.max(0, current.data.writers + delta)
|
|
674
|
+
});
|
|
669
675
|
if (!written.success)
|
|
670
676
|
return written;
|
|
671
677
|
if (written.data)
|
|
@@ -873,7 +879,9 @@ var DbApiKeyRepository = class extends BaseReldbCrudRepository {
|
|
|
873
879
|
if (!result.success) {
|
|
874
880
|
return err(
|
|
875
881
|
HaiIamError.REPOSITORY_ERROR,
|
|
876
|
-
iamM("iam_apikeyCreateFailed", {
|
|
882
|
+
iamM("iam_apikeyCreateFailed", {
|
|
883
|
+
params: { message: result.error.message }
|
|
884
|
+
}),
|
|
877
885
|
result.error
|
|
878
886
|
);
|
|
879
887
|
}
|
|
@@ -887,7 +895,10 @@ var DbApiKeyRepository = class extends BaseReldbCrudRepository {
|
|
|
887
895
|
return ok(result.data);
|
|
888
896
|
}
|
|
889
897
|
async findByKeyPrefix(prefix2, tx) {
|
|
890
|
-
const result = await this.findAll({
|
|
898
|
+
const result = await this.findAll({
|
|
899
|
+
where: "key_prefix = ?",
|
|
900
|
+
params: [prefix2]
|
|
901
|
+
}, tx);
|
|
891
902
|
if (!result.success) {
|
|
892
903
|
return this.buildQueryError(result.error);
|
|
893
904
|
}
|
|
@@ -920,7 +931,10 @@ var DbApiKeyRepository = class extends BaseReldbCrudRepository {
|
|
|
920
931
|
return result;
|
|
921
932
|
}
|
|
922
933
|
async countByUserId(userId, tx) {
|
|
923
|
-
const result = await this.count({
|
|
934
|
+
const result = await this.count({
|
|
935
|
+
where: "user_id = ?",
|
|
936
|
+
params: [userId]
|
|
937
|
+
}, tx);
|
|
924
938
|
if (!result.success) {
|
|
925
939
|
return this.buildQueryError(result.error);
|
|
926
940
|
}
|
|
@@ -931,7 +945,9 @@ var DbApiKeyRepository = class extends BaseReldbCrudRepository {
|
|
|
931
945
|
if (!result.success) {
|
|
932
946
|
return err(
|
|
933
947
|
HaiIamError.REPOSITORY_ERROR,
|
|
934
|
-
iamM("iam_apikeyUpdateFailed", {
|
|
948
|
+
iamM("iam_apikeyUpdateFailed", {
|
|
949
|
+
params: { message: result.error.message }
|
|
950
|
+
}),
|
|
935
951
|
result.error
|
|
936
952
|
);
|
|
937
953
|
}
|
|
@@ -942,7 +958,9 @@ var DbApiKeyRepository = class extends BaseReldbCrudRepository {
|
|
|
942
958
|
if (!result.success) {
|
|
943
959
|
return err(
|
|
944
960
|
HaiIamError.REPOSITORY_ERROR,
|
|
945
|
-
iamM("iam_apikeyDeleteFailed", {
|
|
961
|
+
iamM("iam_apikeyDeleteFailed", {
|
|
962
|
+
params: { message: result.error.message }
|
|
963
|
+
}),
|
|
946
964
|
result.error
|
|
947
965
|
);
|
|
948
966
|
}
|
|
@@ -951,7 +969,9 @@ var DbApiKeyRepository = class extends BaseReldbCrudRepository {
|
|
|
951
969
|
buildQueryError(error) {
|
|
952
970
|
return err(
|
|
953
971
|
HaiIamError.REPOSITORY_ERROR,
|
|
954
|
-
iamM("iam_apikeyQueryFailed", {
|
|
972
|
+
iamM("iam_apikeyQueryFailed", {
|
|
973
|
+
params: { message: error.message }
|
|
974
|
+
}),
|
|
955
975
|
error
|
|
956
976
|
);
|
|
957
977
|
}
|
|
@@ -1016,7 +1036,10 @@ function buildSession(options, now, sessionTtl, accessToken, credentialVersion)
|
|
|
1016
1036
|
function applySessionPatch(session, patch) {
|
|
1017
1037
|
const nextSession = { ...session };
|
|
1018
1038
|
if (patch.data !== void 0) {
|
|
1019
|
-
nextSession.data = {
|
|
1039
|
+
nextSession.data = {
|
|
1040
|
+
...nextSession.data,
|
|
1041
|
+
...patch.data
|
|
1042
|
+
};
|
|
1020
1043
|
}
|
|
1021
1044
|
if (patch.roles !== void 0) {
|
|
1022
1045
|
nextSession.roles = patch.roles;
|
|
@@ -1140,16 +1163,86 @@ function toUser(storedUser) {
|
|
|
1140
1163
|
// src/user/iam-user-repository-user.ts
|
|
1141
1164
|
var TABLE_NAME2 = "hai_iam_users";
|
|
1142
1165
|
var USER_FIELDS = [
|
|
1143
|
-
{
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
{
|
|
1152
|
-
|
|
1166
|
+
{
|
|
1167
|
+
fieldName: "mustChangePassword",
|
|
1168
|
+
columnName: "must_change_password",
|
|
1169
|
+
def: { type: "BOOLEAN", notNull: true, defaultValue: false },
|
|
1170
|
+
select: true,
|
|
1171
|
+
create: true,
|
|
1172
|
+
update: false
|
|
1173
|
+
},
|
|
1174
|
+
{
|
|
1175
|
+
fieldName: "accountType",
|
|
1176
|
+
columnName: "account_type",
|
|
1177
|
+
def: { type: "TEXT", notNull: true, defaultValue: "permanent" },
|
|
1178
|
+
select: true,
|
|
1179
|
+
create: true,
|
|
1180
|
+
update: true
|
|
1181
|
+
},
|
|
1182
|
+
{
|
|
1183
|
+
fieldName: "accountStatus",
|
|
1184
|
+
columnName: "account_status",
|
|
1185
|
+
def: { type: "TEXT", notNull: true, defaultValue: "active" },
|
|
1186
|
+
select: true,
|
|
1187
|
+
create: true,
|
|
1188
|
+
update: true
|
|
1189
|
+
},
|
|
1190
|
+
{
|
|
1191
|
+
fieldName: "accountExpiresAt",
|
|
1192
|
+
columnName: "account_expires_at",
|
|
1193
|
+
def: { type: "TIMESTAMP" },
|
|
1194
|
+
select: true,
|
|
1195
|
+
create: true,
|
|
1196
|
+
update: true
|
|
1197
|
+
},
|
|
1198
|
+
{
|
|
1199
|
+
fieldName: "lastLoginAt",
|
|
1200
|
+
columnName: "last_login_at",
|
|
1201
|
+
def: { type: "TIMESTAMP" },
|
|
1202
|
+
select: true,
|
|
1203
|
+
create: false,
|
|
1204
|
+
update: false
|
|
1205
|
+
},
|
|
1206
|
+
{
|
|
1207
|
+
fieldName: "credentialVersion",
|
|
1208
|
+
columnName: "credential_version",
|
|
1209
|
+
def: { type: "TEXT", notNull: true, defaultValue: "" },
|
|
1210
|
+
select: true,
|
|
1211
|
+
create: false,
|
|
1212
|
+
update: false
|
|
1213
|
+
},
|
|
1214
|
+
{
|
|
1215
|
+
fieldName: "resetVersion",
|
|
1216
|
+
columnName: "reset_version",
|
|
1217
|
+
def: { type: "TEXT", notNull: true, defaultValue: "" },
|
|
1218
|
+
select: true,
|
|
1219
|
+
create: false,
|
|
1220
|
+
update: false
|
|
1221
|
+
},
|
|
1222
|
+
{
|
|
1223
|
+
fieldName: "resetHash",
|
|
1224
|
+
columnName: "reset_hash",
|
|
1225
|
+
def: { type: "TEXT" },
|
|
1226
|
+
select: true,
|
|
1227
|
+
create: false,
|
|
1228
|
+
update: false
|
|
1229
|
+
},
|
|
1230
|
+
{
|
|
1231
|
+
fieldName: "resetExpiresAt",
|
|
1232
|
+
columnName: "reset_expires_at",
|
|
1233
|
+
def: { type: "BIGINT" },
|
|
1234
|
+
select: true,
|
|
1235
|
+
create: false,
|
|
1236
|
+
update: false
|
|
1237
|
+
},
|
|
1238
|
+
{
|
|
1239
|
+
fieldName: "resetAttempts",
|
|
1240
|
+
columnName: "reset_attempts",
|
|
1241
|
+
def: { type: "INTEGER", notNull: true, defaultValue: 0 },
|
|
1242
|
+
select: true,
|
|
1243
|
+
create: false,
|
|
1244
|
+
update: false
|
|
1245
|
+
},
|
|
1153
1246
|
{
|
|
1154
1247
|
fieldName: "id",
|
|
1155
1248
|
columnName: "id",
|
|
@@ -1324,13 +1417,22 @@ var DbUserRepository = class extends BaseReldbCrudRepository {
|
|
|
1324
1417
|
return "CAST((julianday('now') - 2440587.5) * 86400000 AS INTEGER)";
|
|
1325
1418
|
}
|
|
1326
1419
|
async recordSuccessfulLogin(userId) {
|
|
1327
|
-
const result = await reldb.sql.execute(`UPDATE ${TABLE_NAME2} SET last_login_at = ? WHERE id = ?`, [
|
|
1420
|
+
const result = await reldb.sql.execute(`UPDATE ${TABLE_NAME2} SET last_login_at = ? WHERE id = ?`, [
|
|
1421
|
+
reldb.config?.type === "sqlite" ? Date.now() : /* @__PURE__ */ new Date(),
|
|
1422
|
+
userId
|
|
1423
|
+
]);
|
|
1328
1424
|
return result.success ? ok(void 0) : this.buildQueryError(result.error, result.error);
|
|
1329
1425
|
}
|
|
1330
1426
|
async suspendInactive(cutoff) {
|
|
1331
1427
|
const result = await reldb.sql.execute(
|
|
1332
1428
|
`UPDATE ${TABLE_NAME2} SET account_status = ?, credential_version = ?, reset_version = ?, reset_hash = NULL, reset_expires_at = NULL WHERE account_status = ? AND COALESCE(last_login_at, created_at) <= ?`,
|
|
1333
|
-
[
|
|
1429
|
+
[
|
|
1430
|
+
"dormant",
|
|
1431
|
+
crypto.randomUUID(),
|
|
1432
|
+
crypto.randomUUID(),
|
|
1433
|
+
"active",
|
|
1434
|
+
reldb.config?.type === "sqlite" ? cutoff.getTime() : cutoff
|
|
1435
|
+
]
|
|
1334
1436
|
);
|
|
1335
1437
|
return result.success ? ok(result.data.changes) : this.buildQueryError(result.error, result.error);
|
|
1336
1438
|
}
|
|
@@ -1338,7 +1440,15 @@ var DbUserRepository = class extends BaseReldbCrudRepository {
|
|
|
1338
1440
|
async setPassword(input) {
|
|
1339
1441
|
const result = await reldb.sql.execute(
|
|
1340
1442
|
`UPDATE ${TABLE_NAME2} SET password_hash = ?, password_updated_at = ?, credential_version = ?, reset_version = ?, must_change_password = ?, reset_hash = NULL, reset_expires_at = NULL, reset_attempts = 0 WHERE id = ? AND credential_version = ?`,
|
|
1341
|
-
[
|
|
1443
|
+
[
|
|
1444
|
+
input.passwordHash,
|
|
1445
|
+
reldb.config?.type === "sqlite" ? Date.now() : /* @__PURE__ */ new Date(),
|
|
1446
|
+
crypto.randomUUID(),
|
|
1447
|
+
crypto.randomUUID(),
|
|
1448
|
+
reldb.config?.type === "postgresql" ? input.mustChangePassword === true : Number(input.mustChangePassword === true),
|
|
1449
|
+
input.userId,
|
|
1450
|
+
input.expectedVersion
|
|
1451
|
+
]
|
|
1342
1452
|
);
|
|
1343
1453
|
return result.success ? ok(result.data.changes === 1) : this.buildQueryError(result.error, result.error);
|
|
1344
1454
|
}
|
|
@@ -1349,7 +1459,16 @@ var DbUserRepository = class extends BaseReldbCrudRepository {
|
|
|
1349
1459
|
return err(HaiIamError.INVALID_ARGUMENT, iamM("iam_resetTokenInvalid"));
|
|
1350
1460
|
const result = await reldb.sql.execute(
|
|
1351
1461
|
`UPDATE ${TABLE_NAME2} SET reset_hash = ?, reset_expires_at = ?, reset_attempts = 0, reset_version = ? WHERE id = ? AND credential_version = ? AND reset_version = ? AND account_status = ? AND ? > ${this.databaseNow()}`,
|
|
1352
|
-
[
|
|
1462
|
+
[
|
|
1463
|
+
input.tokenHash,
|
|
1464
|
+
expiry,
|
|
1465
|
+
crypto.randomUUID(),
|
|
1466
|
+
input.user.id,
|
|
1467
|
+
input.user.credentialVersion ?? "",
|
|
1468
|
+
input.user.resetVersion ?? "",
|
|
1469
|
+
"active",
|
|
1470
|
+
expiry
|
|
1471
|
+
]
|
|
1353
1472
|
);
|
|
1354
1473
|
return result.success ? ok(result.data.changes === 1) : this.buildQueryError(result.error, result.error);
|
|
1355
1474
|
}
|
|
@@ -1368,7 +1487,15 @@ var DbUserRepository = class extends BaseReldbCrudRepository {
|
|
|
1368
1487
|
const version = crypto.randomUUID();
|
|
1369
1488
|
const result = await reldb.sql.execute(
|
|
1370
1489
|
`UPDATE ${TABLE_NAME2} SET reset_attempts = reset_attempts + 1, reset_version = ? WHERE id = ? AND reset_hash = ? AND reset_version = ? AND credential_version = ? AND reset_attempts < ? AND reset_expires_at > ${this.databaseNow()} AND account_status = ?`,
|
|
1371
|
-
[
|
|
1490
|
+
[
|
|
1491
|
+
version,
|
|
1492
|
+
user.id,
|
|
1493
|
+
tokenHash,
|
|
1494
|
+
user.resetVersion ?? "",
|
|
1495
|
+
user.credentialVersion ?? "",
|
|
1496
|
+
maxAttempts,
|
|
1497
|
+
"active"
|
|
1498
|
+
]
|
|
1372
1499
|
);
|
|
1373
1500
|
if (!result.success)
|
|
1374
1501
|
return this.buildQueryError(result.error, result.error);
|
|
@@ -1380,7 +1507,17 @@ var DbUserRepository = class extends BaseReldbCrudRepository {
|
|
|
1380
1507
|
async commitPasswordReset(input) {
|
|
1381
1508
|
const result = await reldb.sql.execute(
|
|
1382
1509
|
`UPDATE ${TABLE_NAME2} SET password_hash = ?, password_updated_at = ?, credential_version = ?, reset_version = ?, must_change_password = FALSE, reset_hash = NULL, reset_expires_at = NULL, reset_attempts = 0, login_failed_count = 0, first_login_failed_at = NULL, locked_until = NULL, last_login_failed_at = NULL WHERE id = ? AND reset_hash = ? AND reset_version = ? AND credential_version = ? AND reset_expires_at > ${this.databaseNow()} AND account_status = ?`,
|
|
1383
|
-
[
|
|
1510
|
+
[
|
|
1511
|
+
input.passwordHash,
|
|
1512
|
+
reldb.config?.type === "sqlite" ? Date.now() : /* @__PURE__ */ new Date(),
|
|
1513
|
+
crypto.randomUUID(),
|
|
1514
|
+
crypto.randomUUID(),
|
|
1515
|
+
input.user.id,
|
|
1516
|
+
input.tokenHash,
|
|
1517
|
+
input.user.resetVersion ?? "",
|
|
1518
|
+
input.user.credentialVersion ?? "",
|
|
1519
|
+
"active"
|
|
1520
|
+
]
|
|
1384
1521
|
);
|
|
1385
1522
|
return result.success ? ok(result.data.changes === 1) : this.buildQueryError(result.error, result.error);
|
|
1386
1523
|
}
|
|
@@ -1399,7 +1536,12 @@ var DbUserRepository = class extends BaseReldbCrudRepository {
|
|
|
1399
1536
|
return this.buildQueryError(expired.error, expired.error);
|
|
1400
1537
|
const result = await reldb.sql.execute(
|
|
1401
1538
|
`UPDATE ${TABLE_NAME2} SET locked_until = CASE WHEN COALESCE(login_failed_count, 0) + 1 >= ? THEN ? ELSE locked_until END, login_failed_count = COALESCE(login_failed_count, 0) + 1, last_login_failed_at = ? WHERE id = ? AND locked_until IS NULL`,
|
|
1402
|
-
[
|
|
1539
|
+
[
|
|
1540
|
+
policy.maxLoginAttempts,
|
|
1541
|
+
lockedUntil,
|
|
1542
|
+
now,
|
|
1543
|
+
userId
|
|
1544
|
+
]
|
|
1403
1545
|
);
|
|
1404
1546
|
if (!result.success)
|
|
1405
1547
|
return this.buildQueryError(result.error, result.error);
|
|
@@ -1407,8 +1549,9 @@ var DbUserRepository = class extends BaseReldbCrudRepository {
|
|
|
1407
1549
|
}
|
|
1408
1550
|
async resetLoginFailures(userId, expectedCredentialVersion, lockoutEnabled = true) {
|
|
1409
1551
|
const params = [userId, expectedCredentialVersion, "active"];
|
|
1410
|
-
if (lockoutEnabled)
|
|
1552
|
+
if (lockoutEnabled) {
|
|
1411
1553
|
params.push(reldb.config?.type === "sqlite" ? Date.now() : /* @__PURE__ */ new Date());
|
|
1554
|
+
}
|
|
1412
1555
|
const result = await reldb.sql.execute(
|
|
1413
1556
|
`UPDATE ${TABLE_NAME2} SET login_failed_count = 0, first_login_failed_at = NULL, last_login_failed_at = NULL, locked_until = NULL WHERE id = ? AND credential_version = ? AND account_status = ? ${lockoutEnabled ? "AND (locked_until IS NULL OR locked_until <= ?)" : ""}`,
|
|
1414
1557
|
params
|
|
@@ -1462,7 +1605,9 @@ var DbUserRepository = class extends BaseReldbCrudRepository {
|
|
|
1462
1605
|
buildQueryError(error, cause) {
|
|
1463
1606
|
return err(
|
|
1464
1607
|
HaiIamError.REPOSITORY_ERROR,
|
|
1465
|
-
iamM("iam_queryUserFailed", {
|
|
1608
|
+
iamM("iam_queryUserFailed", {
|
|
1609
|
+
params: { message: error.message }
|
|
1610
|
+
}),
|
|
1466
1611
|
cause
|
|
1467
1612
|
);
|
|
1468
1613
|
}
|
|
@@ -1511,7 +1656,11 @@ function applyApiKeyScopes(access, scopes) {
|
|
|
1511
1656
|
permissions.add(permission);
|
|
1512
1657
|
}
|
|
1513
1658
|
}
|
|
1514
|
-
return {
|
|
1659
|
+
return {
|
|
1660
|
+
...access,
|
|
1661
|
+
roles: scopes.includes("*") ? access.roles : [],
|
|
1662
|
+
permissions: [...permissions]
|
|
1663
|
+
};
|
|
1515
1664
|
}
|
|
1516
1665
|
function rejectMissingPasswordCredentials() {
|
|
1517
1666
|
const padded = crypto$1.password.hash("dummy-password-to-prevent-timing-leak");
|
|
@@ -1586,7 +1735,9 @@ function createApiKeyStrategy(config) {
|
|
|
1586
1735
|
if (candidate.expiresAt && /* @__PURE__ */ new Date() >= candidate.expiresAt) {
|
|
1587
1736
|
return err(HaiIamError.APIKEY_EXPIRED, iamM("iam_apikeyExpired"));
|
|
1588
1737
|
}
|
|
1589
|
-
apiKeyRepository.updateFields(candidate.id, {
|
|
1738
|
+
apiKeyRepository.updateFields(candidate.id, {
|
|
1739
|
+
lastUsedAt: /* @__PURE__ */ new Date()
|
|
1740
|
+
}).catch(() => {
|
|
1590
1741
|
logger3.warn("Failed to update API Key lastUsedAt", { keyId: candidate.id });
|
|
1591
1742
|
});
|
|
1592
1743
|
return ok(toApiKey(candidate));
|
|
@@ -1597,7 +1748,12 @@ function createApiKeyStrategy(config) {
|
|
|
1597
1748
|
const fingerprint = createHash("sha256").update(rawKey).digest("hex");
|
|
1598
1749
|
const result = await readIamCache(`verified_apikey:${fingerprint}`, async () => {
|
|
1599
1750
|
const verified = await loadRawKey(rawKey);
|
|
1600
|
-
return verified.success ? ok({
|
|
1751
|
+
return verified.success ? ok({
|
|
1752
|
+
...verified.data,
|
|
1753
|
+
expiresAt: verified.data.expiresAt?.getTime() ?? null,
|
|
1754
|
+
createdAt: verified.data.createdAt.getTime(),
|
|
1755
|
+
lastUsedAt: verified.data.lastUsedAt?.getTime() ?? null
|
|
1756
|
+
}) : verified;
|
|
1601
1757
|
});
|
|
1602
1758
|
if (!result.success)
|
|
1603
1759
|
return result;
|
|
@@ -1605,7 +1761,12 @@ function createApiKeyStrategy(config) {
|
|
|
1605
1761
|
return err(HaiIamError.APIKEY_DISABLED, iamM("iam_apikeyDisabled"));
|
|
1606
1762
|
if (result.data.expiresAt !== null && result.data.expiresAt <= Date.now())
|
|
1607
1763
|
return err(HaiIamError.APIKEY_EXPIRED, iamM("iam_apikeyExpired"));
|
|
1608
|
-
return ok({
|
|
1764
|
+
return ok({
|
|
1765
|
+
...result.data,
|
|
1766
|
+
expiresAt: result.data.expiresAt === null ? null : new Date(result.data.expiresAt),
|
|
1767
|
+
createdAt: new Date(result.data.createdAt),
|
|
1768
|
+
lastUsedAt: result.data.lastUsedAt === null ? null : new Date(result.data.lastUsedAt)
|
|
1769
|
+
});
|
|
1609
1770
|
}
|
|
1610
1771
|
const strategy = {
|
|
1611
1772
|
type: "apikey",
|
|
@@ -1632,7 +1793,13 @@ function createApiKeyStrategy(config) {
|
|
|
1632
1793
|
return err(HaiIamError.ACCOUNT_UNAVAILABLE, iamM("iam_accountUnavailable"));
|
|
1633
1794
|
}
|
|
1634
1795
|
logger3.debug("API Key authentication succeeded", { userId: userResult.data.id, keyId: apiKey.id });
|
|
1635
|
-
return ok({
|
|
1796
|
+
return ok({
|
|
1797
|
+
user: {
|
|
1798
|
+
...toUser(userResult.data),
|
|
1799
|
+
credentialVersion: userResult.data.credentialVersion ?? ""
|
|
1800
|
+
},
|
|
1801
|
+
apiKey
|
|
1802
|
+
});
|
|
1636
1803
|
}
|
|
1637
1804
|
};
|
|
1638
1805
|
const apiKeyFunctions = {
|
|
@@ -1642,7 +1809,9 @@ function createApiKeyStrategy(config) {
|
|
|
1642
1809
|
if (!countResult.success)
|
|
1643
1810
|
return countResult;
|
|
1644
1811
|
if (countResult.data >= apikeyConfig.maxKeysPerUser) {
|
|
1645
|
-
return err(HaiIamError.INVALID_ARGUMENT, iamM("iam_apikeyMaxKeysReached", {
|
|
1812
|
+
return err(HaiIamError.INVALID_ARGUMENT, iamM("iam_apikeyMaxKeysReached", {
|
|
1813
|
+
params: { max: apikeyConfig.maxKeysPerUser }
|
|
1814
|
+
}));
|
|
1646
1815
|
}
|
|
1647
1816
|
const rawKey = generateRawKey(apikeyConfig.prefix);
|
|
1648
1817
|
const hashResult = passwordOps.hash(rawKey);
|
|
@@ -1669,16 +1838,26 @@ function createApiKeyStrategy(config) {
|
|
|
1669
1838
|
if (!insertResult.success)
|
|
1670
1839
|
return insertResult;
|
|
1671
1840
|
logger3.info("API Key created", { userId, keyId: id, name: options.name });
|
|
1672
|
-
return ok({
|
|
1841
|
+
return ok({
|
|
1842
|
+
apiKey: toApiKey(storedApiKey),
|
|
1843
|
+
rawKey
|
|
1844
|
+
});
|
|
1673
1845
|
} catch (error) {
|
|
1674
|
-
return err(HaiIamError.INTERNAL_ERROR, iamM("iam_apikeyCreateFailed", {
|
|
1846
|
+
return err(HaiIamError.INTERNAL_ERROR, iamM("iam_apikeyCreateFailed", {
|
|
1847
|
+
params: {
|
|
1848
|
+
message: String(error)
|
|
1849
|
+
}
|
|
1850
|
+
}), error);
|
|
1675
1851
|
}
|
|
1676
1852
|
},
|
|
1677
1853
|
async listApiKeys(userId, options) {
|
|
1678
1854
|
const result = await apiKeyRepository.findPageByUserId(userId, options ?? {});
|
|
1679
1855
|
if (!result.success)
|
|
1680
1856
|
return result;
|
|
1681
|
-
return ok({
|
|
1857
|
+
return ok({
|
|
1858
|
+
...result.data,
|
|
1859
|
+
items: result.data.items.map(toApiKey)
|
|
1860
|
+
});
|
|
1682
1861
|
},
|
|
1683
1862
|
async getApiKey(keyId) {
|
|
1684
1863
|
const result = await apiKeyRepository.findOneById(keyId);
|
|
@@ -1708,10 +1887,20 @@ function createLoginAccessCheck(policy) {
|
|
|
1708
1887
|
}
|
|
1709
1888
|
var logger4 = core.logger.child({ module: "iam", scope: "captcha" });
|
|
1710
1889
|
var prefix = "hai:iam:captcha:";
|
|
1711
|
-
var operationSchema = z.enum([
|
|
1890
|
+
var operationSchema = z.enum([
|
|
1891
|
+
"login",
|
|
1892
|
+
"register",
|
|
1893
|
+
"sendOtp",
|
|
1894
|
+
"passwordReset",
|
|
1895
|
+
"renewPassword"
|
|
1896
|
+
]);
|
|
1712
1897
|
var proofSchema = z.object({
|
|
1713
1898
|
id: z.string().regex(/^[a-f0-9]{64}$/),
|
|
1714
|
-
solution: z.object({
|
|
1899
|
+
solution: z.object({
|
|
1900
|
+
counter: z.number().int().min(0).max(2e3),
|
|
1901
|
+
derivedKey: z.string().regex(/^[a-f0-9]{64}$/i),
|
|
1902
|
+
time: z.number().finite().nonnegative().optional()
|
|
1903
|
+
})
|
|
1715
1904
|
});
|
|
1716
1905
|
async function createIamCaptcha(config, input) {
|
|
1717
1906
|
if (!config.enabled)
|
|
@@ -1732,7 +1921,12 @@ async function createIamCaptcha(config, input) {
|
|
|
1732
1921
|
hmacKeySignatureSecret: keySecret,
|
|
1733
1922
|
data: { operation: operation.data }
|
|
1734
1923
|
});
|
|
1735
|
-
const record = {
|
|
1924
|
+
const record = {
|
|
1925
|
+
operation: operation.data,
|
|
1926
|
+
challenge,
|
|
1927
|
+
signatureSecret,
|
|
1928
|
+
keySecret
|
|
1929
|
+
};
|
|
1736
1930
|
const saved = await cache.kv.set(`${prefix}${id}`, record, { nx: true, ex: config.ttlSeconds });
|
|
1737
1931
|
if (!saved.success || !saved.data)
|
|
1738
1932
|
return err(HaiIamError.CAPTCHA_UNAVAILABLE, iamM("iam_captchaUnavailable"));
|
|
@@ -1766,7 +1960,13 @@ async function verifyIamCaptcha(config, operation, proof) {
|
|
|
1766
1960
|
if (!found.data || found.data.operation !== operation)
|
|
1767
1961
|
return err(HaiIamError.CAPTCHA_INVALID, iamM("iam_captchaInvalid"));
|
|
1768
1962
|
const record = found.data;
|
|
1769
|
-
const result = await verifySolution({
|
|
1963
|
+
const result = await verifySolution({
|
|
1964
|
+
challenge: record.challenge,
|
|
1965
|
+
solution: payload.solution,
|
|
1966
|
+
deriveKey: pbkdf2.deriveKey,
|
|
1967
|
+
hmacSignatureSecret: record.signatureSecret,
|
|
1968
|
+
hmacKeySignatureSecret: record.keySecret
|
|
1969
|
+
});
|
|
1770
1970
|
if (!result.verified)
|
|
1771
1971
|
return err(HaiIamError.CAPTCHA_INVALID, iamM("iam_captchaInvalid"));
|
|
1772
1972
|
const removed = await cache.kv.del(key);
|
|
@@ -1848,7 +2048,12 @@ function createLdapStrategy(config) {
|
|
|
1848
2048
|
maxLoginAttempts = 5,
|
|
1849
2049
|
lockoutDuration = 900
|
|
1850
2050
|
} = config;
|
|
1851
|
-
const failurePolicy = {
|
|
2051
|
+
const failurePolicy = {
|
|
2052
|
+
maxLoginAttempts,
|
|
2053
|
+
lockoutDuration,
|
|
2054
|
+
lockoutEnabled: config.lockoutEnabled ?? true,
|
|
2055
|
+
failureWindow: config.failureWindow ?? 86400
|
|
2056
|
+
};
|
|
1852
2057
|
function getAttributeValue(entry, attr) {
|
|
1853
2058
|
const value = entry.attributes[attr];
|
|
1854
2059
|
if (Array.isArray(value)) {
|
|
@@ -1956,7 +2161,9 @@ function createLdapStrategy(config) {
|
|
|
1956
2161
|
if (!createResult.success) {
|
|
1957
2162
|
return err(
|
|
1958
2163
|
HaiIamError.REPOSITORY_ERROR,
|
|
1959
|
-
iamM("iam_createUserFailed", {
|
|
2164
|
+
iamM("iam_createUserFailed", {
|
|
2165
|
+
params: { message: createResult.error.message }
|
|
2166
|
+
}),
|
|
1960
2167
|
createResult.error
|
|
1961
2168
|
);
|
|
1962
2169
|
}
|
|
@@ -1999,7 +2206,10 @@ function createLdapStrategy(config) {
|
|
|
1999
2206
|
if (!resetResult.success)
|
|
2000
2207
|
return resetResult;
|
|
2001
2208
|
logger5.debug("LDAP authentication succeeded", { userId: storedUser.id });
|
|
2002
|
-
return ok({
|
|
2209
|
+
return ok({
|
|
2210
|
+
...toUser(storedUser),
|
|
2211
|
+
credentialVersion: storedUser.credentialVersion ?? ""
|
|
2212
|
+
});
|
|
2003
2213
|
} finally {
|
|
2004
2214
|
await client.unbind();
|
|
2005
2215
|
}
|
|
@@ -2035,7 +2245,11 @@ function createCacheOtpRepository() {
|
|
|
2035
2245
|
if (!current.success)
|
|
2036
2246
|
return failure2();
|
|
2037
2247
|
if (current.data && current.data.cooldownUntil > Date.now()) {
|
|
2038
|
-
return err(HaiIamError.OTP_RESEND_TOO_FAST, iamM("iam_otpResendTooFast", {
|
|
2248
|
+
return err(HaiIamError.OTP_RESEND_TOO_FAST, iamM("iam_otpResendTooFast", {
|
|
2249
|
+
params: {
|
|
2250
|
+
seconds: Math.ceil((current.data.cooldownUntil - Date.now()) / 1e3)
|
|
2251
|
+
}
|
|
2252
|
+
}));
|
|
2039
2253
|
}
|
|
2040
2254
|
if (Date.now() >= state.expiresAt)
|
|
2041
2255
|
return invalid();
|
|
@@ -2046,7 +2260,10 @@ function createCacheOtpRepository() {
|
|
|
2046
2260
|
return err(HaiIamError.SECURITY_BUSY, iamM("iam_securityBusy"));
|
|
2047
2261
|
if (Date.now() >= state.expiresAt)
|
|
2048
2262
|
return invalid();
|
|
2049
|
-
return ok({
|
|
2263
|
+
return ok({
|
|
2264
|
+
idempotencyKey: `iam-otp:${state.version}`,
|
|
2265
|
+
expiresAt: new Date(state.expiresAt)
|
|
2266
|
+
});
|
|
2050
2267
|
},
|
|
2051
2268
|
async consumeOtp(identifier, code, maxAttempts) {
|
|
2052
2269
|
const key = keyFor(identifier);
|
|
@@ -2061,7 +2278,11 @@ function createCacheOtpRepository() {
|
|
|
2061
2278
|
version = state.version;
|
|
2062
2279
|
const correct = core.string.constantTimeEqual(state.code, code);
|
|
2063
2280
|
const attempts = state.attempts + 1;
|
|
2064
|
-
const next = {
|
|
2281
|
+
const next = {
|
|
2282
|
+
...state,
|
|
2283
|
+
attempts,
|
|
2284
|
+
code: correct || attempts >= maxAttempts ? null : state.code
|
|
2285
|
+
};
|
|
2065
2286
|
const committed = await cache.kv.compareAndSwap(key, state, next, { keepTtl: true });
|
|
2066
2287
|
if (!committed.success)
|
|
2067
2288
|
return failure2();
|
|
@@ -2106,7 +2327,12 @@ function createOtpStrategy(config) {
|
|
|
2106
2327
|
const otpConfig = config.otpConfig ? OtpConfigSchema.parse(config.otpConfig) : OtpConfigSchema.parse({});
|
|
2107
2328
|
const maxLoginAttempts = config.maxLoginAttempts ?? 5;
|
|
2108
2329
|
const lockoutDuration = config.lockoutDuration ?? 900;
|
|
2109
|
-
const failurePolicy = {
|
|
2330
|
+
const failurePolicy = {
|
|
2331
|
+
maxLoginAttempts,
|
|
2332
|
+
lockoutDuration,
|
|
2333
|
+
lockoutEnabled: config.lockoutEnabled ?? true,
|
|
2334
|
+
failureWindow: config.failureWindow ?? 86400
|
|
2335
|
+
};
|
|
2110
2336
|
const registerConfig = config.registerConfig;
|
|
2111
2337
|
const allowAutoRegister = registerConfig?.enabled ?? config.autoRegister ?? false;
|
|
2112
2338
|
const defaultAccountStatus = registerConfig?.defaultAccountStatus ?? "active";
|
|
@@ -2157,7 +2383,9 @@ function createOtpStrategy(config) {
|
|
|
2157
2383
|
if (!createResult.success) {
|
|
2158
2384
|
return err(
|
|
2159
2385
|
HaiIamError.REPOSITORY_ERROR,
|
|
2160
|
-
iamM("iam_createUserFailed", {
|
|
2386
|
+
iamM("iam_createUserFailed", {
|
|
2387
|
+
params: { message: createResult.error.message }
|
|
2388
|
+
}),
|
|
2161
2389
|
createResult.error
|
|
2162
2390
|
);
|
|
2163
2391
|
}
|
|
@@ -2187,7 +2415,10 @@ function createOtpStrategy(config) {
|
|
|
2187
2415
|
if (!resetResult.success)
|
|
2188
2416
|
return resetResult;
|
|
2189
2417
|
logger6.debug("OTP authentication succeeded", { userId: storedUser.id });
|
|
2190
|
-
return ok({
|
|
2418
|
+
return ok({
|
|
2419
|
+
...toUser(storedUser),
|
|
2420
|
+
credentialVersion: storedUser.credentialVersion ?? ""
|
|
2421
|
+
});
|
|
2191
2422
|
}
|
|
2192
2423
|
};
|
|
2193
2424
|
function isChannelEnabled(identifier) {
|
|
@@ -2199,7 +2430,12 @@ function createOtpStrategy(config) {
|
|
|
2199
2430
|
if (!isChannelEnabled(identifier))
|
|
2200
2431
|
return err(HaiIamError.LOGIN_DISABLED, iamM("iam_identifierTypeNotSupported"));
|
|
2201
2432
|
const code = generateOtpCode(otpConfig.length);
|
|
2202
|
-
const saved = await config.otpRepository.issueOtp({
|
|
2433
|
+
const saved = await config.otpRepository.issueOtp({
|
|
2434
|
+
identifier,
|
|
2435
|
+
code,
|
|
2436
|
+
expiresIn: otpConfig.expiresIn,
|
|
2437
|
+
resendInterval: otpConfig.resendInterval
|
|
2438
|
+
});
|
|
2203
2439
|
if (!saved.success)
|
|
2204
2440
|
return saved;
|
|
2205
2441
|
const context = saved.data;
|
|
@@ -2244,7 +2480,12 @@ function createPasswordStrategy(config) {
|
|
|
2244
2480
|
const passwordConfig = config.passwordConfig ? PasswordConfigSchema.parse(config.passwordConfig) : PasswordConfigSchema.parse({});
|
|
2245
2481
|
const maxLoginAttempts = config.maxLoginAttempts ?? 5;
|
|
2246
2482
|
const lockoutDuration = config.lockoutDuration ?? 900;
|
|
2247
|
-
const failurePolicy = {
|
|
2483
|
+
const failurePolicy = {
|
|
2484
|
+
maxLoginAttempts,
|
|
2485
|
+
lockoutDuration,
|
|
2486
|
+
lockoutEnabled: config.lockoutEnabled ?? true,
|
|
2487
|
+
failureWindow: config.failureWindow ?? 86400
|
|
2488
|
+
};
|
|
2248
2489
|
const passwordOps = crypto$1.password;
|
|
2249
2490
|
function mapPasswordError(message) {
|
|
2250
2491
|
return err(
|
|
@@ -2256,13 +2497,17 @@ function createPasswordStrategy(config) {
|
|
|
2256
2497
|
if (password.length < passwordConfig.minLength) {
|
|
2257
2498
|
return err(
|
|
2258
2499
|
HaiIamError.PASSWORD_POLICY_VIOLATION,
|
|
2259
|
-
iamM("iam_passwordMinLength", {
|
|
2500
|
+
iamM("iam_passwordMinLength", {
|
|
2501
|
+
params: { minLength: passwordConfig.minLength }
|
|
2502
|
+
})
|
|
2260
2503
|
);
|
|
2261
2504
|
}
|
|
2262
2505
|
if (password.length > passwordConfig.maxLength) {
|
|
2263
2506
|
return err(
|
|
2264
2507
|
HaiIamError.PASSWORD_POLICY_VIOLATION,
|
|
2265
|
-
iamM("iam_passwordMaxLength", {
|
|
2508
|
+
iamM("iam_passwordMaxLength", {
|
|
2509
|
+
params: { maxLength: passwordConfig.maxLength }
|
|
2510
|
+
})
|
|
2266
2511
|
);
|
|
2267
2512
|
}
|
|
2268
2513
|
if (passwordConfig.requireUppercase && !PASSWORD_UPPERCASE_REGEX.test(password)) {
|
|
@@ -2289,9 +2534,17 @@ function createPasswordStrategy(config) {
|
|
|
2289
2534
|
iamM("iam_passwordNeedSpecialChar")
|
|
2290
2535
|
);
|
|
2291
2536
|
}
|
|
2292
|
-
const classes = [
|
|
2293
|
-
|
|
2294
|
-
|
|
2537
|
+
const classes = [
|
|
2538
|
+
PASSWORD_UPPERCASE_REGEX,
|
|
2539
|
+
PASSWORD_LOWERCASE_REGEX,
|
|
2540
|
+
PASSWORD_DIGIT_REGEX,
|
|
2541
|
+
PASSWORD_SPECIAL_CHAR_REGEX
|
|
2542
|
+
].filter((pattern) => pattern.test(password)).length;
|
|
2543
|
+
if (classes < passwordConfig.minCharacterClasses) {
|
|
2544
|
+
return err(HaiIamError.PASSWORD_POLICY_VIOLATION, iamM("iam_passwordCharacterClasses", {
|
|
2545
|
+
params: { count: passwordConfig.minCharacterClasses }
|
|
2546
|
+
}));
|
|
2547
|
+
}
|
|
2295
2548
|
return ok(void 0);
|
|
2296
2549
|
}
|
|
2297
2550
|
function checkAccountStatus(user) {
|
|
@@ -2347,7 +2600,10 @@ function createPasswordStrategy(config) {
|
|
|
2347
2600
|
if (!resetResult.success)
|
|
2348
2601
|
return resetResult;
|
|
2349
2602
|
logger7.debug("Password authentication succeeded", { userId: storedUser.id });
|
|
2350
|
-
return ok({
|
|
2603
|
+
return ok({
|
|
2604
|
+
...toUser(storedUser),
|
|
2605
|
+
credentialVersion: storedUser.credentialVersion ?? ""
|
|
2606
|
+
});
|
|
2351
2607
|
}
|
|
2352
2608
|
};
|
|
2353
2609
|
function hashPassword(password) {
|
|
@@ -2451,7 +2707,12 @@ async function createAuthnOperations(deps) {
|
|
|
2451
2707
|
config
|
|
2452
2708
|
});
|
|
2453
2709
|
logger8.info("Authn sub-feature initialized");
|
|
2454
|
-
return ok({
|
|
2710
|
+
return ok({
|
|
2711
|
+
authn: operations.authn,
|
|
2712
|
+
loginRegisteredUser: operations.loginRegisteredUser,
|
|
2713
|
+
passwordStrategyResult: passwordResult,
|
|
2714
|
+
apiKeyFunctions
|
|
2715
|
+
});
|
|
2455
2716
|
} catch (error) {
|
|
2456
2717
|
logger8.error("Authn sub-feature initialization failed", { error });
|
|
2457
2718
|
return err(
|
|
@@ -2480,7 +2741,9 @@ function buildAuthnOperations(deps) {
|
|
|
2480
2741
|
if (!loginConfig[type]) {
|
|
2481
2742
|
return err(
|
|
2482
2743
|
HaiIamError.LOGIN_DISABLED,
|
|
2483
|
-
iamM("iam_loginDisabled", {
|
|
2744
|
+
iamM("iam_loginDisabled", {
|
|
2745
|
+
params: { type }
|
|
2746
|
+
})
|
|
2484
2747
|
);
|
|
2485
2748
|
}
|
|
2486
2749
|
return null;
|
|
@@ -2601,7 +2864,10 @@ function buildAuthnOperations(deps) {
|
|
|
2601
2864
|
const authorization = await authzFunctions.getUserAuthorization(session.data.userId);
|
|
2602
2865
|
if (!authorization.success)
|
|
2603
2866
|
return authorization;
|
|
2604
|
-
const current = {
|
|
2867
|
+
const current = {
|
|
2868
|
+
...session.data,
|
|
2869
|
+
...authorization.data
|
|
2870
|
+
};
|
|
2605
2871
|
return ok(current.apiKeyId ? applyApiKeyScopes(current, current.apiKeyScopes ?? []) : current);
|
|
2606
2872
|
}
|
|
2607
2873
|
const tickets = createTicketOperations();
|
|
@@ -2616,99 +2882,114 @@ function buildAuthnOperations(deps) {
|
|
|
2616
2882
|
return configured;
|
|
2617
2883
|
return ok(sensitive.operations.includes(operation) || configured.data.includes(operation));
|
|
2618
2884
|
}
|
|
2619
|
-
return {
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
return stored;
|
|
2623
|
-
if (!stored.data || !isUserActive(stored.data))
|
|
2624
|
-
return err(HaiIamError.ACCOUNT_UNAVAILABLE, iamM("iam_accountUnavailable"));
|
|
2625
|
-
return buildAuthResult({ ...user, credentialVersion: stored.data.credentialVersion ?? "" }, void 0, context);
|
|
2626
|
-
}, authn: {
|
|
2627
|
-
createCaptcha: (input) => createIamCaptcha(config.captcha, input),
|
|
2628
|
-
async reauthenticate(accessToken, password, operation) {
|
|
2629
|
-
const required = await requiresProof(operation);
|
|
2630
|
-
if (!required.success)
|
|
2631
|
-
return required;
|
|
2632
|
-
if (!required.data)
|
|
2633
|
-
return err(HaiIamError.PERMISSION_DENIED, iamM("iam_reauthenticationRequired"));
|
|
2634
|
-
const current = await verifyCurrentSession(accessToken);
|
|
2635
|
-
if (!current.success)
|
|
2636
|
-
return current;
|
|
2637
|
-
const stored = await userRepository.findById(current.data.userId);
|
|
2885
|
+
return {
|
|
2886
|
+
loginRegisteredUser: async (user, context) => {
|
|
2887
|
+
const stored = await userRepository.findById(user.id);
|
|
2638
2888
|
if (!stored.success)
|
|
2639
2889
|
return stored;
|
|
2640
|
-
if (!stored.data)
|
|
2641
|
-
return err(HaiIamError.
|
|
2642
|
-
|
|
2643
|
-
if (!verified.success)
|
|
2644
|
-
return verified;
|
|
2645
|
-
if (verified.data.credentialVersion !== current.data.credentialVersion)
|
|
2646
|
-
return err(HaiIamError.SESSION_INVALID, iamM("iam_sessionExpired"));
|
|
2647
|
-
return tickets.issue({ subjectId: current.data.userId, purpose: `iam:reauth:${operation}`, ttlMs: sensitive.ttlSeconds * 1e3, grant: { operation, sessionVersion: current.data.version, credentialVersion: current.data.credentialVersion } });
|
|
2890
|
+
if (!stored.data || !isUserActive(stored.data))
|
|
2891
|
+
return err(HaiIamError.ACCOUNT_UNAVAILABLE, iamM("iam_accountUnavailable"));
|
|
2892
|
+
return buildAuthResult({ ...user, credentialVersion: stored.data.credentialVersion ?? "" }, void 0, context);
|
|
2648
2893
|
},
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2894
|
+
authn: {
|
|
2895
|
+
createCaptcha: (input) => createIamCaptcha(config.captcha, input),
|
|
2896
|
+
async reauthenticate(accessToken, password, operation) {
|
|
2897
|
+
const required = await requiresProof(operation);
|
|
2898
|
+
if (!required.success)
|
|
2899
|
+
return required;
|
|
2900
|
+
if (!required.data)
|
|
2901
|
+
return err(HaiIamError.PERMISSION_DENIED, iamM("iam_reauthenticationRequired"));
|
|
2902
|
+
const current = await verifyCurrentSession(accessToken);
|
|
2903
|
+
if (!current.success)
|
|
2904
|
+
return current;
|
|
2905
|
+
const stored = await userRepository.findById(current.data.userId);
|
|
2906
|
+
if (!stored.success)
|
|
2907
|
+
return stored;
|
|
2908
|
+
if (!stored.data)
|
|
2909
|
+
return err(HaiIamError.INVALID_CREDENTIALS, iamM("iam_passwordWrong"));
|
|
2910
|
+
const verified = await passwordStrategy.authenticate({ type: "password", identifier: stored.data.username, password });
|
|
2911
|
+
if (!verified.success)
|
|
2912
|
+
return verified;
|
|
2913
|
+
if (verified.data.credentialVersion !== current.data.credentialVersion)
|
|
2914
|
+
return err(HaiIamError.SESSION_INVALID, iamM("iam_sessionExpired"));
|
|
2915
|
+
return tickets.issue({
|
|
2916
|
+
subjectId: current.data.userId,
|
|
2917
|
+
purpose: `iam:reauth:${operation}`,
|
|
2918
|
+
ttlMs: sensitive.ttlSeconds * 1e3,
|
|
2919
|
+
grant: { operation, sessionVersion: current.data.version, credentialVersion: current.data.credentialVersion }
|
|
2920
|
+
});
|
|
2921
|
+
},
|
|
2922
|
+
async checkSensitiveOperation(accessToken, operation, ticket) {
|
|
2923
|
+
const current = await verifyCurrentSession(accessToken);
|
|
2924
|
+
if (!current.success)
|
|
2925
|
+
return current;
|
|
2926
|
+
const required = await requiresProof(operation);
|
|
2927
|
+
if (!required.success)
|
|
2928
|
+
return required;
|
|
2929
|
+
if (!required.data)
|
|
2930
|
+
return ok(void 0);
|
|
2931
|
+
if (!ticket)
|
|
2932
|
+
return err(HaiIamError.REAUTHENTICATION_REQUIRED, iamM("iam_reauthenticationRequired"));
|
|
2933
|
+
const proof = await tickets.consume(ticket, { purpose: `iam:reauth:${operation}` });
|
|
2934
|
+
if (!proof.success)
|
|
2935
|
+
return proof;
|
|
2936
|
+
if (proof.data.subjectId !== current.data.userId || proof.data.grant.operation !== operation || proof.data.grant.sessionVersion !== current.data.version || proof.data.grant.credentialVersion !== current.data.credentialVersion)
|
|
2937
|
+
return err(HaiIamError.REAUTHENTICATION_REQUIRED, iamM("iam_reauthenticationRequired"));
|
|
2657
2938
|
return ok(void 0);
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
return
|
|
2698
|
-
}
|
|
2699
|
-
if (!otpChallenge) {
|
|
2700
|
-
return err(
|
|
2701
|
-
HaiIamError.STRATEGY_NOT_SUPPORTED,
|
|
2702
|
-
iamM("iam_otpStrategyRequiredForSend")
|
|
2703
|
-
);
|
|
2939
|
+
},
|
|
2940
|
+
async login(credentials, context) {
|
|
2941
|
+
return loginWithStrategy("password", credentials, passwordStrategy, context);
|
|
2942
|
+
},
|
|
2943
|
+
async loginWithOtp(credentials, context) {
|
|
2944
|
+
return loginWithStrategy("otp", credentials, otpStrategy, context);
|
|
2945
|
+
},
|
|
2946
|
+
async loginWithLdap(credentials, context) {
|
|
2947
|
+
return loginWithStrategy("ldap", credentials, ldapStrategy, context);
|
|
2948
|
+
},
|
|
2949
|
+
async loginWithApiKey(credentials, context) {
|
|
2950
|
+
const disabled = loginDisabled("apikey");
|
|
2951
|
+
if (disabled)
|
|
2952
|
+
return disabled;
|
|
2953
|
+
if (!apiKeyStrategy)
|
|
2954
|
+
return err(HaiIamError.STRATEGY_NOT_SUPPORTED, iamM("iam_featureNotImplemented"));
|
|
2955
|
+
const authenticated = await apiKeyStrategy.authenticate({ type: "apikey", ...credentials });
|
|
2956
|
+
if (!authenticated.success)
|
|
2957
|
+
return authenticated;
|
|
2958
|
+
return buildAuthResult(authenticated.data.user, authenticated.data.apiKey, context);
|
|
2959
|
+
},
|
|
2960
|
+
async logout(accessToken) {
|
|
2961
|
+
return sessionFunctions.delete(accessToken);
|
|
2962
|
+
},
|
|
2963
|
+
verifyToken: verifyCurrentSession,
|
|
2964
|
+
async sendOtp(identifier, options) {
|
|
2965
|
+
const captcha = await verifyIamCaptcha(config.captcha, "sendOtp", options?.captcha);
|
|
2966
|
+
if (!captcha.success)
|
|
2967
|
+
return captcha;
|
|
2968
|
+
const disabled = loginDisabled("otp");
|
|
2969
|
+
if (disabled) {
|
|
2970
|
+
return disabled;
|
|
2971
|
+
}
|
|
2972
|
+
if (!otpChallenge) {
|
|
2973
|
+
return err(
|
|
2974
|
+
HaiIamError.STRATEGY_NOT_SUPPORTED,
|
|
2975
|
+
iamM("iam_otpStrategyRequiredForSend")
|
|
2976
|
+
);
|
|
2977
|
+
}
|
|
2978
|
+
return otpChallenge(identifier);
|
|
2704
2979
|
}
|
|
2705
|
-
return otpChallenge(identifier);
|
|
2706
2980
|
}
|
|
2707
|
-
}
|
|
2981
|
+
};
|
|
2708
2982
|
}
|
|
2709
2983
|
var TABLE_NAME3 = "hai_iam_permissions";
|
|
2710
2984
|
var PERMISSION_FIELDS = [
|
|
2711
|
-
{
|
|
2985
|
+
{
|
|
2986
|
+
fieldName: "sensitiveOperations",
|
|
2987
|
+
columnName: "sensitive_operations",
|
|
2988
|
+
def: { type: "JSON" },
|
|
2989
|
+
select: true,
|
|
2990
|
+
create: true,
|
|
2991
|
+
update: true
|
|
2992
|
+
},
|
|
2712
2993
|
{
|
|
2713
2994
|
fieldName: "id",
|
|
2714
2995
|
columnName: "id",
|
|
@@ -2802,7 +3083,9 @@ var DbPermissionRepository = class extends BaseReldbCrudRepository {
|
|
|
2802
3083
|
if (operation !== void 0 && (typeof operation !== "string" || !operation.trim() || operation.length > 128))
|
|
2803
3084
|
return err(HaiIamError.INVALID_ARGUMENT, iamM("iam_sensitiveOperationsInvalid"));
|
|
2804
3085
|
const match = reldb.config?.type === "postgresql" ? "sensitive_operations::jsonb @> CAST(? AS jsonb)" : reldb.config?.type === "mysql" ? "JSON_CONTAINS(sensitive_operations, JSON_QUOTE(?))" : "EXISTS (SELECT 1 FROM json_each(sensitive_operations) WHERE type = 'text' AND value = ?)";
|
|
2805
|
-
const params = operation === void 0 ? [] : [
|
|
3086
|
+
const params = operation === void 0 ? [] : [
|
|
3087
|
+
reldb.config?.type === "postgresql" ? JSON.stringify([operation]) : operation
|
|
3088
|
+
];
|
|
2806
3089
|
const result = await reldb.sql.query(`SELECT sensitive_operations FROM hai_iam_permissions WHERE sensitive_operations IS NOT NULL${operation === void 0 ? "" : ` AND ${match} LIMIT 1`}`, params);
|
|
2807
3090
|
if (!result.success)
|
|
2808
3091
|
return result;
|
|
@@ -2837,7 +3120,9 @@ var DbPermissionRepository = class extends BaseReldbCrudRepository {
|
|
|
2837
3120
|
buildQueryError(error, cause) {
|
|
2838
3121
|
return err(
|
|
2839
3122
|
HaiIamError.REPOSITORY_ERROR,
|
|
2840
|
-
iamM("iam_queryPermissionFailed", {
|
|
3123
|
+
iamM("iam_queryPermissionFailed", {
|
|
3124
|
+
params: { message: error.message }
|
|
3125
|
+
}),
|
|
2841
3126
|
cause
|
|
2842
3127
|
);
|
|
2843
3128
|
}
|
|
@@ -2861,19 +3146,28 @@ async function createDbRolePermissionRepository() {
|
|
|
2861
3146
|
if (!result.success) {
|
|
2862
3147
|
return err(
|
|
2863
3148
|
HaiIamError.REPOSITORY_ERROR,
|
|
2864
|
-
iamM("iam_createRolePermissionTableFailed", {
|
|
3149
|
+
iamM("iam_createRolePermissionTableFailed", {
|
|
3150
|
+
params: { message: result.error.message }
|
|
3151
|
+
}),
|
|
2865
3152
|
result.error
|
|
2866
3153
|
);
|
|
2867
3154
|
}
|
|
2868
3155
|
const indexResults = await Promise.all([
|
|
2869
|
-
reldb.ddl.createIndex(ROLE_PERMISSION_TABLE, "idx_role_perm_role_perm", {
|
|
2870
|
-
|
|
3156
|
+
reldb.ddl.createIndex(ROLE_PERMISSION_TABLE, "idx_role_perm_role_perm", {
|
|
3157
|
+
columns: ["role_id", "permission_id"],
|
|
3158
|
+
unique: true
|
|
3159
|
+
}),
|
|
3160
|
+
reldb.ddl.createIndex(ROLE_PERMISSION_TABLE, "idx_role_perm_role", {
|
|
3161
|
+
columns: ["role_id"]
|
|
3162
|
+
})
|
|
2871
3163
|
]);
|
|
2872
3164
|
for (const indexResult of indexResults) {
|
|
2873
3165
|
if (!indexResult.success) {
|
|
2874
3166
|
return err(
|
|
2875
3167
|
HaiIamError.REPOSITORY_ERROR,
|
|
2876
|
-
iamM("iam_createRolePermissionIndexFailed", {
|
|
3168
|
+
iamM("iam_createRolePermissionIndexFailed", {
|
|
3169
|
+
params: { message: indexResult.error.message }
|
|
3170
|
+
}),
|
|
2877
3171
|
indexResult.error
|
|
2878
3172
|
);
|
|
2879
3173
|
}
|
|
@@ -2897,7 +3191,9 @@ async function createDbRolePermissionRepository() {
|
|
|
2897
3191
|
if (!result.success) {
|
|
2898
3192
|
return err(
|
|
2899
3193
|
HaiIamError.REPOSITORY_ERROR,
|
|
2900
|
-
iamM("iam_queryPermissionFailed", {
|
|
3194
|
+
iamM("iam_queryPermissionFailed", {
|
|
3195
|
+
params: { message: result.error.message }
|
|
3196
|
+
}),
|
|
2901
3197
|
result.error
|
|
2902
3198
|
);
|
|
2903
3199
|
}
|
|
@@ -2913,7 +3209,9 @@ async function createDbRolePermissionRepository() {
|
|
|
2913
3209
|
if (!result.success) {
|
|
2914
3210
|
return err(
|
|
2915
3211
|
HaiIamError.REPOSITORY_ERROR,
|
|
2916
|
-
iamM("iam_assignPermissionFailed", {
|
|
3212
|
+
iamM("iam_assignPermissionFailed", {
|
|
3213
|
+
params: { message: result.error.message }
|
|
3214
|
+
}),
|
|
2917
3215
|
result.error
|
|
2918
3216
|
);
|
|
2919
3217
|
}
|
|
@@ -2928,7 +3226,9 @@ async function createDbRolePermissionRepository() {
|
|
|
2928
3226
|
if (!result.success) {
|
|
2929
3227
|
return err(
|
|
2930
3228
|
HaiIamError.REPOSITORY_ERROR,
|
|
2931
|
-
iamM("iam_removePermissionFailed", {
|
|
3229
|
+
iamM("iam_removePermissionFailed", {
|
|
3230
|
+
params: { message: result.error.message }
|
|
3231
|
+
}),
|
|
2932
3232
|
result.error
|
|
2933
3233
|
);
|
|
2934
3234
|
}
|
|
@@ -2949,7 +3249,9 @@ async function createDbRolePermissionRepository() {
|
|
|
2949
3249
|
if (!result.success) {
|
|
2950
3250
|
return err(
|
|
2951
3251
|
HaiIamError.REPOSITORY_ERROR,
|
|
2952
|
-
iamM("iam_queryPermissionFailed", {
|
|
3252
|
+
iamM("iam_queryPermissionFailed", {
|
|
3253
|
+
params: { message: result.error.message }
|
|
3254
|
+
}),
|
|
2953
3255
|
result.error
|
|
2954
3256
|
);
|
|
2955
3257
|
}
|
|
@@ -2967,7 +3269,9 @@ async function createDbRolePermissionRepository() {
|
|
|
2967
3269
|
if (!result.success) {
|
|
2968
3270
|
return err(
|
|
2969
3271
|
HaiIamError.REPOSITORY_ERROR,
|
|
2970
|
-
iamM("iam_queryPermissionFailed", {
|
|
3272
|
+
iamM("iam_queryPermissionFailed", {
|
|
3273
|
+
params: { message: result.error.message }
|
|
3274
|
+
}),
|
|
2971
3275
|
result.error
|
|
2972
3276
|
);
|
|
2973
3277
|
}
|
|
@@ -2982,7 +3286,9 @@ async function createDbRolePermissionRepository() {
|
|
|
2982
3286
|
if (!result.success) {
|
|
2983
3287
|
return err(
|
|
2984
3288
|
HaiIamError.REPOSITORY_ERROR,
|
|
2985
|
-
iamM("iam_removePermissionFailed", {
|
|
3289
|
+
iamM("iam_removePermissionFailed", {
|
|
3290
|
+
params: { message: result.error.message }
|
|
3291
|
+
}),
|
|
2986
3292
|
result.error
|
|
2987
3293
|
);
|
|
2988
3294
|
}
|
|
@@ -2997,7 +3303,9 @@ async function createDbRolePermissionRepository() {
|
|
|
2997
3303
|
if (!result.success) {
|
|
2998
3304
|
return err(
|
|
2999
3305
|
HaiIamError.REPOSITORY_ERROR,
|
|
3000
|
-
iamM("iam_removePermissionFailed", {
|
|
3306
|
+
iamM("iam_removePermissionFailed", {
|
|
3307
|
+
params: { message: result.error.message }
|
|
3308
|
+
}),
|
|
3001
3309
|
result.error
|
|
3002
3310
|
);
|
|
3003
3311
|
}
|
|
@@ -3011,7 +3319,9 @@ async function createDbRolePermissionRepository() {
|
|
|
3011
3319
|
if (!result.success) {
|
|
3012
3320
|
return err(
|
|
3013
3321
|
HaiIamError.REPOSITORY_ERROR,
|
|
3014
|
-
iamM("iam_queryPermissionFailed", {
|
|
3322
|
+
iamM("iam_queryPermissionFailed", {
|
|
3323
|
+
params: { message: result.error.message }
|
|
3324
|
+
}),
|
|
3015
3325
|
result.error
|
|
3016
3326
|
);
|
|
3017
3327
|
}
|
|
@@ -3030,7 +3340,9 @@ async function createDbRolePermissionRepository() {
|
|
|
3030
3340
|
if (!relationsResult.success) {
|
|
3031
3341
|
return err(
|
|
3032
3342
|
HaiIamError.REPOSITORY_ERROR,
|
|
3033
|
-
iamM("iam_queryPermissionFailed", {
|
|
3343
|
+
iamM("iam_queryPermissionFailed", {
|
|
3344
|
+
params: { message: relationsResult.error.message }
|
|
3345
|
+
}),
|
|
3034
3346
|
relationsResult.error
|
|
3035
3347
|
);
|
|
3036
3348
|
}
|
|
@@ -3040,7 +3352,9 @@ async function createDbRolePermissionRepository() {
|
|
|
3040
3352
|
if (relationsResult.data.length === 0) {
|
|
3041
3353
|
return ok(result);
|
|
3042
3354
|
}
|
|
3043
|
-
const uniquePermIds = [
|
|
3355
|
+
const uniquePermIds = [
|
|
3356
|
+
...new Set(relationsResult.data.map((r) => r.permission_id))
|
|
3357
|
+
];
|
|
3044
3358
|
const permPlaceholders = uniquePermIds.map(() => "?").join(", ");
|
|
3045
3359
|
const permsResult = await reldb.sql.query(
|
|
3046
3360
|
`SELECT * FROM ${PERMISSION_TABLE} WHERE id IN (${permPlaceholders})`,
|
|
@@ -3049,7 +3363,9 @@ async function createDbRolePermissionRepository() {
|
|
|
3049
3363
|
if (!permsResult.success) {
|
|
3050
3364
|
return err(
|
|
3051
3365
|
HaiIamError.REPOSITORY_ERROR,
|
|
3052
|
-
iamM("iam_queryPermissionFailed", {
|
|
3366
|
+
iamM("iam_queryPermissionFailed", {
|
|
3367
|
+
params: { message: permsResult.error.message }
|
|
3368
|
+
}),
|
|
3053
3369
|
permsResult.error
|
|
3054
3370
|
);
|
|
3055
3371
|
}
|
|
@@ -3079,19 +3395,28 @@ async function createDbUserRoleRepository(roleRepository) {
|
|
|
3079
3395
|
if (!result.success) {
|
|
3080
3396
|
return err(
|
|
3081
3397
|
HaiIamError.REPOSITORY_ERROR,
|
|
3082
|
-
iamM("iam_createUserRoleTableFailed", {
|
|
3398
|
+
iamM("iam_createUserRoleTableFailed", {
|
|
3399
|
+
params: { message: result.error.message }
|
|
3400
|
+
}),
|
|
3083
3401
|
result.error
|
|
3084
3402
|
);
|
|
3085
3403
|
}
|
|
3086
3404
|
const indexResults = await Promise.all([
|
|
3087
|
-
reldb.ddl.createIndex(USER_ROLE_TABLE, "idx_user_role_user_role", {
|
|
3088
|
-
|
|
3405
|
+
reldb.ddl.createIndex(USER_ROLE_TABLE, "idx_user_role_user_role", {
|
|
3406
|
+
columns: ["user_id", "role_id"],
|
|
3407
|
+
unique: true
|
|
3408
|
+
}),
|
|
3409
|
+
reldb.ddl.createIndex(USER_ROLE_TABLE, "idx_user_role_user", {
|
|
3410
|
+
columns: ["user_id"]
|
|
3411
|
+
})
|
|
3089
3412
|
]);
|
|
3090
3413
|
for (const indexResult of indexResults) {
|
|
3091
3414
|
if (!indexResult.success) {
|
|
3092
3415
|
return err(
|
|
3093
3416
|
HaiIamError.REPOSITORY_ERROR,
|
|
3094
|
-
iamM("iam_createUserRoleIndexFailed", {
|
|
3417
|
+
iamM("iam_createUserRoleIndexFailed", {
|
|
3418
|
+
params: { message: indexResult.error.message }
|
|
3419
|
+
}),
|
|
3095
3420
|
indexResult.error
|
|
3096
3421
|
);
|
|
3097
3422
|
}
|
|
@@ -3115,7 +3440,9 @@ async function createDbUserRoleRepository(roleRepository) {
|
|
|
3115
3440
|
if (!result.success) {
|
|
3116
3441
|
return err(
|
|
3117
3442
|
HaiIamError.REPOSITORY_ERROR,
|
|
3118
|
-
iamM("iam_queryRoleFailed", {
|
|
3443
|
+
iamM("iam_queryRoleFailed", {
|
|
3444
|
+
params: { message: result.error.message }
|
|
3445
|
+
}),
|
|
3119
3446
|
result.error
|
|
3120
3447
|
);
|
|
3121
3448
|
}
|
|
@@ -3131,7 +3458,9 @@ async function createDbUserRoleRepository(roleRepository) {
|
|
|
3131
3458
|
if (!result.success) {
|
|
3132
3459
|
return err(
|
|
3133
3460
|
HaiIamError.REPOSITORY_ERROR,
|
|
3134
|
-
iamM("iam_assignRoleFailed", {
|
|
3461
|
+
iamM("iam_assignRoleFailed", {
|
|
3462
|
+
params: { message: result.error.message }
|
|
3463
|
+
}),
|
|
3135
3464
|
result.error
|
|
3136
3465
|
);
|
|
3137
3466
|
}
|
|
@@ -3146,7 +3475,9 @@ async function createDbUserRoleRepository(roleRepository) {
|
|
|
3146
3475
|
if (!result.success) {
|
|
3147
3476
|
return err(
|
|
3148
3477
|
HaiIamError.REPOSITORY_ERROR,
|
|
3149
|
-
iamM("iam_removeRoleFailed", {
|
|
3478
|
+
iamM("iam_removeRoleFailed", {
|
|
3479
|
+
params: { message: result.error.message }
|
|
3480
|
+
}),
|
|
3150
3481
|
result.error
|
|
3151
3482
|
);
|
|
3152
3483
|
}
|
|
@@ -3176,7 +3507,9 @@ async function createDbUserRoleRepository(roleRepository) {
|
|
|
3176
3507
|
if (!roleResult.success) {
|
|
3177
3508
|
return err(
|
|
3178
3509
|
HaiIamError.REPOSITORY_ERROR,
|
|
3179
|
-
iamM("iam_queryRoleFailed", {
|
|
3510
|
+
iamM("iam_queryRoleFailed", {
|
|
3511
|
+
params: { message: roleResult.error.message }
|
|
3512
|
+
}),
|
|
3180
3513
|
roleResult.error
|
|
3181
3514
|
);
|
|
3182
3515
|
}
|
|
@@ -3191,7 +3524,9 @@ async function createDbUserRoleRepository(roleRepository) {
|
|
|
3191
3524
|
if (!usersResult.success) {
|
|
3192
3525
|
return err(
|
|
3193
3526
|
HaiIamError.REPOSITORY_ERROR,
|
|
3194
|
-
iamM("iam_queryRoleFailed", {
|
|
3527
|
+
iamM("iam_queryRoleFailed", {
|
|
3528
|
+
params: { message: usersResult.error.message }
|
|
3529
|
+
}),
|
|
3195
3530
|
usersResult.error
|
|
3196
3531
|
);
|
|
3197
3532
|
}
|
|
@@ -3202,7 +3537,9 @@ async function createDbUserRoleRepository(roleRepository) {
|
|
|
3202
3537
|
if (!deleteResult.success) {
|
|
3203
3538
|
return err(
|
|
3204
3539
|
HaiIamError.REPOSITORY_ERROR,
|
|
3205
|
-
iamM("iam_removeRoleFailed", {
|
|
3540
|
+
iamM("iam_removeRoleFailed", {
|
|
3541
|
+
params: { message: deleteResult.error.message }
|
|
3542
|
+
}),
|
|
3206
3543
|
deleteResult.error
|
|
3207
3544
|
);
|
|
3208
3545
|
}
|
|
@@ -3216,14 +3553,18 @@ async function createDbUserRoleRepository(roleRepository) {
|
|
|
3216
3553
|
if (!result.success) {
|
|
3217
3554
|
return err(
|
|
3218
3555
|
HaiIamError.REPOSITORY_ERROR,
|
|
3219
|
-
iamM("iam_queryRoleFailed", {
|
|
3556
|
+
iamM("iam_queryRoleFailed", {
|
|
3557
|
+
params: { message: result.error.message }
|
|
3558
|
+
}),
|
|
3220
3559
|
result.error
|
|
3221
3560
|
);
|
|
3222
3561
|
}
|
|
3223
3562
|
return ok(result.data.map((r) => r.user_id));
|
|
3224
3563
|
},
|
|
3225
3564
|
async getUserCounts(roleIds) {
|
|
3226
|
-
const ids = [
|
|
3565
|
+
const ids = [
|
|
3566
|
+
...new Set(roleIds)
|
|
3567
|
+
];
|
|
3227
3568
|
const counts = new Map(ids.map((id) => [id, 0]));
|
|
3228
3569
|
if (ids.length === 0)
|
|
3229
3570
|
return ok(counts);
|
|
@@ -3231,8 +3572,11 @@ async function createDbUserRoleRepository(roleRepository) {
|
|
|
3231
3572
|
`SELECT role_id, COUNT(DISTINCT user_id) AS user_count FROM ${USER_ROLE_TABLE} WHERE role_id IN (${ids.map(() => "?").join(", ")}) GROUP BY role_id`,
|
|
3232
3573
|
ids
|
|
3233
3574
|
);
|
|
3234
|
-
if (!result.success)
|
|
3235
|
-
return err(HaiIamError.REPOSITORY_ERROR, iamM("iam_queryRoleFailed", {
|
|
3575
|
+
if (!result.success) {
|
|
3576
|
+
return err(HaiIamError.REPOSITORY_ERROR, iamM("iam_queryRoleFailed", {
|
|
3577
|
+
params: { message: result.error.message }
|
|
3578
|
+
}), result.error);
|
|
3579
|
+
}
|
|
3236
3580
|
for (const row of result.data)
|
|
3237
3581
|
counts.set(row.role_id, Number(row.user_count));
|
|
3238
3582
|
return ok(counts);
|
|
@@ -3250,7 +3594,9 @@ async function createDbUserRoleRepository(roleRepository) {
|
|
|
3250
3594
|
if (!relationsResult.success) {
|
|
3251
3595
|
return err(
|
|
3252
3596
|
HaiIamError.REPOSITORY_ERROR,
|
|
3253
|
-
iamM("iam_queryRoleFailed", {
|
|
3597
|
+
iamM("iam_queryRoleFailed", {
|
|
3598
|
+
params: { message: relationsResult.error.message }
|
|
3599
|
+
}),
|
|
3254
3600
|
relationsResult.error
|
|
3255
3601
|
);
|
|
3256
3602
|
}
|
|
@@ -3260,7 +3606,9 @@ async function createDbUserRoleRepository(roleRepository) {
|
|
|
3260
3606
|
if (relationsResult.data.length === 0) {
|
|
3261
3607
|
return ok(result);
|
|
3262
3608
|
}
|
|
3263
|
-
const uniqueRoleIds = [
|
|
3609
|
+
const uniqueRoleIds = [
|
|
3610
|
+
...new Set(relationsResult.data.map((r) => r.role_id))
|
|
3611
|
+
];
|
|
3264
3612
|
const rolePlaceholders = uniqueRoleIds.map(() => "?").join(", ");
|
|
3265
3613
|
const rolesResult = await roleRepository.findAll({
|
|
3266
3614
|
where: `id IN (${rolePlaceholders})`,
|
|
@@ -3269,7 +3617,9 @@ async function createDbUserRoleRepository(roleRepository) {
|
|
|
3269
3617
|
if (!rolesResult.success) {
|
|
3270
3618
|
return err(
|
|
3271
3619
|
HaiIamError.REPOSITORY_ERROR,
|
|
3272
|
-
iamM("iam_queryRoleFailed", {
|
|
3620
|
+
iamM("iam_queryRoleFailed", {
|
|
3621
|
+
params: { message: rolesResult.error.message }
|
|
3622
|
+
}),
|
|
3273
3623
|
rolesResult.error
|
|
3274
3624
|
);
|
|
3275
3625
|
}
|
|
@@ -3363,7 +3713,10 @@ async function createDbRoleRepository() {
|
|
|
3363
3713
|
}
|
|
3364
3714
|
var DbRoleRepository = class extends BaseReldbCrudRepository {
|
|
3365
3715
|
async touch(roleId, tx) {
|
|
3366
|
-
return tx.execute(`UPDATE ${TABLE_NAME4} SET updated_at = ? WHERE id = ?`, [
|
|
3716
|
+
return tx.execute(`UPDATE ${TABLE_NAME4} SET updated_at = ? WHERE id = ?`, [
|
|
3717
|
+
(/* @__PURE__ */ new Date()).toISOString(),
|
|
3718
|
+
roleId
|
|
3719
|
+
]);
|
|
3367
3720
|
}
|
|
3368
3721
|
constructor() {
|
|
3369
3722
|
super(reldb, {
|
|
@@ -3378,7 +3731,9 @@ var DbRoleRepository = class extends BaseReldbCrudRepository {
|
|
|
3378
3731
|
buildQueryError(error, cause) {
|
|
3379
3732
|
return err(
|
|
3380
3733
|
HaiIamError.REPOSITORY_ERROR,
|
|
3381
|
-
iamM("iam_queryRoleFailed", {
|
|
3734
|
+
iamM("iam_queryRoleFailed", {
|
|
3735
|
+
params: { message: error.message }
|
|
3736
|
+
}),
|
|
3382
3737
|
cause
|
|
3383
3738
|
);
|
|
3384
3739
|
}
|
|
@@ -3450,7 +3805,9 @@ function createRbacManager(config) {
|
|
|
3450
3805
|
function mapRepositoryError2(messageKey, message) {
|
|
3451
3806
|
return err(
|
|
3452
3807
|
HaiIamError.REPOSITORY_ERROR,
|
|
3453
|
-
iamM(messageKey, {
|
|
3808
|
+
iamM(messageKey, {
|
|
3809
|
+
params: { message }
|
|
3810
|
+
})
|
|
3454
3811
|
);
|
|
3455
3812
|
}
|
|
3456
3813
|
function matchesPermission(permission, code) {
|
|
@@ -3545,7 +3902,9 @@ function createRbacManager(config) {
|
|
|
3545
3902
|
}
|
|
3546
3903
|
}
|
|
3547
3904
|
async function replaceRolePermissions(roleId, permissionIds, tx) {
|
|
3548
|
-
const uniqueIds = [
|
|
3905
|
+
const uniqueIds = [
|
|
3906
|
+
...new Set(permissionIds)
|
|
3907
|
+
];
|
|
3549
3908
|
for (const id of uniqueIds) {
|
|
3550
3909
|
const permission = await permissionRepository.findById(id, tx);
|
|
3551
3910
|
if (!permission.success)
|
|
@@ -3566,7 +3925,14 @@ function createRbacManager(config) {
|
|
|
3566
3925
|
async function getUserAuthorization(userId) {
|
|
3567
3926
|
return readIamCache(`authorization:${userId}`, async () => {
|
|
3568
3927
|
const rows = await reldb.sql.query("SELECT r.code AS role_code, p.code AS permission_code FROM hai_iam_user_roles ur JOIN hai_iam_roles r ON r.id = ur.role_id LEFT JOIN hai_iam_role_permissions rp ON rp.role_id = r.id LEFT JOIN hai_iam_permissions p ON p.id = rp.permission_id WHERE ur.user_id = ?", [userId]);
|
|
3569
|
-
return rows.success ? ok({
|
|
3928
|
+
return rows.success ? ok({
|
|
3929
|
+
roles: [
|
|
3930
|
+
...new Set(rows.data.map((row) => row.role_code))
|
|
3931
|
+
],
|
|
3932
|
+
permissions: [
|
|
3933
|
+
...new Set(rows.data.flatMap((row) => row.permission_code ? [row.permission_code] : []))
|
|
3934
|
+
]
|
|
3935
|
+
}) : rows;
|
|
3570
3936
|
});
|
|
3571
3937
|
}
|
|
3572
3938
|
return {
|
|
@@ -3574,7 +3940,11 @@ function createRbacManager(config) {
|
|
|
3574
3940
|
async setPermissionSensitiveOperations(permissionId, operations) {
|
|
3575
3941
|
if (!Array.isArray(operations) || operations.length > 1e3 || operations.some((value) => typeof value !== "string" || !value.trim() || value.length > 128))
|
|
3576
3942
|
return err(HaiIamError.INVALID_ARGUMENT, iamM("iam_sensitiveOperationsInvalid"));
|
|
3577
|
-
const updated = await permissionRepository.updateById(permissionId, {
|
|
3943
|
+
const updated = await permissionRepository.updateById(permissionId, {
|
|
3944
|
+
sensitiveOperations: [
|
|
3945
|
+
...new Set(operations.map((value) => value.trim()))
|
|
3946
|
+
]
|
|
3947
|
+
});
|
|
3578
3948
|
if (!updated.success)
|
|
3579
3949
|
return updated;
|
|
3580
3950
|
if (updated.data.changes === 0) {
|
|
@@ -3686,7 +4056,11 @@ function createRbacManager(config) {
|
|
|
3686
4056
|
await tx.rollback();
|
|
3687
4057
|
return err(
|
|
3688
4058
|
HaiIamError.REPOSITORY_ERROR,
|
|
3689
|
-
iamM("iam_syncRolesFailed", {
|
|
4059
|
+
iamM("iam_syncRolesFailed", {
|
|
4060
|
+
params: {
|
|
4061
|
+
message: String(error)
|
|
4062
|
+
}
|
|
4063
|
+
}),
|
|
3690
4064
|
error
|
|
3691
4065
|
);
|
|
3692
4066
|
}
|
|
@@ -3749,7 +4123,11 @@ function createRbacManager(config) {
|
|
|
3749
4123
|
await tx.rollback();
|
|
3750
4124
|
return err(
|
|
3751
4125
|
HaiIamError.REPOSITORY_ERROR,
|
|
3752
|
-
iamM("iam_createRoleFailed", {
|
|
4126
|
+
iamM("iam_createRoleFailed", {
|
|
4127
|
+
params: {
|
|
4128
|
+
message: String(error)
|
|
4129
|
+
}
|
|
4130
|
+
}),
|
|
3753
4131
|
error
|
|
3754
4132
|
);
|
|
3755
4133
|
}
|
|
@@ -3844,7 +4222,11 @@ function createRbacManager(config) {
|
|
|
3844
4222
|
await tx.rollback();
|
|
3845
4223
|
return err(
|
|
3846
4224
|
HaiIamError.REPOSITORY_ERROR,
|
|
3847
|
-
iamM("iam_updateRoleFailed", {
|
|
4225
|
+
iamM("iam_updateRoleFailed", {
|
|
4226
|
+
params: {
|
|
4227
|
+
message: String(error)
|
|
4228
|
+
}
|
|
4229
|
+
}),
|
|
3848
4230
|
error
|
|
3849
4231
|
);
|
|
3850
4232
|
}
|
|
@@ -3900,7 +4282,11 @@ function createRbacManager(config) {
|
|
|
3900
4282
|
await tx.rollback();
|
|
3901
4283
|
return err(
|
|
3902
4284
|
HaiIamError.REPOSITORY_ERROR,
|
|
3903
|
-
iamM("iam_deleteRoleFailed", {
|
|
4285
|
+
iamM("iam_deleteRoleFailed", {
|
|
4286
|
+
params: {
|
|
4287
|
+
message: String(error)
|
|
4288
|
+
}
|
|
4289
|
+
}),
|
|
3904
4290
|
error
|
|
3905
4291
|
);
|
|
3906
4292
|
}
|
|
@@ -3917,7 +4303,12 @@ function createRbacManager(config) {
|
|
|
3917
4303
|
const operations = permission.sensitiveOperations ?? [];
|
|
3918
4304
|
if (!Array.isArray(operations) || operations.length > 1e3 || operations.some((value) => typeof value !== "string" || !value.trim() || value.length > 128))
|
|
3919
4305
|
return err(HaiIamError.INVALID_ARGUMENT, iamM("iam_sensitiveOperationsInvalid"));
|
|
3920
|
-
permission = {
|
|
4306
|
+
permission = {
|
|
4307
|
+
...permission,
|
|
4308
|
+
sensitiveOperations: [
|
|
4309
|
+
...new Set(operations.map((value) => value.trim()))
|
|
4310
|
+
]
|
|
4311
|
+
};
|
|
3921
4312
|
const ownTx = !tx;
|
|
3922
4313
|
if (!tx) {
|
|
3923
4314
|
const txResult = await reldb.tx.begin();
|
|
@@ -3960,7 +4351,11 @@ function createRbacManager(config) {
|
|
|
3960
4351
|
await tx.rollback();
|
|
3961
4352
|
return err(
|
|
3962
4353
|
HaiIamError.REPOSITORY_ERROR,
|
|
3963
|
-
iamM("iam_createPermissionFailed", {
|
|
4354
|
+
iamM("iam_createPermissionFailed", {
|
|
4355
|
+
params: {
|
|
4356
|
+
message: String(error)
|
|
4357
|
+
}
|
|
4358
|
+
}),
|
|
3964
4359
|
error
|
|
3965
4360
|
);
|
|
3966
4361
|
}
|
|
@@ -4048,7 +4443,11 @@ function createRbacManager(config) {
|
|
|
4048
4443
|
await tx.rollback();
|
|
4049
4444
|
return err(
|
|
4050
4445
|
HaiIamError.REPOSITORY_ERROR,
|
|
4051
|
-
iamM("iam_deletePermissionFailed", {
|
|
4446
|
+
iamM("iam_deletePermissionFailed", {
|
|
4447
|
+
params: {
|
|
4448
|
+
message: String(error)
|
|
4449
|
+
}
|
|
4450
|
+
}),
|
|
4052
4451
|
error
|
|
4053
4452
|
);
|
|
4054
4453
|
}
|
|
@@ -4142,7 +4541,10 @@ async function observe(operation, event, tx) {
|
|
|
4142
4541
|
...fields,
|
|
4143
4542
|
category: "system",
|
|
4144
4543
|
outcome: result.success ? "success" : [401, 403, 409, 423, 429].includes(result.error.httpStatus ?? 500) ? "denied" : "failure",
|
|
4145
|
-
details: {
|
|
4544
|
+
details: {
|
|
4545
|
+
...fields.details,
|
|
4546
|
+
...!result.success ? { reasonCode: result.error.code } : {}
|
|
4547
|
+
}
|
|
4146
4548
|
}, { tx });
|
|
4147
4549
|
if (!recorded.success) {
|
|
4148
4550
|
logger10.error("IAM event audit failed", { action: fields.action, resource: fields.resource, error: recorded.error });
|
|
@@ -4158,7 +4560,12 @@ function loginEvent(method, result, context) {
|
|
|
4158
4560
|
details: { method },
|
|
4159
4561
|
// 登录结果本身已完成鉴别,不把客户端提交的标识声明当成已验证主体。
|
|
4160
4562
|
userId: result.success ? result.data.user.id : null,
|
|
4161
|
-
actorSnapshot: result.success ? {
|
|
4563
|
+
actorSnapshot: result.success ? {
|
|
4564
|
+
id: result.data.user.id,
|
|
4565
|
+
username: result.data.user.username,
|
|
4566
|
+
displayName: result.data.user.displayName,
|
|
4567
|
+
roles: result.data.roles
|
|
4568
|
+
} : null,
|
|
4162
4569
|
ipAddress: context?.ip,
|
|
4163
4570
|
userAgent: context?.userAgent?.slice(0, 4096),
|
|
4164
4571
|
requestId: context?.requestId
|
|
@@ -4174,18 +4581,54 @@ function observeAuth(operations) {
|
|
|
4174
4581
|
registerAndLogin: (options, context) => observe(() => operations.registerAndLogin(options, context), (result) => loginEvent("registration", result, context)),
|
|
4175
4582
|
logout: (token) => observe(() => operations.logout(token), { action: "auth.logout", resource: "iam.auth" }),
|
|
4176
4583
|
sendOtp: (identifier, options) => observe(() => operations.sendOtp(identifier, options), { action: "otp.send", resource: "iam.auth" }),
|
|
4177
|
-
reauthenticate: (token, password, operation) => observe(() => operations.reauthenticate(token, password, operation), {
|
|
4178
|
-
|
|
4584
|
+
reauthenticate: (token, password, operation) => observe(() => operations.reauthenticate(token, password, operation), {
|
|
4585
|
+
action: "auth.reauthenticate",
|
|
4586
|
+
resource: "iam.auth",
|
|
4587
|
+
details: { operation }
|
|
4588
|
+
}),
|
|
4589
|
+
checkSensitiveOperation: (token, operation, ticket) => observe(() => operations.checkSensitiveOperation(token, operation, ticket), (result) => !result.success ? {
|
|
4590
|
+
action: "auth.sensitive-denied",
|
|
4591
|
+
resource: "iam.auth",
|
|
4592
|
+
details: { operation }
|
|
4593
|
+
} : void 0)
|
|
4179
4594
|
};
|
|
4180
4595
|
}
|
|
4181
4596
|
function observeUsers(operations) {
|
|
4182
4597
|
return {
|
|
4183
4598
|
...operations,
|
|
4184
|
-
register: (options) => observe(() => operations.register(options), (result) => ({
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4599
|
+
register: (options) => observe(() => operations.register(options), (result) => ({
|
|
4600
|
+
action: "user.register",
|
|
4601
|
+
resource: "iam.users",
|
|
4602
|
+
resourceId: result.success ? result.data.user.id : void 0
|
|
4603
|
+
})),
|
|
4604
|
+
createUser: (options) => observe(() => operations.createUser(options), (result) => ({
|
|
4605
|
+
action: "user.create",
|
|
4606
|
+
resource: "iam.users",
|
|
4607
|
+
resourceId: result.success ? result.data.user.id : void 0
|
|
4608
|
+
})),
|
|
4609
|
+
inviteUser: (options) => observe(() => operations.inviteUser(options), (result) => ({
|
|
4610
|
+
action: "user.invite",
|
|
4611
|
+
resource: "iam.users",
|
|
4612
|
+
resourceId: result.success ? result.data.user.id : void 0
|
|
4613
|
+
})),
|
|
4614
|
+
updateUser: (id, data) => observe(() => operations.updateUser(id, data), {
|
|
4615
|
+
action: "user.update",
|
|
4616
|
+
resource: "iam.users",
|
|
4617
|
+
resourceId: id,
|
|
4618
|
+
details: {
|
|
4619
|
+
fields: Object.keys(data),
|
|
4620
|
+
...data.accountStatus ? { accountStatus: data.accountStatus } : {},
|
|
4621
|
+
...data.accountType ? { accountType: data.accountType } : {}
|
|
4622
|
+
}
|
|
4623
|
+
}),
|
|
4624
|
+
updateCurrentUser: (token, data) => observe(() => operations.updateCurrentUser(token, data), (result) => ({
|
|
4625
|
+
action: "user.profile",
|
|
4626
|
+
resource: "iam.users",
|
|
4627
|
+
resourceId: result.success ? result.data.id : void 0,
|
|
4628
|
+
details: {
|
|
4629
|
+
fields: Object.keys(data)
|
|
4630
|
+
}
|
|
4631
|
+
})),
|
|
4189
4632
|
deleteUser: (id) => observe(() => operations.deleteUser(id), { action: "user.delete", resource: "iam.users", resourceId: id }),
|
|
4190
4633
|
adminResetPassword: (id, password) => observe(() => operations.adminResetPassword(id, password), { action: "password.admin-reset", resource: "iam.users", resourceId: id }),
|
|
4191
4634
|
changePassword: (id, previous, next) => observe(() => operations.changePassword(id, previous, next), { action: "password.change", resource: "iam.users", resourceId: id }),
|
|
@@ -4193,31 +4636,92 @@ function observeUsers(operations) {
|
|
|
4193
4636
|
renewPassword: (input) => observe(() => operations.renewPassword(input), { action: "password.renew", resource: "iam.users" }),
|
|
4194
4637
|
requestPasswordReset: (identifier, options) => observe(() => operations.requestPasswordReset(identifier, options), { action: "password.reset-request", resource: "iam.users" }),
|
|
4195
4638
|
confirmPasswordReset: (token, password) => observe(() => operations.confirmPasswordReset(token, password), { action: "password.reset-complete", resource: "iam.users" }),
|
|
4196
|
-
scanInactiveAccounts: () => observe(() => operations.scanInactiveAccounts(), (result) => result.success && result.data === 0 ? void 0 : {
|
|
4639
|
+
scanInactiveAccounts: () => observe(() => operations.scanInactiveAccounts(), (result) => result.success && result.data === 0 ? void 0 : {
|
|
4640
|
+
action: "account.inactivity-scan",
|
|
4641
|
+
resource: "iam.users",
|
|
4642
|
+
details: result.success ? { count: result.data } : {}
|
|
4643
|
+
})
|
|
4197
4644
|
};
|
|
4198
4645
|
}
|
|
4199
4646
|
function observeAuthorization(operations) {
|
|
4200
4647
|
return {
|
|
4201
4648
|
...operations,
|
|
4202
|
-
assignRole: (id, roleId, tx) => observe(() => operations.assignRole(id, roleId, tx), {
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4649
|
+
assignRole: (id, roleId, tx) => observe(() => operations.assignRole(id, roleId, tx), {
|
|
4650
|
+
action: "role.assign",
|
|
4651
|
+
resource: "iam.user-roles",
|
|
4652
|
+
resourceId: id,
|
|
4653
|
+
details: { roleId }
|
|
4654
|
+
}, tx),
|
|
4655
|
+
removeRole: (id, roleId, tx) => observe(() => operations.removeRole(id, roleId, tx), {
|
|
4656
|
+
action: "role.remove",
|
|
4657
|
+
resource: "iam.user-roles",
|
|
4658
|
+
resourceId: id,
|
|
4659
|
+
details: { roleId }
|
|
4660
|
+
}, tx),
|
|
4661
|
+
syncRoles: (id, roleIds, tx) => observe(() => operations.syncRoles(id, roleIds, tx), {
|
|
4662
|
+
action: "role.sync",
|
|
4663
|
+
resource: "iam.user-roles",
|
|
4664
|
+
resourceId: id,
|
|
4665
|
+
details: { roleIds }
|
|
4666
|
+
}, tx),
|
|
4667
|
+
createRole: (input, tx) => observe(() => operations.createRole(input, tx), (result) => ({
|
|
4668
|
+
action: "role.create",
|
|
4669
|
+
resource: "iam.roles",
|
|
4670
|
+
resourceId: result.success ? result.data.id : void 0,
|
|
4671
|
+
details: { code: input.code }
|
|
4672
|
+
}), tx),
|
|
4673
|
+
updateRole: (id, data, tx) => observe(() => operations.updateRole(id, data, tx), {
|
|
4674
|
+
action: "role.update",
|
|
4675
|
+
resource: "iam.roles",
|
|
4676
|
+
resourceId: id,
|
|
4677
|
+
details: {
|
|
4678
|
+
fields: Object.keys(data),
|
|
4679
|
+
permissionIds: data.permissionIds
|
|
4680
|
+
}
|
|
4681
|
+
}, tx),
|
|
4207
4682
|
deleteRole: (id, tx) => observe(() => operations.deleteRole(id, tx), { action: "role.delete", resource: "iam.roles", resourceId: id }, tx),
|
|
4208
|
-
createPermission: (input, tx) => observe(() => operations.createPermission(input, tx), (result) => ({
|
|
4209
|
-
|
|
4683
|
+
createPermission: (input, tx) => observe(() => operations.createPermission(input, tx), (result) => ({
|
|
4684
|
+
action: "permission.create",
|
|
4685
|
+
resource: "iam.permissions",
|
|
4686
|
+
resourceId: result.success ? result.data.id : void 0,
|
|
4687
|
+
details: { code: input.code }
|
|
4688
|
+
}), tx),
|
|
4689
|
+
setPermissionSensitiveOperations: (id, operationIds) => observe(() => operations.setPermissionSensitiveOperations(id, operationIds), {
|
|
4690
|
+
action: "permission.sensitive-operations",
|
|
4691
|
+
resource: "iam.permissions",
|
|
4692
|
+
resourceId: id,
|
|
4693
|
+
details: { operations: operationIds }
|
|
4694
|
+
}),
|
|
4210
4695
|
deletePermission: (id, tx) => observe(() => operations.deletePermission(id, tx), { action: "permission.delete", resource: "iam.permissions", resourceId: id }, tx),
|
|
4211
|
-
assignPermissionToRole: (id, permissionId, tx) => observe(() => operations.assignPermissionToRole(id, permissionId, tx), {
|
|
4212
|
-
|
|
4696
|
+
assignPermissionToRole: (id, permissionId, tx) => observe(() => operations.assignPermissionToRole(id, permissionId, tx), {
|
|
4697
|
+
action: "permission.assign",
|
|
4698
|
+
resource: "iam.role-permissions",
|
|
4699
|
+
resourceId: id,
|
|
4700
|
+
details: { permissionId }
|
|
4701
|
+
}, tx),
|
|
4702
|
+
removePermissionFromRole: (id, permissionId, tx) => observe(() => operations.removePermissionFromRole(id, permissionId, tx), {
|
|
4703
|
+
action: "permission.remove",
|
|
4704
|
+
resource: "iam.role-permissions",
|
|
4705
|
+
resourceId: id,
|
|
4706
|
+
details: { permissionId }
|
|
4707
|
+
}, tx)
|
|
4213
4708
|
};
|
|
4214
4709
|
}
|
|
4215
4710
|
function observeSessions(operations) {
|
|
4216
4711
|
return {
|
|
4217
4712
|
...operations,
|
|
4218
|
-
create: (options) => observe(() => operations.create(options), (result) => ({
|
|
4713
|
+
create: (options) => observe(() => operations.create(options), (result) => ({
|
|
4714
|
+
action: "session.create",
|
|
4715
|
+
resource: "iam.sessions",
|
|
4716
|
+
resourceId: result.success ? result.data.version : options.userId
|
|
4717
|
+
})),
|
|
4219
4718
|
delete: (token) => observe(() => operations.delete(token), { action: "session.delete", resource: "iam.sessions" }),
|
|
4220
|
-
deleteByUserId: (id) => observe(() => operations.deleteByUserId(id), (result) => ({
|
|
4719
|
+
deleteByUserId: (id) => observe(() => operations.deleteByUserId(id), (result) => ({
|
|
4720
|
+
action: "session.revoke-user",
|
|
4721
|
+
resource: "iam.sessions",
|
|
4722
|
+
resourceId: id,
|
|
4723
|
+
details: result.success ? { count: result.data } : {}
|
|
4724
|
+
})),
|
|
4221
4725
|
refresh: (token) => observe(() => operations.refresh(token), { action: "session.refresh", resource: "iam.sessions" }),
|
|
4222
4726
|
revokeRefresh: (token) => observe(() => operations.revokeRefresh(token), { action: "session.revoke-refresh", resource: "iam.sessions" })
|
|
4223
4727
|
};
|
|
@@ -4225,102 +4729,131 @@ function observeSessions(operations) {
|
|
|
4225
4729
|
function observeApiKeys(operations) {
|
|
4226
4730
|
return {
|
|
4227
4731
|
...operations,
|
|
4228
|
-
createApiKey: (userId, options) => observe(() => operations.createApiKey(userId, options), (result) => ({
|
|
4732
|
+
createApiKey: (userId, options) => observe(() => operations.createApiKey(userId, options), (result) => ({
|
|
4733
|
+
action: "apikey.create",
|
|
4734
|
+
resource: "iam.apikeys",
|
|
4735
|
+
resourceId: result.success ? result.data.apiKey.id : void 0,
|
|
4736
|
+
details: { subjectId: userId, scopes: options.scopes }
|
|
4737
|
+
})),
|
|
4229
4738
|
revokeApiKey: (id) => observe(() => operations.revokeApiKey(id), { action: "apikey.revoke", resource: "iam.apikeys", resourceId: id })
|
|
4230
4739
|
};
|
|
4231
4740
|
}
|
|
4232
|
-
var columns = {
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4741
|
+
var columns = {
|
|
4742
|
+
credential_version: "VARCHAR(255) NOT NULL DEFAULT ''",
|
|
4743
|
+
reset_version: "VARCHAR(255) NOT NULL DEFAULT ''",
|
|
4744
|
+
reset_hash: "TEXT",
|
|
4745
|
+
reset_expires_at: "BIGINT",
|
|
4746
|
+
reset_attempts: "INTEGER NOT NULL DEFAULT 0"
|
|
4747
|
+
};
|
|
4748
|
+
var accountColumns = {
|
|
4749
|
+
account_type: "VARCHAR(16) NOT NULL DEFAULT 'permanent'",
|
|
4750
|
+
account_status: "VARCHAR(16) NOT NULL DEFAULT 'active'",
|
|
4751
|
+
account_expires_at: "TIMESTAMP",
|
|
4752
|
+
last_login_at: "TIMESTAMP"
|
|
4753
|
+
};
|
|
4754
|
+
var steps = [
|
|
4755
|
+
{
|
|
4756
|
+
version: 1,
|
|
4757
|
+
name: "credential-and-reset-versions",
|
|
4758
|
+
async needed(sql) {
|
|
4759
|
+
const found = await reldb.schemaColumns("hai_iam_users", sql);
|
|
4760
|
+
return found.success ? ok(found.data.length > 0 && Object.keys(columns).some((name) => !found.data.includes(name))) : found;
|
|
4761
|
+
},
|
|
4762
|
+
async apply(sql) {
|
|
4763
|
+
const found = await reldb.schemaColumns("hai_iam_users", sql);
|
|
4764
|
+
if (!found.success)
|
|
4765
|
+
return found;
|
|
4766
|
+
for (const [name, definition] of Object.entries(columns)) {
|
|
4767
|
+
if (found.data.includes(name))
|
|
4768
|
+
continue;
|
|
4769
|
+
const added = await sql.execute(`ALTER TABLE hai_iam_users ADD COLUMN ${name} ${definition}`);
|
|
4770
|
+
if (!added.success)
|
|
4771
|
+
return added;
|
|
4772
|
+
}
|
|
4773
|
+
return ok(void 0);
|
|
4251
4774
|
}
|
|
4252
|
-
return ok(void 0);
|
|
4253
|
-
}
|
|
4254
|
-
}, {
|
|
4255
|
-
version: 2,
|
|
4256
|
-
name: "account-lifecycle",
|
|
4257
|
-
async needed(sql) {
|
|
4258
|
-
const found = await schemaColumns(reldb, "hai_iam_users", sql);
|
|
4259
|
-
return found.success ? ok(found.data.length > 0 && Object.keys(accountColumns).some((name) => !found.data.includes(name))) : found;
|
|
4260
4775
|
},
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4776
|
+
{
|
|
4777
|
+
version: 2,
|
|
4778
|
+
name: "account-lifecycle",
|
|
4779
|
+
async needed(sql) {
|
|
4780
|
+
const found = await reldb.schemaColumns("hai_iam_users", sql);
|
|
4781
|
+
return found.success ? ok(found.data.length > 0 && Object.keys(accountColumns).some((name) => !found.data.includes(name))) : found;
|
|
4782
|
+
},
|
|
4783
|
+
async apply(sql) {
|
|
4784
|
+
const found = await reldb.schemaColumns("hai_iam_users", sql);
|
|
4785
|
+
if (!found.success)
|
|
4786
|
+
return found;
|
|
4787
|
+
for (const [name, definition] of Object.entries(accountColumns)) {
|
|
4788
|
+
if (found.data.includes(name))
|
|
4789
|
+
continue;
|
|
4790
|
+
const added = await sql.execute(`ALTER TABLE hai_iam_users ADD COLUMN ${name} ${definition}`);
|
|
4791
|
+
if (!added.success)
|
|
4792
|
+
return added;
|
|
4793
|
+
}
|
|
4794
|
+
return ok(void 0);
|
|
4271
4795
|
}
|
|
4272
|
-
return ok(void 0);
|
|
4273
|
-
}
|
|
4274
|
-
}, {
|
|
4275
|
-
version: 3,
|
|
4276
|
-
name: "initial-password-state",
|
|
4277
|
-
async needed(sql) {
|
|
4278
|
-
const found = await schemaColumns(reldb, "hai_iam_users", sql);
|
|
4279
|
-
return found.success ? ok(found.data.length > 0 && !found.data.includes("must_change_password")) : found;
|
|
4280
4796
|
},
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4797
|
+
{
|
|
4798
|
+
version: 3,
|
|
4799
|
+
name: "initial-password-state",
|
|
4800
|
+
async needed(sql) {
|
|
4801
|
+
const found = await reldb.schemaColumns("hai_iam_users", sql);
|
|
4802
|
+
return found.success ? ok(found.data.length > 0 && !found.data.includes("must_change_password")) : found;
|
|
4803
|
+
},
|
|
4804
|
+
async apply(sql) {
|
|
4805
|
+
const added = await sql.execute("ALTER TABLE hai_iam_users ADD COLUMN must_change_password BOOLEAN NOT NULL DEFAULT FALSE");
|
|
4806
|
+
return added.success ? ok(void 0) : added;
|
|
4807
|
+
}
|
|
4291
4808
|
},
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4809
|
+
{
|
|
4810
|
+
version: 4,
|
|
4811
|
+
name: "login-failure-window",
|
|
4812
|
+
async needed(sql) {
|
|
4813
|
+
const found = await reldb.schemaColumns("hai_iam_users", sql);
|
|
4814
|
+
return found.success ? ok(found.data.length > 0 && !found.data.includes("first_login_failed_at")) : found;
|
|
4815
|
+
},
|
|
4816
|
+
async apply(sql) {
|
|
4817
|
+
const added = await sql.execute("ALTER TABLE hai_iam_users ADD COLUMN first_login_failed_at TIMESTAMP");
|
|
4818
|
+
return added.success ? ok(void 0) : added;
|
|
4819
|
+
}
|
|
4302
4820
|
},
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4821
|
+
{
|
|
4822
|
+
version: 5,
|
|
4823
|
+
name: "permission-sensitive-operations",
|
|
4824
|
+
async needed(sql) {
|
|
4825
|
+
const found = await reldb.schemaColumns("hai_iam_permissions", sql);
|
|
4826
|
+
return found.success ? ok(found.data.length > 0 && !found.data.includes("sensitive_operations")) : found;
|
|
4827
|
+
},
|
|
4828
|
+
async apply(sql) {
|
|
4829
|
+
const added = await sql.execute("ALTER TABLE hai_iam_permissions ADD COLUMN sensitive_operations JSON");
|
|
4830
|
+
return added.success ? ok(void 0) : added;
|
|
4831
|
+
}
|
|
4313
4832
|
},
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4833
|
+
{
|
|
4834
|
+
version: 6,
|
|
4835
|
+
name: "account-lifecycle-only",
|
|
4836
|
+
async needed(sql) {
|
|
4837
|
+
const found = await reldb.schemaColumns("hai_iam_users", sql);
|
|
4838
|
+
return found.success ? ok(found.data.includes("enabled")) : found;
|
|
4839
|
+
},
|
|
4840
|
+
async apply(sql) {
|
|
4841
|
+
const converted = await sql.execute("UPDATE hai_iam_users SET account_status = ?, credential_version = ?, reset_version = ?, reset_hash = NULL, reset_expires_at = NULL WHERE (enabled = ? OR enabled IS NULL) AND account_status = ?", [
|
|
4842
|
+
"dormant",
|
|
4843
|
+
globalThis.crypto.randomUUID(),
|
|
4844
|
+
globalThis.crypto.randomUUID(),
|
|
4845
|
+
reldb.config?.type === "postgresql" ? false : 0,
|
|
4846
|
+
"active"
|
|
4847
|
+
]);
|
|
4848
|
+
if (!converted.success)
|
|
4849
|
+
return converted;
|
|
4850
|
+
const removed = await sql.execute("ALTER TABLE hai_iam_users DROP COLUMN enabled");
|
|
4851
|
+
return removed.success ? ok(void 0) : removed;
|
|
4852
|
+
}
|
|
4320
4853
|
}
|
|
4321
|
-
|
|
4854
|
+
];
|
|
4322
4855
|
function migrateIam(options = {}) {
|
|
4323
|
-
return migrateSchema(
|
|
4856
|
+
return reldb.migrateSchema("iam", steps, options);
|
|
4324
4857
|
}
|
|
4325
4858
|
|
|
4326
4859
|
// src/iam-notifications.ts
|
|
@@ -4342,7 +4875,15 @@ async function resolveIamNotifications(input) {
|
|
|
4342
4875
|
const provider = configured.get(template);
|
|
4343
4876
|
if (!reach?.isInitialized || !provider || !to)
|
|
4344
4877
|
throw new Error(iamM("iam_passwordResetDeliveryFailed"));
|
|
4345
|
-
const result = await reach.send({
|
|
4878
|
+
const result = await reach.send({
|
|
4879
|
+
provider,
|
|
4880
|
+
to,
|
|
4881
|
+
template,
|
|
4882
|
+
vars,
|
|
4883
|
+
idempotencyKey: context.idempotencyKey,
|
|
4884
|
+
sensitive: true,
|
|
4885
|
+
expiresAt: context.expiresAt.getTime()
|
|
4886
|
+
});
|
|
4346
4887
|
if (!result.success || !result.data.success)
|
|
4347
4888
|
throw new Error(iamM("iam_passwordResetDeliveryFailed"));
|
|
4348
4889
|
}
|
|
@@ -4355,7 +4896,11 @@ async function resolveIamNotifications(input) {
|
|
|
4355
4896
|
callbacks.onPasswordResetRequest = (user, token, context) => {
|
|
4356
4897
|
const url = input.passwordReset?.resetUrl ? new URL(input.passwordReset.resetUrl) : null;
|
|
4357
4898
|
url?.searchParams.set("token", token);
|
|
4358
|
-
const vars = {
|
|
4899
|
+
const vars = {
|
|
4900
|
+
token,
|
|
4901
|
+
expiresAt: context.expiresAt.toISOString(),
|
|
4902
|
+
resetUrl: url?.toString() ?? token
|
|
4903
|
+
};
|
|
4359
4904
|
return send(resetTemplate, input.passwordReset?.mode === "phone" ? user.phone ?? "" : user.email ?? "", vars, context);
|
|
4360
4905
|
};
|
|
4361
4906
|
}
|
|
@@ -4372,14 +4917,27 @@ function buildIamPublicConfig(config, runtime) {
|
|
|
4372
4917
|
captcha: { enabled: config.captcha.enabled },
|
|
4373
4918
|
password: PasswordConfigSchema.parse(config.password ?? {}),
|
|
4374
4919
|
register: { enabled: config.register.enabled },
|
|
4375
|
-
login: {
|
|
4920
|
+
login: {
|
|
4921
|
+
password: config.login.password,
|
|
4922
|
+
otp: email || sms,
|
|
4923
|
+
ldap: config.login.ldap && !!config.ldap,
|
|
4924
|
+
email,
|
|
4925
|
+
sms
|
|
4926
|
+
},
|
|
4376
4927
|
passwordReset: { enabled: config.passwordReset?.enabled !== false && !!runtime.onPasswordResetRequest, mode: config.passwordReset?.mode ?? "email" },
|
|
4377
4928
|
otp: { length: otp.length, resendInterval: otp.resendInterval },
|
|
4378
4929
|
agreements: { ...config.agreements },
|
|
4379
4930
|
ui: AuthUiConfigSchema.parse(config.ui ?? {}),
|
|
4380
4931
|
session: { maxAge: session.maxAge, endOnPageHide: session.endOnPageHide },
|
|
4381
|
-
reauthentication: {
|
|
4382
|
-
|
|
4932
|
+
reauthentication: {
|
|
4933
|
+
enabled: config.reauthentication?.enabled ?? false,
|
|
4934
|
+
operations: [
|
|
4935
|
+
...config.reauthentication?.operations ?? []
|
|
4936
|
+
]
|
|
4937
|
+
},
|
|
4938
|
+
initialCredentials: {
|
|
4939
|
+
enabled: Boolean(config.initialCredentials?.enabled && config.passwordReset?.enabled !== false && runtime.onPasswordResetRequest)
|
|
4940
|
+
}
|
|
4383
4941
|
};
|
|
4384
4942
|
}
|
|
4385
4943
|
async function readIamUserSecurity(repository, userId) {
|
|
@@ -4401,47 +4959,241 @@ async function readIamUserSecurity(repository, userId) {
|
|
|
4401
4959
|
passwordUpdatedAt: user.passwordUpdatedAt?.getTime() ?? null
|
|
4402
4960
|
} : null);
|
|
4403
4961
|
});
|
|
4404
|
-
return found.success ? ok(found.data ? {
|
|
4962
|
+
return found.success ? ok(found.data ? {
|
|
4963
|
+
...found.data,
|
|
4964
|
+
accountExpiresAt: found.data.accountExpiresAt === null ? null : new Date(found.data.accountExpiresAt),
|
|
4965
|
+
passwordUpdatedAt: found.data.passwordUpdatedAt === null ? void 0 : new Date(found.data.passwordUpdatedAt),
|
|
4966
|
+
passwordHash: found.data.hasPassword ? "present" : void 0
|
|
4967
|
+
} : null) : found;
|
|
4405
4968
|
}
|
|
4406
4969
|
var logger11 = core.logger.child({ module: "iam", scope: "seed" });
|
|
4407
4970
|
var DEFAULT_ROLES = [
|
|
4408
|
-
{
|
|
4409
|
-
|
|
4410
|
-
|
|
4971
|
+
{
|
|
4972
|
+
code: "admin",
|
|
4973
|
+
name: () => iamM("iam_seedRoleAdminName"),
|
|
4974
|
+
description: () => iamM("iam_seedRoleAdminDesc"),
|
|
4975
|
+
isSystem: true
|
|
4976
|
+
},
|
|
4977
|
+
{
|
|
4978
|
+
code: "user",
|
|
4979
|
+
name: () => iamM("iam_seedRoleUserName"),
|
|
4980
|
+
description: () => iamM("iam_seedRoleUserDesc"),
|
|
4981
|
+
isSystem: true
|
|
4982
|
+
},
|
|
4983
|
+
{
|
|
4984
|
+
code: "guest",
|
|
4985
|
+
name: () => iamM("iam_seedRoleGuestName"),
|
|
4986
|
+
description: () => iamM("iam_seedRoleGuestDesc"),
|
|
4987
|
+
isSystem: true
|
|
4988
|
+
}
|
|
4411
4989
|
];
|
|
4412
4990
|
var DEFAULT_PERMISSIONS = [
|
|
4413
4991
|
// ─── 菜单权限 ───
|
|
4414
|
-
{
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
{
|
|
4992
|
+
{
|
|
4993
|
+
code: "dashboard:view",
|
|
4994
|
+
name: () => iamM("iam_seedPermDashboardView"),
|
|
4995
|
+
type: "menu",
|
|
4996
|
+
resource: "dashboard",
|
|
4997
|
+
action: "view"
|
|
4998
|
+
},
|
|
4999
|
+
{
|
|
5000
|
+
code: "user:read",
|
|
5001
|
+
name: () => iamM("iam_seedPermUserRead"),
|
|
5002
|
+
type: "menu",
|
|
5003
|
+
resource: "user",
|
|
5004
|
+
action: "read"
|
|
5005
|
+
},
|
|
5006
|
+
{
|
|
5007
|
+
code: "role:read",
|
|
5008
|
+
name: () => iamM("iam_seedPermRoleRead"),
|
|
5009
|
+
type: "menu",
|
|
5010
|
+
resource: "role",
|
|
5011
|
+
action: "read"
|
|
5012
|
+
},
|
|
5013
|
+
{
|
|
5014
|
+
code: "permission:read",
|
|
5015
|
+
name: () => iamM("iam_seedPermPermRead"),
|
|
5016
|
+
type: "menu",
|
|
5017
|
+
resource: "permission",
|
|
5018
|
+
action: "read"
|
|
5019
|
+
},
|
|
5020
|
+
{
|
|
5021
|
+
code: "system:logs",
|
|
5022
|
+
name: () => iamM("iam_seedPermSystemLogs"),
|
|
5023
|
+
type: "menu",
|
|
5024
|
+
resource: "system",
|
|
5025
|
+
action: "logs"
|
|
5026
|
+
},
|
|
5027
|
+
{
|
|
5028
|
+
code: "system:settings",
|
|
5029
|
+
name: () => iamM("iam_seedPermSystemSettings"),
|
|
5030
|
+
type: "menu",
|
|
5031
|
+
resource: "system",
|
|
5032
|
+
action: "settings"
|
|
5033
|
+
},
|
|
5034
|
+
{
|
|
5035
|
+
code: "system:modules",
|
|
5036
|
+
name: () => iamM("iam_seedPermSystemModules"),
|
|
5037
|
+
type: "menu",
|
|
5038
|
+
resource: "system",
|
|
5039
|
+
action: "modules"
|
|
5040
|
+
},
|
|
5041
|
+
{
|
|
5042
|
+
code: "profile:read",
|
|
5043
|
+
name: () => iamM("iam_seedPermProfileRead"),
|
|
5044
|
+
type: "menu",
|
|
5045
|
+
resource: "profile",
|
|
5046
|
+
action: "read"
|
|
5047
|
+
},
|
|
4422
5048
|
// ─── API 权限 ───
|
|
4423
|
-
{
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
{
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
5049
|
+
{
|
|
5050
|
+
code: "user:list",
|
|
5051
|
+
name: () => iamM("iam_seedPermUserList"),
|
|
5052
|
+
type: "api",
|
|
5053
|
+
resource: "user",
|
|
5054
|
+
action: "list"
|
|
5055
|
+
},
|
|
5056
|
+
{
|
|
5057
|
+
code: "user:api:create",
|
|
5058
|
+
name: () => iamM("iam_seedPermUserApiCreate"),
|
|
5059
|
+
type: "api",
|
|
5060
|
+
resource: "user",
|
|
5061
|
+
action: "api:create"
|
|
5062
|
+
},
|
|
5063
|
+
{
|
|
5064
|
+
code: "user:api:update",
|
|
5065
|
+
name: () => iamM("iam_seedPermUserApiUpdate"),
|
|
5066
|
+
type: "api",
|
|
5067
|
+
resource: "user",
|
|
5068
|
+
action: "api:update"
|
|
5069
|
+
},
|
|
5070
|
+
{
|
|
5071
|
+
code: "user:api:delete",
|
|
5072
|
+
name: () => iamM("iam_seedPermUserApiDelete"),
|
|
5073
|
+
type: "api",
|
|
5074
|
+
resource: "user",
|
|
5075
|
+
action: "api:delete"
|
|
5076
|
+
},
|
|
5077
|
+
{
|
|
5078
|
+
code: "role:list",
|
|
5079
|
+
name: () => iamM("iam_seedPermRoleList"),
|
|
5080
|
+
type: "api",
|
|
5081
|
+
resource: "role",
|
|
5082
|
+
action: "list"
|
|
5083
|
+
},
|
|
5084
|
+
{
|
|
5085
|
+
code: "role:api:create",
|
|
5086
|
+
name: () => iamM("iam_seedPermRoleApiCreate"),
|
|
5087
|
+
type: "api",
|
|
5088
|
+
resource: "role",
|
|
5089
|
+
action: "api:create"
|
|
5090
|
+
},
|
|
5091
|
+
{
|
|
5092
|
+
code: "role:api:update",
|
|
5093
|
+
name: () => iamM("iam_seedPermRoleApiUpdate"),
|
|
5094
|
+
type: "api",
|
|
5095
|
+
resource: "role",
|
|
5096
|
+
action: "api:update"
|
|
5097
|
+
},
|
|
5098
|
+
{
|
|
5099
|
+
code: "role:api:delete",
|
|
5100
|
+
name: () => iamM("iam_seedPermRoleApiDelete"),
|
|
5101
|
+
type: "api",
|
|
5102
|
+
resource: "role",
|
|
5103
|
+
action: "api:delete"
|
|
5104
|
+
},
|
|
5105
|
+
{
|
|
5106
|
+
code: "permission:list",
|
|
5107
|
+
name: () => iamM("iam_seedPermPermList"),
|
|
5108
|
+
type: "api",
|
|
5109
|
+
resource: "permission",
|
|
5110
|
+
action: "list"
|
|
5111
|
+
},
|
|
5112
|
+
{
|
|
5113
|
+
code: "permission:manage",
|
|
5114
|
+
name: () => iamM("iam_seedPermPermManage"),
|
|
5115
|
+
type: "api",
|
|
5116
|
+
resource: "permission",
|
|
5117
|
+
action: "manage"
|
|
5118
|
+
},
|
|
5119
|
+
{
|
|
5120
|
+
code: "permission:api:create",
|
|
5121
|
+
name: () => iamM("iam_seedPermPermApiCreate"),
|
|
5122
|
+
type: "api",
|
|
5123
|
+
resource: "permission",
|
|
5124
|
+
action: "api:create"
|
|
5125
|
+
},
|
|
5126
|
+
{
|
|
5127
|
+
code: "permission:api:delete",
|
|
5128
|
+
name: () => iamM("iam_seedPermPermApiDelete"),
|
|
5129
|
+
type: "api",
|
|
5130
|
+
resource: "permission",
|
|
5131
|
+
action: "api:delete"
|
|
5132
|
+
},
|
|
5133
|
+
{
|
|
5134
|
+
code: "audit:read",
|
|
5135
|
+
name: () => iamM("iam_seedPermAuditRead"),
|
|
5136
|
+
type: "api",
|
|
5137
|
+
resource: "audit",
|
|
5138
|
+
action: "read"
|
|
5139
|
+
},
|
|
4436
5140
|
// ─── 按钮权限 ───
|
|
4437
|
-
{
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
{
|
|
5141
|
+
{
|
|
5142
|
+
code: "user:create",
|
|
5143
|
+
name: () => iamM("iam_seedPermUserCreate"),
|
|
5144
|
+
type: "button",
|
|
5145
|
+
resource: "user",
|
|
5146
|
+
action: "create"
|
|
5147
|
+
},
|
|
5148
|
+
{
|
|
5149
|
+
code: "user:update",
|
|
5150
|
+
name: () => iamM("iam_seedPermUserUpdate"),
|
|
5151
|
+
type: "button",
|
|
5152
|
+
resource: "user",
|
|
5153
|
+
action: "update"
|
|
5154
|
+
},
|
|
5155
|
+
{
|
|
5156
|
+
code: "user:delete",
|
|
5157
|
+
name: () => iamM("iam_seedPermUserDelete"),
|
|
5158
|
+
type: "button",
|
|
5159
|
+
resource: "user",
|
|
5160
|
+
action: "delete"
|
|
5161
|
+
},
|
|
5162
|
+
{
|
|
5163
|
+
code: "role:create",
|
|
5164
|
+
name: () => iamM("iam_seedPermRoleCreate"),
|
|
5165
|
+
type: "button",
|
|
5166
|
+
resource: "role",
|
|
5167
|
+
action: "create"
|
|
5168
|
+
},
|
|
5169
|
+
{
|
|
5170
|
+
code: "role:update",
|
|
5171
|
+
name: () => iamM("iam_seedPermRoleUpdate"),
|
|
5172
|
+
type: "button",
|
|
5173
|
+
resource: "role",
|
|
5174
|
+
action: "update"
|
|
5175
|
+
},
|
|
5176
|
+
{
|
|
5177
|
+
code: "role:delete",
|
|
5178
|
+
name: () => iamM("iam_seedPermRoleDelete"),
|
|
5179
|
+
type: "button",
|
|
5180
|
+
resource: "role",
|
|
5181
|
+
action: "delete"
|
|
5182
|
+
},
|
|
5183
|
+
{
|
|
5184
|
+
code: "permission:create",
|
|
5185
|
+
name: () => iamM("iam_seedPermPermCreate"),
|
|
5186
|
+
type: "button",
|
|
5187
|
+
resource: "permission",
|
|
5188
|
+
action: "create"
|
|
5189
|
+
},
|
|
5190
|
+
{
|
|
5191
|
+
code: "permission:delete",
|
|
5192
|
+
name: () => iamM("iam_seedPermPermDelete"),
|
|
5193
|
+
type: "button",
|
|
5194
|
+
resource: "permission",
|
|
5195
|
+
action: "delete"
|
|
5196
|
+
}
|
|
4445
5197
|
];
|
|
4446
5198
|
var USER_ROLE_PERMISSIONS = ["dashboard:view", "profile:read"];
|
|
4447
5199
|
async function seedIamData(authz) {
|
|
@@ -4459,7 +5211,12 @@ async function seedIamData(authz) {
|
|
|
4459
5211
|
roleMap.set(role.code, existingId);
|
|
4460
5212
|
continue;
|
|
4461
5213
|
}
|
|
4462
|
-
const result = await authz.createRole({
|
|
5214
|
+
const result = await authz.createRole({
|
|
5215
|
+
code: role.code,
|
|
5216
|
+
name: role.name(),
|
|
5217
|
+
description: role.description(),
|
|
5218
|
+
isSystem: role.isSystem
|
|
5219
|
+
});
|
|
4463
5220
|
if (result.success) {
|
|
4464
5221
|
roleMap.set(role.code, result.data.id);
|
|
4465
5222
|
} else if (result.error.code === HaiIamError.ROLE_ALREADY_EXISTS.code) {
|
|
@@ -4486,7 +5243,13 @@ async function seedIamData(authz) {
|
|
|
4486
5243
|
permMap.set(perm.code, existingId);
|
|
4487
5244
|
continue;
|
|
4488
5245
|
}
|
|
4489
|
-
const result = await authz.createPermission({
|
|
5246
|
+
const result = await authz.createPermission({
|
|
5247
|
+
code: perm.code,
|
|
5248
|
+
name: perm.name(),
|
|
5249
|
+
type: perm.type,
|
|
5250
|
+
resource: perm.resource,
|
|
5251
|
+
action: perm.action
|
|
5252
|
+
});
|
|
4490
5253
|
if (result.success) {
|
|
4491
5254
|
permMap.set(perm.code, result.data.id);
|
|
4492
5255
|
} else if (result.error.code === HaiIamError.PERMISSION_ALREADY_EXISTS.code) {
|
|
@@ -4534,8 +5297,19 @@ function retainedUntil(entry) {
|
|
|
4534
5297
|
return Math.max(new Date(entry.session.expiresAt).getTime(), entry.refreshToken ? entry.refreshExpiresAt : 0);
|
|
4535
5298
|
}
|
|
4536
5299
|
function restoreSession(session) {
|
|
4537
|
-
const result = {
|
|
4538
|
-
|
|
5300
|
+
const result = {
|
|
5301
|
+
...session,
|
|
5302
|
+
createdAt: new Date(session.createdAt),
|
|
5303
|
+
startedAt: new Date(session.startedAt ?? session.createdAt),
|
|
5304
|
+
lastActiveAt: new Date(session.lastActiveAt),
|
|
5305
|
+
expiresAt: new Date(session.expiresAt)
|
|
5306
|
+
};
|
|
5307
|
+
if (result.terminalType !== void 0 && !["web", "app", "desktop", "api"].includes(result.terminalType) || typeof result.credentialVersion !== "string" || !result.version || !Number.isSafeInteger(result.maxAge) || result.maxAge <= 0 || !Array.isArray(result.roles) || !result.roles.every((role) => typeof role === "string") || !Array.isArray(result.permissions) || !result.permissions.every((permission) => typeof permission === "string") || ![
|
|
5308
|
+
result.createdAt,
|
|
5309
|
+
result.startedAt,
|
|
5310
|
+
result.lastActiveAt,
|
|
5311
|
+
result.expiresAt
|
|
5312
|
+
].every((date) => Number.isFinite(date.getTime()))) {
|
|
4539
5313
|
throw new TypeError("Invalid session state");
|
|
4540
5314
|
}
|
|
4541
5315
|
return result;
|
|
@@ -4692,7 +5466,10 @@ function createCacheSessionRepository(refreshTokenMaxAge, policy = {}) {
|
|
|
4692
5466
|
const updated = applySessionPatch(restoreSession(entry.session), patch);
|
|
4693
5467
|
if (![updated.expiresAt, updated.lastActiveAt].every((date) => Number.isFinite(date.getTime())))
|
|
4694
5468
|
return err(HaiIamError.INVALID_ARGUMENT, iamM("iam_sessionMaxAgeInvalid"));
|
|
4695
|
-
entry.session = {
|
|
5469
|
+
entry.session = {
|
|
5470
|
+
...updated,
|
|
5471
|
+
version: crypto.randomUUID()
|
|
5472
|
+
};
|
|
4696
5473
|
return ok(void 0);
|
|
4697
5474
|
});
|
|
4698
5475
|
},
|
|
@@ -4752,7 +5529,10 @@ function createCacheSessionRepository(refreshTokenMaxAge, policy = {}) {
|
|
|
4752
5529
|
continue;
|
|
4753
5530
|
if (entry.session.version !== version)
|
|
4754
5531
|
return err(HaiIamError.CONFLICT, iamM("iam_sessionStateConflict"));
|
|
4755
|
-
entry.session = {
|
|
5532
|
+
entry.session = {
|
|
5533
|
+
...applySessionPatch(restoreSession(entry.session), patch),
|
|
5534
|
+
version: crypto.randomUUID()
|
|
5535
|
+
};
|
|
4756
5536
|
}
|
|
4757
5537
|
return ok(void 0);
|
|
4758
5538
|
});
|
|
@@ -4766,7 +5546,10 @@ function stripInternalSessionFields(session) {
|
|
|
4766
5546
|
return session;
|
|
4767
5547
|
const { _tokenPair, ...rest } = session.data;
|
|
4768
5548
|
const hasExtraKeys = Object.keys(rest).length > 0;
|
|
4769
|
-
return {
|
|
5549
|
+
return {
|
|
5550
|
+
...session,
|
|
5551
|
+
data: hasExtraKeys ? rest : void 0
|
|
5552
|
+
};
|
|
4770
5553
|
}
|
|
4771
5554
|
var logger12 = core.logger.child({ module: "iam", scope: "session" });
|
|
4772
5555
|
async function createSessionOperations(deps) {
|
|
@@ -4810,7 +5593,12 @@ function buildSessionFunctions(config) {
|
|
|
4810
5593
|
if (!Number.isSafeInteger(ttl) || ttl <= 0 || !Number.isFinite(new Date(now.getTime() + ttl * 1e3).getTime()))
|
|
4811
5594
|
return err(HaiIamError.INVALID_ARGUMENT, iamM("iam_sessionMaxAgeInvalid"));
|
|
4812
5595
|
const accessToken = generateSessionToken(options.userId);
|
|
4813
|
-
const tokens = {
|
|
5596
|
+
const tokens = {
|
|
5597
|
+
accessToken,
|
|
5598
|
+
refreshToken: generateSessionToken(options.userId),
|
|
5599
|
+
expiresIn: ttl,
|
|
5600
|
+
tokenType: "Bearer"
|
|
5601
|
+
};
|
|
4814
5602
|
const session = buildSession(options, now, ttl, accessToken, credentialVersion);
|
|
4815
5603
|
session.data = { ...session.data, _tokenPair: tokens };
|
|
4816
5604
|
return ok({ session, tokens });
|
|
@@ -4836,7 +5624,12 @@ function buildSessionFunctions(config) {
|
|
|
4836
5624
|
return err(HaiIamError.APIKEY_INVALID, iamM("iam_apikeyInvalid"));
|
|
4837
5625
|
const found = await readIamCache(`apikey:${session.apiKeyId}`, async () => {
|
|
4838
5626
|
const row = await config.apiKeyRepository.findOneById(session.apiKeyId);
|
|
4839
|
-
return row.success ? ok(row.data ? {
|
|
5627
|
+
return row.success ? ok(row.data ? {
|
|
5628
|
+
userId: row.data.userId,
|
|
5629
|
+
enabled: row.data.enabled,
|
|
5630
|
+
expiresAt: row.data.expiresAt?.getTime() ?? null,
|
|
5631
|
+
scopes: row.data.scopes
|
|
5632
|
+
} : null) : row;
|
|
4840
5633
|
});
|
|
4841
5634
|
if (!found.success)
|
|
4842
5635
|
return found;
|
|
@@ -4894,7 +5687,10 @@ function buildSessionFunctions(config) {
|
|
|
4894
5687
|
const bound = await credential(session.userId, session.credentialVersion);
|
|
4895
5688
|
if (!bound.success)
|
|
4896
5689
|
return bound;
|
|
4897
|
-
const validated = await validateApiKey({
|
|
5690
|
+
const validated = await validateApiKey({
|
|
5691
|
+
...session,
|
|
5692
|
+
...bound.data.profile
|
|
5693
|
+
});
|
|
4898
5694
|
if (!validated.success)
|
|
4899
5695
|
return validated;
|
|
4900
5696
|
let output = validated.data;
|
|
@@ -5038,7 +5834,9 @@ function hasUpdateFields(data) {
|
|
|
5038
5834
|
function mapRepositoryError(messageKey, message) {
|
|
5039
5835
|
return err(
|
|
5040
5836
|
HaiIamError.REPOSITORY_ERROR,
|
|
5041
|
-
iamM(messageKey, {
|
|
5837
|
+
iamM(messageKey, {
|
|
5838
|
+
params: { message }
|
|
5839
|
+
})
|
|
5042
5840
|
);
|
|
5043
5841
|
}
|
|
5044
5842
|
function mapUpdateErrorAsDomainError(error) {
|
|
@@ -5219,7 +6017,9 @@ function buildUserQueryOps(ctx) {
|
|
|
5219
6017
|
async getUsers(userIds, options) {
|
|
5220
6018
|
if (userIds.length > 1e3 || userIds.some((id) => typeof id !== "string" || !id.trim()))
|
|
5221
6019
|
return err(HaiIamError.INVALID_ARGUMENT, iamM("iam_batchUserIdsInvalid"));
|
|
5222
|
-
const ids = [
|
|
6020
|
+
const ids = [
|
|
6021
|
+
...new Set(userIds)
|
|
6022
|
+
];
|
|
5223
6023
|
const found = /* @__PURE__ */ new Map();
|
|
5224
6024
|
for (let offset = 0; offset < ids.length; offset += 500) {
|
|
5225
6025
|
const batch = ids.slice(offset, offset + 500);
|
|
@@ -5229,7 +6029,9 @@ function buildUserQueryOps(ctx) {
|
|
|
5229
6029
|
for (const user of users.data)
|
|
5230
6030
|
found.set(user.id, toUser(user));
|
|
5231
6031
|
}
|
|
5232
|
-
const items = ids.flatMap((id) => found.has(id) ? [
|
|
6032
|
+
const items = ids.flatMap((id) => found.has(id) ? [
|
|
6033
|
+
found.get(id)
|
|
6034
|
+
] : []);
|
|
5233
6035
|
if (options?.include?.includes("roles") && items.length) {
|
|
5234
6036
|
const roles = await authzFunctions.getUserRolesForMany(items.map((user) => user.id));
|
|
5235
6037
|
if (!roles.success)
|
|
@@ -5426,9 +6228,15 @@ function buildUserMutationOps(ctx) {
|
|
|
5426
6228
|
if (updateResult.data.changes === 0)
|
|
5427
6229
|
return err(HaiIamError.USER_NOT_FOUND, iamM("iam_userNotExist"));
|
|
5428
6230
|
if (tx) {
|
|
5429
|
-
const advanced = await tx.execute("UPDATE hai_iam_users SET credential_version = ?, reset_version = ?, reset_hash = NULL, reset_expires_at = NULL WHERE id = ? AND credential_version = ?", [
|
|
5430
|
-
|
|
6231
|
+
const advanced = await tx.execute("UPDATE hai_iam_users SET credential_version = ?, reset_version = ?, reset_hash = NULL, reset_expires_at = NULL WHERE id = ? AND credential_version = ?", [
|
|
6232
|
+
globalThis.crypto.randomUUID(),
|
|
6233
|
+
globalThis.crypto.randomUUID(),
|
|
6234
|
+
userId,
|
|
6235
|
+
currentAccount.data.credentialVersion ?? ""
|
|
6236
|
+
]);
|
|
6237
|
+
if (!advanced.success || advanced.data.changes !== 1) {
|
|
5431
6238
|
return advanced.success ? err(HaiIamError.CONFLICT, iamM("iam_sessionStateConflict")) : advanced;
|
|
6239
|
+
}
|
|
5432
6240
|
const committed = await tx.commit();
|
|
5433
6241
|
if (!committed.success)
|
|
5434
6242
|
return committed;
|
|
@@ -5493,7 +6301,11 @@ function buildUserMutationOps(ctx) {
|
|
|
5493
6301
|
await tx.rollback();
|
|
5494
6302
|
return err(
|
|
5495
6303
|
HaiIamError.REPOSITORY_ERROR,
|
|
5496
|
-
iamM("iam_deleteUserFailed", {
|
|
6304
|
+
iamM("iam_deleteUserFailed", {
|
|
6305
|
+
params: {
|
|
6306
|
+
message: String(error)
|
|
6307
|
+
}
|
|
6308
|
+
}),
|
|
5497
6309
|
error
|
|
5498
6310
|
);
|
|
5499
6311
|
}
|
|
@@ -5525,7 +6337,12 @@ function buildPasswordChangeOps(ctx) {
|
|
|
5525
6337
|
const hashResult = hashPassword(newPassword);
|
|
5526
6338
|
if (!hashResult.success)
|
|
5527
6339
|
return hashResult;
|
|
5528
|
-
const updateResult = await userRepository.setPassword({
|
|
6340
|
+
const updateResult = await userRepository.setPassword({
|
|
6341
|
+
userId,
|
|
6342
|
+
expectedVersion: userResult.data.credentialVersion ?? "",
|
|
6343
|
+
passwordHash: hashResult.data,
|
|
6344
|
+
mustChangePassword: ctx.config.password?.requireInitialChange === true
|
|
6345
|
+
});
|
|
5529
6346
|
if (!updateResult.success)
|
|
5530
6347
|
return updateResult;
|
|
5531
6348
|
if (!updateResult.data)
|
|
@@ -5710,7 +6527,9 @@ function buildUserFunctions(deps) {
|
|
|
5710
6527
|
return err(HaiIamError.INVITATION_DISABLED, iamM("iam_invitationDisabled"));
|
|
5711
6528
|
const policy = deps.passwordStrategyResult.passwordConfig;
|
|
5712
6529
|
const length = Math.min(policy.maxLength, Math.max(32, policy.minLength));
|
|
5713
|
-
const password = `Aa1!${Array.from({
|
|
6530
|
+
const password = `Aa1!${Array.from({
|
|
6531
|
+
length: Math.ceil(length / 32)
|
|
6532
|
+
}, () => generateToken()).join("")}`.slice(0, length);
|
|
5714
6533
|
const created = await registration.createUser({ ...options, password });
|
|
5715
6534
|
if (!created.success)
|
|
5716
6535
|
return created;
|
|
@@ -5798,13 +6617,17 @@ var iam = {
|
|
|
5798
6617
|
if (!reldb.isInitialized) {
|
|
5799
6618
|
return err(
|
|
5800
6619
|
HaiIamError.CONFIG_ERROR,
|
|
5801
|
-
iamM("iam_depsNotInitialized", {
|
|
6620
|
+
iamM("iam_depsNotInitialized", {
|
|
6621
|
+
params: { dep: "reldb" }
|
|
6622
|
+
})
|
|
5802
6623
|
);
|
|
5803
6624
|
}
|
|
5804
6625
|
if (!cache.isInitialized) {
|
|
5805
6626
|
return err(
|
|
5806
6627
|
HaiIamError.CONFIG_ERROR,
|
|
5807
|
-
iamM("iam_depsNotInitialized", {
|
|
6628
|
+
iamM("iam_depsNotInitialized", {
|
|
6629
|
+
params: { dep: "cache" }
|
|
6630
|
+
})
|
|
5808
6631
|
);
|
|
5809
6632
|
}
|
|
5810
6633
|
if (!crypto$1.isInitialized) {
|
|
@@ -5822,13 +6645,19 @@ var iam = {
|
|
|
5822
6645
|
logger14.error("IAM config validation failed", { error: parseResult.error.message });
|
|
5823
6646
|
return err(
|
|
5824
6647
|
HaiIamError.CONFIG_ERROR,
|
|
5825
|
-
iamM("iam_configError", {
|
|
6648
|
+
iamM("iam_configError", {
|
|
6649
|
+
params: { error: parseResult.error.message }
|
|
6650
|
+
}),
|
|
5826
6651
|
parseResult.error
|
|
5827
6652
|
);
|
|
5828
6653
|
}
|
|
5829
6654
|
const parsed = parseResult.data;
|
|
5830
6655
|
const { onPasswordResetRequest, onOtpSendEmail, onOtpSendSms } = await resolveIamNotifications(config);
|
|
5831
|
-
logger14.info("IAM notification channels resolved", {
|
|
6656
|
+
logger14.info("IAM notification channels resolved", {
|
|
6657
|
+
email: Boolean(onOtpSendEmail),
|
|
6658
|
+
sms: Boolean(onOtpSendSms),
|
|
6659
|
+
passwordReset: Boolean(onPasswordResetRequest)
|
|
6660
|
+
});
|
|
5832
6661
|
const migration = await migrateIam();
|
|
5833
6662
|
if (!migration.success)
|
|
5834
6663
|
return migration;
|
|
@@ -5844,7 +6673,11 @@ var iam = {
|
|
|
5844
6673
|
if (!passwordState.success)
|
|
5845
6674
|
return passwordState;
|
|
5846
6675
|
}
|
|
5847
|
-
return ok(found.data ? {
|
|
6676
|
+
return ok(found.data ? {
|
|
6677
|
+
version: found.data.credentialVersion ?? "",
|
|
6678
|
+
available: isUserActive(found.data),
|
|
6679
|
+
profile: { username: found.data.username, displayName: found.data.displayName, avatarUrl: found.data.avatarUrl }
|
|
6680
|
+
} : null);
|
|
5848
6681
|
}
|
|
5849
6682
|
});
|
|
5850
6683
|
if (!sessionResult.success) {
|
|
@@ -5889,7 +6722,12 @@ var iam = {
|
|
|
5889
6722
|
currentAuthz = authorization;
|
|
5890
6723
|
currentUser = users;
|
|
5891
6724
|
currentConfig = parsed;
|
|
5892
|
-
currentPublicConfig = buildIamPublicConfig(parsed, {
|
|
6725
|
+
currentPublicConfig = buildIamPublicConfig(parsed, {
|
|
6726
|
+
...config,
|
|
6727
|
+
onPasswordResetRequest,
|
|
6728
|
+
onOtpSendEmail,
|
|
6729
|
+
onOtpSendSms
|
|
6730
|
+
});
|
|
5893
6731
|
currentApiKey = authnResult.data.apiKeyFunctions ? observeApiKeys(authnResult.data.apiKeyFunctions) : null;
|
|
5894
6732
|
currentTicket = createTicketOperations();
|
|
5895
6733
|
const authn = authnResult.data.authn;
|
|
@@ -5944,7 +6782,18 @@ var iam = {
|
|
|
5944
6782
|
const configured = await authz.getSensitiveOperations();
|
|
5945
6783
|
if (!configured.success)
|
|
5946
6784
|
return configured;
|
|
5947
|
-
return ok({
|
|
6785
|
+
return ok({
|
|
6786
|
+
...snapshot,
|
|
6787
|
+
reauthentication: {
|
|
6788
|
+
...snapshot.reauthentication,
|
|
6789
|
+
operations: [
|
|
6790
|
+
.../* @__PURE__ */ new Set([
|
|
6791
|
+
...snapshot.reauthentication.operations,
|
|
6792
|
+
...configured.data
|
|
6793
|
+
])
|
|
6794
|
+
].sort()
|
|
6795
|
+
}
|
|
6796
|
+
});
|
|
5948
6797
|
},
|
|
5949
6798
|
get user() {
|
|
5950
6799
|
return currentUser ?? notInitializedUser;
|