@simple-auth-kit/cli 1.5.0 → 1.6.1
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/lib/copy.ts +1 -1
- package/package.json +1 -1
- package/registry/combos/express-drizzle/shared/src/{ability.ts → ability/ability.ts} +8 -2
- package/registry/combos/express-drizzle/shared/src/{permission-cache.ts → cache/permission-cache.ts} +25 -9
- package/registry/combos/express-drizzle/shared/src/{rate-limit.store.ts → cache/rate-limit.store.ts} +4 -1
- package/registry/combos/{express-prisma/shared/src → express-drizzle/shared/src/config}/auth.config.ts +1 -1
- package/registry/combos/express-drizzle/shared/src/{db.ts → config/db.ts} +1 -1
- package/registry/combos/express-drizzle/shared/src/{key-provider.ts → config/key-provider.ts} +6 -2
- package/registry/combos/express-drizzle/shared/src/{id.helper.ts → helpers/id.helper.ts} +6 -2
- package/registry/combos/{nestjs-drizzle/shared/src → express-drizzle/shared/src/helpers}/pagination.ts +17 -3
- package/registry/combos/express-drizzle/shared/src/middleware/auth-core-error.middleware.ts +6 -8
- package/registry/combos/express-drizzle/shared/src/middleware/auth.middleware.ts +6 -8
- package/registry/combos/{express-prisma/shared/src → express-drizzle/shared/src/openapi}/openapi-fragment.ts +13 -4
- package/registry/combos/express-drizzle/shared/src/{openapi-spec.ts → openapi/openapi-spec.ts} +251 -52
- package/registry/combos/express-drizzle/shared/src/repositories/oauth.repository.ts +2 -2
- package/registry/combos/express-drizzle/shared/src/repositories/password-reset.repository.ts +2 -4
- package/registry/combos/express-drizzle/shared/src/repositories/session.repository.ts +3 -3
- package/registry/combos/express-drizzle/shared/src/request-context.ts +1 -1
- package/registry/combos/express-drizzle/shared/src/route-tiers.ts +52 -11
- package/registry/combos/express-drizzle/shared/src/routers/auth.router.ts +2 -2
- package/registry/combos/express-drizzle/test/bootstrap.ts +4 -2
- package/registry/combos/express-drizzle/test/prove-cycle.ts +1095 -308
- package/registry/combos/express-drizzle/variants/base/src/create-auth-app.ts +5 -5
- package/registry/combos/express-drizzle/variants/base/src/index.ts +8 -0
- package/registry/combos/express-drizzle/variants/base/src/middleware/authz.middleware.ts +2 -2
- package/registry/combos/express-drizzle/variants/base/src/openapi/openapi-admin.ts +1082 -0
- package/registry/combos/express-drizzle/variants/base/src/rbac.defaults.ts +60 -17
- package/registry/combos/express-drizzle/variants/base/src/repositories/audit-log.repository.ts +2 -2
- package/registry/combos/express-drizzle/variants/base/src/repositories/rbac.repository.ts +5 -5
- package/registry/combos/express-drizzle/variants/base/src/routers/admin.router.ts +18 -22
- package/registry/combos/express-drizzle/variants/base/src/seed.ts +76 -17
- package/registry/combos/express-drizzle/variants/base/src/services/auth.service.ts +7 -9
- package/registry/combos/express-drizzle/variants/base/test/variant-hooks.ts +167 -45
- package/registry/combos/express-drizzle/variants/workspaces/src/create-auth-app.ts +5 -5
- package/registry/combos/express-drizzle/variants/workspaces/src/index.ts +8 -0
- package/registry/combos/express-drizzle/variants/workspaces/src/middleware/authz.middleware.ts +3 -3
- package/registry/combos/express-drizzle/variants/workspaces/src/openapi/openapi-admin.ts +1176 -0
- package/registry/combos/express-drizzle/variants/workspaces/src/openapi-workspace.ts +1 -1
- package/registry/combos/express-drizzle/variants/workspaces/src/rbac.defaults.ts +66 -19
- package/registry/combos/express-drizzle/variants/workspaces/src/repositories/audit-log.repository.ts +2 -2
- package/registry/combos/express-drizzle/variants/workspaces/src/repositories/rbac.repository.ts +5 -5
- package/registry/combos/express-drizzle/variants/workspaces/src/repositories/workspace.repository.ts +9 -11
- package/registry/combos/express-drizzle/variants/workspaces/src/routers/admin.router.ts +18 -22
- package/registry/combos/express-drizzle/variants/workspaces/src/routers/workspace.router.ts +1 -1
- package/registry/combos/express-drizzle/variants/workspaces/src/seed.ts +168 -35
- package/registry/combos/express-drizzle/variants/workspaces/src/services/auth.service.ts +7 -9
- package/registry/combos/express-drizzle/variants/workspaces/test/variant-hooks.ts +539 -133
- package/registry/combos/express-prisma/shared/src/{ability.ts → ability/ability.ts} +8 -2
- package/registry/combos/express-prisma/shared/src/{permission-cache.ts → cache/permission-cache.ts} +25 -9
- package/registry/combos/express-prisma/shared/src/{rate-limit.store.ts → cache/rate-limit.store.ts} +4 -1
- package/registry/combos/{express-drizzle/shared/src → express-prisma/shared/src/config}/auth.config.ts +1 -1
- package/registry/combos/express-prisma/shared/src/{key-provider.ts → config/key-provider.ts} +6 -2
- package/registry/combos/{nestjs-prisma/shared/src → express-prisma/shared/src/helpers}/id.helper.ts +6 -2
- package/registry/combos/{express-drizzle/shared/src → express-prisma/shared/src/helpers}/pagination.ts +17 -3
- package/registry/combos/express-prisma/shared/src/middleware/auth-core-error.middleware.ts +6 -8
- package/registry/combos/express-prisma/shared/src/middleware/auth.middleware.ts +6 -8
- package/registry/combos/{express-drizzle/shared/src → express-prisma/shared/src/openapi}/openapi-fragment.ts +13 -4
- package/registry/combos/express-prisma/shared/src/{openapi-spec.ts → openapi/openapi-spec.ts} +313 -55
- package/registry/combos/express-prisma/shared/src/repositories/oauth.repository.ts +2 -2
- package/registry/combos/express-prisma/shared/src/repositories/password-reset.repository.ts +2 -4
- package/registry/combos/express-prisma/shared/src/repositories/session.repository.ts +2 -2
- package/registry/combos/express-prisma/shared/src/request-context.ts +1 -1
- package/registry/combos/express-prisma/shared/src/route-tiers.ts +52 -11
- package/registry/combos/express-prisma/shared/src/routers/auth.router.ts +2 -2
- package/registry/combos/express-prisma/test/bootstrap.ts +7 -3
- package/registry/combos/express-prisma/test/prove-cycle.ts +1095 -308
- package/registry/combos/express-prisma/variants/base/src/create-auth-app.ts +5 -5
- package/registry/combos/express-prisma/variants/base/src/index.ts +8 -0
- package/registry/combos/express-prisma/variants/base/src/middleware/authz.middleware.ts +2 -2
- package/registry/combos/express-prisma/variants/base/src/openapi/openapi-admin.ts +1102 -0
- package/registry/combos/express-prisma/variants/base/src/repositories/audit-log.repository.ts +2 -2
- package/registry/combos/express-prisma/variants/base/src/repositories/rbac.repository.ts +4 -4
- package/registry/combos/express-prisma/variants/base/src/routers/admin.router.ts +18 -22
- package/registry/combos/express-prisma/variants/base/src/services/auth.service.ts +6 -8
- package/registry/combos/express-prisma/variants/base/test/variant-hooks.ts +176 -47
- package/registry/combos/express-prisma/variants/workspaces/src/create-auth-app.ts +5 -5
- package/registry/combos/express-prisma/variants/workspaces/src/index.ts +8 -0
- package/registry/combos/express-prisma/variants/workspaces/src/middleware/authz.middleware.ts +3 -3
- package/registry/combos/express-prisma/variants/workspaces/src/openapi/openapi-admin.ts +1218 -0
- package/registry/combos/express-prisma/variants/workspaces/src/openapi-workspace.ts +1 -1
- package/registry/combos/express-prisma/variants/workspaces/src/repositories/audit-log.repository.ts +2 -2
- package/registry/combos/express-prisma/variants/workspaces/src/repositories/rbac.repository.ts +4 -4
- package/registry/combos/express-prisma/variants/workspaces/src/repositories/workspace.repository.ts +2 -2
- package/registry/combos/express-prisma/variants/workspaces/src/routers/admin.router.ts +18 -22
- package/registry/combos/express-prisma/variants/workspaces/src/routers/workspace.router.ts +1 -1
- package/registry/combos/express-prisma/variants/workspaces/src/services/auth.service.ts +6 -8
- package/registry/combos/express-prisma/variants/workspaces/test/variant-hooks.ts +538 -134
- package/registry/combos/nestjs-drizzle/shared/src/{guards → ability}/ability.guard.ts +1 -1
- package/registry/combos/nestjs-drizzle/shared/src/{ability.ts → ability/ability.ts} +8 -2
- package/registry/combos/nestjs-drizzle/shared/src/{permission-cache.ts → cache/permission-cache.ts} +27 -10
- package/registry/combos/{nestjs-prisma/shared/src → nestjs-drizzle/shared/src/cache}/rate-limit.store.ts +4 -1
- package/registry/combos/nestjs-drizzle/shared/src/{auth.config.ts → config/auth.config.ts} +1 -1
- package/registry/combos/nestjs-drizzle/shared/src/{db.ts → config/db.ts} +1 -1
- package/registry/combos/nestjs-drizzle/shared/src/{key-provider.ts → config/key-provider.ts} +6 -2
- package/registry/combos/nestjs-drizzle/shared/src/controllers/auth.controller.ts +1 -1
- package/registry/combos/nestjs-drizzle/shared/src/guards/auth.guard.ts +1 -1
- package/registry/combos/nestjs-drizzle/shared/src/{id.helper.ts → helpers/id.helper.ts} +6 -2
- package/registry/combos/{express-prisma/shared/src → nestjs-drizzle/shared/src/helpers}/pagination.ts +17 -3
- package/registry/combos/nestjs-drizzle/shared/src/repositories/oauth.repository.ts +2 -2
- package/registry/combos/nestjs-drizzle/shared/src/repositories/password-reset.repository.ts +8 -12
- package/registry/combos/nestjs-drizzle/shared/src/repositories/session.repository.ts +3 -3
- package/registry/combos/nestjs-drizzle/shared/src/repositories/two-factor.repository.ts +1 -1
- package/registry/combos/nestjs-drizzle/shared/src/request-context.ts +1 -1
- package/registry/combos/nestjs-drizzle/test/bootstrap.ts +1 -1
- package/registry/combos/nestjs-drizzle/test/prove-cycle.ts +1 -1
- package/registry/combos/nestjs-drizzle/variants/base/src/auth.module.ts +10 -6
- package/registry/combos/nestjs-drizzle/variants/base/src/controllers/admin.controller.ts +1 -1
- package/registry/combos/nestjs-drizzle/variants/base/src/guards/authz.guard.ts +2 -2
- package/registry/combos/nestjs-drizzle/variants/base/src/index.ts +9 -0
- package/registry/combos/nestjs-drizzle/variants/base/src/rbac.defaults.ts +58 -16
- package/registry/combos/nestjs-drizzle/variants/base/src/repositories/audit-log.repository.ts +3 -3
- package/registry/combos/nestjs-drizzle/variants/base/src/repositories/rbac.repository.ts +4 -4
- package/registry/combos/nestjs-drizzle/variants/base/src/seed.ts +76 -17
- package/registry/combos/nestjs-drizzle/variants/base/src/services/auth.service.ts +7 -9
- package/registry/combos/nestjs-drizzle/variants/base/test/variant-hooks.ts +167 -45
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/auth.module.ts +10 -6
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/controllers/admin.controller.ts +1 -1
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/controllers/workspace.controller.ts +1 -1
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/guards/authz.guard.ts +2 -2
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/index.ts +9 -0
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/rbac.defaults.ts +64 -18
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/repositories/audit-log.repository.ts +3 -3
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/repositories/rbac.repository.ts +4 -4
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/repositories/workspace.repository.ts +8 -10
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/seed.ts +171 -36
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/services/auth.service.ts +7 -9
- package/registry/combos/nestjs-drizzle/variants/workspaces/test/variant-hooks.ts +539 -133
- package/registry/combos/nestjs-prisma/shared/src/{guards → ability}/ability.guard.ts +1 -1
- package/registry/combos/nestjs-prisma/shared/src/{ability.ts → ability/ability.ts} +8 -2
- package/registry/combos/nestjs-prisma/shared/src/{permission-cache.ts → cache/permission-cache.ts} +27 -10
- package/registry/combos/{nestjs-drizzle/shared/src → nestjs-prisma/shared/src/cache}/rate-limit.store.ts +4 -1
- package/registry/combos/nestjs-prisma/shared/src/{auth.config.ts → config/auth.config.ts} +1 -1
- package/registry/combos/nestjs-prisma/shared/src/{key-provider.ts → config/key-provider.ts} +6 -2
- package/registry/combos/nestjs-prisma/shared/src/controllers/auth.controller.ts +1 -1
- package/registry/combos/nestjs-prisma/shared/src/guards/auth.guard.ts +1 -1
- package/registry/combos/{express-prisma/shared/src → nestjs-prisma/shared/src/helpers}/id.helper.ts +6 -2
- package/registry/combos/nestjs-prisma/shared/src/{pagination.ts → helpers/pagination.ts} +17 -3
- package/registry/combos/nestjs-prisma/shared/src/repositories/oauth.repository.ts +1 -1
- package/registry/combos/nestjs-prisma/shared/src/repositories/password-reset.repository.ts +2 -4
- package/registry/combos/nestjs-prisma/shared/src/repositories/session.repository.ts +2 -2
- package/registry/combos/nestjs-prisma/shared/src/request-context.ts +1 -1
- package/registry/combos/nestjs-prisma/test/bootstrap.ts +1 -1
- package/registry/combos/nestjs-prisma/test/prove-cycle.ts +1 -1
- package/registry/combos/nestjs-prisma/variants/base/src/auth.module.ts +9 -5
- package/registry/combos/nestjs-prisma/variants/base/src/controllers/admin.controller.ts +1 -1
- package/registry/combos/nestjs-prisma/variants/base/src/gateways/audit-log.gateway.ts +1 -1
- package/registry/combos/nestjs-prisma/variants/base/src/guards/authz.guard.ts +2 -2
- package/registry/combos/nestjs-prisma/variants/base/src/index.ts +9 -0
- package/registry/combos/nestjs-prisma/variants/base/src/{docs.ts → openapi/docs.ts} +23 -6
- package/registry/combos/nestjs-prisma/variants/base/src/repositories/audit-log.repository.ts +2 -2
- package/registry/combos/nestjs-prisma/variants/base/src/repositories/country.repository.ts +2 -2
- package/registry/combos/nestjs-prisma/variants/base/src/repositories/customer.repository.ts +6 -5
- package/registry/combos/nestjs-prisma/variants/base/src/repositories/language.repository.ts +2 -2
- package/registry/combos/nestjs-prisma/variants/base/src/repositories/rbac.repository.ts +3 -3
- package/registry/combos/nestjs-prisma/variants/base/src/services/auth.service.ts +6 -8
- package/registry/combos/nestjs-prisma/variants/base/test/variant-hooks.ts +176 -47
- package/registry/combos/nestjs-prisma/variants/workspaces/src/auth.module.ts +9 -5
- package/registry/combos/nestjs-prisma/variants/workspaces/src/controllers/admin.controller.ts +1 -1
- package/registry/combos/nestjs-prisma/variants/workspaces/src/controllers/workspace.controller.ts +1 -1
- package/registry/combos/nestjs-prisma/variants/workspaces/src/guards/authz.guard.ts +2 -2
- package/registry/combos/nestjs-prisma/variants/workspaces/src/index.ts +9 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/repositories/audit-log.repository.ts +2 -2
- package/registry/combos/nestjs-prisma/variants/workspaces/src/repositories/country.repository.ts +2 -2
- package/registry/combos/nestjs-prisma/variants/workspaces/src/repositories/customer.repository.ts +6 -5
- package/registry/combos/nestjs-prisma/variants/workspaces/src/repositories/language.repository.ts +2 -2
- package/registry/combos/nestjs-prisma/variants/workspaces/src/repositories/rbac.repository.ts +3 -3
- package/registry/combos/nestjs-prisma/variants/workspaces/src/repositories/workspace.repository.ts +1 -1
- package/registry/combos/nestjs-prisma/variants/workspaces/src/services/auth.service.ts +6 -8
- package/registry/combos/nestjs-prisma/variants/workspaces/test/variant-hooks.ts +538 -134
- package/simple-auth-kit.ts +13 -9
- package/registry/combos/express-drizzle/variants/base/src/openapi-admin.ts +0 -565
- package/registry/combos/express-drizzle/variants/workspaces/src/openapi-admin.ts +0 -596
- package/registry/combos/express-prisma/variants/base/src/openapi-admin.ts +0 -569
- package/registry/combos/express-prisma/variants/workspaces/src/openapi-admin.ts +0 -601
- /package/registry/combos/express-drizzle/shared/src/{http-error.ts → errors/http-error.ts} +0 -0
- /package/registry/combos/express-prisma/shared/src/{http-error.ts → errors/http-error.ts} +0 -0
package/registry/combos/express-prisma/variants/base/src/repositories/audit-log.repository.ts
CHANGED
|
@@ -5,8 +5,8 @@ import {
|
|
|
5
5
|
normalizeLimit,
|
|
6
6
|
normalizePage,
|
|
7
7
|
type Paginated,
|
|
8
|
-
} from "../pagination.js";
|
|
9
|
-
import { toIdOrNull, toIdOrUndefined } from "../id.helper.js";
|
|
8
|
+
} from "../helpers/pagination.js";
|
|
9
|
+
import { toIdOrNull, toIdOrUndefined } from "../helpers/id.helper.js";
|
|
10
10
|
|
|
11
11
|
export interface AuditLogEntry {
|
|
12
12
|
id: string;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { resolvePermissions } from "@/lib/auth/core/rbac.js";
|
|
2
2
|
import { Prisma, PrismaClient } from "../../generated/prisma/client.js";
|
|
3
|
-
import { HttpError } from "../http-error.js";
|
|
3
|
+
import { HttpError } from "../errors/http-error.js";
|
|
4
4
|
import {
|
|
5
5
|
buildPageMeta,
|
|
6
6
|
normalizeLimit,
|
|
7
7
|
normalizePage,
|
|
8
8
|
type Paginated,
|
|
9
|
-
} from "../pagination.js";
|
|
10
|
-
import { PermissionCache } from "../permission-cache.js";
|
|
11
|
-
import { toId, toIdOrNull } from "../id.helper.js";
|
|
9
|
+
} from "../helpers/pagination.js";
|
|
10
|
+
import { PermissionCache } from "../cache/permission-cache.js";
|
|
11
|
+
import { toId, toIdOrNull } from "../helpers/id.helper.js";
|
|
12
12
|
|
|
13
13
|
export interface UserSummary {
|
|
14
14
|
id: string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { NextFunction, Request, RequestHandler, Response } from "express";
|
|
2
2
|
import { AuthService } from "../services/auth.service.js";
|
|
3
|
-
import { HttpError } from "../http-error.js";
|
|
3
|
+
import { HttpError } from "../errors/http-error.js";
|
|
4
4
|
import {
|
|
5
5
|
ability,
|
|
6
6
|
createTieredRouter,
|
|
@@ -54,15 +54,13 @@ export function createAdminRouter(deps: AdminRouterDeps): RequestHandler {
|
|
|
54
54
|
string,
|
|
55
55
|
string | undefined
|
|
56
56
|
>;
|
|
57
|
-
res
|
|
58
|
-
.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}),
|
|
65
|
-
);
|
|
57
|
+
res.status(200).json(
|
|
58
|
+
await auth.listUsers({
|
|
59
|
+
search,
|
|
60
|
+
page: page ? Number(page) : undefined,
|
|
61
|
+
limit: limit ? Number(limit) : undefined,
|
|
62
|
+
}),
|
|
63
|
+
);
|
|
66
64
|
} catch (err) {
|
|
67
65
|
next(err);
|
|
68
66
|
}
|
|
@@ -185,18 +183,16 @@ export function createAdminRouter(deps: AdminRouterDeps): RequestHandler {
|
|
|
185
183
|
try {
|
|
186
184
|
const { userId, action, since, until, page, limit } =
|
|
187
185
|
req.query as Record<string, string | undefined>;
|
|
188
|
-
res
|
|
189
|
-
.
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
}),
|
|
199
|
-
);
|
|
186
|
+
res.status(200).json(
|
|
187
|
+
await auth.listAuditLog({
|
|
188
|
+
userId,
|
|
189
|
+
action,
|
|
190
|
+
since,
|
|
191
|
+
until,
|
|
192
|
+
page: page ? Number(page) : undefined,
|
|
193
|
+
limit: limit ? Number(limit) : undefined,
|
|
194
|
+
}),
|
|
195
|
+
);
|
|
200
196
|
} catch (err) {
|
|
201
197
|
next(err);
|
|
202
198
|
}
|
|
@@ -42,17 +42,17 @@ import {
|
|
|
42
42
|
verifyTotpCode,
|
|
43
43
|
} from "@/lib/auth/core/two-factor.js";
|
|
44
44
|
import type { Revoker } from "@/lib/auth/core/types.js";
|
|
45
|
-
import type { AuthConfig } from "../auth.config.js";
|
|
46
|
-
import { HttpError } from "../http-error.js";
|
|
45
|
+
import type { AuthConfig } from "../config/auth.config.js";
|
|
46
|
+
import { HttpError } from "../errors/http-error.js";
|
|
47
47
|
import {
|
|
48
48
|
AuditLogEntry,
|
|
49
49
|
AuditLogListFilter,
|
|
50
50
|
AuditLogRepository,
|
|
51
51
|
toAuditLogEntry,
|
|
52
52
|
} from "../repositories/audit-log.repository.js";
|
|
53
|
-
import type { Paginated } from "../pagination.js";
|
|
53
|
+
import type { Paginated } from "../helpers/pagination.js";
|
|
54
54
|
import { PrismaClient } from "../../generated/prisma/client.js";
|
|
55
|
-
import { KeyProviderService } from "../key-provider.js";
|
|
55
|
+
import { KeyProviderService } from "../config/key-provider.js";
|
|
56
56
|
import { OAuthRepository } from "../repositories/oauth.repository.js";
|
|
57
57
|
import { PasswordResetRepository } from "../repositories/password-reset.repository.js";
|
|
58
58
|
import {
|
|
@@ -67,7 +67,7 @@ import {
|
|
|
67
67
|
} from "../repositories/rbac.repository.js";
|
|
68
68
|
import { SessionRepository } from "../repositories/session.repository.js";
|
|
69
69
|
import { TwoFactorRepository } from "../repositories/two-factor.repository.js";
|
|
70
|
-
import { toId, toIdOrNull } from "../id.helper.js";
|
|
70
|
+
import { toId, toIdOrNull } from "../helpers/id.helper.js";
|
|
71
71
|
|
|
72
72
|
export interface AuthTokens {
|
|
73
73
|
accessToken: string;
|
|
@@ -454,9 +454,7 @@ export class AuthService {
|
|
|
454
454
|
return { twoFactorEnabled: user.twoFactorEnabled };
|
|
455
455
|
}
|
|
456
456
|
|
|
457
|
-
async listActiveSessions(
|
|
458
|
-
userId: string,
|
|
459
|
-
): Promise<
|
|
457
|
+
async listActiveSessions(userId: string): Promise<
|
|
460
458
|
Array<{
|
|
461
459
|
id: string;
|
|
462
460
|
createdAt: string;
|
|
@@ -6,13 +6,25 @@
|
|
|
6
6
|
import "dotenv/config";
|
|
7
7
|
import { PrismaPg } from "@prisma/adapter-pg";
|
|
8
8
|
import { PrismaClient } from "../generated/prisma/client.js";
|
|
9
|
-
import { toId } from "../src/id.helper.js";
|
|
10
|
-
import {
|
|
11
|
-
|
|
9
|
+
import { toId } from "../src/helpers/id.helper.js";
|
|
10
|
+
import {
|
|
11
|
+
provisionDefaultRoles,
|
|
12
|
+
SEED_ADMIN_ROLES,
|
|
13
|
+
} from "../src/rbac.defaults.js";
|
|
14
|
+
import { POLICY_VERSION_KEY } from "../src/cache/permission-cache.js";
|
|
12
15
|
import { permissionCacheStore } from "./bootstrap.js";
|
|
13
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
renewingToken,
|
|
18
|
+
type AdminSession,
|
|
19
|
+
type AuthTokens,
|
|
20
|
+
type Principal,
|
|
21
|
+
type ProofContext,
|
|
22
|
+
type VariantHooks,
|
|
23
|
+
} from "./harness.js";
|
|
14
24
|
|
|
15
|
-
const prisma = new PrismaClient({
|
|
25
|
+
const prisma = new PrismaClient({
|
|
26
|
+
adapter: new PrismaPg({ connectionString: process.env["DATABASE_URL"] }),
|
|
27
|
+
});
|
|
16
28
|
|
|
17
29
|
/** Remembered by `makeAdmin` so `freshToken()` can mint a new one whenever a section needs it. */
|
|
18
30
|
let adminCredentials: { identifier: string; password: string } | undefined;
|
|
@@ -20,30 +32,49 @@ let adminCredentials: { identifier: string; password: string } | undefined;
|
|
|
20
32
|
export const hooks: VariantHooks = {
|
|
21
33
|
variant: "base",
|
|
22
34
|
|
|
23
|
-
async makeAdmin(
|
|
35
|
+
async makeAdmin(
|
|
36
|
+
ctx: ProofContext,
|
|
37
|
+
principal: Principal,
|
|
38
|
+
): Promise<AdminSession> {
|
|
24
39
|
// The role slug on its own confers nothing — no guard checks for the string "admin". Authority
|
|
25
40
|
// comes from the PermissionRole rows behind it, and in this variant the seeder is the only
|
|
26
41
|
// thing that writes them, so the hook does what `npm run seed` does: calls the same
|
|
27
42
|
// provisioning function, from the same definition the routes are typed against. Idempotent,
|
|
28
43
|
// so it is safe against a database that has already been seeded.
|
|
29
44
|
await provisionDefaultRoles(prisma);
|
|
30
|
-
const roles = await prisma.role.findMany({
|
|
31
|
-
|
|
45
|
+
const roles = await prisma.role.findMany({
|
|
46
|
+
where: { slug: { in: SEED_ADMIN_ROLES } },
|
|
47
|
+
select: { id: true },
|
|
48
|
+
});
|
|
49
|
+
await prisma.roleUser.createMany({
|
|
50
|
+
data: roles.map((role) => ({
|
|
51
|
+
userId: toId(principal.userId),
|
|
52
|
+
roleId: role.id,
|
|
53
|
+
})),
|
|
54
|
+
skipDuplicates: true,
|
|
55
|
+
});
|
|
32
56
|
|
|
33
|
-
adminCredentials = {
|
|
57
|
+
adminCredentials = {
|
|
58
|
+
identifier: principal.email,
|
|
59
|
+
password: principal.password,
|
|
60
|
+
};
|
|
34
61
|
// Those writes went straight to the database, behind the running app's back, so nothing
|
|
35
62
|
// bumped a version counter and the caller's already-cached (empty) permissions would still be
|
|
36
63
|
// served. Bumping the policy counter is exactly what an out-of-band writer is supposed to do —
|
|
37
64
|
// see permission-cache.ts. Logging in would not have helped: nothing about authorization is
|
|
38
65
|
// in the token.
|
|
39
66
|
await permissionCacheStore.bump(POLICY_VERSION_KEY);
|
|
40
|
-
const login = await ctx.call("POST", "/auth/login", {
|
|
67
|
+
const login = await ctx.call("POST", "/auth/login", {
|
|
68
|
+
body: { identifier: principal.email, password: principal.password },
|
|
69
|
+
});
|
|
41
70
|
const token = (login.body as AuthTokens).accessToken;
|
|
42
71
|
ctx.assert(!!token, `the seeded admin can log in (got ${login.status})`);
|
|
43
72
|
return {
|
|
44
73
|
token,
|
|
45
74
|
freshToken: renewingToken(async () => {
|
|
46
|
-
const res = await ctx.call("POST", "/auth/login", {
|
|
75
|
+
const res = await ctx.call("POST", "/auth/login", {
|
|
76
|
+
body: adminCredentials!,
|
|
77
|
+
});
|
|
47
78
|
return (res.body as AuthTokens).accessToken;
|
|
48
79
|
}),
|
|
49
80
|
};
|
|
@@ -53,16 +84,37 @@ export const hooks: VariantHooks = {
|
|
|
53
84
|
// Nothing to do: admin endpoints operate on the whole deployment.
|
|
54
85
|
},
|
|
55
86
|
|
|
56
|
-
async proveVariantProperties(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
87
|
+
async proveVariantProperties(
|
|
88
|
+
ctx: ProofContext,
|
|
89
|
+
admin: AdminSession,
|
|
90
|
+
): Promise<void> {
|
|
91
|
+
const listed = await ctx.call("GET", "/auth/admin/users", {
|
|
92
|
+
token: await admin.freshToken(),
|
|
93
|
+
});
|
|
94
|
+
ctx.assert(
|
|
95
|
+
listed.status === 200,
|
|
96
|
+
`admin can list every user in the deployment (got ${listed.status})`,
|
|
97
|
+
);
|
|
98
|
+
ctx.assert(
|
|
99
|
+
Array.isArray(listed.body?.items) && listed.body.items.length > 0,
|
|
100
|
+
"the user list is non-empty",
|
|
101
|
+
);
|
|
60
102
|
|
|
61
|
-
const workspaces = await ctx.call("GET", "/workspaces", {
|
|
62
|
-
|
|
103
|
+
const workspaces = await ctx.call("GET", "/workspaces", {
|
|
104
|
+
token: await admin.freshToken(),
|
|
105
|
+
});
|
|
106
|
+
ctx.assert(
|
|
107
|
+
workspaces.status === 404,
|
|
108
|
+
`no workspace endpoints exist in this variant (got ${workspaces.status})`,
|
|
109
|
+
);
|
|
63
110
|
|
|
64
|
-
const me = await ctx.call("GET", "/auth/me", {
|
|
65
|
-
|
|
111
|
+
const me = await ctx.call("GET", "/auth/me", {
|
|
112
|
+
token: await admin.freshToken(),
|
|
113
|
+
});
|
|
114
|
+
ctx.assert(
|
|
115
|
+
me.body?.roles?.includes("admin"),
|
|
116
|
+
"roles are global: they apply to every request this user makes",
|
|
117
|
+
);
|
|
66
118
|
|
|
67
119
|
// --- authorization is not in the token, so a change lands on the next *request* ---
|
|
68
120
|
//
|
|
@@ -76,27 +128,58 @@ export const hooks: VariantHooks = {
|
|
|
76
128
|
const email = ctx.uniqueEmail("immediate");
|
|
77
129
|
const password = "immediate-pw-12345";
|
|
78
130
|
const tokens = await ctx.signup(email, password);
|
|
79
|
-
const userId = (
|
|
131
|
+
const userId = (
|
|
132
|
+
await ctx.call("GET", "/auth/me", { token: tokens.accessToken })
|
|
133
|
+
).body.sub as string;
|
|
80
134
|
|
|
81
|
-
const beforeGrant = await ctx.call("GET", "/auth/admin/users", {
|
|
82
|
-
|
|
135
|
+
const beforeGrant = await ctx.call("GET", "/auth/admin/users", {
|
|
136
|
+
token: tokens.accessToken,
|
|
137
|
+
});
|
|
138
|
+
ctx.assert(
|
|
139
|
+
beforeGrant.status === 403,
|
|
140
|
+
`a user with no grant is refused (got ${beforeGrant.status})`,
|
|
141
|
+
);
|
|
83
142
|
|
|
84
|
-
const granted = await ctx.call(
|
|
85
|
-
|
|
143
|
+
const granted = await ctx.call(
|
|
144
|
+
"POST",
|
|
145
|
+
`/auth/admin/users/${userId}/permissions`,
|
|
146
|
+
{ token: await admin.freshToken(), body: { permission: "users:read" } },
|
|
147
|
+
);
|
|
148
|
+
ctx.assert(
|
|
149
|
+
granted.status === 200 || granted.status === 201,
|
|
150
|
+
`admin can grant a permission directly (got ${granted.status})`,
|
|
151
|
+
);
|
|
86
152
|
|
|
87
|
-
const afterGrant = await ctx.call("GET", "/auth/admin/users", {
|
|
88
|
-
|
|
153
|
+
const afterGrant = await ctx.call("GET", "/auth/admin/users", {
|
|
154
|
+
token: tokens.accessToken,
|
|
155
|
+
});
|
|
156
|
+
ctx.assert(
|
|
157
|
+
afterGrant.status === 200,
|
|
158
|
+
`…and the grant is in effect on the very next request, on the token they already held (got ${afterGrant.status})`,
|
|
159
|
+
);
|
|
89
160
|
|
|
90
161
|
// The status of this revoke used to be the one admin call in the whole run that was never
|
|
91
162
|
// asserted, which is exactly why a token that had quietly expired surfaced as a confusing
|
|
92
163
|
// failure of the *next* assertion instead of this one.
|
|
93
|
-
const revoked = await ctx.call(
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
164
|
+
const revoked = await ctx.call(
|
|
165
|
+
"POST",
|
|
166
|
+
`/auth/admin/users/${userId}/permissions/${encodeURIComponent("users:read")}/revoke`,
|
|
167
|
+
{
|
|
168
|
+
token: await admin.freshToken(),
|
|
169
|
+
},
|
|
170
|
+
);
|
|
171
|
+
ctx.assert(
|
|
172
|
+
revoked.status === 200 || revoked.status === 201,
|
|
173
|
+
`admin can revoke the direct grant (got ${revoked.status})`,
|
|
174
|
+
);
|
|
97
175
|
|
|
98
|
-
const afterRevoke = await ctx.call("GET", "/auth/admin/users", {
|
|
99
|
-
|
|
176
|
+
const afterRevoke = await ctx.call("GET", "/auth/admin/users", {
|
|
177
|
+
token: tokens.accessToken,
|
|
178
|
+
});
|
|
179
|
+
ctx.assert(
|
|
180
|
+
afterRevoke.status === 403,
|
|
181
|
+
`and the revocation lands on the very next request too, on the same token (got ${afterRevoke.status})`,
|
|
182
|
+
);
|
|
100
183
|
|
|
101
184
|
await proveTheCacheIsReal(ctx, admin, tokens.accessToken);
|
|
102
185
|
await proveARawDatabaseEditChangesEnforcement(ctx, admin);
|
|
@@ -108,7 +191,11 @@ export const hooks: VariantHooks = {
|
|
|
108
191
|
* app resolves through (`test/bootstrap.ts` hands it to `AuthModule.forRoot`), so "the second
|
|
109
192
|
* request did not touch the database" is a measurement rather than a claim.
|
|
110
193
|
*/
|
|
111
|
-
async function proveTheCacheIsReal(
|
|
194
|
+
async function proveTheCacheIsReal(
|
|
195
|
+
ctx: ProofContext,
|
|
196
|
+
admin: AdminSession,
|
|
197
|
+
token: string,
|
|
198
|
+
): Promise<void> {
|
|
112
199
|
const N = 8;
|
|
113
200
|
await ctx.call("GET", "/auth/me", { token }); // warm, so the measurement is of the steady state
|
|
114
201
|
const before = { ...permissionCacheStore.stats };
|
|
@@ -120,16 +207,27 @@ async function proveTheCacheIsReal(ctx: ProofContext, admin: AdminSession, token
|
|
|
120
207
|
`${N} identical authorized requests resolve permissions 0 further times — all ${N} were cache hits (misses +${after.misses - before.misses}, hits +${after.hits - before.hits})`,
|
|
121
208
|
);
|
|
122
209
|
|
|
123
|
-
const userId = (await ctx.call("GET", "/auth/me", { token })).body
|
|
210
|
+
const userId = (await ctx.call("GET", "/auth/me", { token })).body
|
|
211
|
+
.sub as string;
|
|
124
212
|
const beforeGrant = { ...permissionCacheStore.stats };
|
|
125
|
-
await ctx.call("POST", `/auth/admin/users/${userId}/permissions`, {
|
|
213
|
+
await ctx.call("POST", `/auth/admin/users/${userId}/permissions`, {
|
|
214
|
+
token: await admin.freshToken(),
|
|
215
|
+
body: { permission: "probe:cache" },
|
|
216
|
+
});
|
|
126
217
|
const afterGrantCall = await ctx.call("GET", "/auth/me", { token });
|
|
127
218
|
ctx.assert(
|
|
128
219
|
permissionCacheStore.stats.misses > beforeGrant.misses,
|
|
129
220
|
"a grant bumps this user's version counter, so the next request misses the cache and re-resolves rather than serving a stale entry",
|
|
130
221
|
);
|
|
131
|
-
ctx.assert(
|
|
132
|
-
|
|
222
|
+
ctx.assert(
|
|
223
|
+
(afterGrantCall.body?.permissions ?? []).includes("probe:cache"),
|
|
224
|
+
"…and the freshly resolved answer contains the new permission",
|
|
225
|
+
);
|
|
226
|
+
await ctx.call(
|
|
227
|
+
"POST",
|
|
228
|
+
`/auth/admin/users/${userId}/permissions/${encodeURIComponent("probe:cache")}/revoke`,
|
|
229
|
+
{ token: await admin.freshToken() },
|
|
230
|
+
);
|
|
133
231
|
}
|
|
134
232
|
|
|
135
233
|
/**
|
|
@@ -137,23 +235,54 @@ async function proveTheCacheIsReal(ctx: ProofContext, admin: AdminSession, token
|
|
|
137
235
|
* doing the writing — this rules out any possibility that it is also doing something in memory.
|
|
138
236
|
* A `psql` session would look exactly like this.
|
|
139
237
|
*/
|
|
140
|
-
async function proveARawDatabaseEditChangesEnforcement(
|
|
238
|
+
async function proveARawDatabaseEditChangesEnforcement(
|
|
239
|
+
ctx: ProofContext,
|
|
240
|
+
admin: AdminSession,
|
|
241
|
+
): Promise<void> {
|
|
141
242
|
const email = ctx.uniqueEmail("rawedit");
|
|
142
243
|
const tokens = await ctx.signup(email, "rawedit-pw-12345");
|
|
143
|
-
const userId = (
|
|
144
|
-
|
|
145
|
-
|
|
244
|
+
const userId = (
|
|
245
|
+
await ctx.call("GET", "/auth/me", { token: tokens.accessToken })
|
|
246
|
+
).body.sub as string;
|
|
247
|
+
await ctx.call("POST", `/auth/admin/users/${userId}/permissions`, {
|
|
248
|
+
token: await admin.freshToken(),
|
|
249
|
+
body: { permission: "audit-log:read" },
|
|
250
|
+
});
|
|
251
|
+
ctx.assert(
|
|
252
|
+
(
|
|
253
|
+
await ctx.call("GET", "/auth/admin/audit-log", {
|
|
254
|
+
token: tokens.accessToken,
|
|
255
|
+
})
|
|
256
|
+
).status === 200,
|
|
257
|
+
"a direct grant opens the audit log",
|
|
258
|
+
);
|
|
146
259
|
|
|
147
260
|
// Written straight to the table, with no code change and no redeploy. The bump that follows is
|
|
148
261
|
// the out-of-band writer's half of the cache contract (permission-cache.ts) — without it the
|
|
149
262
|
// edit would still land, but only once the cached entry expired.
|
|
150
|
-
await prisma.permission.update({
|
|
263
|
+
await prisma.permission.update({
|
|
264
|
+
where: { slug: "audit-log:read" },
|
|
265
|
+
data: { isActive: false },
|
|
266
|
+
});
|
|
151
267
|
await permissionCacheStore.bump(POLICY_VERSION_KEY);
|
|
152
|
-
const denied = await ctx.call("GET", "/auth/admin/audit-log", {
|
|
153
|
-
|
|
268
|
+
const denied = await ctx.call("GET", "/auth/admin/audit-log", {
|
|
269
|
+
token: tokens.accessToken,
|
|
270
|
+
});
|
|
271
|
+
ctx.assert(
|
|
272
|
+
denied.status === 403,
|
|
273
|
+
`a permission deactivated by a raw database write stops opening its route (got ${denied.status})`,
|
|
274
|
+
);
|
|
154
275
|
|
|
155
|
-
await prisma.permission.update({
|
|
276
|
+
await prisma.permission.update({
|
|
277
|
+
where: { slug: "audit-log:read" },
|
|
278
|
+
data: { isActive: true },
|
|
279
|
+
});
|
|
156
280
|
await permissionCacheStore.bump(POLICY_VERSION_KEY);
|
|
157
|
-
const restored = await ctx.call("GET", "/auth/admin/audit-log", {
|
|
158
|
-
|
|
281
|
+
const restored = await ctx.call("GET", "/auth/admin/audit-log", {
|
|
282
|
+
token: tokens.accessToken,
|
|
283
|
+
});
|
|
284
|
+
ctx.assert(
|
|
285
|
+
restored.status === 200,
|
|
286
|
+
`…and reactivating it in the database opens the route again (got ${restored.status})`,
|
|
287
|
+
);
|
|
159
288
|
}
|
|
@@ -5,7 +5,7 @@ import type { RateLimitDeps } from "@/lib/auth/core/rate-limit.js";
|
|
|
5
5
|
import { createAdminRouter } from "./routers/admin.router.js";
|
|
6
6
|
import { AuditLogRepository } from "./repositories/audit-log.repository.js";
|
|
7
7
|
import { authCoreErrorMiddleware } from "./middleware/auth-core-error.middleware.js";
|
|
8
|
-
import { AuthConfig, defaultAuthConfig } from "./auth.config.js";
|
|
8
|
+
import { AuthConfig, defaultAuthConfig } from "./config/auth.config.js";
|
|
9
9
|
import { createAuthMiddleware } from "./middleware/auth.middleware.js";
|
|
10
10
|
import { createAuthRouter } from "./routers/auth.router.js";
|
|
11
11
|
import { AuthService } from "./services/auth.service.js";
|
|
@@ -14,15 +14,15 @@ import {
|
|
|
14
14
|
createWorkspaceMiddleware,
|
|
15
15
|
} from "./middleware/authz.middleware.js";
|
|
16
16
|
import { PrismaClient } from "./../generated/prisma/client.js";
|
|
17
|
-
import { KeyProviderService } from "./key-provider.js";
|
|
17
|
+
import { KeyProviderService } from "./config/key-provider.js";
|
|
18
18
|
import { OAuthRepository } from "./repositories/oauth.repository.js";
|
|
19
|
-
import { openApiSpec } from "./openapi-spec.js";
|
|
19
|
+
import { openApiSpec } from "./openapi/openapi-spec.js";
|
|
20
20
|
import { PasswordResetRepository } from "./repositories/password-reset.repository.js";
|
|
21
21
|
import {
|
|
22
22
|
InMemoryPermissionCacheStore,
|
|
23
23
|
PermissionCache,
|
|
24
|
-
} from "./permission-cache.js";
|
|
25
|
-
import { InMemoryRateLimitStore } from "./rate-limit.store.js";
|
|
24
|
+
} from "./cache/permission-cache.js";
|
|
25
|
+
import { InMemoryRateLimitStore } from "./cache/rate-limit.store.js";
|
|
26
26
|
import { RbacRepository } from "./repositories/rbac.repository.js";
|
|
27
27
|
import { responseEnvelope } from "./middleware/response-envelope.middleware.js";
|
|
28
28
|
import { SessionRepository } from "./repositories/session.repository.js";
|
package/registry/combos/express-prisma/variants/workspaces/src/middleware/authz.middleware.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { NextFunction, Request, RequestHandler, Response } from "express";
|
|
2
|
-
import { defineAbilitiesFor } from "../ability.js";
|
|
3
|
-
import { HttpError } from "../http-error.js";
|
|
4
|
-
import type { PermissionCache } from "../permission-cache.js";
|
|
2
|
+
import { defineAbilitiesFor } from "../ability/ability.js";
|
|
3
|
+
import { HttpError } from "../errors/http-error.js";
|
|
4
|
+
import type { PermissionCache } from "../cache/permission-cache.js";
|
|
5
5
|
import {
|
|
6
6
|
memberCacheKey,
|
|
7
7
|
type RbacRepository,
|