@objectstack/service-settings 10.2.0 → 11.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -93
- package/dist/index.cjs +118 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +118 -5
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -852,6 +852,7 @@ import { dirname, join } from "path";
|
|
|
852
852
|
var SECRET_KEY_ENV = "OS_SECRET_KEY";
|
|
853
853
|
var DEV_KEY_ENV = "OS_DEV_CRYPTO_KEY";
|
|
854
854
|
var DEV_KEY_LEGACY_ENV = "OBJECTSTACK_DEV_CRYPTO_KEY";
|
|
855
|
+
var AUTOKEY_ENV = "OS_CRYPTO_AUTOKEY";
|
|
855
856
|
var processEnv = () => globalThis.process?.env ?? {};
|
|
856
857
|
var detectMode = (env) => {
|
|
857
858
|
if (env.VITEST || env.NODE_ENV === "test") return "test";
|
|
@@ -875,6 +876,10 @@ var parseKey = (raw) => {
|
|
|
875
876
|
}
|
|
876
877
|
return void 0;
|
|
877
878
|
};
|
|
879
|
+
var parseBool = (raw) => {
|
|
880
|
+
const v = raw?.trim().toLowerCase();
|
|
881
|
+
return v === "1" || v === "true" || v === "yes";
|
|
882
|
+
};
|
|
878
883
|
var loadExistingKey = (path) => {
|
|
879
884
|
try {
|
|
880
885
|
if (!existsSync(path)) return void 0;
|
|
@@ -948,6 +953,17 @@ function resolveDataKey(opts) {
|
|
|
948
953
|
);
|
|
949
954
|
return { key: existing, source: "file" };
|
|
950
955
|
}
|
|
956
|
+
if (parseBool(env[AUTOKEY_ENV])) {
|
|
957
|
+
const persisted2 = loadOrCreateKey(path);
|
|
958
|
+
if (persisted2) {
|
|
959
|
+
if (persisted2.generated) {
|
|
960
|
+
warn(
|
|
961
|
+
`[LocalCryptoProvider] No ${SECRET_KEY_ENV} set \u2014 minted a new AES-256-GCM key and persisted it to ${path} (mode 0600). Restarts on this host reuse it automatically. For containers, CI, or multi-node, set ${SECRET_KEY_ENV} so every node shares one key.`
|
|
962
|
+
);
|
|
963
|
+
}
|
|
964
|
+
return { key: persisted2.key, source: persisted2.generated ? "generated-file" : "file" };
|
|
965
|
+
}
|
|
966
|
+
}
|
|
951
967
|
throw new Error(MISSING_PROD_KEY_MSG(path));
|
|
952
968
|
}
|
|
953
969
|
const persisted = loadOrCreateKey(path);
|
|
@@ -1112,7 +1128,15 @@ function registerSettingsRoutes(http, service, opts = {}) {
|
|
|
1112
1128
|
}));
|
|
1113
1129
|
http.put(`${base}/:namespace`, (async (req, res) => {
|
|
1114
1130
|
const ns = req.params.namespace;
|
|
1115
|
-
|
|
1131
|
+
let body = req.body ?? {};
|
|
1132
|
+
if (Object.keys(body).length === 1 && body.values && typeof body.values === "object" && !Array.isArray(body.values)) {
|
|
1133
|
+
const inner = body.values;
|
|
1134
|
+
body = Object.fromEntries(
|
|
1135
|
+
Object.entries(inner).map(
|
|
1136
|
+
([k, v]) => v && typeof v === "object" && !Array.isArray(v) && "value" in v ? [k, v.value] : [k, v]
|
|
1137
|
+
)
|
|
1138
|
+
);
|
|
1139
|
+
}
|
|
1116
1140
|
try {
|
|
1117
1141
|
const ctx = ctxOf(req);
|
|
1118
1142
|
const result = await service.setMany(ns, body, ctx);
|
|
@@ -1241,6 +1265,95 @@ var manifest = {
|
|
|
1241
1265
|
description: "Upper bound guards against denial-of-service via very long password hashing.",
|
|
1242
1266
|
visible: "${data.email_password_enabled !== false}"
|
|
1243
1267
|
},
|
|
1268
|
+
{
|
|
1269
|
+
type: "toggle",
|
|
1270
|
+
key: "password_reject_breached",
|
|
1271
|
+
label: "Reject breached passwords",
|
|
1272
|
+
required: false,
|
|
1273
|
+
default: false,
|
|
1274
|
+
description: "Block passwords found in public breach corpora via Have I Been Pwned (k-anonymity range check; the password is never sent in full).",
|
|
1275
|
+
visible: "${data.email_password_enabled !== false}"
|
|
1276
|
+
},
|
|
1277
|
+
{
|
|
1278
|
+
type: "toggle",
|
|
1279
|
+
key: "password_require_complexity",
|
|
1280
|
+
label: "Require complex passwords",
|
|
1281
|
+
required: false,
|
|
1282
|
+
default: false,
|
|
1283
|
+
description: "Require passwords to mix character classes (uppercase, lowercase, digits, symbols) on sign-up and password change/reset.",
|
|
1284
|
+
visible: "${data.email_password_enabled !== false}"
|
|
1285
|
+
},
|
|
1286
|
+
{
|
|
1287
|
+
type: "number",
|
|
1288
|
+
key: "password_min_classes",
|
|
1289
|
+
label: "Minimum character classes",
|
|
1290
|
+
required: false,
|
|
1291
|
+
default: 3,
|
|
1292
|
+
min: 1,
|
|
1293
|
+
max: 4,
|
|
1294
|
+
description: "How many of the four classes (upper / lower / digit / symbol) a password must include.",
|
|
1295
|
+
visible: "${data.email_password_enabled !== false && data.password_require_complexity === true}"
|
|
1296
|
+
},
|
|
1297
|
+
{
|
|
1298
|
+
type: "number",
|
|
1299
|
+
key: "password_history_count",
|
|
1300
|
+
label: "Password history (no reuse)",
|
|
1301
|
+
required: false,
|
|
1302
|
+
default: 0,
|
|
1303
|
+
min: 0,
|
|
1304
|
+
max: 24,
|
|
1305
|
+
description: "Block reusing this many previous passwords on change/reset. 0 disables the check.",
|
|
1306
|
+
visible: "${data.email_password_enabled !== false}"
|
|
1307
|
+
},
|
|
1308
|
+
{
|
|
1309
|
+
type: "group",
|
|
1310
|
+
id: "anti_abuse",
|
|
1311
|
+
label: "Anti-abuse",
|
|
1312
|
+
required: false,
|
|
1313
|
+
description: "Brute-force protection: per-identity account lockout and per-IP rate limiting on auth endpoints."
|
|
1314
|
+
},
|
|
1315
|
+
{
|
|
1316
|
+
type: "number",
|
|
1317
|
+
key: "lockout_threshold",
|
|
1318
|
+
label: "Account lockout threshold",
|
|
1319
|
+
required: false,
|
|
1320
|
+
default: 0,
|
|
1321
|
+
min: 0,
|
|
1322
|
+
max: 20,
|
|
1323
|
+
description: "Lock an account after this many consecutive failed sign-ins. 0 disables lockout. While locked, sign-in is rejected even with the correct password.",
|
|
1324
|
+
visible: "${data.email_password_enabled !== false}"
|
|
1325
|
+
},
|
|
1326
|
+
{
|
|
1327
|
+
type: "number",
|
|
1328
|
+
key: "lockout_duration_minutes",
|
|
1329
|
+
label: "Lockout duration (minutes)",
|
|
1330
|
+
required: false,
|
|
1331
|
+
default: 15,
|
|
1332
|
+
min: 1,
|
|
1333
|
+
max: 1440,
|
|
1334
|
+
description: "How long an account stays locked once the threshold is crossed.",
|
|
1335
|
+
visible: "${data.email_password_enabled !== false && data.lockout_threshold > 0}"
|
|
1336
|
+
},
|
|
1337
|
+
{
|
|
1338
|
+
type: "number",
|
|
1339
|
+
key: "rate_limit_max",
|
|
1340
|
+
label: "Auth rate-limit: max requests",
|
|
1341
|
+
required: false,
|
|
1342
|
+
default: 10,
|
|
1343
|
+
min: 1,
|
|
1344
|
+
max: 1e3,
|
|
1345
|
+
description: "Maximum requests per IP, per window, to the sign-in / sign-up / password-reset endpoints."
|
|
1346
|
+
},
|
|
1347
|
+
{
|
|
1348
|
+
type: "number",
|
|
1349
|
+
key: "rate_limit_window_seconds",
|
|
1350
|
+
label: "Auth rate-limit: window (seconds)",
|
|
1351
|
+
required: false,
|
|
1352
|
+
default: 60,
|
|
1353
|
+
min: 1,
|
|
1354
|
+
max: 3600,
|
|
1355
|
+
description: "Sliding window over which the request cap above is counted."
|
|
1356
|
+
},
|
|
1244
1357
|
{
|
|
1245
1358
|
type: "group",
|
|
1246
1359
|
id: "sessions",
|
|
@@ -3189,10 +3302,10 @@ var zhCN = {
|
|
|
3189
3302
|
default_country: { label: "\u9ED8\u8BA4\u56FD\u5BB6/\u5730\u533A", help: "ISO 3166-1 \u4E8C\u4F4D\u4EE3\u7801(\u5982 US\u3001GB\u3001CN)\u3002" },
|
|
3190
3303
|
date_format: { label: "\u65E5\u671F\u683C\u5F0F" },
|
|
3191
3304
|
time_format: { label: "\u65F6\u95F4\u683C\u5F0F", options: { "24h": "24 \u5C0F\u65F6\u5236(14:30)", "12h": "12 \u5C0F\u65F6\u5236(2:30 PM)" } },
|
|
3192
|
-
number_format: { label: "\u6570\u5B57\u683C\u5F0F" },
|
|
3193
|
-
first_day_of_week: { label: "\u6BCF\u5468\u8D77\u59CB\u65E5", options: { monday: "\u5468\u4E00(ISO)", sunday: "\u5468\u65E5", saturday: "\u5468\u516D" } },
|
|
3194
|
-
currency: { label: "\u9ED8\u8BA4\u8D27\u5E01" },
|
|
3195
|
-
fiscal_year_start: { label: "\u8D22\u5E74\u8D77\u59CB\u6708" }
|
|
3305
|
+
number_format: { label: "\u6570\u5B57\u683C\u5F0F", help: "\u7528\u4E8E\u663E\u793A\u6570\u5B57\u7684\u5343\u5206\u4F4D\u4E0E\u5C0F\u6570\u5206\u9694\u7B26\u3002" },
|
|
3306
|
+
first_day_of_week: { label: "\u6BCF\u5468\u8D77\u59CB\u65E5", help: "\u7528\u4F5C\u5468\u5EA6\u5206\u6790\u5206\u6876\u4E0E\u65E5\u5386\u7F51\u683C\u7684\u8D77\u59CB\u57FA\u51C6\u3002", options: { monday: "\u5468\u4E00(ISO)", sunday: "\u5468\u65E5", saturday: "\u5468\u516D" } },
|
|
3307
|
+
currency: { label: "\u9ED8\u8BA4\u8D27\u5E01", help: "\u5F53\u8D27\u5E01\u5B57\u6BB5\u672A\u6307\u5B9A\u5E01\u79CD\u65F6\u5957\u7528\u7684 ISO 4217 \u4EE3\u7801\u3002" },
|
|
3308
|
+
fiscal_year_start: { label: "\u8D22\u5E74\u8D77\u59CB\u6708", help: '\u8D22\u5E74\u7684\u8D77\u59CB\u6708\u4EFD\u2014\u2014\u51B3\u5B9A\u62A5\u8868\u4E2D\u7684"\u672C\u5B63\u5EA6/\u672C\u8D22\u5E74"\u3002' }
|
|
3196
3309
|
}
|
|
3197
3310
|
},
|
|
3198
3311
|
auth: {
|