@h-ai/iam 1.0.0-beta.21 → 1.0.0-beta.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +1 -1
- package/README.ja.md +1 -1
- package/README.md +1 -1
- package/dist/index.d.ts +3 -15
- package/dist/index.js +17 -127
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
package/README.en.md
CHANGED
|
@@ -1090,7 +1090,7 @@ When these deadlines are disabled, access and refresh retain their independent l
|
|
|
1090
1090
|
|
|
1091
1091
|
### Schema and credential notifications
|
|
1092
1092
|
|
|
1093
|
-
|
|
1093
|
+
Initialize RelDB before `iam.init()`. IAM creates and validates the required tables and fails initialization if their structure is incompatible. Sessions must match the current credential generation. A mismatch requires signing in again. Default Reach notifications use `sensitive`, `expiresAt` and `idempotencyKey`, without persisting credentials or scheduling delayed delivery. Custom callbacks must enforce the same expiry and credential protection.
|
|
1094
1094
|
|
|
1095
1095
|
When audit is initialized and capture.enabled is on, IAM records important account, authentication, password, authorization, API Key and session events, including failed/denied outcomes and safe reason codes. Initialize audit before IAM and close in reverse order. Passwords, one-time proofs and tokens are never copied into events. Only successful login establishes an actor; trusted service/CLI callers supply actor/correlation through audit.withContext. Events inside an external authorization transaction commit or roll back with that transaction; record failed attempts after rollback at the caller boundary. Audit failures emit diagnostics without converting an applied business result into an unsafe retry.
|
|
1096
1096
|
|
package/README.ja.md
CHANGED
|
@@ -1082,7 +1082,7 @@ RBAC 権限は `sensitiveOperations: string[]` を持ち、可空 JSON 列 `sens
|
|
|
1082
1082
|
|
|
1083
1083
|
### スキーマと認証情報の通知
|
|
1084
1084
|
|
|
1085
|
-
RelDB
|
|
1085
|
+
RelDB の初期化後に `iam.init()` を呼び出します。IAM は必要なテーブルを作成・検証し、構造が一致しない場合は初期化に失敗します。セッションの資格世代が現在の世代と一致しない場合は再ログインが必要です。既定の Reach 通知は `sensitive`、`expiresAt`、`idempotencyKey` を使用し、資格情報本文を保存したり遅延配信したりしません。独自コールバックも同じ期限と資格情報保護を守ってください。
|
|
1086
1086
|
|
|
1087
1087
|
audit が初期化済みで capture.enabled が有効なら、IAM はアカウント・認証・パスワード・権限・API Key・セッションの重要イベントと失敗/拒否の安全な理由コードを記録します。audit を IAM より先に初期化し、逆順に終了してください。パスワード・単回証明・トークンは記録しません。ログイン成功後に主体を確定し、信頼できるサービス/CLI は audit.withContext で操作者と関連文脈を設定します。外部権限トランザクション内のイベントは一緒にコミット/ロールバックされ、失敗試行はロールバック後に呼出境界で記録します。監査失敗は診断を出し、反映済み業務を危険な再試行に変えません。
|
|
1088
1088
|
|
package/README.md
CHANGED
|
@@ -1095,7 +1095,7 @@ RBAC 权限支持 `sensitiveOperations: string[]`(持久字段 `sensitive_oper
|
|
|
1095
1095
|
|
|
1096
1096
|
### 持久结构与凭据通知
|
|
1097
1097
|
|
|
1098
|
-
|
|
1098
|
+
先初始化 RelDB,再调用 `iam.init()`。IAM 会创建并校验所需表,结构不符时返回初始化失败。会话必须匹配当前凭据代次,不匹配时需重新登录。默认 Reach 通知使用 `sensitive`、`expiresAt` 和 `idempotencyKey`,不保存凭据正文或延迟重投。自定义回调也须遵守有效期与凭据保护要求。
|
|
1099
1099
|
|
|
1100
1100
|
审计模块已初始化且 capture.enabled 开启时,IAM 自动记录关键账号、鉴别、密码、授权、API Key 和会话事件,包含失败/拒绝与安全原因码。先初始化 audit 再初始化 IAM,关闭时反序。不会记录密码、一次性票据或令牌;成功登录才确认主体,可信服务/CLI 调用应以 audit.withContext 提供操作者和关联信息。外部授权事务的事件随该事务提交或回滚,回滚失败尝试由调用入口在事务结束后记录。审计写失败输出诊断,不把已生效业务改成可盲目重试的失败。
|
|
1101
1101
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { IamFunctions } from './browser.js';
|
|
2
2
|
export { AccountConfigSchema, AgreementConfig, AgreementConfigSchema, AgreementDisplay, ApiKey, ApiKeyConfig, ApiKeyConfigSchema, ApiKeyCredentials, ApiKeyOperations, ApiKeySortDirection, ApiKeySortField, AuthRequestContext, AuthResult, AuthStrategy, AuthStrategyType, AuthStrategyTypeSchema, AuthUiConfig, AuthUiConfigSchema, AuthnOperations, AuthzOperations, CaptchaConfigSchema, CaptchaOperation, ConsumeTicketOptions, ConsumedTicket, CreateApiKeyOptions, CreateApiKeyResult, CreateSessionOptions, Credentials, HaiIamError, IamCaptchaChallenge, IamConfig, IamConfigInput, IamConfigSchema, IamConfigSettingsInput, IamNotificationContext, IamPublicConfig, IssueTicketOptions, IssuedTicket, LdapClientFactory, LdapConfig, LdapConfigSchema, LdapCredentials, ListApiKeysOptions, ListUsersOptions, LoginAccessConfigSchema, LoginConfig, LoginConfigSchema, OtpConfig, OtpConfigSchema, OtpCredentials, PasswordConfig, PasswordConfigSchema, PasswordCredentials, PasswordResetConfig, PasswordResetConfigSchema, Permission, PermissionQueryOptions, PermissionType, RbacConfig, RbacConfigSchema, RegisterConfig, RegisterConfigSchema, RegisterOptions, RegisterResult, Role, SecurityConfig, SecurityConfigSchema, Session, SessionConfig, SessionConfigSchema, SessionData, SessionFieldUpdates, SessionOperations, StoredUser, TicketGrant, TicketOperations, TokenPair, UpdateCurrentUserInput, User, UserOperations, UserSortDirection, UserSortField } from './browser.js';
|
|
3
|
-
import * as _h_ai_core from '@h-ai/core';
|
|
4
|
-
import * as _h_ai_reldb from '@h-ai/reldb';
|
|
5
3
|
import 'zod';
|
|
4
|
+
import '@h-ai/core';
|
|
6
5
|
import '@h-ai/reach';
|
|
7
6
|
import 'altcha/lib';
|
|
7
|
+
import '@h-ai/reldb';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* @h-ai/iam — IAM 服务主入口
|
|
@@ -30,16 +30,4 @@ import 'altcha/lib';
|
|
|
30
30
|
*/
|
|
31
31
|
declare const iam: IamFunctions;
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
* 预检或迁移 IAM 表。保留用户与密码;旧停用账号转休眠,删除 enabled,旧版 SDK 不可与新表混用。
|
|
35
|
-
* @example
|
|
36
|
-
* ```ts
|
|
37
|
-
* import { migrateIam } from '@h-ai/iam'
|
|
38
|
-
* const plan = await migrateIam({ dryRun: true }) // RelDB 已初始化;只读。
|
|
39
|
-
* ```
|
|
40
|
-
*/
|
|
41
|
-
declare function migrateIam(options?: {
|
|
42
|
-
dryRun?: boolean;
|
|
43
|
-
}): Promise<_h_ai_core.HaiResult<_h_ai_reldb.SchemaMigrationPlan>>;
|
|
44
|
-
|
|
45
|
-
export { IamFunctions, iam, migrateIam };
|
|
33
|
+
export { IamFunctions, iam };
|
package/dist/index.js
CHANGED
|
@@ -1390,7 +1390,9 @@ async function createDbUserRepository() {
|
|
|
1390
1390
|
if (userRepoInstance && userRepoSqlOps === reldb.sql)
|
|
1391
1391
|
return userRepoInstance;
|
|
1392
1392
|
const repo = new DbUserRepository();
|
|
1393
|
-
await repo.
|
|
1393
|
+
const ready = await repo.ready();
|
|
1394
|
+
if (!ready.success)
|
|
1395
|
+
throw ready.error;
|
|
1394
1396
|
repo.create = guardIamMutation(repo.create.bind(repo), 1);
|
|
1395
1397
|
repo.updateById = guardIamMutation(repo.updateById.bind(repo), 2);
|
|
1396
1398
|
repo.deleteById = guardIamMutation(repo.deleteById.bind(repo), 1);
|
|
@@ -1408,6 +1410,9 @@ var DbUserRepository = class extends BaseReldbCrudRepository {
|
|
|
1408
1410
|
fields: USER_FIELDS
|
|
1409
1411
|
});
|
|
1410
1412
|
}
|
|
1413
|
+
ready() {
|
|
1414
|
+
return this.ensureSchema();
|
|
1415
|
+
}
|
|
1411
1416
|
/** 数据库时钟在执行最终 SQL 时核验有效期,暂停进程不能复用旧的 Date.now()。 */
|
|
1412
1417
|
databaseNow() {
|
|
1413
1418
|
if (reldb.config?.type === "postgresql")
|
|
@@ -3073,7 +3078,9 @@ async function createDbPermissionRepository() {
|
|
|
3073
3078
|
if (permRepoInstance && permRepoSqlOps === reldb.sql)
|
|
3074
3079
|
return permRepoInstance;
|
|
3075
3080
|
const repo = new DbPermissionRepository();
|
|
3076
|
-
await repo.
|
|
3081
|
+
const ready = await repo.ready();
|
|
3082
|
+
if (!ready.success)
|
|
3083
|
+
throw ready.error;
|
|
3077
3084
|
permRepoInstance = repo;
|
|
3078
3085
|
permRepoSqlOps = reldb.sql;
|
|
3079
3086
|
return repo;
|
|
@@ -3113,6 +3120,9 @@ var DbPermissionRepository = class extends BaseReldbCrudRepository {
|
|
|
3113
3120
|
fields: PERMISSION_FIELDS
|
|
3114
3121
|
});
|
|
3115
3122
|
}
|
|
3123
|
+
ready() {
|
|
3124
|
+
return this.ensureSchema();
|
|
3125
|
+
}
|
|
3116
3126
|
/** 根据权限代码查找权限 */
|
|
3117
3127
|
async findByCode(code, tx) {
|
|
3118
3128
|
return this.findOneBy("code = ?", [code], tx);
|
|
@@ -4738,123 +4748,6 @@ function observeApiKeys(operations) {
|
|
|
4738
4748
|
revokeApiKey: (id) => observe(() => operations.revokeApiKey(id), { action: "apikey.revoke", resource: "iam.apikeys", resourceId: id })
|
|
4739
4749
|
};
|
|
4740
4750
|
}
|
|
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);
|
|
4774
|
-
}
|
|
4775
|
-
},
|
|
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);
|
|
4795
|
-
}
|
|
4796
|
-
},
|
|
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
|
-
}
|
|
4808
|
-
},
|
|
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
|
-
}
|
|
4820
|
-
},
|
|
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
|
-
}
|
|
4832
|
-
},
|
|
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
|
-
}
|
|
4853
|
-
}
|
|
4854
|
-
];
|
|
4855
|
-
function migrateIam(options = {}) {
|
|
4856
|
-
return reldb.migrateSchema("iam", steps, options);
|
|
4857
|
-
}
|
|
4858
4751
|
|
|
4859
4752
|
// src/iam-notifications.ts
|
|
4860
4753
|
async function resolveIamNotifications(input) {
|
|
@@ -5990,7 +5883,7 @@ function buildRegistrationOps(ctx) {
|
|
|
5990
5883
|
await userRepository.deleteById(createdUser.id);
|
|
5991
5884
|
return assigned;
|
|
5992
5885
|
}
|
|
5993
|
-
logger13.info("User registered", { userId: createdUser.id
|
|
5886
|
+
logger13.info("User registered", { userId: createdUser.id });
|
|
5994
5887
|
return ok({
|
|
5995
5888
|
user: toUser(createdUser),
|
|
5996
5889
|
agreements: buildAgreementDisplay(agreementConfig, agreementConfig.showOnRegister)
|
|
@@ -6454,17 +6347,17 @@ function buildPasswordResetOps(ctx) {
|
|
|
6454
6347
|
const captcha = await verifyIamCaptcha(ctx.config.captcha, "passwordReset", options?.captcha);
|
|
6455
6348
|
if (!captcha.success)
|
|
6456
6349
|
return captcha;
|
|
6457
|
-
logger13.debug("Password reset requested"
|
|
6350
|
+
logger13.debug("Password reset requested");
|
|
6458
6351
|
const resetConfig = ctx.resetConfig;
|
|
6459
6352
|
if (!resetConfig.enabled || !onPasswordResetRequest)
|
|
6460
6353
|
return err(HaiIamError.PASSWORD_RESET_DISABLED, iamM("iam_passwordResetDisabled"));
|
|
6461
6354
|
const userResult = await userRepository.findByIdentifier(identifier);
|
|
6462
6355
|
if (!userResult.success) {
|
|
6463
|
-
logger13.warn("Failed to look up user for password reset", {
|
|
6356
|
+
logger13.warn("Failed to look up user for password reset", { reasonCode: userResult.error.code });
|
|
6464
6357
|
return ok(void 0);
|
|
6465
6358
|
}
|
|
6466
6359
|
if (!userResult.data) {
|
|
6467
|
-
logger13.debug("User not found for password reset
|
|
6360
|
+
logger13.debug("User not found for password reset");
|
|
6468
6361
|
return ok(void 0);
|
|
6469
6362
|
}
|
|
6470
6363
|
const user = toUser(userResult.data);
|
|
@@ -6658,9 +6551,6 @@ var iam = {
|
|
|
6658
6551
|
sms: Boolean(onOtpSendSms),
|
|
6659
6552
|
passwordReset: Boolean(onPasswordResetRequest)
|
|
6660
6553
|
});
|
|
6661
|
-
const migration = await migrateIam();
|
|
6662
|
-
if (!migration.success)
|
|
6663
|
-
return migration;
|
|
6664
6554
|
const credentialUsers = await createDbUserRepository();
|
|
6665
6555
|
const sessionResult = await createSessionOperations({
|
|
6666
6556
|
config: parsed,
|
|
@@ -6848,6 +6738,6 @@ var iam = {
|
|
|
6848
6738
|
}
|
|
6849
6739
|
};
|
|
6850
6740
|
|
|
6851
|
-
export { iam
|
|
6741
|
+
export { iam };
|
|
6852
6742
|
//# sourceMappingURL=index.js.map
|
|
6853
6743
|
//# sourceMappingURL=index.js.map
|