@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/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-VR7JW2EW.js';
2
- export { AccountConfigSchema, AgreementConfigSchema, ApiKeyConfigSchema, AuthStrategyTypeSchema, AuthUiConfigSchema, CaptchaConfigSchema, HaiIamError, IamConfigSchema, LdapConfigSchema, LoginAccessConfigSchema, LoginConfigSchema, OtpConfigSchema, PasswordConfigSchema, PasswordResetConfigSchema, RbacConfigSchema, RegisterConfigSchema, SecurityConfigSchema, SessionConfigSchema } from './chunk-VR7JW2EW.js';
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 { migrateSchema, reldb, schemaColumns, BaseReldbCrudRepository, HaiReldbError } from '@h-ai/reldb';
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, { epoch: randomUUID(), writers: 0 }, { nx: true });
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, { epoch: randomUUID(), writers: Math.max(0, current.data.writers + delta) });
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", { params: { message: result.error.message } }),
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({ where: "key_prefix = ?", params: [prefix2] }, tx);
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({ where: "user_id = ?", params: [userId] }, tx);
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", { params: { message: result.error.message } }),
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", { params: { message: result.error.message } }),
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", { params: { message: error.message } }),
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 = { ...nextSession.data, ...patch.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
- { fieldName: "mustChangePassword", columnName: "must_change_password", def: { type: "BOOLEAN", notNull: true, defaultValue: false }, select: true, create: true, update: false },
1144
- { fieldName: "accountType", columnName: "account_type", def: { type: "TEXT", notNull: true, defaultValue: "permanent" }, select: true, create: true, update: true },
1145
- { fieldName: "accountStatus", columnName: "account_status", def: { type: "TEXT", notNull: true, defaultValue: "active" }, select: true, create: true, update: true },
1146
- { fieldName: "accountExpiresAt", columnName: "account_expires_at", def: { type: "TIMESTAMP" }, select: true, create: true, update: true },
1147
- { fieldName: "lastLoginAt", columnName: "last_login_at", def: { type: "TIMESTAMP" }, select: true, create: false, update: false },
1148
- { fieldName: "credentialVersion", columnName: "credential_version", def: { type: "TEXT", notNull: true, defaultValue: "" }, select: true, create: false, update: false },
1149
- { fieldName: "resetVersion", columnName: "reset_version", def: { type: "TEXT", notNull: true, defaultValue: "" }, select: true, create: false, update: false },
1150
- { fieldName: "resetHash", columnName: "reset_hash", def: { type: "TEXT" }, select: true, create: false, update: false },
1151
- { fieldName: "resetExpiresAt", columnName: "reset_expires_at", def: { type: "BIGINT" }, select: true, create: false, update: false },
1152
- { fieldName: "resetAttempts", columnName: "reset_attempts", def: { type: "INTEGER", notNull: true, defaultValue: 0 }, select: true, create: false, update: false },
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 = ?`, [reldb.config?.type === "sqlite" ? Date.now() : /* @__PURE__ */ new Date(), userId]);
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
- ["dormant", crypto.randomUUID(), crypto.randomUUID(), "active", reldb.config?.type === "sqlite" ? cutoff.getTime() : cutoff]
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
- [input.passwordHash, reldb.config?.type === "sqlite" ? Date.now() : /* @__PURE__ */ new Date(), crypto.randomUUID(), crypto.randomUUID(), reldb.config?.type === "postgresql" ? input.mustChangePassword === true : Number(input.mustChangePassword === true), input.userId, input.expectedVersion]
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
- [input.tokenHash, expiry, crypto.randomUUID(), input.user.id, input.user.credentialVersion ?? "", input.user.resetVersion ?? "", "active", expiry]
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
- [version, user.id, tokenHash, user.resetVersion ?? "", user.credentialVersion ?? "", maxAttempts, "active"]
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
- [input.passwordHash, reldb.config?.type === "sqlite" ? Date.now() : /* @__PURE__ */ new Date(), crypto.randomUUID(), crypto.randomUUID(), input.user.id, input.tokenHash, input.user.resetVersion ?? "", input.user.credentialVersion ?? "", "active"]
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
- [policy.maxLoginAttempts, lockedUntil, now, userId]
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", { params: { message: error.message } }),
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 { ...access, roles: scopes.includes("*") ? access.roles : [], permissions: [...permissions] };
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, { lastUsedAt: /* @__PURE__ */ new Date() }).catch(() => {
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({ ...verified.data, expiresAt: verified.data.expiresAt?.getTime() ?? null, createdAt: verified.data.createdAt.getTime(), lastUsedAt: verified.data.lastUsedAt?.getTime() ?? null }) : verified;
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({ ...result.data, expiresAt: result.data.expiresAt === null ? null : new Date(result.data.expiresAt), createdAt: new Date(result.data.createdAt), lastUsedAt: result.data.lastUsedAt === null ? null : new Date(result.data.lastUsedAt) });
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({ user: { ...toUser(userResult.data), credentialVersion: userResult.data.credentialVersion ?? "" }, apiKey });
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", { params: { max: apikeyConfig.maxKeysPerUser } }));
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({ apiKey: toApiKey(storedApiKey), rawKey });
1841
+ return ok({
1842
+ apiKey: toApiKey(storedApiKey),
1843
+ rawKey
1844
+ });
1673
1845
  } catch (error) {
1674
- return err(HaiIamError.INTERNAL_ERROR, iamM("iam_apikeyCreateFailed", { params: { message: String(error) } }), error);
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({ ...result.data, items: result.data.items.map(toApiKey) });
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(["login", "register", "sendOtp", "passwordReset", "renewPassword"]);
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({ counter: z.number().int().min(0).max(2e3), derivedKey: z.string().regex(/^[a-f0-9]{64}$/i), time: z.number().finite().nonnegative().optional() })
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 = { operation: operation.data, challenge, signatureSecret, keySecret };
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({ challenge: record.challenge, solution: payload.solution, deriveKey: pbkdf2.deriveKey, hmacSignatureSecret: record.signatureSecret, hmacKeySignatureSecret: record.keySecret });
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 = { maxLoginAttempts, lockoutDuration, lockoutEnabled: config.lockoutEnabled ?? true, failureWindow: config.failureWindow ?? 86400 };
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", { params: { message: createResult.error.message } }),
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({ ...toUser(storedUser), credentialVersion: storedUser.credentialVersion ?? "" });
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", { params: { seconds: Math.ceil((current.data.cooldownUntil - Date.now()) / 1e3) } }));
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({ idempotencyKey: `iam-otp:${state.version}`, expiresAt: new Date(state.expiresAt) });
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 = { ...state, attempts, code: correct || attempts >= maxAttempts ? null : state.code };
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 = { maxLoginAttempts, lockoutDuration, lockoutEnabled: config.lockoutEnabled ?? true, failureWindow: config.failureWindow ?? 86400 };
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", { params: { message: createResult.error.message } }),
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({ ...toUser(storedUser), credentialVersion: storedUser.credentialVersion ?? "" });
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({ identifier, code, expiresIn: otpConfig.expiresIn, resendInterval: otpConfig.resendInterval });
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 = { maxLoginAttempts, lockoutDuration, lockoutEnabled: config.lockoutEnabled ?? true, failureWindow: config.failureWindow ?? 86400 };
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", { params: { minLength: passwordConfig.minLength } })
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", { params: { maxLength: passwordConfig.maxLength } })
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 = [PASSWORD_UPPERCASE_REGEX, PASSWORD_LOWERCASE_REGEX, PASSWORD_DIGIT_REGEX, PASSWORD_SPECIAL_CHAR_REGEX].filter((pattern) => pattern.test(password)).length;
2293
- if (classes < passwordConfig.minCharacterClasses)
2294
- return err(HaiIamError.PASSWORD_POLICY_VIOLATION, iamM("iam_passwordCharacterClasses", { params: { count: passwordConfig.minCharacterClasses } }));
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({ ...toUser(storedUser), credentialVersion: storedUser.credentialVersion ?? "" });
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({ authn: operations.authn, loginRegisteredUser: operations.loginRegisteredUser, passwordStrategyResult: passwordResult, apiKeyFunctions });
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", { params: { type } })
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 = { ...session.data, ...authorization.data };
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 { loginRegisteredUser: async (user, context) => {
2620
- const stored = await userRepository.findById(user.id);
2621
- if (!stored.success)
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.INVALID_CREDENTIALS, iamM("iam_passwordWrong"));
2642
- const verified = await passwordStrategy.authenticate({ type: "password", identifier: stored.data.username, password });
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
- async checkSensitiveOperation(accessToken, operation, ticket) {
2650
- const current = await verifyCurrentSession(accessToken);
2651
- if (!current.success)
2652
- return current;
2653
- const required = await requiresProof(operation);
2654
- if (!required.success)
2655
- return required;
2656
- if (!required.data)
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
- if (!ticket)
2659
- return err(HaiIamError.REAUTHENTICATION_REQUIRED, iamM("iam_reauthenticationRequired"));
2660
- const proof = await tickets.consume(ticket, { purpose: `iam:reauth:${operation}` });
2661
- if (!proof.success)
2662
- return proof;
2663
- 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)
2664
- return err(HaiIamError.REAUTHENTICATION_REQUIRED, iamM("iam_reauthenticationRequired"));
2665
- return ok(void 0);
2666
- },
2667
- async login(credentials, context) {
2668
- return loginWithStrategy("password", credentials, passwordStrategy, context);
2669
- },
2670
- async loginWithOtp(credentials, context) {
2671
- return loginWithStrategy("otp", credentials, otpStrategy, context);
2672
- },
2673
- async loginWithLdap(credentials, context) {
2674
- return loginWithStrategy("ldap", credentials, ldapStrategy, context);
2675
- },
2676
- async loginWithApiKey(credentials, context) {
2677
- const disabled = loginDisabled("apikey");
2678
- if (disabled)
2679
- return disabled;
2680
- if (!apiKeyStrategy)
2681
- return err(HaiIamError.STRATEGY_NOT_SUPPORTED, iamM("iam_featureNotImplemented"));
2682
- const authenticated = await apiKeyStrategy.authenticate({ type: "apikey", ...credentials });
2683
- if (!authenticated.success)
2684
- return authenticated;
2685
- return buildAuthResult(authenticated.data.user, authenticated.data.apiKey, context);
2686
- },
2687
- async logout(accessToken) {
2688
- return sessionFunctions.delete(accessToken);
2689
- },
2690
- verifyToken: verifyCurrentSession,
2691
- async sendOtp(identifier, options) {
2692
- const captcha = await verifyIamCaptcha(config.captcha, "sendOtp", options?.captcha);
2693
- if (!captcha.success)
2694
- return captcha;
2695
- const disabled = loginDisabled("otp");
2696
- if (disabled) {
2697
- return disabled;
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
- { fieldName: "sensitiveOperations", columnName: "sensitive_operations", def: { type: "JSON" }, select: true, create: true, update: true },
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 ? [] : [reldb.config?.type === "postgresql" ? JSON.stringify([operation]) : operation];
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", { params: { message: error.message } }),
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", { params: { message: result.error.message } }),
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", { columns: ["role_id", "permission_id"], unique: true }),
2870
- reldb.ddl.createIndex(ROLE_PERMISSION_TABLE, "idx_role_perm_role", { columns: ["role_id"] })
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", { params: { message: indexResult.error.message } }),
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", { params: { message: result.error.message } }),
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", { params: { message: result.error.message } }),
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", { params: { message: result.error.message } }),
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", { params: { message: result.error.message } }),
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", { params: { message: result.error.message } }),
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", { params: { message: result.error.message } }),
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", { params: { message: result.error.message } }),
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", { params: { message: result.error.message } }),
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", { params: { message: relationsResult.error.message } }),
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 = [...new Set(relationsResult.data.map((r) => r.permission_id))];
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", { params: { message: permsResult.error.message } }),
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", { params: { message: result.error.message } }),
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", { columns: ["user_id", "role_id"], unique: true }),
3088
- reldb.ddl.createIndex(USER_ROLE_TABLE, "idx_user_role_user", { columns: ["user_id"] })
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", { params: { message: indexResult.error.message } }),
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", { params: { message: result.error.message } }),
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", { params: { message: result.error.message } }),
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", { params: { message: result.error.message } }),
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", { params: { message: roleResult.error.message } }),
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", { params: { message: usersResult.error.message } }),
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", { params: { message: deleteResult.error.message } }),
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", { params: { message: result.error.message } }),
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 = [...new Set(roleIds)];
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", { params: { message: result.error.message } }), result.error);
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", { params: { message: relationsResult.error.message } }),
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 = [...new Set(relationsResult.data.map((r) => r.role_id))];
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", { params: { message: rolesResult.error.message } }),
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 = ?`, [(/* @__PURE__ */ new Date()).toISOString(), roleId]);
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", { params: { message: error.message } }),
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, { params: { message } })
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 = [...new Set(permissionIds)];
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({ roles: [...new Set(rows.data.map((row) => row.role_code))], permissions: [...new Set(rows.data.flatMap((row) => row.permission_code ? [row.permission_code] : []))] }) : rows;
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, { sensitiveOperations: [...new Set(operations.map((value) => value.trim()))] });
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", { params: { message: String(error) } }),
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", { params: { message: String(error) } }),
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", { params: { message: String(error) } }),
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", { params: { message: String(error) } }),
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 = { ...permission, sensitiveOperations: [...new Set(operations.map((value) => value.trim()))] };
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", { params: { message: String(error) } }),
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", { params: { message: String(error) } }),
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: { ...fields.details, ...!result.success ? { reasonCode: result.error.code } : {} }
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 ? { id: result.data.user.id, username: result.data.user.username, displayName: result.data.user.displayName, roles: result.data.roles } : null,
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), { action: "auth.reauthenticate", resource: "iam.auth", details: { operation } }),
4178
- checkSensitiveOperation: (token, operation, ticket) => observe(() => operations.checkSensitiveOperation(token, operation, ticket), (result) => !result.success ? { action: "auth.sensitive-denied", resource: "iam.auth", details: { operation } } : void 0)
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) => ({ action: "user.register", resource: "iam.users", resourceId: result.success ? result.data.user.id : void 0 })),
4185
- createUser: (options) => observe(() => operations.createUser(options), (result) => ({ action: "user.create", resource: "iam.users", resourceId: result.success ? result.data.user.id : void 0 })),
4186
- inviteUser: (options) => observe(() => operations.inviteUser(options), (result) => ({ action: "user.invite", resource: "iam.users", resourceId: result.success ? result.data.user.id : void 0 })),
4187
- updateUser: (id, data) => observe(() => operations.updateUser(id, data), { action: "user.update", resource: "iam.users", resourceId: id, details: { fields: Object.keys(data), ...data.accountStatus ? { accountStatus: data.accountStatus } : {}, ...data.accountType ? { accountType: data.accountType } : {} } }),
4188
- updateCurrentUser: (token, data) => observe(() => operations.updateCurrentUser(token, data), (result) => ({ action: "user.profile", resource: "iam.users", resourceId: result.success ? result.data.id : void 0, details: { fields: Object.keys(data) } })),
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 : { action: "account.inactivity-scan", resource: "iam.users", details: result.success ? { count: result.data } : {} })
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), { action: "role.assign", resource: "iam.user-roles", resourceId: id, details: { roleId } }, tx),
4203
- removeRole: (id, roleId, tx) => observe(() => operations.removeRole(id, roleId, tx), { action: "role.remove", resource: "iam.user-roles", resourceId: id, details: { roleId } }, tx),
4204
- syncRoles: (id, roleIds, tx) => observe(() => operations.syncRoles(id, roleIds, tx), { action: "role.sync", resource: "iam.user-roles", resourceId: id, details: { roleIds } }, tx),
4205
- createRole: (input, tx) => observe(() => operations.createRole(input, tx), (result) => ({ action: "role.create", resource: "iam.roles", resourceId: result.success ? result.data.id : void 0, details: { code: input.code } }), tx),
4206
- updateRole: (id, data, tx) => observe(() => operations.updateRole(id, data, tx), { action: "role.update", resource: "iam.roles", resourceId: id, details: { fields: Object.keys(data), permissionIds: data.permissionIds } }, tx),
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) => ({ action: "permission.create", resource: "iam.permissions", resourceId: result.success ? result.data.id : void 0, details: { code: input.code } }), tx),
4209
- setPermissionSensitiveOperations: (id, operationIds) => observe(() => operations.setPermissionSensitiveOperations(id, operationIds), { action: "permission.sensitive-operations", resource: "iam.permissions", resourceId: id, details: { operations: operationIds } }),
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), { action: "permission.assign", resource: "iam.role-permissions", resourceId: id, details: { permissionId } }, tx),
4212
- removePermissionFromRole: (id, permissionId, tx) => observe(() => operations.removePermissionFromRole(id, permissionId, tx), { action: "permission.remove", resource: "iam.role-permissions", resourceId: id, details: { permissionId } }, tx)
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) => ({ action: "session.create", resource: "iam.sessions", resourceId: result.success ? result.data.version : options.userId })),
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) => ({ action: "session.revoke-user", resource: "iam.sessions", resourceId: id, details: result.success ? { count: result.data } : {} })),
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) => ({ action: "apikey.create", resource: "iam.apikeys", resourceId: result.success ? result.data.apiKey.id : void 0, details: { subjectId: userId, scopes: options.scopes } })),
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 = { credential_version: "VARCHAR(255) NOT NULL DEFAULT ''", reset_version: "VARCHAR(255) NOT NULL DEFAULT ''", reset_hash: "TEXT", reset_expires_at: "BIGINT", reset_attempts: "INTEGER NOT NULL DEFAULT 0" };
4233
- var accountColumns = { account_type: "VARCHAR(16) NOT NULL DEFAULT 'permanent'", account_status: "VARCHAR(16) NOT NULL DEFAULT 'active'", account_expires_at: "TIMESTAMP", last_login_at: "TIMESTAMP" };
4234
- var steps = [{
4235
- version: 1,
4236
- name: "credential-and-reset-versions",
4237
- async needed(sql) {
4238
- const found = await schemaColumns(reldb, "hai_iam_users", sql);
4239
- return found.success ? ok(found.data.length > 0 && Object.keys(columns).some((name) => !found.data.includes(name))) : found;
4240
- },
4241
- async apply(sql) {
4242
- const found = await schemaColumns(reldb, "hai_iam_users", sql);
4243
- if (!found.success)
4244
- return found;
4245
- for (const [name, definition] of Object.entries(columns)) {
4246
- if (found.data.includes(name))
4247
- continue;
4248
- const added = await sql.execute(`ALTER TABLE hai_iam_users ADD COLUMN ${name} ${definition}`);
4249
- if (!added.success)
4250
- return added;
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
- async apply(sql) {
4262
- const found = await schemaColumns(reldb, "hai_iam_users", sql);
4263
- if (!found.success)
4264
- return found;
4265
- for (const [name, definition] of Object.entries(accountColumns)) {
4266
- if (found.data.includes(name))
4267
- continue;
4268
- const added = await sql.execute(`ALTER TABLE hai_iam_users ADD COLUMN ${name} ${definition}`);
4269
- if (!added.success)
4270
- return added;
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
- async apply(sql) {
4282
- const added = await sql.execute("ALTER TABLE hai_iam_users ADD COLUMN must_change_password BOOLEAN NOT NULL DEFAULT FALSE");
4283
- return added.success ? ok(void 0) : added;
4284
- }
4285
- }, {
4286
- version: 4,
4287
- name: "login-failure-window",
4288
- async needed(sql) {
4289
- const found = await schemaColumns(reldb, "hai_iam_users", sql);
4290
- return found.success ? ok(found.data.length > 0 && !found.data.includes("first_login_failed_at")) : found;
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
- async apply(sql) {
4293
- const added = await sql.execute("ALTER TABLE hai_iam_users ADD COLUMN first_login_failed_at TIMESTAMP");
4294
- return added.success ? ok(void 0) : added;
4295
- }
4296
- }, {
4297
- version: 5,
4298
- name: "permission-sensitive-operations",
4299
- async needed(sql) {
4300
- const found = await schemaColumns(reldb, "hai_iam_permissions", sql);
4301
- return found.success ? ok(found.data.length > 0 && !found.data.includes("sensitive_operations")) : found;
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
- async apply(sql) {
4304
- const added = await sql.execute("ALTER TABLE hai_iam_permissions ADD COLUMN sensitive_operations JSON");
4305
- return added.success ? ok(void 0) : added;
4306
- }
4307
- }, {
4308
- version: 6,
4309
- name: "account-lifecycle-only",
4310
- async needed(sql) {
4311
- const found = await schemaColumns(reldb, "hai_iam_users", sql);
4312
- return found.success ? ok(found.data.includes("enabled")) : found;
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
- async apply(sql) {
4315
- 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 = ?", ["dormant", globalThis.crypto.randomUUID(), globalThis.crypto.randomUUID(), reldb.config?.type === "postgresql" ? false : 0, "active"]);
4316
- if (!converted.success)
4317
- return converted;
4318
- const removed = await sql.execute("ALTER TABLE hai_iam_users DROP COLUMN enabled");
4319
- return removed.success ? ok(void 0) : removed;
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(reldb, "iam", steps, options);
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({ provider, to, template, vars, idempotencyKey: context.idempotencyKey, sensitive: true, expiresAt: context.expiresAt.getTime() });
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 = { token, expiresAt: context.expiresAt.toISOString(), resetUrl: url?.toString() ?? token };
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: { password: config.login.password, otp: email || sms, ldap: config.login.ldap && !!config.ldap, email, sms },
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: { enabled: config.reauthentication?.enabled ?? false, operations: [...config.reauthentication?.operations ?? []] },
4382
- initialCredentials: { enabled: Boolean(config.initialCredentials?.enabled && config.passwordReset?.enabled !== false && runtime.onPasswordResetRequest) }
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 ? { ...found.data, accountExpiresAt: found.data.accountExpiresAt === null ? null : new Date(found.data.accountExpiresAt), passwordUpdatedAt: found.data.passwordUpdatedAt === null ? void 0 : new Date(found.data.passwordUpdatedAt), passwordHash: found.data.hasPassword ? "present" : void 0 } : null) : found;
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
- { code: "admin", name: () => iamM("iam_seedRoleAdminName"), description: () => iamM("iam_seedRoleAdminDesc"), isSystem: true },
4409
- { code: "user", name: () => iamM("iam_seedRoleUserName"), description: () => iamM("iam_seedRoleUserDesc"), isSystem: true },
4410
- { code: "guest", name: () => iamM("iam_seedRoleGuestName"), description: () => iamM("iam_seedRoleGuestDesc"), isSystem: true }
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
- { code: "dashboard:view", name: () => iamM("iam_seedPermDashboardView"), type: "menu", resource: "dashboard", action: "view" },
4415
- { code: "user:read", name: () => iamM("iam_seedPermUserRead"), type: "menu", resource: "user", action: "read" },
4416
- { code: "role:read", name: () => iamM("iam_seedPermRoleRead"), type: "menu", resource: "role", action: "read" },
4417
- { code: "permission:read", name: () => iamM("iam_seedPermPermRead"), type: "menu", resource: "permission", action: "read" },
4418
- { code: "system:logs", name: () => iamM("iam_seedPermSystemLogs"), type: "menu", resource: "system", action: "logs" },
4419
- { code: "system:settings", name: () => iamM("iam_seedPermSystemSettings"), type: "menu", resource: "system", action: "settings" },
4420
- { code: "system:modules", name: () => iamM("iam_seedPermSystemModules"), type: "menu", resource: "system", action: "modules" },
4421
- { code: "profile:read", name: () => iamM("iam_seedPermProfileRead"), type: "menu", resource: "profile", action: "read" },
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
- { code: "user:list", name: () => iamM("iam_seedPermUserList"), type: "api", resource: "user", action: "list" },
4424
- { code: "user:api:create", name: () => iamM("iam_seedPermUserApiCreate"), type: "api", resource: "user", action: "api:create" },
4425
- { code: "user:api:update", name: () => iamM("iam_seedPermUserApiUpdate"), type: "api", resource: "user", action: "api:update" },
4426
- { code: "user:api:delete", name: () => iamM("iam_seedPermUserApiDelete"), type: "api", resource: "user", action: "api:delete" },
4427
- { code: "role:list", name: () => iamM("iam_seedPermRoleList"), type: "api", resource: "role", action: "list" },
4428
- { code: "role:api:create", name: () => iamM("iam_seedPermRoleApiCreate"), type: "api", resource: "role", action: "api:create" },
4429
- { code: "role:api:update", name: () => iamM("iam_seedPermRoleApiUpdate"), type: "api", resource: "role", action: "api:update" },
4430
- { code: "role:api:delete", name: () => iamM("iam_seedPermRoleApiDelete"), type: "api", resource: "role", action: "api:delete" },
4431
- { code: "permission:list", name: () => iamM("iam_seedPermPermList"), type: "api", resource: "permission", action: "list" },
4432
- { code: "permission:manage", name: () => iamM("iam_seedPermPermManage"), type: "api", resource: "permission", action: "manage" },
4433
- { code: "permission:api:create", name: () => iamM("iam_seedPermPermApiCreate"), type: "api", resource: "permission", action: "api:create" },
4434
- { code: "permission:api:delete", name: () => iamM("iam_seedPermPermApiDelete"), type: "api", resource: "permission", action: "api:delete" },
4435
- { code: "audit:read", name: () => iamM("iam_seedPermAuditRead"), type: "api", resource: "audit", action: "read" },
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
- { code: "user:create", name: () => iamM("iam_seedPermUserCreate"), type: "button", resource: "user", action: "create" },
4438
- { code: "user:update", name: () => iamM("iam_seedPermUserUpdate"), type: "button", resource: "user", action: "update" },
4439
- { code: "user:delete", name: () => iamM("iam_seedPermUserDelete"), type: "button", resource: "user", action: "delete" },
4440
- { code: "role:create", name: () => iamM("iam_seedPermRoleCreate"), type: "button", resource: "role", action: "create" },
4441
- { code: "role:update", name: () => iamM("iam_seedPermRoleUpdate"), type: "button", resource: "role", action: "update" },
4442
- { code: "role:delete", name: () => iamM("iam_seedPermRoleDelete"), type: "button", resource: "role", action: "delete" },
4443
- { code: "permission:create", name: () => iamM("iam_seedPermPermCreate"), type: "button", resource: "permission", action: "create" },
4444
- { code: "permission:delete", name: () => iamM("iam_seedPermPermDelete"), type: "button", resource: "permission", action: "delete" }
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({ code: role.code, name: role.name(), description: role.description(), isSystem: role.isSystem });
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({ code: perm.code, name: perm.name(), type: perm.type, resource: perm.resource, action: perm.action });
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 = { ...session, createdAt: new Date(session.createdAt), startedAt: new Date(session.startedAt ?? session.createdAt), lastActiveAt: new Date(session.lastActiveAt), expiresAt: new Date(session.expiresAt) };
4538
- 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") || ![result.createdAt, result.startedAt, result.lastActiveAt, result.expiresAt].every((date) => Number.isFinite(date.getTime()))) {
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 = { ...updated, version: crypto.randomUUID() };
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 = { ...applySessionPatch(restoreSession(entry.session), patch), version: crypto.randomUUID() };
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 { ...session, data: hasExtraKeys ? rest : void 0 };
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 = { accessToken, refreshToken: generateSessionToken(options.userId), expiresIn: ttl, tokenType: "Bearer" };
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 ? { userId: row.data.userId, enabled: row.data.enabled, expiresAt: row.data.expiresAt?.getTime() ?? null, scopes: row.data.scopes } : null) : row;
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({ ...session, ...bound.data.profile });
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, { params: { message } })
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 = [...new Set(userIds)];
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) ? [found.get(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 = ?", [globalThis.crypto.randomUUID(), globalThis.crypto.randomUUID(), userId, currentAccount.data.credentialVersion ?? ""]);
5430
- if (!advanced.success || advanced.data.changes !== 1)
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", { params: { message: String(error) } }),
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({ userId, expectedVersion: userResult.data.credentialVersion ?? "", passwordHash: hashResult.data, mustChangePassword: ctx.config.password?.requireInitialChange === true });
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({ length: Math.ceil(length / 32) }, () => generateToken()).join("")}`.slice(0, length);
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", { params: { dep: "reldb" } })
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", { params: { dep: "cache" } })
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", { params: { error: parseResult.error.message } }),
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", { email: Boolean(onOtpSendEmail), sms: Boolean(onOtpSendSms), passwordReset: Boolean(onPasswordResetRequest) });
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 ? { version: found.data.credentialVersion ?? "", available: isUserActive(found.data), profile: { username: found.data.username, displayName: found.data.displayName, avatarUrl: found.data.avatarUrl } } : null);
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, { ...config, onPasswordResetRequest, onOtpSendEmail, onOtpSendSms });
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({ ...snapshot, reauthentication: { ...snapshot.reauthentication, operations: [.../* @__PURE__ */ new Set([...snapshot.reauthentication.operations, ...configured.data])].sort() } });
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;