najm-auth 1.1.39 → 1.1.42
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.md +12 -12
- package/dist/index.d.ts +151 -19
- package/dist/index.js +708 -587
- package/dist/schema/pg.d.ts +2 -2
- package/dist/schema/pg.js +3 -2
- package/dist/schema/sqlite.d.ts +2 -2
- package/dist/schema/sqlite.js +3 -2
- package/package.json +10 -15
- package/dist/schema/mysql.d.ts +0 -1535
- package/dist/schema/mysql.js +0 -85
package/dist/index.js
CHANGED
|
@@ -67,9 +67,9 @@ var permissionsTable = pgTable("permissions", {
|
|
|
67
67
|
});
|
|
68
68
|
var tokensTable = pgTable("tokens", {
|
|
69
69
|
...baseFields(10),
|
|
70
|
-
userId: text("user_id").references(() => usersTable.id, { onDelete: "cascade" }).
|
|
70
|
+
userId: text("user_id").references(() => usersTable.id, { onDelete: "cascade" }).notNull(),
|
|
71
71
|
token: text("token").notNull(),
|
|
72
|
-
tokenFamily: text("token_family"),
|
|
72
|
+
tokenFamily: text("token_family").notNull().unique(),
|
|
73
73
|
previousHash: text("previous_hash"),
|
|
74
74
|
previousValidUntil: timestamp("previous_valid_until", { mode: "string" }),
|
|
75
75
|
previousUsedAt: timestamp("previous_used_at", { mode: "string" }),
|
|
@@ -77,6 +77,7 @@ var tokensTable = pgTable("tokens", {
|
|
|
77
77
|
status: tokenStatusEnum("status").default("active"),
|
|
78
78
|
expiresAt: timestamp("expires_at", { mode: "string" }).notNull()
|
|
79
79
|
}, (table) => ({
|
|
80
|
+
userIdIdx: index("tokens_user_id_idx").on(table.userId),
|
|
80
81
|
expiresAtIdx: index("tokens_expires_at_idx").on(table.expiresAt)
|
|
81
82
|
}));
|
|
82
83
|
var rolePermissionsTable = pgTable("role_permissions", {
|
|
@@ -134,9 +135,9 @@ var permissionsTable2 = sqliteTable("permissions", {
|
|
|
134
135
|
});
|
|
135
136
|
var tokensTable2 = sqliteTable("tokens", {
|
|
136
137
|
...baseFields2(10),
|
|
137
|
-
userId: text2("user_id").references(() => usersTable2.id, { onDelete: "cascade" }).
|
|
138
|
+
userId: text2("user_id").references(() => usersTable2.id, { onDelete: "cascade" }).notNull(),
|
|
138
139
|
token: text2("token").notNull(),
|
|
139
|
-
tokenFamily: text2("token_family"),
|
|
140
|
+
tokenFamily: text2("token_family").notNull().unique(),
|
|
140
141
|
previousHash: text2("previous_hash"),
|
|
141
142
|
previousValidUntil: text2("previous_valid_until"),
|
|
142
143
|
previousUsedAt: text2("previous_used_at"),
|
|
@@ -144,6 +145,7 @@ var tokensTable2 = sqliteTable("tokens", {
|
|
|
144
145
|
status: text2("status").$type().default("active"),
|
|
145
146
|
expiresAt: text2("expires_at").notNull()
|
|
146
147
|
}, (table) => ({
|
|
148
|
+
userIdIdx: index2("tokens_user_id_idx").on(table.userId),
|
|
147
149
|
expiresAtIdx: index2("tokens_expires_at_idx").on(table.expiresAt)
|
|
148
150
|
}));
|
|
149
151
|
var rolePermissionsTable2 = sqliteTable("role_permissions", {
|
|
@@ -161,87 +163,6 @@ var authSchema2 = {
|
|
|
161
163
|
rolePermissions: rolePermissionsTable2
|
|
162
164
|
};
|
|
163
165
|
|
|
164
|
-
// src/schema/mysql.ts
|
|
165
|
-
import { mysqlTable, varchar, boolean as boolean2, timestamp as timestamp2, mysqlEnum, primaryKey as primaryKey2, int, index as index3 } from "drizzle-orm/mysql-core";
|
|
166
|
-
import { sql as sql3 } from "drizzle-orm";
|
|
167
|
-
import { nanoid as nanoid3 } from "nanoid";
|
|
168
|
-
var baseFields3 = /* @__PURE__ */ __name((idLength = 5) => ({
|
|
169
|
-
id: varchar("id", { length: 21 }).primaryKey().$defaultFn(() => nanoid3(idLength)),
|
|
170
|
-
createdAt: timestamp2("created_at", { mode: "string" }).defaultNow(),
|
|
171
|
-
updatedAt: timestamp2("updated_at", { mode: "string" }).defaultNow().$onUpdate(() => sql3`CURRENT_TIMESTAMP`)
|
|
172
|
-
}), "baseFields");
|
|
173
|
-
var rolesTable3 = mysqlTable("roles", {
|
|
174
|
-
...baseFields3(5),
|
|
175
|
-
name: varchar("name", { length: 255 }).notNull(),
|
|
176
|
-
description: varchar("description", { length: 1e3 })
|
|
177
|
-
});
|
|
178
|
-
var usersTable3 = mysqlTable("users", {
|
|
179
|
-
...baseFields3(8),
|
|
180
|
-
name: varchar("name", { length: 255 }),
|
|
181
|
-
email: varchar("email", { length: 255 }).notNull().unique(),
|
|
182
|
-
emailVerified: boolean2("email_verified").default(false),
|
|
183
|
-
phone: varchar("phone", { length: 255 }).unique(),
|
|
184
|
-
phoneVerified: boolean2("phone_verified").default(false),
|
|
185
|
-
password: varchar("password", { length: 255 }).notNull(),
|
|
186
|
-
image: varchar("image", { length: 255 }).default("noavatar.png"),
|
|
187
|
-
status: mysqlEnum("status", [...USER_STATUS]).default("pending"),
|
|
188
|
-
roleId: varchar("role_id", { length: 21 }).references(() => rolesTable3.id),
|
|
189
|
-
lastLogin: timestamp2("last_login", { mode: "string" }),
|
|
190
|
-
failedLoginAttempts: int("failed_login_attempts").default(0),
|
|
191
|
-
lockoutUntil: timestamp2("lockout_until", { mode: "string" })
|
|
192
|
-
}, (table) => ({
|
|
193
|
-
roleIdx: index3("users_role_id_idx").on(table.roleId)
|
|
194
|
-
}));
|
|
195
|
-
var permissionsTable3 = mysqlTable("permissions", {
|
|
196
|
-
...baseFields3(5),
|
|
197
|
-
name: varchar("name", { length: 255 }).notNull().unique(),
|
|
198
|
-
description: varchar("description", { length: 1e3 }),
|
|
199
|
-
resource: varchar("resource", { length: 255 }).notNull(),
|
|
200
|
-
action: varchar("action", { length: 255 }).notNull()
|
|
201
|
-
});
|
|
202
|
-
var tokensTable3 = mysqlTable("tokens", {
|
|
203
|
-
...baseFields3(10),
|
|
204
|
-
userId: varchar("user_id", { length: 21 }).references(() => usersTable3.id, { onDelete: "cascade" }).unique().notNull(),
|
|
205
|
-
token: varchar("token", { length: 500 }).notNull(),
|
|
206
|
-
tokenFamily: varchar("token_family", { length: 16 }),
|
|
207
|
-
previousHash: varchar("previous_hash", { length: 500 }),
|
|
208
|
-
previousValidUntil: timestamp2("previous_valid_until", { mode: "string" }),
|
|
209
|
-
previousUsedAt: timestamp2("previous_used_at", { mode: "string" }),
|
|
210
|
-
type: mysqlEnum("type", [...TOKEN_TYPE]).default("refresh"),
|
|
211
|
-
status: mysqlEnum("status", [...TOKEN_STATUS]).default("active"),
|
|
212
|
-
expiresAt: timestamp2("expires_at", { mode: "string" }).notNull()
|
|
213
|
-
}, (table) => ({
|
|
214
|
-
expiresAtIdx: index3("tokens_expires_at_idx").on(table.expiresAt)
|
|
215
|
-
}));
|
|
216
|
-
var rolePermissionsTable3 = mysqlTable("role_permissions", {
|
|
217
|
-
roleId: varchar("role_id", { length: 21 }).notNull().references(() => rolesTable3.id, { onDelete: "cascade" }),
|
|
218
|
-
permissionId: varchar("permission_id", { length: 21 }).notNull().references(() => permissionsTable3.id, { onDelete: "cascade" }),
|
|
219
|
-
createdAt: timestamp2("created_at", { mode: "string" }).defaultNow()
|
|
220
|
-
}, (table) => ({
|
|
221
|
-
pk: primaryKey2({ columns: [table.roleId, table.permissionId] })
|
|
222
|
-
}));
|
|
223
|
-
var authSchema3 = {
|
|
224
|
-
users: usersTable3,
|
|
225
|
-
tokens: tokensTable3,
|
|
226
|
-
roles: rolesTable3,
|
|
227
|
-
permissions: permissionsTable3,
|
|
228
|
-
rolePermissions: rolePermissionsTable3
|
|
229
|
-
};
|
|
230
|
-
|
|
231
|
-
// src/auth/index.ts
|
|
232
|
-
var auth_exports = {};
|
|
233
|
-
__export(auth_exports, {
|
|
234
|
-
AUTH_MODULE: () => AUTH_MODULE,
|
|
235
|
-
AuthController: () => AuthController,
|
|
236
|
-
AuthGuard: () => AuthGuard,
|
|
237
|
-
AuthResolver: () => AuthResolver,
|
|
238
|
-
AuthService: () => AuthService,
|
|
239
|
-
CookieManager: () => CookieManager,
|
|
240
|
-
EncryptionService: () => EncryptionService,
|
|
241
|
-
isAuth: () => isAuth,
|
|
242
|
-
runAsUser: () => runAsUser
|
|
243
|
-
});
|
|
244
|
-
|
|
245
166
|
// src/auth/EncryptionService.ts
|
|
246
167
|
import { Inject, Injectable } from "najm-core";
|
|
247
168
|
import { createCipheriv, createDecipheriv, randomBytes } from "crypto";
|
|
@@ -452,15 +373,16 @@ CookieManager = __decorate2([
|
|
|
452
373
|
], CookieManager);
|
|
453
374
|
|
|
454
375
|
// src/auth/AuthController.ts
|
|
455
|
-
import { Controller } from "najm-core";
|
|
456
|
-
import { Get, Post, ResMsg } from "najm-core";
|
|
457
|
-
import { Body, User as
|
|
376
|
+
import { Controller as Controller2 } from "najm-core";
|
|
377
|
+
import { Get as Get2, Post as Post2, ResMsg as ResMsg2 } from "najm-core";
|
|
378
|
+
import { Body as Body2, User as User3, Headers } from "najm-core";
|
|
458
379
|
|
|
459
380
|
// src/auth/AuthService.ts
|
|
460
381
|
import { Injectable as Injectable7, Inject as Inject8 } from "najm-core";
|
|
461
382
|
import { Err as Err6, Log } from "najm-core";
|
|
462
383
|
import { I18n as I18n5, I18nService as I18nService2 } from "najm-i18n";
|
|
463
|
-
import { EmailService, passwordResetTemplate } from "najm-email";
|
|
384
|
+
import { EmailService, passwordResetTemplate, accountInviteTemplate } from "najm-email";
|
|
385
|
+
import { nanoid as nanoid5 } from "nanoid";
|
|
464
386
|
|
|
465
387
|
// src/users/UserService.ts
|
|
466
388
|
import { Injectable as Injectable5, Inject as Inject5 } from "najm-core";
|
|
@@ -469,7 +391,7 @@ import { I18nService } from "najm-i18n";
|
|
|
469
391
|
import { I18n as I18n3 } from "najm-i18n";
|
|
470
392
|
|
|
471
393
|
// src/users/UserRepository.ts
|
|
472
|
-
import { eq as eq2, ne, sql as
|
|
394
|
+
import { eq as eq2, ne, sql as sql3 } from "drizzle-orm";
|
|
473
395
|
import { Repository, Inject as Inject3 } from "najm-core";
|
|
474
396
|
import { DB } from "najm-database";
|
|
475
397
|
|
|
@@ -639,7 +561,7 @@ var UserRepository = class UserRepository2 {
|
|
|
639
561
|
return updatedUser;
|
|
640
562
|
}
|
|
641
563
|
async incrementFailedAttempts(id) {
|
|
642
|
-
const [updatedUser] = await this.db.update(this.users).set({ failedLoginAttempts:
|
|
564
|
+
const [updatedUser] = await this.db.update(this.users).set({ failedLoginAttempts: sql3`coalesce(${this.users.failedLoginAttempts}, 0) + 1` }).where(eq2(this.users.id, id)).returning();
|
|
643
565
|
return updatedUser;
|
|
644
566
|
}
|
|
645
567
|
async resetFailedAttempts(id) {
|
|
@@ -1044,7 +966,7 @@ RoleService = __decorate7([
|
|
|
1044
966
|
], RoleService);
|
|
1045
967
|
|
|
1046
968
|
// src/users/UserService.ts
|
|
1047
|
-
import { nanoid as
|
|
969
|
+
import { nanoid as nanoid3 } from "nanoid";
|
|
1048
970
|
|
|
1049
971
|
// src/shared/index.ts
|
|
1050
972
|
import * as fs from "fs/promises";
|
|
@@ -1254,7 +1176,7 @@ var UserService = class UserService2 {
|
|
|
1254
1176
|
Err4("Password is required");
|
|
1255
1177
|
}
|
|
1256
1178
|
this.userValidator.validatePasswordStrength(password);
|
|
1257
|
-
let userId = id ||
|
|
1179
|
+
let userId = id || nanoid3(10);
|
|
1258
1180
|
await this.userValidator.checkEmailUnique(data.email);
|
|
1259
1181
|
await this.userValidator.checkUserIdIsUnique(userId);
|
|
1260
1182
|
const hashedPassword = await this.encryptionService.hashPassword(password);
|
|
@@ -1336,7 +1258,7 @@ var UserService = class UserService2 {
|
|
|
1336
1258
|
await this.delete(existingUser.id);
|
|
1337
1259
|
}
|
|
1338
1260
|
const newAdminUser = await this.create({
|
|
1339
|
-
id:
|
|
1261
|
+
id: nanoid3(10),
|
|
1340
1262
|
// Random ID instead of predictable 'USR00'
|
|
1341
1263
|
name: config.name || "System Administrator",
|
|
1342
1264
|
email: config.email,
|
|
@@ -1378,10 +1300,10 @@ import { I18n as I18n4 } from "najm-i18n";
|
|
|
1378
1300
|
import { CacheService } from "najm-cache";
|
|
1379
1301
|
import { createHash } from "crypto";
|
|
1380
1302
|
import jwt from "jsonwebtoken";
|
|
1381
|
-
import { nanoid as
|
|
1303
|
+
import { nanoid as nanoid4 } from "nanoid";
|
|
1382
1304
|
|
|
1383
1305
|
// src/tokens/TokenRepository.ts
|
|
1384
|
-
import { and, eq as eq4, isNull } from "drizzle-orm";
|
|
1306
|
+
import { and, eq as eq4, isNull, lt } from "drizzle-orm";
|
|
1385
1307
|
import { Repository as Repository3, Inject as Inject6 } from "najm-core";
|
|
1386
1308
|
import { DB as DB3 } from "najm-database";
|
|
1387
1309
|
var __decorate9 = function(decorators, target, key, desc) {
|
|
@@ -1410,12 +1332,17 @@ var TokenRepository = class TokenRepository2 {
|
|
|
1410
1332
|
get q() {
|
|
1411
1333
|
return this.queryHelper ??= new AuthQueries(this.db, this.schema);
|
|
1412
1334
|
}
|
|
1335
|
+
/**
|
|
1336
|
+
* Upsert the refresh-token row for a session, keyed on `tokenFamily` (the
|
|
1337
|
+
* per-login session identifier, unique). A brand-new login inserts a fresh
|
|
1338
|
+
* family row; a refresh rotation updates only that family's row, leaving the
|
|
1339
|
+
* user's other sessions untouched.
|
|
1340
|
+
*/
|
|
1413
1341
|
async storeRefreshToken(tokenData) {
|
|
1414
1342
|
return await this.db.insert(this.tokens).values(tokenData).onConflictDoUpdate({
|
|
1415
|
-
target: this.tokens.
|
|
1343
|
+
target: this.tokens.tokenFamily,
|
|
1416
1344
|
set: {
|
|
1417
1345
|
token: tokenData.token,
|
|
1418
|
-
tokenFamily: tokenData.tokenFamily,
|
|
1419
1346
|
expiresAt: tokenData.expiresAt,
|
|
1420
1347
|
previousHash: tokenData.previousHash ?? null,
|
|
1421
1348
|
previousValidUntil: tokenData.previousValidUntil ?? null,
|
|
@@ -1424,27 +1351,37 @@ var TokenRepository = class TokenRepository2 {
|
|
|
1424
1351
|
}).returning();
|
|
1425
1352
|
}
|
|
1426
1353
|
/**
|
|
1427
|
-
* Claim the previous-token grace slot. Conditional on
|
|
1428
|
-
* previousHash still matching the presented token AND
|
|
1429
|
-
* NULL. Gating on the hash (not just the flag) closes
|
|
1430
|
-
* winner's rotation rewrites previousHash via
|
|
1431
|
-
*
|
|
1432
|
-
*
|
|
1354
|
+
* Claim the previous-token grace slot for a single family. Conditional on
|
|
1355
|
+
* BOTH the stored previousHash still matching the presented token AND
|
|
1356
|
+
* previousUsedAt being NULL. Gating on the hash (not just the flag) closes
|
|
1357
|
+
* the rotation race: the winner's rotation rewrites previousHash via
|
|
1358
|
+
* storeRefreshToken (and resets previousUsedAt to NULL), so a loser whose
|
|
1359
|
+
* UPDATE lands after that rotation no longer matches and gets zero rows —
|
|
1360
|
+
* exactly one caller ever claims the slot.
|
|
1433
1361
|
*/
|
|
1434
|
-
async markPreviousUsed(
|
|
1435
|
-
return await this.db.update(this.tokens).set({ previousUsedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(and(eq4(this.tokens.
|
|
1362
|
+
async markPreviousUsed(tokenFamily, previousHash) {
|
|
1363
|
+
return await this.db.update(this.tokens).set({ previousUsedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(and(eq4(this.tokens.tokenFamily, tokenFamily), eq4(this.tokens.previousHash, previousHash), isNull(this.tokens.previousUsedAt))).returning();
|
|
1436
1364
|
}
|
|
1437
|
-
|
|
1438
|
-
|
|
1365
|
+
/** Look up a single session's token row by its family identifier. */
|
|
1366
|
+
async getByFamily(tokenFamily) {
|
|
1367
|
+
const [token] = await this.db.select().from(this.tokens).where(eq4(this.tokens.tokenFamily, tokenFamily));
|
|
1439
1368
|
return token ?? null;
|
|
1440
1369
|
}
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
return deletedToken;
|
|
1444
|
-
}
|
|
1445
|
-
async revokeByFamily(tokenFamily) {
|
|
1370
|
+
/** Revoke a single session (one family). */
|
|
1371
|
+
async revokeFamily(tokenFamily) {
|
|
1446
1372
|
return this.db.delete(this.tokens).where(eq4(this.tokens.tokenFamily, tokenFamily)).returning();
|
|
1447
1373
|
}
|
|
1374
|
+
/** Revoke every session for a user (password change/reset, logout-all). */
|
|
1375
|
+
async revokeAllForUser(userId) {
|
|
1376
|
+
return this.db.delete(this.tokens).where(eq4(this.tokens.userId, userId)).returning();
|
|
1377
|
+
}
|
|
1378
|
+
/**
|
|
1379
|
+
* Opportunistic cleanup: with one row per family (no unique userId), expired
|
|
1380
|
+
* and abandoned sessions accumulate. Delete every expired row.
|
|
1381
|
+
*/
|
|
1382
|
+
async deleteExpired() {
|
|
1383
|
+
return this.db.delete(this.tokens).where(lt(this.tokens.expiresAt, (/* @__PURE__ */ new Date()).toISOString())).returning();
|
|
1384
|
+
}
|
|
1448
1385
|
async isUserExists(userId) {
|
|
1449
1386
|
const [user] = await this.db.select({ id: this.users.id }).from(this.users).where(eq4(this.users.id, userId)).limit(1);
|
|
1450
1387
|
return !!user;
|
|
@@ -1559,19 +1496,22 @@ var TokenService = class TokenService2 {
|
|
|
1559
1496
|
Err5(this.t("errors.tokenVerificationFailed"));
|
|
1560
1497
|
}
|
|
1561
1498
|
const sessionKey = this.sessionVersionKey(payload.userId);
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
]
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
activeSessionVersion = this.parseSessionVersion(await this.cache.get(sessionKey));
|
|
1499
|
+
const blacklistKey = payload.jti ? `${this.blacklistPrefix}${payload.jti}` : null;
|
|
1500
|
+
const familyKey = payload.tokenFamily ? this.revokedFamilyKey(payload.tokenFamily) : null;
|
|
1501
|
+
const keys = [
|
|
1502
|
+
...blacklistKey ? [blacklistKey] : [],
|
|
1503
|
+
sessionKey,
|
|
1504
|
+
...familyKey ? [familyKey] : []
|
|
1505
|
+
];
|
|
1506
|
+
const values = await this.getCacheValues(keys);
|
|
1507
|
+
const valueByKey = new Map(keys.map((key, i) => [key, values[i]]));
|
|
1508
|
+
if (blacklistKey && valueByKey.get(blacklistKey) != null) {
|
|
1509
|
+
Err5(this.t("errors.tokenRevoked"));
|
|
1574
1510
|
}
|
|
1511
|
+
if (familyKey && valueByKey.get(familyKey) != null) {
|
|
1512
|
+
Err5(this.t("errors.tokenRevoked"));
|
|
1513
|
+
}
|
|
1514
|
+
const activeSessionVersion = this.parseSessionVersion(valueByKey.get(sessionKey) ?? null);
|
|
1575
1515
|
const tokenSessionVersion = payload.sessionVersion ?? 0;
|
|
1576
1516
|
if (tokenSessionVersion !== activeSessionVersion) {
|
|
1577
1517
|
Err5(this.t("errors.tokenRevoked"));
|
|
@@ -1579,15 +1519,19 @@ var TokenService = class TokenService2 {
|
|
|
1579
1519
|
return payload;
|
|
1580
1520
|
}
|
|
1581
1521
|
verifyRefreshToken(token) {
|
|
1522
|
+
let decoded;
|
|
1582
1523
|
try {
|
|
1583
|
-
|
|
1584
|
-
if (decoded.type && decoded.type !== "refresh") {
|
|
1585
|
-
Err5(this.t("errors.tokenVerificationFailed"));
|
|
1586
|
-
}
|
|
1587
|
-
return decoded.userId;
|
|
1524
|
+
decoded = jwt.verify(token, this.config.jwt.refreshSecret);
|
|
1588
1525
|
} catch {
|
|
1589
1526
|
Err5(this.t("errors.tokenVerificationFailed"));
|
|
1590
1527
|
}
|
|
1528
|
+
if (decoded.type && decoded.type !== "refresh") {
|
|
1529
|
+
Err5(this.t("errors.tokenVerificationFailed"));
|
|
1530
|
+
}
|
|
1531
|
+
if (!decoded.tokenFamily) {
|
|
1532
|
+
Err5(this.t("errors.tokenVerificationFailed"));
|
|
1533
|
+
}
|
|
1534
|
+
return { userId: decoded.userId, tokenFamily: decoded.tokenFamily };
|
|
1591
1535
|
}
|
|
1592
1536
|
static PREVIOUS_GRACE_SECONDS = 120;
|
|
1593
1537
|
/**
|
|
@@ -1606,9 +1550,9 @@ var TokenService = class TokenService2 {
|
|
|
1606
1550
|
if (!refreshToken) {
|
|
1607
1551
|
Err5(this.t("errors.refreshTokenMissing"));
|
|
1608
1552
|
}
|
|
1609
|
-
const userId = this.verifyRefreshToken(refreshToken);
|
|
1610
|
-
const stored = await this.tokenRepository.
|
|
1611
|
-
if (!stored) {
|
|
1553
|
+
const { userId, tokenFamily } = this.verifyRefreshToken(refreshToken);
|
|
1554
|
+
const stored = await this.tokenRepository.getByFamily(tokenFamily);
|
|
1555
|
+
if (!stored || stored.userId !== userId) {
|
|
1612
1556
|
Err5(this.t("errors.refreshTokenInvalid"));
|
|
1613
1557
|
}
|
|
1614
1558
|
const presentedHash = this.hashToken(refreshToken);
|
|
@@ -1653,7 +1597,7 @@ var TokenService = class TokenService2 {
|
|
|
1653
1597
|
return jwt.decode(token);
|
|
1654
1598
|
}
|
|
1655
1599
|
async signAccessToken(data) {
|
|
1656
|
-
const jti =
|
|
1600
|
+
const jti = nanoid4(16);
|
|
1657
1601
|
const sessionVersion = await this.getUserSessionVersion(data.userId);
|
|
1658
1602
|
const expiresAt = this.expiresAt(this.config.jwt.accessExpiresIn);
|
|
1659
1603
|
if (sessionVersion > 0) {
|
|
@@ -1670,30 +1614,33 @@ var TokenService = class TokenService2 {
|
|
|
1670
1614
|
return (await this.signAccessToken(data)).token;
|
|
1671
1615
|
}
|
|
1672
1616
|
/**
|
|
1673
|
-
* Generate refresh token with unique jti
|
|
1617
|
+
* Generate refresh token with unique jti. The token carries its session's
|
|
1618
|
+
* family so rotation/revocation can target a single session.
|
|
1674
1619
|
*/
|
|
1675
1620
|
signRefreshToken(data) {
|
|
1676
|
-
const jti =
|
|
1621
|
+
const jti = nanoid4(16);
|
|
1677
1622
|
const expiresAt = this.expiresAt(this.config.jwt.refreshExpiresIn);
|
|
1678
1623
|
const token = jwt.sign({ ...data, jti, type: "refresh", exp: expiresAt }, this.config.jwt.refreshSecret);
|
|
1679
1624
|
return { token, expiresAt };
|
|
1680
1625
|
}
|
|
1681
1626
|
generateRefreshToken(data) {
|
|
1682
|
-
return this.signRefreshToken(data).token;
|
|
1627
|
+
return this.signRefreshToken({ userId: data.userId, tokenFamily: data.tokenFamily ?? nanoid4(16) }).token;
|
|
1683
1628
|
}
|
|
1684
1629
|
async generateTokens(userId, tokenFamily) {
|
|
1685
|
-
const family = tokenFamily ??
|
|
1630
|
+
const family = tokenFamily ?? nanoid4(16);
|
|
1686
1631
|
const { roleName, permissions } = await this.tokenRepository.getRoleAndPermissions(userId);
|
|
1687
1632
|
const accessTokenData = {
|
|
1688
1633
|
userId,
|
|
1689
1634
|
roles: roleName ? [roleName] : [],
|
|
1690
|
-
permissions: permissions ?? []
|
|
1635
|
+
permissions: permissions ?? [],
|
|
1636
|
+
tokenFamily: family
|
|
1691
1637
|
};
|
|
1692
1638
|
const access = await this.signAccessToken(accessTokenData);
|
|
1693
|
-
const refresh = this.signRefreshToken({ userId });
|
|
1639
|
+
const refresh = this.signRefreshToken({ userId, tokenFamily: family });
|
|
1694
1640
|
await this.storeRefreshToken(userId, refresh.token, family);
|
|
1695
1641
|
return {
|
|
1696
1642
|
userId,
|
|
1643
|
+
tokenFamily: family,
|
|
1697
1644
|
roles: accessTokenData.roles,
|
|
1698
1645
|
permissions: accessTokenData.permissions,
|
|
1699
1646
|
accessToken: access.token,
|
|
@@ -1743,7 +1690,7 @@ var TokenService = class TokenService2 {
|
|
|
1743
1690
|
async storeRefreshToken(userId, refreshToken, tokenFamily) {
|
|
1744
1691
|
const expireInSecond = timestring2(this.config.jwt.refreshExpiresIn, "s");
|
|
1745
1692
|
const hashedToken = this.hashToken(refreshToken);
|
|
1746
|
-
const existing = await this.tokenRepository.
|
|
1693
|
+
const existing = await this.tokenRepository.getByFamily(tokenFamily);
|
|
1747
1694
|
const previousHash = existing?.token ?? null;
|
|
1748
1695
|
const previousValidUntil = previousHash ? new Date(Date.now() + TokenService_1.PREVIOUS_GRACE_SECONDS * 1e3).toISOString() : null;
|
|
1749
1696
|
await this.tokenRepository.storeRefreshToken({
|
|
@@ -1765,28 +1712,45 @@ var TokenService = class TokenService2 {
|
|
|
1765
1712
|
if (!refreshToken) {
|
|
1766
1713
|
Err5(this.t("errors.refreshTokenMissing"));
|
|
1767
1714
|
}
|
|
1768
|
-
const userId = this.verifyRefreshToken(refreshToken);
|
|
1769
|
-
const stored = await this.tokenRepository.
|
|
1770
|
-
if (!stored) {
|
|
1715
|
+
const { userId, tokenFamily } = this.verifyRefreshToken(refreshToken);
|
|
1716
|
+
const stored = await this.tokenRepository.getByFamily(tokenFamily);
|
|
1717
|
+
if (!stored || stored.userId !== userId) {
|
|
1771
1718
|
Err5(this.t("errors.refreshTokenInvalid"));
|
|
1772
1719
|
}
|
|
1773
1720
|
const presentedHash = this.hashToken(refreshToken);
|
|
1774
1721
|
if (presentedHash === stored.token) {
|
|
1775
|
-
return this.generateTokens(userId,
|
|
1722
|
+
return this.generateTokens(userId, tokenFamily);
|
|
1776
1723
|
}
|
|
1777
1724
|
const canRecover = stored.previousHash && presentedHash === stored.previousHash && stored.previousValidUntil && new Date(stored.previousValidUntil).getTime() > Date.now() && !stored.previousUsedAt;
|
|
1778
1725
|
if (canRecover) {
|
|
1779
|
-
const claimed = await this.tokenRepository.markPreviousUsed(
|
|
1726
|
+
const claimed = await this.tokenRepository.markPreviousUsed(tokenFamily, presentedHash);
|
|
1780
1727
|
if (!claimed?.length) {
|
|
1781
1728
|
Err5(this.t("errors.refreshTokenInvalid"));
|
|
1782
1729
|
}
|
|
1783
|
-
return this.generateTokens(userId,
|
|
1730
|
+
return this.generateTokens(userId, tokenFamily);
|
|
1784
1731
|
}
|
|
1785
|
-
await this.revokeSuspectRefreshFamily(userId,
|
|
1732
|
+
await this.revokeSuspectRefreshFamily(userId, tokenFamily);
|
|
1786
1733
|
Err5(this.t("errors.refreshTokenInvalid"));
|
|
1787
1734
|
}
|
|
1788
|
-
|
|
1789
|
-
|
|
1735
|
+
/** Revoke every refresh session for a user (password change/reset, logout-all). */
|
|
1736
|
+
async revokeAllForUser(userId) {
|
|
1737
|
+
return this.tokenRepository.revokeAllForUser(userId);
|
|
1738
|
+
}
|
|
1739
|
+
/** Revoke a single refresh session (one family). */
|
|
1740
|
+
async revokeFamily(tokenFamily) {
|
|
1741
|
+
await this.markFamilyRevoked(tokenFamily);
|
|
1742
|
+
return this.tokenRepository.revokeFamily(tokenFamily);
|
|
1743
|
+
}
|
|
1744
|
+
/**
|
|
1745
|
+
* Opportunistic cleanup of expired/abandoned sessions. With one row per
|
|
1746
|
+
* family (no unique userId), abandoned logins would otherwise accumulate.
|
|
1747
|
+
* Best-effort — never let cleanup failure break the calling flow.
|
|
1748
|
+
*/
|
|
1749
|
+
async deleteExpiredSessions() {
|
|
1750
|
+
try {
|
|
1751
|
+
await this.tokenRepository.deleteExpired();
|
|
1752
|
+
} catch {
|
|
1753
|
+
}
|
|
1790
1754
|
}
|
|
1791
1755
|
async invalidateUserAccessTokens(userId) {
|
|
1792
1756
|
const nextVersion = await this.getUserSessionVersion(userId) + 1;
|
|
@@ -1802,47 +1766,135 @@ var TokenService = class TokenService2 {
|
|
|
1802
1766
|
}
|
|
1803
1767
|
return user;
|
|
1804
1768
|
}
|
|
1769
|
+
get revokedFamilyPrefix() {
|
|
1770
|
+
return "auth:revoked-family:";
|
|
1771
|
+
}
|
|
1772
|
+
revokedFamilyKey(tokenFamily) {
|
|
1773
|
+
return `${this.revokedFamilyPrefix}${tokenFamily}`;
|
|
1774
|
+
}
|
|
1775
|
+
/**
|
|
1776
|
+
* Mark a family as revoked in cache for the access-token TTL, so every
|
|
1777
|
+
* access token minted for that family (not just the presented one) is
|
|
1778
|
+
* rejected by verifyAccessToken until it would have expired anyway.
|
|
1779
|
+
*/
|
|
1780
|
+
async markFamilyRevoked(tokenFamily) {
|
|
1781
|
+
await this.cache.set(this.revokedFamilyKey(tokenFamily), "1", this.accessTokenTtlMs());
|
|
1782
|
+
}
|
|
1783
|
+
/**
|
|
1784
|
+
* Revoke only the suspect family — NOT the whole user. Bumping the global
|
|
1785
|
+
* per-user session version here would kill every device's access tokens on a
|
|
1786
|
+
* single family's reuse detection. Instead drop the family's refresh row and
|
|
1787
|
+
* mark the family revoked so its access tokens stop verifying.
|
|
1788
|
+
*/
|
|
1805
1789
|
async revokeSuspectRefreshFamily(userId, tokenFamily) {
|
|
1806
|
-
await this.invalidateUserAccessTokens(userId);
|
|
1807
1790
|
if (tokenFamily) {
|
|
1808
|
-
await this.
|
|
1791
|
+
await this.revokeFamily(tokenFamily);
|
|
1809
1792
|
return;
|
|
1810
1793
|
}
|
|
1811
|
-
await this.
|
|
1794
|
+
await this.invalidateUserAccessTokens(userId);
|
|
1795
|
+
await this.revokeAllForUser(userId);
|
|
1812
1796
|
}
|
|
1813
1797
|
/**
|
|
1814
|
-
* Logout
|
|
1798
|
+
* Logout the CURRENT session only — blacklist the presented access token,
|
|
1799
|
+
* mark its family revoked, and delete that family's refresh row. Other
|
|
1800
|
+
* devices/sessions for the same user keep working. Use a password change or
|
|
1801
|
+
* reset (revoke-all) to terminate every session.
|
|
1802
|
+
*
|
|
1803
|
+
* The family is resolved from, in order: a verified Bearer access token's
|
|
1804
|
+
* `tokenFamily` claim, then a verified refresh cookie whose hash still
|
|
1805
|
+
* matches the current family row. If neither is available, fall back to
|
|
1806
|
+
* revoke-all.
|
|
1815
1807
|
*/
|
|
1816
1808
|
async logout(userId, authorization) {
|
|
1809
|
+
let tokenFamily = null;
|
|
1817
1810
|
if (authorization) {
|
|
1811
|
+
let accessToken = null;
|
|
1818
1812
|
try {
|
|
1819
|
-
|
|
1820
|
-
await this.blacklistCurrentToken(accessToken);
|
|
1813
|
+
accessToken = this.extractAccessToken(authorization);
|
|
1821
1814
|
} catch {
|
|
1822
1815
|
}
|
|
1816
|
+
if (accessToken) {
|
|
1817
|
+
try {
|
|
1818
|
+
const decoded = await this.verifyAccessToken(accessToken);
|
|
1819
|
+
if (decoded.userId === userId && decoded.tokenFamily) {
|
|
1820
|
+
tokenFamily = decoded.tokenFamily;
|
|
1821
|
+
}
|
|
1822
|
+
} catch {
|
|
1823
|
+
}
|
|
1824
|
+
await this.blacklistCurrentToken(accessToken);
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
if (!tokenFamily) {
|
|
1828
|
+
tokenFamily = await this.resolveRefreshCookieFamily(userId);
|
|
1829
|
+
}
|
|
1830
|
+
if (tokenFamily) {
|
|
1831
|
+
await this.revokeFamily(tokenFamily);
|
|
1832
|
+
return;
|
|
1823
1833
|
}
|
|
1824
1834
|
await this.invalidateUserAccessTokens(userId);
|
|
1825
|
-
await this.
|
|
1835
|
+
await this.revokeAllForUser(userId);
|
|
1836
|
+
}
|
|
1837
|
+
/**
|
|
1838
|
+
* Resolve a logout target from the refresh cookie only if the cookie maps to
|
|
1839
|
+
* the user's current/valid family row. This mirrors resolveUserFromCookie()
|
|
1840
|
+
* without throwing, because logout can still fall back to revoke-all.
|
|
1841
|
+
*/
|
|
1842
|
+
async resolveRefreshCookieFamily(userId) {
|
|
1843
|
+
const refreshToken = this.cookieManager.getRefreshToken();
|
|
1844
|
+
if (!refreshToken)
|
|
1845
|
+
return null;
|
|
1846
|
+
try {
|
|
1847
|
+
const decoded = this.verifyRefreshToken(refreshToken);
|
|
1848
|
+
if (decoded.userId !== userId)
|
|
1849
|
+
return null;
|
|
1850
|
+
const stored = await this.tokenRepository.getByFamily(decoded.tokenFamily);
|
|
1851
|
+
if (!stored || stored.userId !== userId)
|
|
1852
|
+
return null;
|
|
1853
|
+
const presentedHash = this.hashToken(refreshToken);
|
|
1854
|
+
if (presentedHash === stored.token) {
|
|
1855
|
+
return decoded.tokenFamily;
|
|
1856
|
+
}
|
|
1857
|
+
const canRecover = stored.previousHash && presentedHash === stored.previousHash && stored.previousValidUntil && new Date(stored.previousValidUntil).getTime() > Date.now() && !stored.previousUsedAt;
|
|
1858
|
+
return canRecover ? decoded.tokenFamily : null;
|
|
1859
|
+
} catch {
|
|
1860
|
+
return null;
|
|
1861
|
+
}
|
|
1826
1862
|
}
|
|
1827
1863
|
// ============ PASSWORD RESET TOKENS ============
|
|
1828
1864
|
/**
|
|
1829
|
-
* Generate
|
|
1830
|
-
*
|
|
1865
|
+
* Generate a one-time set-password token (used by both password reset and
|
|
1866
|
+
* account invites). Stores the jti in cache so the token can only be used
|
|
1867
|
+
* once, with a TTL matching the JWT expiry.
|
|
1831
1868
|
*/
|
|
1832
|
-
async
|
|
1833
|
-
const jti =
|
|
1834
|
-
const
|
|
1869
|
+
async generateSetPasswordToken(userId, type, expiresIn) {
|
|
1870
|
+
const jti = nanoid4(16);
|
|
1871
|
+
const data = {
|
|
1835
1872
|
userId,
|
|
1836
|
-
type
|
|
1873
|
+
type,
|
|
1837
1874
|
jti,
|
|
1838
1875
|
timestamp: Date.now()
|
|
1839
1876
|
};
|
|
1840
|
-
const token = jwt.sign(
|
|
1841
|
-
expiresIn
|
|
1877
|
+
const token = jwt.sign(data, this.config.jwt.refreshSecret, {
|
|
1878
|
+
expiresIn
|
|
1842
1879
|
});
|
|
1843
|
-
await this.cache.set(`${this.resetTokenPrefix}${userId}`, jti,
|
|
1880
|
+
await this.cache.set(`${this.resetTokenPrefix}${userId}`, jti, timestring2(expiresIn, "ms"));
|
|
1844
1881
|
return { token, userId };
|
|
1845
1882
|
}
|
|
1883
|
+
/**
|
|
1884
|
+
* Generate secure password reset token
|
|
1885
|
+
* Returns both the plain token (to send via email) and userId for identification
|
|
1886
|
+
*/
|
|
1887
|
+
async generateResetToken(userId) {
|
|
1888
|
+
return this.generateSetPasswordToken(userId, "reset", "1h");
|
|
1889
|
+
}
|
|
1890
|
+
/**
|
|
1891
|
+
* Generate secure account-invite token.
|
|
1892
|
+
* Longer expiry (3d) than reset because an invited user may not check
|
|
1893
|
+
* their email immediately. Consumed via the same reset-password endpoint.
|
|
1894
|
+
*/
|
|
1895
|
+
async generateInviteToken(userId) {
|
|
1896
|
+
return this.generateSetPasswordToken(userId, "invite", "3d");
|
|
1897
|
+
}
|
|
1846
1898
|
/**
|
|
1847
1899
|
* Verify password reset token
|
|
1848
1900
|
* Returns userId if valid, throws error if expired/invalid
|
|
@@ -1854,7 +1906,7 @@ var TokenService = class TokenService2 {
|
|
|
1854
1906
|
} catch {
|
|
1855
1907
|
Err5(this.t("errors.resetTokenExpired"));
|
|
1856
1908
|
}
|
|
1857
|
-
if (decoded.type !== "reset" || !decoded.jti) {
|
|
1909
|
+
if (decoded.type !== "reset" && decoded.type !== "invite" || !decoded.jti) {
|
|
1858
1910
|
Err5(this.t("errors.invalidResetToken"));
|
|
1859
1911
|
}
|
|
1860
1912
|
const key = `${this.resetTokenPrefix}${decoded.userId}`;
|
|
@@ -1943,6 +1995,38 @@ var AuthService = class AuthService2 {
|
|
|
1943
1995
|
async registerUser(body) {
|
|
1944
1996
|
return await this.userService.create(body);
|
|
1945
1997
|
}
|
|
1998
|
+
/**
|
|
1999
|
+
* Admin-initiated account creation. The user is created with a random,
|
|
2000
|
+
* unusable password (the schema requires one) and then emailed a one-time
|
|
2001
|
+
* link to set their own. They can't log in until they do, since they never
|
|
2002
|
+
* learn the random password.
|
|
2003
|
+
*
|
|
2004
|
+
* Email is best-effort: a send failure logs a warning but never rolls back
|
|
2005
|
+
* account creation (and with the console provider, nothing is actually sent).
|
|
2006
|
+
*/
|
|
2007
|
+
async inviteUser(body) {
|
|
2008
|
+
const randomPassword = `${nanoid5(24)}Aa1!`;
|
|
2009
|
+
const user = await this.userService.create({
|
|
2010
|
+
name: body.name,
|
|
2011
|
+
email: body.email,
|
|
2012
|
+
roleId: body.roleId,
|
|
2013
|
+
image: body.image,
|
|
2014
|
+
password: randomPassword,
|
|
2015
|
+
status: "active",
|
|
2016
|
+
emailVerified: false
|
|
2017
|
+
});
|
|
2018
|
+
const { token } = await this.tokenService.generateInviteToken(user.id);
|
|
2019
|
+
const inviteLink = `${this.config.frontendUrl}/reset-password?token=${token}`;
|
|
2020
|
+
try {
|
|
2021
|
+
await this.emailService.sendHtml(body.email, this.t("emails.accountInvite.subject"), accountInviteTemplate({
|
|
2022
|
+
inviteLink,
|
|
2023
|
+
userName: user.name || body.email
|
|
2024
|
+
}));
|
|
2025
|
+
} catch (error) {
|
|
2026
|
+
this.logger.warn("Account invite email failed", { email: body.email, error });
|
|
2027
|
+
}
|
|
2028
|
+
return user;
|
|
2029
|
+
}
|
|
1946
2030
|
async loginUser(body) {
|
|
1947
2031
|
const { email: email2, password } = body;
|
|
1948
2032
|
const user = await this.userService.findByEmail(email2);
|
|
@@ -1972,6 +2056,7 @@ var AuthService = class AuthService2 {
|
|
|
1972
2056
|
if ((user.failedLoginAttempts ?? 0) > 0 || user.lockoutUntil) {
|
|
1973
2057
|
await this.userService.resetFailedAttempts(user.id);
|
|
1974
2058
|
}
|
|
2059
|
+
await this.tokenService.deleteExpiredSessions();
|
|
1975
2060
|
const generated = await this.tokenService.generateTokens(user.id);
|
|
1976
2061
|
this.cookieManager.setRefreshToken(generated.refreshToken);
|
|
1977
2062
|
await this.userService.updateLastLogin(user.id);
|
|
@@ -1982,7 +2067,7 @@ var AuthService = class AuthService2 {
|
|
|
1982
2067
|
roles,
|
|
1983
2068
|
permissions
|
|
1984
2069
|
});
|
|
1985
|
-
const { userId: _userId, roles: _roles, permissions: _permissions, ...tokens } = generated;
|
|
2070
|
+
const { userId: _userId, tokenFamily: _tokenFamily, roles: _roles, permissions: _permissions, ...tokens } = generated;
|
|
1986
2071
|
return { ...tokens, user: sanitized };
|
|
1987
2072
|
}
|
|
1988
2073
|
async refreshTokens() {
|
|
@@ -1996,7 +2081,7 @@ var AuthService = class AuthService2 {
|
|
|
1996
2081
|
permissions: generated.permissions
|
|
1997
2082
|
});
|
|
1998
2083
|
}
|
|
1999
|
-
const { userId: _userId, roles: _roles, permissions: _permissions, ...tokens } = generated;
|
|
2084
|
+
const { userId: _userId, tokenFamily: _tokenFamily, roles: _roles, permissions: _permissions, ...tokens } = generated;
|
|
2000
2085
|
return tokens;
|
|
2001
2086
|
}
|
|
2002
2087
|
async logoutUser(userId, authorization) {
|
|
@@ -2005,6 +2090,15 @@ var AuthService = class AuthService2 {
|
|
|
2005
2090
|
this.cookieManager.clearSessionCookie();
|
|
2006
2091
|
return { data: null, message: this.t("auth.success.logout") };
|
|
2007
2092
|
}
|
|
2093
|
+
/**
|
|
2094
|
+
* Prune expired refresh sessions for every user. Login already prunes
|
|
2095
|
+
* opportunistically; expose this so consumers can also run it from a
|
|
2096
|
+
* scheduled job (cron / queue) to reclaim rows from users who never return.
|
|
2097
|
+
* Best-effort — safe to call repeatedly.
|
|
2098
|
+
*/
|
|
2099
|
+
async pruneExpiredSessions() {
|
|
2100
|
+
await this.tokenService.deleteExpiredSessions();
|
|
2101
|
+
}
|
|
2008
2102
|
async getUserProfile(userData) {
|
|
2009
2103
|
const lang = this.i18nService.getCurrentLanguage();
|
|
2010
2104
|
return {
|
|
@@ -2077,7 +2171,7 @@ var AuthService = class AuthService2 {
|
|
|
2077
2171
|
this.userValidator.validatePasswordStrength(newPassword);
|
|
2078
2172
|
await this.userService.update(userId, { password: newPassword });
|
|
2079
2173
|
await this.tokenService.invalidateUserAccessTokens(userId);
|
|
2080
|
-
await this.tokenService.
|
|
2174
|
+
await this.tokenService.revokeAllForUser(userId);
|
|
2081
2175
|
this.cookieManager.clearRefreshToken();
|
|
2082
2176
|
this.cookieManager.clearSessionCookie();
|
|
2083
2177
|
return { message: this.t("success.passwordChanged") };
|
|
@@ -2087,7 +2181,7 @@ var AuthService = class AuthService2 {
|
|
|
2087
2181
|
this.userValidator.validatePasswordStrength(newPassword);
|
|
2088
2182
|
await this.userService.update(userId, { password: newPassword });
|
|
2089
2183
|
await this.tokenService.invalidateUserAccessTokens(userId);
|
|
2090
|
-
await this.tokenService.
|
|
2184
|
+
await this.tokenService.revokeAllForUser(userId);
|
|
2091
2185
|
this.cookieManager.clearRefreshToken();
|
|
2092
2186
|
this.cookieManager.clearSessionCookie();
|
|
2093
2187
|
return { message: this.t("success.passwordReset") };
|
|
@@ -2146,63 +2240,33 @@ AuthGuard = __decorate12([
|
|
|
2146
2240
|
], AuthGuard);
|
|
2147
2241
|
var isAuth = createGuard(AuthGuard);
|
|
2148
2242
|
|
|
2149
|
-
// src/
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
});
|
|
2168
|
-
var updateUserDto = createUserDto.partial();
|
|
2169
|
-
var userIdParam = z.object({
|
|
2170
|
-
id: z.string().min(1, "User ID is required")
|
|
2171
|
-
});
|
|
2172
|
-
var loginDto = z.object({
|
|
2173
|
-
email: emailField,
|
|
2174
|
-
password: passwordField
|
|
2175
|
-
});
|
|
2176
|
-
var changePasswordDto = z.object({
|
|
2177
|
-
currentPassword: passwordField,
|
|
2178
|
-
newPassword: passwordField
|
|
2179
|
-
});
|
|
2180
|
-
var resetPasswordDto = z.object({
|
|
2181
|
-
email: emailField
|
|
2182
|
-
});
|
|
2183
|
-
var confirmResetPasswordDto = z.object({
|
|
2184
|
-
token: z.string().min(10, "Invalid reset token"),
|
|
2185
|
-
newPassword: passwordField
|
|
2186
|
-
});
|
|
2187
|
-
var languageParam = z.object({
|
|
2188
|
-
language: z.string().min(2)
|
|
2189
|
-
});
|
|
2190
|
-
var emailParam = z.object({
|
|
2191
|
-
email: emailField
|
|
2192
|
-
});
|
|
2193
|
-
var userIdInParam = z.object({
|
|
2194
|
-
userId: z.string().min(1, "User ID is required")
|
|
2195
|
-
});
|
|
2196
|
-
var assignRoleParams = z.object({
|
|
2197
|
-
userId: z.string().min(1, "User ID is required"),
|
|
2198
|
-
roleId: z.string().min(1)
|
|
2199
|
-
});
|
|
2200
|
-
var userListQuery = z.object({
|
|
2201
|
-
limit: z.coerce.number().int().min(1).max(100).default(50),
|
|
2202
|
-
offset: z.coerce.number().int().min(0).default(0)
|
|
2243
|
+
// src/roles/index.ts
|
|
2244
|
+
var roles_exports = {};
|
|
2245
|
+
__export(roles_exports, {
|
|
2246
|
+
ROLES: () => ROLES,
|
|
2247
|
+
ROLE_GROUPS: () => ROLE_GROUPS,
|
|
2248
|
+
Role: () => Role,
|
|
2249
|
+
RoleController: () => RoleController,
|
|
2250
|
+
RoleGuard: () => RoleGuard,
|
|
2251
|
+
RoleRepository: () => RoleRepository,
|
|
2252
|
+
RoleService: () => RoleService,
|
|
2253
|
+
RoleValidator: () => RoleValidator,
|
|
2254
|
+
assignRoleDto: () => assignRoleDto,
|
|
2255
|
+
createRoleDto: () => createRoleDto,
|
|
2256
|
+
defineRoles: () => defineRoles,
|
|
2257
|
+
isAdmin: () => isAdmin,
|
|
2258
|
+
isAdministrator: () => isAdministrator,
|
|
2259
|
+
roleIdParam: () => roleIdParam,
|
|
2260
|
+
updateRoleDto: () => updateRoleDto
|
|
2203
2261
|
});
|
|
2204
2262
|
|
|
2205
|
-
// src/
|
|
2263
|
+
// src/roles/defineRoles.ts
|
|
2264
|
+
import { composeGuards as composeGuards2, createGuard as createGuard3 } from "najm-guard";
|
|
2265
|
+
|
|
2266
|
+
// src/roles/RoleGuards.ts
|
|
2267
|
+
import { Service as Service3 } from "najm-core";
|
|
2268
|
+
import { GuardParams, User as User2 } from "najm-core";
|
|
2269
|
+
import { composeGuards, createGuard as createGuard2 } from "najm-guard";
|
|
2206
2270
|
var __decorate13 = function(decorators, target, key, desc) {
|
|
2207
2271
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2208
2272
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -2217,156 +2281,431 @@ var __param4 = function(paramIndex, decorator) {
|
|
|
2217
2281
|
decorator(target, key, paramIndex);
|
|
2218
2282
|
};
|
|
2219
2283
|
};
|
|
2220
|
-
var
|
|
2221
|
-
var hashKeyPart = /* @__PURE__ */ __name((value) => createHash2("sha256").update(value).digest("base64url").slice(0, 32), "hashKeyPart");
|
|
2222
|
-
var cookieFingerprint = /* @__PURE__ */ __name(() => (ctx) => {
|
|
2223
|
-
const ip = ctx.req.header("x-forwarded-for")?.split(",")[0]?.trim() ?? ctx.req.header("x-real-ip") ?? "unknown";
|
|
2224
|
-
const cookie = ctx.req.raw.headers.get("cookie") ?? "";
|
|
2225
|
-
const fingerprint = cookie ? hashKeyPart(cookie) : "none";
|
|
2226
|
-
return `${ip}:${fingerprint}`;
|
|
2227
|
-
}, "cookieFingerprint");
|
|
2228
|
-
var ipAndEmail = /* @__PURE__ */ __name(async (ctx) => {
|
|
2229
|
-
const ip = ctx.req.header("x-forwarded-for")?.split(",")[0]?.trim() ?? ctx.req.header("x-real-ip") ?? "unknown";
|
|
2230
|
-
try {
|
|
2231
|
-
const body = await ctx.req.json();
|
|
2232
|
-
if (body?.email && typeof body.email === "string") {
|
|
2233
|
-
const normalizedEmail = body.email.trim().toLowerCase();
|
|
2234
|
-
if (normalizedEmail)
|
|
2235
|
-
return `${ip}:${hashKeyPart(normalizedEmail)}`;
|
|
2236
|
-
}
|
|
2237
|
-
} catch {
|
|
2238
|
-
}
|
|
2239
|
-
return ip;
|
|
2240
|
-
}, "ipAndEmail");
|
|
2241
|
-
var AuthController = class AuthController2 {
|
|
2284
|
+
var RoleGuard = class RoleGuard2 {
|
|
2242
2285
|
static {
|
|
2243
|
-
__name(this, "
|
|
2244
|
-
}
|
|
2245
|
-
authService;
|
|
2246
|
-
constructor(authService) {
|
|
2247
|
-
this.authService = authService;
|
|
2248
|
-
}
|
|
2249
|
-
async registerUser(body) {
|
|
2250
|
-
return this.authService.registerUser(body);
|
|
2251
|
-
}
|
|
2252
|
-
async loginUser(body) {
|
|
2253
|
-
return this.authService.loginUser(body);
|
|
2286
|
+
__name(this, "RoleGuard");
|
|
2254
2287
|
}
|
|
2255
|
-
|
|
2256
|
-
|
|
2288
|
+
canActivate(allowedRoles, userRole) {
|
|
2289
|
+
if (!userRole)
|
|
2290
|
+
return false;
|
|
2291
|
+
const requiredRoles = Array.isArray(allowedRoles) ? allowedRoles : [allowedRoles];
|
|
2292
|
+
const hasRole = requiredRoles.some((r) => r.toLowerCase() === userRole.toLowerCase());
|
|
2293
|
+
if (hasRole) {
|
|
2294
|
+
return { role: userRole };
|
|
2295
|
+
}
|
|
2296
|
+
return false;
|
|
2257
2297
|
}
|
|
2258
|
-
|
|
2259
|
-
|
|
2298
|
+
};
|
|
2299
|
+
__decorate13([
|
|
2300
|
+
__param4(0, GuardParams()),
|
|
2301
|
+
__param4(1, User2("role")),
|
|
2302
|
+
__metadata13("design:type", Function),
|
|
2303
|
+
__metadata13("design:paramtypes", [Object, String]),
|
|
2304
|
+
__metadata13("design:returntype", void 0)
|
|
2305
|
+
], RoleGuard.prototype, "canActivate", null);
|
|
2306
|
+
RoleGuard = __decorate13([
|
|
2307
|
+
Service3()
|
|
2308
|
+
], RoleGuard);
|
|
2309
|
+
var Role = createGuard2(RoleGuard);
|
|
2310
|
+
var isAdmin = composeGuards(isAuth(), Role(ROLES.ADMIN));
|
|
2311
|
+
var isAdministrator = composeGuards(isAuth(), Role(ROLE_GROUPS.ADMINISTRATORS));
|
|
2312
|
+
|
|
2313
|
+
// src/roles/defineRoles.ts
|
|
2314
|
+
var Role2 = createGuard3(RoleGuard);
|
|
2315
|
+
function defineRoles(roles, options) {
|
|
2316
|
+
const ROLES2 = roles;
|
|
2317
|
+
const superRoleKeys = options?.superRoles ?? [];
|
|
2318
|
+
function resolveRoleValues(keys) {
|
|
2319
|
+
return Array.from(new Set([...keys, ...superRoleKeys].map((key) => roles[key])));
|
|
2260
2320
|
}
|
|
2261
|
-
|
|
2262
|
-
|
|
2321
|
+
__name(resolveRoleValues, "resolveRoleValues");
|
|
2322
|
+
const guards2 = {};
|
|
2323
|
+
for (const [key, value] of Object.entries(roles)) {
|
|
2324
|
+
const name = `is${key.charAt(0).toUpperCase()}${key.slice(1).toLowerCase()}`;
|
|
2325
|
+
const allowedValues = resolveRoleValues([key]);
|
|
2326
|
+
guards2[name] = composeGuards2(isAuth(), Role2(allowedValues.length === 1 ? value : allowedValues));
|
|
2263
2327
|
}
|
|
2264
|
-
|
|
2265
|
-
|
|
2328
|
+
function createGroupGuard(keys) {
|
|
2329
|
+
const values = resolveRoleValues(keys);
|
|
2330
|
+
return composeGuards2(isAuth(), Role2(values));
|
|
2266
2331
|
}
|
|
2267
|
-
|
|
2268
|
-
|
|
2332
|
+
__name(createGroupGuard, "createGroupGuard");
|
|
2333
|
+
function hasRole(userRole, ...keys) {
|
|
2334
|
+
if (!userRole)
|
|
2335
|
+
return false;
|
|
2336
|
+
const normalized = userRole.toLowerCase();
|
|
2337
|
+
return resolveRoleValues(keys).some((role) => role === normalized);
|
|
2269
2338
|
}
|
|
2270
|
-
|
|
2271
|
-
|
|
2339
|
+
__name(hasRole, "hasRole");
|
|
2340
|
+
function isInGroup(userRole, keys) {
|
|
2341
|
+
return hasRole(userRole, ...keys);
|
|
2272
2342
|
}
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2343
|
+
__name(isInGroup, "isInGroup");
|
|
2344
|
+
return { ROLES: ROLES2, createGroupGuard, hasRole, isInGroup, ...guards2 };
|
|
2345
|
+
}
|
|
2346
|
+
__name(defineRoles, "defineRoles");
|
|
2347
|
+
|
|
2348
|
+
// src/roles/RoleController.ts
|
|
2349
|
+
import { Controller } from "najm-core";
|
|
2350
|
+
import { Get, Post, Put, Delete, ResMsg } from "najm-core";
|
|
2351
|
+
import { Params, Body } from "najm-core";
|
|
2352
|
+
import { Validate } from "najm-validation";
|
|
2353
|
+
|
|
2354
|
+
// src/roles/RoleDto.ts
|
|
2355
|
+
import { z } from "zod";
|
|
2356
|
+
var nameField = z.string().min(2, "Name must be at least 2 characters").max(50, "Name too long");
|
|
2357
|
+
var descriptionField = z.string().max(255, "Description too long").optional();
|
|
2358
|
+
var createRoleDto = z.object({
|
|
2359
|
+
name: nameField,
|
|
2360
|
+
description: descriptionField
|
|
2361
|
+
});
|
|
2362
|
+
var updateRoleDto = createRoleDto.partial();
|
|
2363
|
+
var roleIdParam = z.object({
|
|
2364
|
+
id: z.string().length(5, "Role ID must be 5 characters")
|
|
2365
|
+
});
|
|
2366
|
+
var assignRoleDto = z.object({
|
|
2367
|
+
userId: z.string().length(8, "User ID must be 8 characters"),
|
|
2368
|
+
roleId: z.string().length(5, "Role ID must be 5 characters")
|
|
2369
|
+
});
|
|
2370
|
+
|
|
2371
|
+
// src/roles/RoleController.ts
|
|
2372
|
+
var __decorate14 = function(decorators, target, key, desc) {
|
|
2373
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2374
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
2375
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
2376
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2377
|
+
};
|
|
2378
|
+
var __metadata14 = function(k, v) {
|
|
2379
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
2380
|
+
};
|
|
2381
|
+
var __param5 = function(paramIndex, decorator) {
|
|
2382
|
+
return function(target, key) {
|
|
2383
|
+
decorator(target, key, paramIndex);
|
|
2384
|
+
};
|
|
2385
|
+
};
|
|
2386
|
+
var _a8;
|
|
2387
|
+
var RoleController = class RoleController2 {
|
|
2388
|
+
static {
|
|
2389
|
+
__name(this, "RoleController");
|
|
2390
|
+
}
|
|
2391
|
+
roleService;
|
|
2392
|
+
constructor(roleService) {
|
|
2393
|
+
this.roleService = roleService;
|
|
2394
|
+
}
|
|
2395
|
+
async getRoles() {
|
|
2396
|
+
return this.roleService.getAll();
|
|
2397
|
+
}
|
|
2398
|
+
async getRole(params) {
|
|
2399
|
+
return this.roleService.getById(params.id);
|
|
2400
|
+
}
|
|
2401
|
+
async createRole(body) {
|
|
2402
|
+
return this.roleService.create(body);
|
|
2403
|
+
}
|
|
2404
|
+
async updateRole(params, body) {
|
|
2405
|
+
return this.roleService.update(params.id, body);
|
|
2406
|
+
}
|
|
2407
|
+
async deleteRole(params) {
|
|
2408
|
+
return this.roleService.delete(params.id);
|
|
2409
|
+
}
|
|
2410
|
+
};
|
|
2411
|
+
__decorate14([
|
|
2412
|
+
Get(),
|
|
2413
|
+
isAdmin(),
|
|
2414
|
+
ResMsg("roles.success.retrieved"),
|
|
2415
|
+
__metadata14("design:type", Function),
|
|
2416
|
+
__metadata14("design:paramtypes", []),
|
|
2417
|
+
__metadata14("design:returntype", Promise)
|
|
2418
|
+
], RoleController.prototype, "getRoles", null);
|
|
2419
|
+
__decorate14([
|
|
2420
|
+
Get("/:id"),
|
|
2421
|
+
isAdmin(),
|
|
2422
|
+
Validate({ params: roleIdParam }),
|
|
2423
|
+
ResMsg("roles.success.retrieved"),
|
|
2424
|
+
__param5(0, Params()),
|
|
2425
|
+
__metadata14("design:type", Function),
|
|
2426
|
+
__metadata14("design:paramtypes", [Object]),
|
|
2427
|
+
__metadata14("design:returntype", Promise)
|
|
2428
|
+
], RoleController.prototype, "getRole", null);
|
|
2429
|
+
__decorate14([
|
|
2430
|
+
Post(),
|
|
2431
|
+
isAdmin(),
|
|
2432
|
+
Validate(createRoleDto),
|
|
2433
|
+
ResMsg("roles.success.created"),
|
|
2434
|
+
__param5(0, Body()),
|
|
2435
|
+
__metadata14("design:type", Function),
|
|
2436
|
+
__metadata14("design:paramtypes", [Object]),
|
|
2437
|
+
__metadata14("design:returntype", Promise)
|
|
2438
|
+
], RoleController.prototype, "createRole", null);
|
|
2439
|
+
__decorate14([
|
|
2440
|
+
Put("/:id"),
|
|
2441
|
+
isAdmin(),
|
|
2442
|
+
Validate({
|
|
2443
|
+
params: roleIdParam,
|
|
2444
|
+
body: updateRoleDto
|
|
2445
|
+
}),
|
|
2446
|
+
ResMsg("roles.success.updated"),
|
|
2447
|
+
__param5(0, Params()),
|
|
2448
|
+
__param5(1, Body()),
|
|
2449
|
+
__metadata14("design:type", Function),
|
|
2450
|
+
__metadata14("design:paramtypes", [Object, Object]),
|
|
2451
|
+
__metadata14("design:returntype", Promise)
|
|
2452
|
+
], RoleController.prototype, "updateRole", null);
|
|
2453
|
+
__decorate14([
|
|
2454
|
+
Delete("/:id"),
|
|
2455
|
+
isAdmin(),
|
|
2456
|
+
Validate({ params: roleIdParam }),
|
|
2457
|
+
ResMsg("roles.success.deleted"),
|
|
2458
|
+
__param5(0, Params()),
|
|
2459
|
+
__metadata14("design:type", Function),
|
|
2460
|
+
__metadata14("design:paramtypes", [Object]),
|
|
2461
|
+
__metadata14("design:returntype", Promise)
|
|
2462
|
+
], RoleController.prototype, "deleteRole", null);
|
|
2463
|
+
RoleController = __decorate14([
|
|
2464
|
+
Controller("/roles"),
|
|
2465
|
+
__metadata14("design:paramtypes", [typeof (_a8 = typeof RoleService !== "undefined" && RoleService) === "function" ? _a8 : Object])
|
|
2466
|
+
], RoleController);
|
|
2467
|
+
|
|
2468
|
+
// src/auth/AuthController.ts
|
|
2469
|
+
import { Validate as Validate2 } from "najm-validation";
|
|
2470
|
+
import { RateLimit } from "najm-rate";
|
|
2471
|
+
import { createHash as createHash2 } from "crypto";
|
|
2472
|
+
|
|
2473
|
+
// src/users/UserDto.ts
|
|
2474
|
+
import { z as z2 } from "zod";
|
|
2475
|
+
var emailField = z2.string().email("Invalid email format");
|
|
2476
|
+
var passwordField = z2.string().min(8, "Password must be at least 8 characters");
|
|
2477
|
+
var optionalDateField = z2.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Date must be in YYYY-MM-DD format").nullable().optional();
|
|
2478
|
+
var createUserDto = z2.object({
|
|
2479
|
+
name: z2.string().max(100).optional(),
|
|
2480
|
+
email: emailField,
|
|
2481
|
+
password: passwordField,
|
|
2482
|
+
roleId: z2.string().min(1).optional(),
|
|
2483
|
+
image: z2.string().nullish(),
|
|
2484
|
+
emailVerified: z2.boolean().default(false),
|
|
2485
|
+
status: z2.enum(["active", "inactive", "pending"]).optional()
|
|
2486
|
+
});
|
|
2487
|
+
var updateUserDto = createUserDto.partial();
|
|
2488
|
+
var inviteUserDto = z2.object({
|
|
2489
|
+
name: z2.string().max(100).optional(),
|
|
2490
|
+
email: emailField,
|
|
2491
|
+
roleId: z2.string().min(1).optional(),
|
|
2492
|
+
image: z2.string().nullish()
|
|
2493
|
+
});
|
|
2494
|
+
var userIdParam = z2.object({
|
|
2495
|
+
id: z2.string().min(1, "User ID is required")
|
|
2496
|
+
});
|
|
2497
|
+
var loginDto = z2.object({
|
|
2498
|
+
email: emailField,
|
|
2499
|
+
password: passwordField
|
|
2500
|
+
});
|
|
2501
|
+
var changePasswordDto = z2.object({
|
|
2502
|
+
currentPassword: passwordField,
|
|
2503
|
+
newPassword: passwordField
|
|
2504
|
+
});
|
|
2505
|
+
var resetPasswordDto = z2.object({
|
|
2506
|
+
email: emailField
|
|
2507
|
+
});
|
|
2508
|
+
var confirmResetPasswordDto = z2.object({
|
|
2509
|
+
token: z2.string().min(10, "Invalid reset token"),
|
|
2510
|
+
newPassword: passwordField
|
|
2511
|
+
});
|
|
2512
|
+
var languageParam = z2.object({
|
|
2513
|
+
language: z2.string().min(2)
|
|
2514
|
+
});
|
|
2515
|
+
var emailParam = z2.object({
|
|
2516
|
+
email: emailField
|
|
2517
|
+
});
|
|
2518
|
+
var userIdInParam = z2.object({
|
|
2519
|
+
userId: z2.string().min(1, "User ID is required")
|
|
2520
|
+
});
|
|
2521
|
+
var assignRoleParams = z2.object({
|
|
2522
|
+
userId: z2.string().min(1, "User ID is required"),
|
|
2523
|
+
roleId: z2.string().min(1)
|
|
2524
|
+
});
|
|
2525
|
+
var userListQuery = z2.object({
|
|
2526
|
+
limit: z2.coerce.number().int().min(1).max(100).default(50),
|
|
2527
|
+
offset: z2.coerce.number().int().min(0).default(0)
|
|
2528
|
+
});
|
|
2529
|
+
|
|
2530
|
+
// src/auth/AuthController.ts
|
|
2531
|
+
var __decorate15 = function(decorators, target, key, desc) {
|
|
2532
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2533
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
2534
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
2535
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2536
|
+
};
|
|
2537
|
+
var __metadata15 = function(k, v) {
|
|
2538
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
2539
|
+
};
|
|
2540
|
+
var __param6 = function(paramIndex, decorator) {
|
|
2541
|
+
return function(target, key) {
|
|
2542
|
+
decorator(target, key, paramIndex);
|
|
2543
|
+
};
|
|
2544
|
+
};
|
|
2545
|
+
var _a9;
|
|
2546
|
+
var hashKeyPart = /* @__PURE__ */ __name((value) => createHash2("sha256").update(value).digest("base64url").slice(0, 32), "hashKeyPart");
|
|
2547
|
+
var cookieFingerprint = /* @__PURE__ */ __name(() => (ctx) => {
|
|
2548
|
+
const ip = ctx.req.header("x-forwarded-for")?.split(",")[0]?.trim() ?? ctx.req.header("x-real-ip") ?? "unknown";
|
|
2549
|
+
const cookie = ctx.req.raw.headers.get("cookie") ?? "";
|
|
2550
|
+
const fingerprint = cookie ? hashKeyPart(cookie) : "none";
|
|
2551
|
+
return `${ip}:${fingerprint}`;
|
|
2552
|
+
}, "cookieFingerprint");
|
|
2553
|
+
var ipAndEmail = /* @__PURE__ */ __name(async (ctx) => {
|
|
2554
|
+
const ip = ctx.req.header("x-forwarded-for")?.split(",")[0]?.trim() ?? ctx.req.header("x-real-ip") ?? "unknown";
|
|
2555
|
+
try {
|
|
2556
|
+
const body = await ctx.req.json();
|
|
2557
|
+
if (body?.email && typeof body.email === "string") {
|
|
2558
|
+
const normalizedEmail = body.email.trim().toLowerCase();
|
|
2559
|
+
if (normalizedEmail)
|
|
2560
|
+
return `${ip}:${hashKeyPart(normalizedEmail)}`;
|
|
2561
|
+
}
|
|
2562
|
+
} catch {
|
|
2563
|
+
}
|
|
2564
|
+
return ip;
|
|
2565
|
+
}, "ipAndEmail");
|
|
2566
|
+
var AuthController = class AuthController2 {
|
|
2567
|
+
static {
|
|
2568
|
+
__name(this, "AuthController");
|
|
2569
|
+
}
|
|
2570
|
+
authService;
|
|
2571
|
+
constructor(authService) {
|
|
2572
|
+
this.authService = authService;
|
|
2573
|
+
}
|
|
2574
|
+
async registerUser(body) {
|
|
2575
|
+
return this.authService.registerUser(body);
|
|
2576
|
+
}
|
|
2577
|
+
async loginUser(body) {
|
|
2578
|
+
return this.authService.loginUser(body);
|
|
2579
|
+
}
|
|
2580
|
+
async inviteUser(body) {
|
|
2581
|
+
return this.authService.inviteUser(body);
|
|
2582
|
+
}
|
|
2583
|
+
async refreshTokens() {
|
|
2584
|
+
return this.authService.refreshTokens();
|
|
2585
|
+
}
|
|
2586
|
+
async logoutUser(userId, authorization) {
|
|
2587
|
+
return this.authService.logoutUser(userId, authorization);
|
|
2588
|
+
}
|
|
2589
|
+
async changePassword(userId, body) {
|
|
2590
|
+
return this.authService.changePassword(userId, body.currentPassword, body.newPassword);
|
|
2591
|
+
}
|
|
2592
|
+
async userProfile(authorization) {
|
|
2593
|
+
return this.authService.getMe(authorization);
|
|
2594
|
+
}
|
|
2595
|
+
async forgotPassword(body) {
|
|
2596
|
+
return this.authService.forgotPassword(body.email);
|
|
2597
|
+
}
|
|
2598
|
+
async resetPassword(body) {
|
|
2599
|
+
return this.authService.resetPassword(body.token, body.newPassword);
|
|
2600
|
+
}
|
|
2601
|
+
};
|
|
2602
|
+
__decorate15([
|
|
2603
|
+
Post2("/register"),
|
|
2604
|
+
RateLimit({ limit: 5, window: "15m", key: ipAndEmail }),
|
|
2605
|
+
Validate2(createUserDto),
|
|
2606
|
+
ResMsg2("auth.success.register"),
|
|
2607
|
+
__param6(0, Body2()),
|
|
2608
|
+
__metadata15("design:type", Function),
|
|
2609
|
+
__metadata15("design:paramtypes", [Object]),
|
|
2610
|
+
__metadata15("design:returntype", Promise)
|
|
2611
|
+
], AuthController.prototype, "registerUser", null);
|
|
2612
|
+
__decorate15([
|
|
2613
|
+
Post2("/login"),
|
|
2614
|
+
RateLimit({ limit: 5, window: "15m", key: ipAndEmail, message: "Too many login attempts. Please try again later." }),
|
|
2615
|
+
Validate2(loginDto),
|
|
2616
|
+
ResMsg2("auth.success.login"),
|
|
2617
|
+
__param6(0, Body2()),
|
|
2618
|
+
__metadata15("design:type", Function),
|
|
2619
|
+
__metadata15("design:paramtypes", [Object]),
|
|
2620
|
+
__metadata15("design:returntype", Promise)
|
|
2621
|
+
], AuthController.prototype, "loginUser", null);
|
|
2622
|
+
__decorate15([
|
|
2623
|
+
Post2("/invite"),
|
|
2624
|
+
isAdmin(),
|
|
2625
|
+
RateLimit({ limit: 20, window: "15m", key: "user" }),
|
|
2626
|
+
Validate2(inviteUserDto),
|
|
2627
|
+
ResMsg2("auth.success.accountInviteSent"),
|
|
2628
|
+
__param6(0, Body2()),
|
|
2629
|
+
__metadata15("design:type", Function),
|
|
2630
|
+
__metadata15("design:paramtypes", [Object]),
|
|
2631
|
+
__metadata15("design:returntype", Promise)
|
|
2632
|
+
], AuthController.prototype, "inviteUser", null);
|
|
2633
|
+
__decorate15([
|
|
2634
|
+
Post2("/refresh"),
|
|
2296
2635
|
RateLimit({ limit: 15, window: "15m", key: cookieFingerprint() }),
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2636
|
+
ResMsg2("auth.success.tokenRefreshed"),
|
|
2637
|
+
__metadata15("design:type", Function),
|
|
2638
|
+
__metadata15("design:paramtypes", []),
|
|
2639
|
+
__metadata15("design:returntype", Promise)
|
|
2301
2640
|
], AuthController.prototype, "refreshTokens", null);
|
|
2302
|
-
|
|
2303
|
-
|
|
2641
|
+
__decorate15([
|
|
2642
|
+
Post2("/logout"),
|
|
2304
2643
|
isAuth(),
|
|
2305
2644
|
RateLimit({ limit: 10, window: "15m", key: "user" }),
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2645
|
+
__param6(0, User3("id")),
|
|
2646
|
+
__param6(1, Headers("authorization")),
|
|
2647
|
+
__metadata15("design:type", Function),
|
|
2648
|
+
__metadata15("design:paramtypes", [String, String]),
|
|
2649
|
+
__metadata15("design:returntype", Promise)
|
|
2311
2650
|
], AuthController.prototype, "logoutUser", null);
|
|
2312
|
-
|
|
2313
|
-
|
|
2651
|
+
__decorate15([
|
|
2652
|
+
Post2("/change-password"),
|
|
2314
2653
|
isAuth(),
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2654
|
+
Validate2(changePasswordDto),
|
|
2655
|
+
ResMsg2("auth.success.passwordChanged"),
|
|
2656
|
+
__param6(0, User3("id")),
|
|
2657
|
+
__param6(1, Body2()),
|
|
2658
|
+
__metadata15("design:type", Function),
|
|
2659
|
+
__metadata15("design:paramtypes", [String, Object]),
|
|
2660
|
+
__metadata15("design:returntype", Promise)
|
|
2322
2661
|
], AuthController.prototype, "changePassword", null);
|
|
2323
|
-
|
|
2324
|
-
|
|
2662
|
+
__decorate15([
|
|
2663
|
+
Get2("/me"),
|
|
2325
2664
|
RateLimit({ limit: 30, window: "1m", key: cookieFingerprint() }),
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2665
|
+
ResMsg2("auth.users.success.retrieved"),
|
|
2666
|
+
__param6(0, Headers("authorization")),
|
|
2667
|
+
__metadata15("design:type", Function),
|
|
2668
|
+
__metadata15("design:paramtypes", [String]),
|
|
2669
|
+
__metadata15("design:returntype", Promise)
|
|
2331
2670
|
], AuthController.prototype, "userProfile", null);
|
|
2332
|
-
|
|
2333
|
-
|
|
2671
|
+
__decorate15([
|
|
2672
|
+
Post2("/forgot-password"),
|
|
2334
2673
|
RateLimit({ limit: 3, window: "15m", key: ipAndEmail, message: "Too many password reset requests. Please try again later." }),
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2674
|
+
Validate2(resetPasswordDto),
|
|
2675
|
+
ResMsg2("auth.success.passwordResetSent"),
|
|
2676
|
+
__param6(0, Body2()),
|
|
2677
|
+
__metadata15("design:type", Function),
|
|
2678
|
+
__metadata15("design:paramtypes", [Object]),
|
|
2679
|
+
__metadata15("design:returntype", Promise)
|
|
2341
2680
|
], AuthController.prototype, "forgotPassword", null);
|
|
2342
|
-
|
|
2343
|
-
|
|
2681
|
+
__decorate15([
|
|
2682
|
+
Post2("/reset-password"),
|
|
2344
2683
|
RateLimit({ limit: 5, window: "15m", key: "ip", message: "Too many password reset attempts. Please try again later." }),
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2684
|
+
Validate2(confirmResetPasswordDto),
|
|
2685
|
+
ResMsg2("auth.success.passwordReset"),
|
|
2686
|
+
__param6(0, Body2()),
|
|
2687
|
+
__metadata15("design:type", Function),
|
|
2688
|
+
__metadata15("design:paramtypes", [Object]),
|
|
2689
|
+
__metadata15("design:returntype", Promise)
|
|
2351
2690
|
], AuthController.prototype, "resetPassword", null);
|
|
2352
|
-
AuthController =
|
|
2353
|
-
|
|
2354
|
-
|
|
2691
|
+
AuthController = __decorate15([
|
|
2692
|
+
Controller2("/auth"),
|
|
2693
|
+
__metadata15("design:paramtypes", [typeof (_a9 = typeof AuthService !== "undefined" && AuthService) === "function" ? _a9 : Object])
|
|
2355
2694
|
], AuthController);
|
|
2356
2695
|
|
|
2357
2696
|
// src/auth/AuthResolver.ts
|
|
2358
|
-
import { APP, Container, DI, Inject as Inject9, LOGGER, Meta, Service as
|
|
2697
|
+
import { APP, Container, DI, Inject as Inject9, LOGGER, Meta, Service as Service4 } from "najm-core";
|
|
2359
2698
|
import { USER, ROLE, PERMISSIONS } from "najm-guard";
|
|
2360
|
-
var
|
|
2699
|
+
var __decorate16 = function(decorators, target, key, desc) {
|
|
2361
2700
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2362
2701
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
2363
2702
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
2364
2703
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2365
2704
|
};
|
|
2366
|
-
var
|
|
2705
|
+
var __metadata16 = function(k, v) {
|
|
2367
2706
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
2368
2707
|
};
|
|
2369
|
-
var
|
|
2708
|
+
var _a10;
|
|
2370
2709
|
var AuthResolver = class AuthResolver2 {
|
|
2371
2710
|
static {
|
|
2372
2711
|
__name(this, "AuthResolver");
|
|
@@ -2464,20 +2803,20 @@ var AuthResolver = class AuthResolver2 {
|
|
|
2464
2803
|
await authService.warmupPasswordHash();
|
|
2465
2804
|
}
|
|
2466
2805
|
};
|
|
2467
|
-
|
|
2806
|
+
__decorate16([
|
|
2468
2807
|
DI(),
|
|
2469
|
-
|
|
2808
|
+
__metadata16("design:type", typeof (_a10 = typeof Container !== "undefined" && Container) === "function" ? _a10 : Object)
|
|
2470
2809
|
], AuthResolver.prototype, "container", void 0);
|
|
2471
|
-
|
|
2810
|
+
__decorate16([
|
|
2472
2811
|
Inject9(APP),
|
|
2473
|
-
|
|
2812
|
+
__metadata16("design:type", Object)
|
|
2474
2813
|
], AuthResolver.prototype, "app", void 0);
|
|
2475
|
-
|
|
2814
|
+
__decorate16([
|
|
2476
2815
|
Inject9(LOGGER),
|
|
2477
|
-
|
|
2816
|
+
__metadata16("design:type", Object)
|
|
2478
2817
|
], AuthResolver.prototype, "log", void 0);
|
|
2479
|
-
AuthResolver =
|
|
2480
|
-
|
|
2818
|
+
AuthResolver = __decorate16([
|
|
2819
|
+
Service4(),
|
|
2481
2820
|
Meta({ layer: "plugin", order: 30 })
|
|
2482
2821
|
], AuthResolver);
|
|
2483
2822
|
|
|
@@ -2526,6 +2865,7 @@ __export(users_exports, {
|
|
|
2526
2865
|
confirmResetPasswordDto: () => confirmResetPasswordDto,
|
|
2527
2866
|
createUserDto: () => createUserDto,
|
|
2528
2867
|
emailParam: () => emailParam,
|
|
2868
|
+
inviteUserDto: () => inviteUserDto,
|
|
2529
2869
|
languageParam: () => languageParam,
|
|
2530
2870
|
loginDto: () => loginDto,
|
|
2531
2871
|
resetPasswordDto: () => resetPasswordDto,
|
|
@@ -2539,233 +2879,6 @@ __export(users_exports, {
|
|
|
2539
2879
|
import { Controller as Controller3 } from "najm-core";
|
|
2540
2880
|
import { Get as Get3, Post as Post3, Put as Put2, Delete as Delete2, ResMsg as ResMsg3 } from "najm-core";
|
|
2541
2881
|
import { Params as Params2, Body as Body3, Query } from "najm-core";
|
|
2542
|
-
|
|
2543
|
-
// src/roles/index.ts
|
|
2544
|
-
var roles_exports = {};
|
|
2545
|
-
__export(roles_exports, {
|
|
2546
|
-
ROLES: () => ROLES,
|
|
2547
|
-
ROLE_GROUPS: () => ROLE_GROUPS,
|
|
2548
|
-
Role: () => Role,
|
|
2549
|
-
RoleController: () => RoleController,
|
|
2550
|
-
RoleGuard: () => RoleGuard,
|
|
2551
|
-
RoleRepository: () => RoleRepository,
|
|
2552
|
-
RoleService: () => RoleService,
|
|
2553
|
-
RoleValidator: () => RoleValidator,
|
|
2554
|
-
assignRoleDto: () => assignRoleDto,
|
|
2555
|
-
createRoleDto: () => createRoleDto,
|
|
2556
|
-
defineRoles: () => defineRoles,
|
|
2557
|
-
isAdmin: () => isAdmin,
|
|
2558
|
-
isAdministrator: () => isAdministrator,
|
|
2559
|
-
roleIdParam: () => roleIdParam,
|
|
2560
|
-
updateRoleDto: () => updateRoleDto
|
|
2561
|
-
});
|
|
2562
|
-
|
|
2563
|
-
// src/roles/defineRoles.ts
|
|
2564
|
-
import { composeGuards as composeGuards2, createGuard as createGuard3 } from "najm-guard";
|
|
2565
|
-
|
|
2566
|
-
// src/roles/RoleGuards.ts
|
|
2567
|
-
import { Service as Service4 } from "najm-core";
|
|
2568
|
-
import { GuardParams, User as User3 } from "najm-core";
|
|
2569
|
-
import { composeGuards, createGuard as createGuard2 } from "najm-guard";
|
|
2570
|
-
var __decorate15 = function(decorators, target, key, desc) {
|
|
2571
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2572
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
2573
|
-
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
2574
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2575
|
-
};
|
|
2576
|
-
var __metadata15 = function(k, v) {
|
|
2577
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
2578
|
-
};
|
|
2579
|
-
var __param5 = function(paramIndex, decorator) {
|
|
2580
|
-
return function(target, key) {
|
|
2581
|
-
decorator(target, key, paramIndex);
|
|
2582
|
-
};
|
|
2583
|
-
};
|
|
2584
|
-
var RoleGuard = class RoleGuard2 {
|
|
2585
|
-
static {
|
|
2586
|
-
__name(this, "RoleGuard");
|
|
2587
|
-
}
|
|
2588
|
-
canActivate(allowedRoles, userRole) {
|
|
2589
|
-
if (!userRole)
|
|
2590
|
-
return false;
|
|
2591
|
-
const requiredRoles = Array.isArray(allowedRoles) ? allowedRoles : [allowedRoles];
|
|
2592
|
-
const hasRole = requiredRoles.some((r) => r.toLowerCase() === userRole.toLowerCase());
|
|
2593
|
-
if (hasRole) {
|
|
2594
|
-
return { role: userRole };
|
|
2595
|
-
}
|
|
2596
|
-
return false;
|
|
2597
|
-
}
|
|
2598
|
-
};
|
|
2599
|
-
__decorate15([
|
|
2600
|
-
__param5(0, GuardParams()),
|
|
2601
|
-
__param5(1, User3("role")),
|
|
2602
|
-
__metadata15("design:type", Function),
|
|
2603
|
-
__metadata15("design:paramtypes", [Object, String]),
|
|
2604
|
-
__metadata15("design:returntype", void 0)
|
|
2605
|
-
], RoleGuard.prototype, "canActivate", null);
|
|
2606
|
-
RoleGuard = __decorate15([
|
|
2607
|
-
Service4()
|
|
2608
|
-
], RoleGuard);
|
|
2609
|
-
var Role = createGuard2(RoleGuard);
|
|
2610
|
-
var isAdmin = composeGuards(isAuth(), Role(ROLES.ADMIN));
|
|
2611
|
-
var isAdministrator = composeGuards(isAuth(), Role(ROLE_GROUPS.ADMINISTRATORS));
|
|
2612
|
-
|
|
2613
|
-
// src/roles/defineRoles.ts
|
|
2614
|
-
var Role2 = createGuard3(RoleGuard);
|
|
2615
|
-
function defineRoles(roles, options) {
|
|
2616
|
-
const ROLES2 = roles;
|
|
2617
|
-
const superRoleKeys = options?.superRoles ?? [];
|
|
2618
|
-
function resolveRoleValues(keys) {
|
|
2619
|
-
return Array.from(new Set([...keys, ...superRoleKeys].map((key) => roles[key])));
|
|
2620
|
-
}
|
|
2621
|
-
__name(resolveRoleValues, "resolveRoleValues");
|
|
2622
|
-
const guards2 = {};
|
|
2623
|
-
for (const [key, value] of Object.entries(roles)) {
|
|
2624
|
-
const name = `is${key.charAt(0).toUpperCase()}${key.slice(1).toLowerCase()}`;
|
|
2625
|
-
const allowedValues = resolveRoleValues([key]);
|
|
2626
|
-
guards2[name] = composeGuards2(isAuth(), Role2(allowedValues.length === 1 ? value : allowedValues));
|
|
2627
|
-
}
|
|
2628
|
-
function createGroupGuard(keys) {
|
|
2629
|
-
const values = resolveRoleValues(keys);
|
|
2630
|
-
return composeGuards2(isAuth(), Role2(values));
|
|
2631
|
-
}
|
|
2632
|
-
__name(createGroupGuard, "createGroupGuard");
|
|
2633
|
-
function hasRole(userRole, ...keys) {
|
|
2634
|
-
if (!userRole)
|
|
2635
|
-
return false;
|
|
2636
|
-
const normalized = userRole.toLowerCase();
|
|
2637
|
-
return resolveRoleValues(keys).some((role) => role === normalized);
|
|
2638
|
-
}
|
|
2639
|
-
__name(hasRole, "hasRole");
|
|
2640
|
-
function isInGroup(userRole, keys) {
|
|
2641
|
-
return hasRole(userRole, ...keys);
|
|
2642
|
-
}
|
|
2643
|
-
__name(isInGroup, "isInGroup");
|
|
2644
|
-
return { ROLES: ROLES2, createGroupGuard, hasRole, isInGroup, ...guards2 };
|
|
2645
|
-
}
|
|
2646
|
-
__name(defineRoles, "defineRoles");
|
|
2647
|
-
|
|
2648
|
-
// src/roles/RoleController.ts
|
|
2649
|
-
import { Controller as Controller2 } from "najm-core";
|
|
2650
|
-
import { Get as Get2, Post as Post2, Put, Delete, ResMsg as ResMsg2 } from "najm-core";
|
|
2651
|
-
import { Params, Body as Body2 } from "najm-core";
|
|
2652
|
-
import { Validate as Validate2 } from "najm-validation";
|
|
2653
|
-
|
|
2654
|
-
// src/roles/RoleDto.ts
|
|
2655
|
-
import { z as z2 } from "zod";
|
|
2656
|
-
var nameField = z2.string().min(2, "Name must be at least 2 characters").max(50, "Name too long");
|
|
2657
|
-
var descriptionField = z2.string().max(255, "Description too long").optional();
|
|
2658
|
-
var createRoleDto = z2.object({
|
|
2659
|
-
name: nameField,
|
|
2660
|
-
description: descriptionField
|
|
2661
|
-
});
|
|
2662
|
-
var updateRoleDto = createRoleDto.partial();
|
|
2663
|
-
var roleIdParam = z2.object({
|
|
2664
|
-
id: z2.string().length(5, "Role ID must be 5 characters")
|
|
2665
|
-
});
|
|
2666
|
-
var assignRoleDto = z2.object({
|
|
2667
|
-
userId: z2.string().length(8, "User ID must be 8 characters"),
|
|
2668
|
-
roleId: z2.string().length(5, "Role ID must be 5 characters")
|
|
2669
|
-
});
|
|
2670
|
-
|
|
2671
|
-
// src/roles/RoleController.ts
|
|
2672
|
-
var __decorate16 = function(decorators, target, key, desc) {
|
|
2673
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2674
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
2675
|
-
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
2676
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2677
|
-
};
|
|
2678
|
-
var __metadata16 = function(k, v) {
|
|
2679
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
2680
|
-
};
|
|
2681
|
-
var __param6 = function(paramIndex, decorator) {
|
|
2682
|
-
return function(target, key) {
|
|
2683
|
-
decorator(target, key, paramIndex);
|
|
2684
|
-
};
|
|
2685
|
-
};
|
|
2686
|
-
var _a10;
|
|
2687
|
-
var RoleController = class RoleController2 {
|
|
2688
|
-
static {
|
|
2689
|
-
__name(this, "RoleController");
|
|
2690
|
-
}
|
|
2691
|
-
roleService;
|
|
2692
|
-
constructor(roleService) {
|
|
2693
|
-
this.roleService = roleService;
|
|
2694
|
-
}
|
|
2695
|
-
async getRoles() {
|
|
2696
|
-
return this.roleService.getAll();
|
|
2697
|
-
}
|
|
2698
|
-
async getRole(params) {
|
|
2699
|
-
return this.roleService.getById(params.id);
|
|
2700
|
-
}
|
|
2701
|
-
async createRole(body) {
|
|
2702
|
-
return this.roleService.create(body);
|
|
2703
|
-
}
|
|
2704
|
-
async updateRole(params, body) {
|
|
2705
|
-
return this.roleService.update(params.id, body);
|
|
2706
|
-
}
|
|
2707
|
-
async deleteRole(params) {
|
|
2708
|
-
return this.roleService.delete(params.id);
|
|
2709
|
-
}
|
|
2710
|
-
};
|
|
2711
|
-
__decorate16([
|
|
2712
|
-
Get2(),
|
|
2713
|
-
isAdmin(),
|
|
2714
|
-
ResMsg2("roles.success.retrieved"),
|
|
2715
|
-
__metadata16("design:type", Function),
|
|
2716
|
-
__metadata16("design:paramtypes", []),
|
|
2717
|
-
__metadata16("design:returntype", Promise)
|
|
2718
|
-
], RoleController.prototype, "getRoles", null);
|
|
2719
|
-
__decorate16([
|
|
2720
|
-
Get2("/:id"),
|
|
2721
|
-
isAdmin(),
|
|
2722
|
-
Validate2({ params: roleIdParam }),
|
|
2723
|
-
ResMsg2("roles.success.retrieved"),
|
|
2724
|
-
__param6(0, Params()),
|
|
2725
|
-
__metadata16("design:type", Function),
|
|
2726
|
-
__metadata16("design:paramtypes", [Object]),
|
|
2727
|
-
__metadata16("design:returntype", Promise)
|
|
2728
|
-
], RoleController.prototype, "getRole", null);
|
|
2729
|
-
__decorate16([
|
|
2730
|
-
Post2(),
|
|
2731
|
-
isAdmin(),
|
|
2732
|
-
Validate2(createRoleDto),
|
|
2733
|
-
ResMsg2("roles.success.created"),
|
|
2734
|
-
__param6(0, Body2()),
|
|
2735
|
-
__metadata16("design:type", Function),
|
|
2736
|
-
__metadata16("design:paramtypes", [Object]),
|
|
2737
|
-
__metadata16("design:returntype", Promise)
|
|
2738
|
-
], RoleController.prototype, "createRole", null);
|
|
2739
|
-
__decorate16([
|
|
2740
|
-
Put("/:id"),
|
|
2741
|
-
isAdmin(),
|
|
2742
|
-
Validate2({
|
|
2743
|
-
params: roleIdParam,
|
|
2744
|
-
body: updateRoleDto
|
|
2745
|
-
}),
|
|
2746
|
-
ResMsg2("roles.success.updated"),
|
|
2747
|
-
__param6(0, Params()),
|
|
2748
|
-
__param6(1, Body2()),
|
|
2749
|
-
__metadata16("design:type", Function),
|
|
2750
|
-
__metadata16("design:paramtypes", [Object, Object]),
|
|
2751
|
-
__metadata16("design:returntype", Promise)
|
|
2752
|
-
], RoleController.prototype, "updateRole", null);
|
|
2753
|
-
__decorate16([
|
|
2754
|
-
Delete("/:id"),
|
|
2755
|
-
isAdmin(),
|
|
2756
|
-
Validate2({ params: roleIdParam }),
|
|
2757
|
-
ResMsg2("roles.success.deleted"),
|
|
2758
|
-
__param6(0, Params()),
|
|
2759
|
-
__metadata16("design:type", Function),
|
|
2760
|
-
__metadata16("design:paramtypes", [Object]),
|
|
2761
|
-
__metadata16("design:returntype", Promise)
|
|
2762
|
-
], RoleController.prototype, "deleteRole", null);
|
|
2763
|
-
RoleController = __decorate16([
|
|
2764
|
-
Controller2("/roles"),
|
|
2765
|
-
__metadata16("design:paramtypes", [typeof (_a10 = typeof RoleService !== "undefined" && RoleService) === "function" ? _a10 : Object])
|
|
2766
|
-
], RoleController);
|
|
2767
|
-
|
|
2768
|
-
// src/users/UserController.ts
|
|
2769
2882
|
import { Validate as Validate3 } from "najm-validation";
|
|
2770
2883
|
var __decorate17 = function(decorators, target, key, desc) {
|
|
2771
2884
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
@@ -3577,7 +3690,7 @@ var revokeTokenDto = z4.object({
|
|
|
3577
3690
|
});
|
|
3578
3691
|
|
|
3579
3692
|
// src/ownership/scopedOwnership.ts
|
|
3580
|
-
import { aliasedTable, eq as eq6, getTableColumns, sql as
|
|
3693
|
+
import { aliasedTable, eq as eq6, getTableColumns, sql as sql4 } from "drizzle-orm";
|
|
3581
3694
|
var DEFAULT_ADMIN_ROLES = ["admin"];
|
|
3582
3695
|
var DRIZZLE_NAME = /* @__PURE__ */ Symbol.for("drizzle:Name");
|
|
3583
3696
|
var DRIZZLE_BASE_NAME = /* @__PURE__ */ Symbol.for("drizzle:BaseName");
|
|
@@ -3714,7 +3827,7 @@ var OwnershipToken = class {
|
|
|
3714
3827
|
return query;
|
|
3715
3828
|
const rule = this._rules[role];
|
|
3716
3829
|
if (!rule)
|
|
3717
|
-
return query.where(
|
|
3830
|
+
return query.where(sql4`1 = 0`);
|
|
3718
3831
|
const { query: q, condition } = rule(uid, query);
|
|
3719
3832
|
return q.where(condition);
|
|
3720
3833
|
}
|
|
@@ -3729,7 +3842,7 @@ var OwnershipToken = class {
|
|
|
3729
3842
|
return { query, condition: null };
|
|
3730
3843
|
const rule = this._rules[role];
|
|
3731
3844
|
if (!rule)
|
|
3732
|
-
return { query, condition:
|
|
3845
|
+
return { query, condition: sql4`1 = 0` };
|
|
3733
3846
|
return rule(uid, query);
|
|
3734
3847
|
}
|
|
3735
3848
|
};
|
|
@@ -4088,7 +4201,7 @@ __name(Policy, "Policy");
|
|
|
4088
4201
|
|
|
4089
4202
|
// src/ownership/OwnedDecorator.ts
|
|
4090
4203
|
import "reflect-metadata";
|
|
4091
|
-
import { sql as
|
|
4204
|
+
import { sql as sql5, and as and3 } from "drizzle-orm";
|
|
4092
4205
|
import { Injectable as Injectable12, Inject as Inject12, DI as DI2, Container as Container2, REQUEST_ID } from "najm-core";
|
|
4093
4206
|
import { USER as USER2 } from "najm-guard";
|
|
4094
4207
|
var __decorate24 = function(decorators, target, key, desc) {
|
|
@@ -4177,7 +4290,7 @@ function Owned(token) {
|
|
|
4177
4290
|
return query;
|
|
4178
4291
|
const user = getUser(this);
|
|
4179
4292
|
if (!user)
|
|
4180
|
-
return query.where(
|
|
4293
|
+
return query.where(sql5`1 = 0`);
|
|
4181
4294
|
return token.applyScope(user.id, user.role, query);
|
|
4182
4295
|
};
|
|
4183
4296
|
},
|
|
@@ -4249,7 +4362,16 @@ var en_default = {
|
|
|
4249
4362
|
passwordChanged: "Password changed successfully",
|
|
4250
4363
|
passwordResetSent: "If that email exists, a reset link has been sent",
|
|
4251
4364
|
passwordReset: "Password has been reset successfully",
|
|
4365
|
+
accountInviteSent: "Invitation sent successfully",
|
|
4252
4366
|
tokenRefreshed: "Token refreshed successfully"
|
|
4367
|
+
},
|
|
4368
|
+
emails: {
|
|
4369
|
+
passwordReset: {
|
|
4370
|
+
subject: "Reset your password"
|
|
4371
|
+
},
|
|
4372
|
+
accountInvite: {
|
|
4373
|
+
subject: "You've been invited \u2014 set up your account"
|
|
4374
|
+
}
|
|
4253
4375
|
}
|
|
4254
4376
|
},
|
|
4255
4377
|
users: {
|
|
@@ -4365,14 +4487,12 @@ var selectSchema = /* @__PURE__ */ __name((config) => {
|
|
|
4365
4487
|
switch (dialect) {
|
|
4366
4488
|
case "sqlite":
|
|
4367
4489
|
return authSchema2;
|
|
4368
|
-
case "mysql":
|
|
4369
|
-
return authSchema3;
|
|
4370
4490
|
case "pg":
|
|
4371
4491
|
default:
|
|
4372
4492
|
return authSchema;
|
|
4373
4493
|
}
|
|
4374
4494
|
}, "selectSchema");
|
|
4375
|
-
var auth = /* @__PURE__ */ __name((config) => plugin("auth").version("1.0.0").depends(cache(), cookies(), i18n(), guards(), validation(config?.validation), rateLimit(config?.rateLimit), email()).requires("database").contributes(I18N_CONTRIBUTIONS, AUTH_LOCALES).services(
|
|
4495
|
+
var auth = /* @__PURE__ */ __name((config) => plugin("auth").version("1.0.0").depends(cache(), cookies(), i18n(), guards(), validation(config?.validation), rateLimit(config?.rateLimit), email()).requires("database").contributes(I18N_CONTRIBUTIONS, AUTH_LOCALES).services(AUTH_MODULE, users_exports, roles_exports, permissions_exports, tokens_exports, ScopeContext).config(AUTH_CONFIG, mergeConfig(config)).set(AUTH_SCHEMA, selectSchema(config)).set(AUTH_ENCRYPTION_KEY, config?.encryptionKey ?? null).build(), "auth");
|
|
4376
4496
|
|
|
4377
4497
|
// src/seed.ts
|
|
4378
4498
|
var toSeedId = /* @__PURE__ */ __name((prefix, value) => {
|
|
@@ -4576,6 +4696,7 @@ export {
|
|
|
4576
4696
|
formatDate,
|
|
4577
4697
|
getAuthLocale,
|
|
4578
4698
|
getAvatarFile,
|
|
4699
|
+
inviteUserDto,
|
|
4579
4700
|
isAdmin,
|
|
4580
4701
|
isAdministrator,
|
|
4581
4702
|
isAuth,
|