@infuro/cms-core 1.0.8 → 1.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin.cjs +2562 -1176
- package/dist/admin.cjs.map +1 -1
- package/dist/admin.d.cts +41 -2
- package/dist/admin.d.ts +41 -2
- package/dist/admin.js +2596 -1214
- package/dist/admin.js.map +1 -1
- package/dist/api.cjs +1695 -151
- package/dist/api.cjs.map +1 -1
- package/dist/api.d.cts +2 -1
- package/dist/api.d.ts +2 -1
- package/dist/api.js +1689 -146
- package/dist/api.js.map +1 -1
- package/dist/auth.cjs +153 -9
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.d.cts +17 -27
- package/dist/auth.d.ts +17 -27
- package/dist/auth.js +143 -8
- package/dist/auth.js.map +1 -1
- package/dist/cli.cjs +1 -1
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/helpers-dlrF_49e.d.cts +60 -0
- package/dist/helpers-dlrF_49e.d.ts +60 -0
- package/dist/{index-P5ajDo8-.d.ts → index-C_CZLmHD.d.cts} +88 -1
- package/dist/{index-P5ajDo8-.d.cts → index-DeO4AnAj.d.ts} +88 -1
- package/dist/index.cjs +3340 -715
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +154 -5
- package/dist/index.d.ts +154 -5
- package/dist/index.js +2821 -223
- package/dist/index.js.map +1 -1
- package/dist/migrations/1772178563555-ChatAndKnowledgeBase.ts +55 -0
- package/dist/migrations/{1731900000000-KnowledgeBaseVector.ts → 1772178563556-KnowledgeBaseVector.ts} +3 -4
- package/dist/migrations/1774300000000-RbacSeedGroupsAndPermissionUnique.ts +24 -0
- package/dist/migrations/1774300000001-SeedAdministratorUsersPermission.ts +35 -0
- package/dist/migrations/1774400000000-CustomerAdminAccessContactUser.ts +37 -0
- package/dist/migrations/1774400000001-StorefrontCartWishlist.ts +100 -0
- package/dist/migrations/1774400000002-WishlistGuestId.ts +29 -0
- package/dist/migrations/1774500000000-ProductCollectionHsn.ts +15 -0
- package/package.json +13 -7
- package/dist/migrations/1731800000000-ChatAndKnowledgeBase.ts +0 -39
- /package/{dist → src/admin}/admin.css +0 -0
package/dist/auth.cjs
CHANGED
|
@@ -30,19 +30,93 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/auth/index.ts
|
|
31
31
|
var auth_exports = {};
|
|
32
32
|
__export(auth_exports, {
|
|
33
|
+
ADMIN_GROUP_NAME: () => ADMIN_GROUP_NAME,
|
|
33
34
|
OPEN_ENDPOINTS: () => OPEN_ENDPOINTS,
|
|
34
35
|
PERMISSION_REQUIRED_ENDPOINTS: () => PERMISSION_REQUIRED_ENDPOINTS,
|
|
36
|
+
RBAC_ADMIN_ONLY_ENTITIES: () => RBAC_ADMIN_ONLY_ENTITIES,
|
|
37
|
+
canManageRoles: () => canManageRoles,
|
|
35
38
|
createAuthHelpers: () => createAuthHelpers,
|
|
36
39
|
createCmsMiddleware: () => createCmsMiddleware,
|
|
37
40
|
defaultPublicApiMethods: () => defaultPublicApiMethods,
|
|
38
41
|
getNextAuthOptions: () => getNextAuthOptions,
|
|
42
|
+
getPermissionableEntityKeys: () => getPermissionableEntityKeys,
|
|
39
43
|
getRequiredPermission: () => getRequiredPermission,
|
|
44
|
+
hasEntityPermission: () => hasEntityPermission,
|
|
40
45
|
isOpenEndpoint: () => isOpenEndpoint,
|
|
41
|
-
isPublicMethod: () => isPublicMethod
|
|
46
|
+
isPublicMethod: () => isPublicMethod,
|
|
47
|
+
isSuperAdminGroupName: () => isSuperAdminGroupName,
|
|
48
|
+
permissionRowsToRecord: () => permissionRowsToRecord,
|
|
49
|
+
seedAdministratorPermissions: () => seedAdministratorPermissions,
|
|
50
|
+
sessionHasEntityAccess: () => sessionHasEntityAccess
|
|
42
51
|
});
|
|
43
52
|
module.exports = __toCommonJS(auth_exports);
|
|
44
53
|
|
|
54
|
+
// src/auth/permission-entities.ts
|
|
55
|
+
var PERMISSION_ENTITY_INTERNAL_EXCLUDE = /* @__PURE__ */ new Set([
|
|
56
|
+
"users",
|
|
57
|
+
"password_reset_tokens",
|
|
58
|
+
"user_groups",
|
|
59
|
+
"permissions",
|
|
60
|
+
"comments",
|
|
61
|
+
"form_fields",
|
|
62
|
+
"configs",
|
|
63
|
+
"knowledge_base_chunks",
|
|
64
|
+
"carts",
|
|
65
|
+
"cart_items",
|
|
66
|
+
"wishlists",
|
|
67
|
+
"wishlist_items"
|
|
68
|
+
]);
|
|
69
|
+
var PERMISSION_LOGICAL_ENTITIES = [
|
|
70
|
+
"users",
|
|
71
|
+
"forms",
|
|
72
|
+
"form_submissions",
|
|
73
|
+
"dashboard",
|
|
74
|
+
"upload",
|
|
75
|
+
"settings",
|
|
76
|
+
"analytics",
|
|
77
|
+
"chat"
|
|
78
|
+
];
|
|
79
|
+
var ADMIN_GROUP_NAME = "Administrator";
|
|
80
|
+
function isSuperAdminGroupName(name) {
|
|
81
|
+
return name === ADMIN_GROUP_NAME;
|
|
82
|
+
}
|
|
83
|
+
function getPermissionableEntityKeys(entityMap) {
|
|
84
|
+
const fromMap = Object.keys(entityMap).filter((k) => !PERMISSION_ENTITY_INTERNAL_EXCLUDE.has(k));
|
|
85
|
+
const logical = PERMISSION_LOGICAL_ENTITIES.filter((k) => !fromMap.includes(k));
|
|
86
|
+
return [...fromMap.sort(), ...logical].filter((k, i, a) => a.indexOf(k) === i);
|
|
87
|
+
}
|
|
88
|
+
function permissionRowsToRecord(rows) {
|
|
89
|
+
const out = {};
|
|
90
|
+
if (!rows?.length) return out;
|
|
91
|
+
for (const p of rows) {
|
|
92
|
+
out[p.entity] = {
|
|
93
|
+
c: !!p.canCreate,
|
|
94
|
+
r: !!p.canRead,
|
|
95
|
+
u: !!p.canUpdate,
|
|
96
|
+
d: !!p.canDelete
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
return out;
|
|
100
|
+
}
|
|
101
|
+
function hasEntityPermission(record, entity, action) {
|
|
102
|
+
const p = record?.[entity];
|
|
103
|
+
if (!p) return false;
|
|
104
|
+
if (action === "create") return p.c;
|
|
105
|
+
if (action === "read") return p.r;
|
|
106
|
+
if (action === "update") return p.u;
|
|
107
|
+
return p.d;
|
|
108
|
+
}
|
|
109
|
+
|
|
45
110
|
// src/auth/helpers.ts
|
|
111
|
+
var RBAC_ADMIN_ONLY_ENTITIES = /* @__PURE__ */ new Set(["users", "user_groups", "permissions"]);
|
|
112
|
+
function sessionHasEntityAccess(user, entity, action) {
|
|
113
|
+
if (!user?.email) return false;
|
|
114
|
+
if (user.isRBACAdmin && RBAC_ADMIN_ONLY_ENTITIES.has(entity)) return true;
|
|
115
|
+
return hasEntityPermission(user.entityPerms, entity, action);
|
|
116
|
+
}
|
|
117
|
+
function canManageRoles(user) {
|
|
118
|
+
return !!(user?.email && user.isRBACAdmin);
|
|
119
|
+
}
|
|
46
120
|
var OPEN_ENDPOINTS = [
|
|
47
121
|
{ "/api/contacts": ["POST"] },
|
|
48
122
|
{ "/api/form-submissions": ["POST"] },
|
|
@@ -78,6 +152,25 @@ function createAuthHelpers(getSession, NextResponse) {
|
|
|
78
152
|
}
|
|
79
153
|
return null;
|
|
80
154
|
},
|
|
155
|
+
async requireEntityPermission(_req, entity, action) {
|
|
156
|
+
const session = await getSession();
|
|
157
|
+
if (!session?.user?.email) {
|
|
158
|
+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
159
|
+
}
|
|
160
|
+
const u = session.user;
|
|
161
|
+
if (sessionHasEntityAccess(u, entity, action)) return null;
|
|
162
|
+
return NextResponse.json({ error: "Forbidden", entity, action }, { status: 403 });
|
|
163
|
+
},
|
|
164
|
+
async requireAdminAccess() {
|
|
165
|
+
const session = await getSession();
|
|
166
|
+
if (!session?.user?.email) {
|
|
167
|
+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
168
|
+
}
|
|
169
|
+
const u = session.user;
|
|
170
|
+
if (u.isRBACAdmin) return null;
|
|
171
|
+
if (u.adminAccess === false) return NextResponse.json({ error: "Forbidden", reason: "admin_access" }, { status: 403 });
|
|
172
|
+
return null;
|
|
173
|
+
},
|
|
81
174
|
async getAuthenticatedUser() {
|
|
82
175
|
const session = await getSession();
|
|
83
176
|
return session?.user ?? null;
|
|
@@ -85,6 +178,36 @@ function createAuthHelpers(getSession, NextResponse) {
|
|
|
85
178
|
};
|
|
86
179
|
}
|
|
87
180
|
|
|
181
|
+
// src/auth/seed-permissions.ts
|
|
182
|
+
async function seedAdministratorPermissions(dataSource, entityMap) {
|
|
183
|
+
const entities = getPermissionableEntityKeys(entityMap);
|
|
184
|
+
const groupRepo = dataSource.getRepository(entityMap.user_groups);
|
|
185
|
+
const permRepo = dataSource.getRepository(entityMap.permissions);
|
|
186
|
+
const adminGroup = await groupRepo.findOne({ where: { name: ADMIN_GROUP_NAME, deleted: false } });
|
|
187
|
+
if (!adminGroup) return;
|
|
188
|
+
const fullCrud = { canCreate: true, canRead: true, canUpdate: true, canDelete: true };
|
|
189
|
+
for (const entity of entities) {
|
|
190
|
+
const existing = await permRepo.findOne({
|
|
191
|
+
where: { groupId: adminGroup.id, entity }
|
|
192
|
+
});
|
|
193
|
+
if (existing) {
|
|
194
|
+
existing.canCreate = true;
|
|
195
|
+
existing.canRead = true;
|
|
196
|
+
existing.canUpdate = true;
|
|
197
|
+
existing.canDelete = true;
|
|
198
|
+
await permRepo.save(existing);
|
|
199
|
+
} else {
|
|
200
|
+
await permRepo.save(
|
|
201
|
+
permRepo.create({
|
|
202
|
+
groupId: adminGroup.id,
|
|
203
|
+
entity,
|
|
204
|
+
...fullCrud
|
|
205
|
+
})
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
88
211
|
// src/auth/middleware.ts
|
|
89
212
|
var defaultPublicApiMethods = {
|
|
90
213
|
"/api/contacts": ["POST"],
|
|
@@ -159,12 +282,18 @@ function getNextAuthOptions(config) {
|
|
|
159
282
|
if (!user || user.blocked || user.deleted || !user.password) return null;
|
|
160
283
|
const valid = await comparePassword(credentials.password, user.password);
|
|
161
284
|
if (!valid) return null;
|
|
285
|
+
const g = user.group;
|
|
286
|
+
const isRBACAdmin = isSuperAdminGroupName(g?.name);
|
|
287
|
+
const entityPerms = permissionRowsToRecord(g?.permissions);
|
|
288
|
+
const adminAccess = user.adminAccess === true;
|
|
162
289
|
return {
|
|
163
290
|
id: user.id.toString(),
|
|
164
291
|
email: user.email,
|
|
165
292
|
name: user.name,
|
|
166
293
|
groupId: user.groupId ?? void 0,
|
|
167
|
-
|
|
294
|
+
isRBACAdmin,
|
|
295
|
+
entityPerms,
|
|
296
|
+
adminAccess
|
|
168
297
|
};
|
|
169
298
|
} catch {
|
|
170
299
|
return null;
|
|
@@ -188,17 +317,23 @@ function getNextAuthOptions(config) {
|
|
|
188
317
|
callbacks: {
|
|
189
318
|
async jwt({ token, user }) {
|
|
190
319
|
if (user) {
|
|
191
|
-
|
|
192
|
-
token.
|
|
193
|
-
token.
|
|
320
|
+
const u = user;
|
|
321
|
+
token.id = u.id;
|
|
322
|
+
token.groupId = u.groupId;
|
|
323
|
+
token.isRBACAdmin = u.isRBACAdmin;
|
|
324
|
+
token.entityPerms = u.entityPerms;
|
|
325
|
+
token.adminAccess = u.adminAccess;
|
|
194
326
|
}
|
|
195
327
|
return token;
|
|
196
328
|
},
|
|
197
329
|
async session({ session, token }) {
|
|
198
330
|
if (session.user) {
|
|
199
|
-
|
|
200
|
-
session.user.
|
|
201
|
-
session.user.
|
|
331
|
+
const t = token;
|
|
332
|
+
session.user.id = t.id;
|
|
333
|
+
session.user.groupId = t.groupId;
|
|
334
|
+
session.user.isRBACAdmin = t.isRBACAdmin;
|
|
335
|
+
session.user.entityPerms = t.entityPerms;
|
|
336
|
+
session.user.adminAccess = t.adminAccess;
|
|
202
337
|
}
|
|
203
338
|
return session;
|
|
204
339
|
}
|
|
@@ -208,14 +343,23 @@ function getNextAuthOptions(config) {
|
|
|
208
343
|
}
|
|
209
344
|
// Annotate the CommonJS export names for ESM import in node:
|
|
210
345
|
0 && (module.exports = {
|
|
346
|
+
ADMIN_GROUP_NAME,
|
|
211
347
|
OPEN_ENDPOINTS,
|
|
212
348
|
PERMISSION_REQUIRED_ENDPOINTS,
|
|
349
|
+
RBAC_ADMIN_ONLY_ENTITIES,
|
|
350
|
+
canManageRoles,
|
|
213
351
|
createAuthHelpers,
|
|
214
352
|
createCmsMiddleware,
|
|
215
353
|
defaultPublicApiMethods,
|
|
216
354
|
getNextAuthOptions,
|
|
355
|
+
getPermissionableEntityKeys,
|
|
217
356
|
getRequiredPermission,
|
|
357
|
+
hasEntityPermission,
|
|
218
358
|
isOpenEndpoint,
|
|
219
|
-
isPublicMethod
|
|
359
|
+
isPublicMethod,
|
|
360
|
+
isSuperAdminGroupName,
|
|
361
|
+
permissionRowsToRecord,
|
|
362
|
+
seedAdministratorPermissions,
|
|
363
|
+
sessionHasEntityAccess
|
|
220
364
|
});
|
|
221
365
|
//# sourceMappingURL=auth.cjs.map
|
package/dist/auth.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/auth/index.ts","../src/auth/helpers.ts","../src/auth/middleware.ts","../src/auth/nextauth-options.ts"],"sourcesContent":["export {\n createAuthHelpers,\n OPEN_ENDPOINTS,\n PERMISSION_REQUIRED_ENDPOINTS,\n isOpenEndpoint,\n isPublicMethod,\n getRequiredPermission,\n} from './helpers';\nexport type { SessionUser, GetSession, AuthHelpers } from './helpers';\nexport { createCmsMiddleware, defaultPublicApiMethods } from './middleware';\nexport type { CmsMiddlewareConfig } from './middleware';\nexport { getNextAuthOptions } from './nextauth-options';\nexport type { NextAuthOptionsConfig, NextAuthUser } from './nextauth-options';\n","export interface SessionUser {\n id?: string;\n email?: string | null;\n name?: string | null;\n groupId?: number;\n permissions?: string[];\n}\n\nexport type GetSession = () => Promise<{ user?: SessionUser } | null>;\n\nexport const OPEN_ENDPOINTS: Array<Record<string, string[]>> = [\n { '/api/contacts': ['POST'] },\n { '/api/form-submissions': ['POST'] },\n { '/api/blogs': ['GET'] },\n];\n\nexport const PERMISSION_REQUIRED_ENDPOINTS: Record<string, string[]> = {};\n\nexport function isOpenEndpoint(pathname: string): boolean {\n return OPEN_ENDPOINTS.some((endpoint) => pathname.startsWith(Object.keys(endpoint)[0]));\n}\n\nexport function getRequiredPermission(pathname: string): string[] | null {\n return null;\n}\n\nexport function isPublicMethod(pathname: string, method: string): boolean {\n for (const endpoint of OPEN_ENDPOINTS) {\n const key = Object.keys(endpoint)[0];\n if (pathname.startsWith(key) && endpoint[key].includes(method)) return true;\n }\n return false;\n}\n\nexport interface AuthHelpers {\n requireAuth(req: Request): Promise<Response | null>;\n requirePermission(req: Request, permission: string): Promise<Response | null>;\n getAuthenticatedUser(): Promise<SessionUser | null>;\n}\n\nexport function createAuthHelpers(getSession: GetSession, NextResponse: { json: (body: unknown, init?: { status?: number }) => Response }): AuthHelpers {\n return {\n async requireAuth() {\n const session = await getSession();\n if (!session?.user?.email) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });\n }\n return null;\n },\n async requirePermission() {\n const session = await getSession();\n if (!session?.user?.email) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });\n }\n return null;\n },\n async getAuthenticatedUser() {\n const session = await getSession();\n return session?.user ?? null;\n },\n };\n}\n","export interface CmsMiddlewareConfig {\n publicAdminPaths?: string[];\n publicApiPaths?: string[];\n /** path -> allowed methods */\n publicApiMethods?: Record<string, string[]>;\n signInPath?: string;\n getSessionToken?: (request: { cookies: { get: (name: string) => { value?: string } | undefined } }) => string | undefined;\n}\n\n/** Default public API paths (no auth). Sites should extend this with their own routes. */\nexport const defaultPublicApiMethods: Record<string, string[]> = {\n '/api/contacts': ['POST'],\n '/api/form-submissions': ['POST'],\n '/api/blogs': ['GET'],\n '/api/forms': ['GET'],\n '/api/auth': ['GET', 'POST'],\n '/api/health': ['GET'],\n '/api/users/forgot-password': ['POST'],\n '/api/users/set-password': ['POST'],\n '/api/users/invite': ['POST'],\n};\n\nfunction defaultGetSessionToken(request: { cookies: { get: (name: string) => { value?: string } | undefined } }): string | undefined {\n return (\n request.cookies.get('__Secure-next-auth.session-token')?.value ??\n request.cookies.get('next-auth.session-token')?.value\n );\n}\n\nfunction isPublicMethod(pathname: string, method: string, publicApiMethods: Record<string, string[]>): boolean {\n for (const [endpoint, methods] of Object.entries(publicApiMethods)) {\n if (pathname.startsWith(endpoint) && methods.includes(method)) return true;\n }\n return false;\n}\n\n/**\n * Returns middleware logic. Use from Next.js middleware:\n * import { createCmsMiddleware } from '@infuro/cms-core';\n * export const middleware = createCmsMiddleware({ ... });\n * export const config = { matcher: ['/admin/:path*', '/api/:path*'] };\n */\nexport function createCmsMiddleware(config: CmsMiddlewareConfig = {}) {\n const {\n publicAdminPaths = ['/admin/signin', '/admin/forgot-password', '/admin/reset-password', '/admin/invite'],\n publicApiMethods = defaultPublicApiMethods,\n signInPath = '/admin/signin',\n getSessionToken = defaultGetSessionToken,\n } = config;\n\n return function cmsMiddleware(request: {\n nextUrl: { pathname: string };\n url: string;\n method: string;\n cookies: { get: (name: string) => { value?: string } | undefined };\n }): { type: 'next' } | { type: 'redirect'; url: string } | { type: 'json'; status: number; body: unknown } {\n const pathname = request.nextUrl.pathname;\n const method = request.method;\n\n if (publicAdminPaths.some((p) => pathname === p || pathname.startsWith(p + '/'))) {\n return { type: 'next' };\n }\n\n if (pathname.startsWith('/admin')) {\n const token = getSessionToken(request);\n if (!token) {\n return { type: 'redirect', url: new URL(signInPath, request.url).toString() };\n }\n }\n\n if (pathname.startsWith('/api')) {\n if (isPublicMethod(pathname, method, publicApiMethods)) {\n return { type: 'next' };\n }\n const token = getSessionToken(request);\n if (!token) {\n return { type: 'json', status: 401, body: { error: 'Unauthorized' } };\n }\n }\n\n return { type: 'next' };\n };\n}\n","/**\n * Build NextAuth options for credentials auth. App can extend/override via extend().\n */\nimport type { NextAuthOptions } from 'next-auth';\nimport _CredentialsProvider from 'next-auth/providers/credentials';\nconst CredentialsProvider = (_CredentialsProvider as unknown as { default: typeof _CredentialsProvider }).default ?? _CredentialsProvider;\n\nexport interface NextAuthUser {\n id: number;\n email: string;\n name: string | null;\n password: string | null;\n blocked?: boolean;\n deleted?: boolean;\n groupId?: number | null;\n group?: { permissions?: unknown[] };\n}\n\nexport interface NextAuthOptionsConfig {\n /** Resolve user by email (e.g. from TypeORM). Return null if not found. */\n getUserByEmail: (email: string) => Promise<NextAuthUser | null>;\n comparePassword: (plain: string, hash: string) => Promise<boolean>;\n signInPage?: string;\n secret?: string;\n extend?: (options: NextAuthOptions) => NextAuthOptions;\n}\n\nexport function getNextAuthOptions(config: NextAuthOptionsConfig): NextAuthOptions {\n const { getUserByEmail, comparePassword, signInPage = '/admin/signin', secret, extend } = config;\n\n const options: NextAuthOptions = {\n secret: secret ?? process.env.NEXTAUTH_SECRET,\n providers: [\n CredentialsProvider({\n name: 'credentials',\n credentials: {\n email: { label: 'Email', type: 'email' },\n password: { label: 'Password', type: 'password' },\n },\n async authorize(credentials) {\n if (!credentials?.email || !credentials?.password) return null;\n try {\n const user = await getUserByEmail(credentials.email);\n if (!user || user.blocked || (user as { deleted?: boolean }).deleted || !user.password) return null;\n const valid = await comparePassword(credentials.password, user.password);\n if (!valid) return null;\n return {\n id: user.id.toString(),\n email: user.email,\n name: user.name,\n groupId: user.groupId ?? undefined,\n permissions: ['admin'],\n };\n } catch {\n return null;\n }\n },\n }),\n ],\n session: { strategy: 'jwt' },\n pages: { signIn: signInPage },\n cookies: {\n sessionToken: {\n name: process.env.NEXTAUTH_URL?.startsWith('https')\n ? '__Secure-next-auth.session-token'\n : 'next-auth.session-token',\n options: {\n httpOnly: true,\n sameSite: 'lax',\n path: '/',\n secure: process.env.NEXTAUTH_URL?.startsWith('https') ?? false,\n },\n },\n },\n callbacks: {\n async jwt({ token, user }) {\n if (user) {\n (token as Record<string, unknown>).id = user.id;\n (token as Record<string, unknown>).groupId = (user as { groupId?: number }).groupId;\n (token as Record<string, unknown>).permissions = (user as { permissions?: string[] }).permissions;\n }\n return token;\n },\n async session({ session, token }) {\n if (session.user) {\n (session.user as Record<string, unknown>).id = (token as Record<string, unknown>).id;\n (session.user as Record<string, unknown>).groupId = (token as Record<string, unknown>).groupId;\n (session.user as Record<string, unknown>).permissions = (token as Record<string, unknown>).permissions;\n }\n return session;\n },\n },\n };\n\n return extend ? extend(options) : options;\n}\n"],"mappings":"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","names":["isPublicMethod","_CredentialsProvider"]}
|
|
1
|
+
{"version":3,"sources":["../src/auth/index.ts","../src/auth/permission-entities.ts","../src/auth/helpers.ts","../src/auth/seed-permissions.ts","../src/auth/middleware.ts","../src/auth/nextauth-options.ts"],"sourcesContent":["export {\n createAuthHelpers,\n OPEN_ENDPOINTS,\n PERMISSION_REQUIRED_ENDPOINTS,\n isOpenEndpoint,\n isPublicMethod,\n getRequiredPermission,\n} from './helpers';\nexport type { SessionUser, GetSession, AuthHelpers } from './helpers';\nexport { sessionHasEntityAccess, canManageRoles, RBAC_ADMIN_ONLY_ENTITIES } from './helpers';\nexport {\n getPermissionableEntityKeys,\n ADMIN_GROUP_NAME,\n isSuperAdminGroupName,\n permissionRowsToRecord,\n hasEntityPermission,\n} from './permission-entities';\nexport { seedAdministratorPermissions } from './seed-permissions';\nexport type { EntityCrudAction, EntityPermissionFlags } from './permission-entities';\nexport { createCmsMiddleware, defaultPublicApiMethods } from './middleware';\nexport type { CmsMiddlewareConfig } from './middleware';\nexport { getNextAuthOptions } from './nextauth-options';\nexport type { NextAuthOptionsConfig, NextAuthUser } from './nextauth-options';\n","/** API resource keys excluded from the permission matrix (internal / not admin-CRUD). */\nexport const PERMISSION_ENTITY_INTERNAL_EXCLUDE = new Set([\n 'users',\n 'password_reset_tokens',\n 'user_groups',\n 'permissions',\n 'comments',\n 'form_fields',\n 'configs',\n 'knowledge_base_chunks',\n 'carts',\n 'cart_items',\n 'wishlists',\n 'wishlist_items',\n]);\n\n/** Non-CRUD admin surfaces mapped to entity keys for RBAC. */\nexport const PERMISSION_LOGICAL_ENTITIES = [\n 'users',\n 'forms',\n 'form_submissions',\n 'dashboard',\n 'upload',\n 'settings',\n 'analytics',\n 'chat',\n] as const;\n\n/** Canonical name for new installs / migrations */\nexport const ADMIN_GROUP_NAME = 'Administrator';\n\n/** System administrator group (roles UI + users / user_groups / permissions bypass). */\nexport function isSuperAdminGroupName(name: string | null | undefined): boolean {\n return name === ADMIN_GROUP_NAME;\n}\n\nexport type EntityCrudAction = 'create' | 'read' | 'update' | 'delete';\n\nexport type EntityPermissionFlags = { c: boolean; r: boolean; u: boolean; d: boolean };\n\nexport function getPermissionableEntityKeys(entityMap: Record<string, unknown>): string[] {\n const fromMap = Object.keys(entityMap).filter((k) => !PERMISSION_ENTITY_INTERNAL_EXCLUDE.has(k));\n const logical = PERMISSION_LOGICAL_ENTITIES.filter((k) => !fromMap.includes(k));\n return [...fromMap.sort(), ...logical].filter((k, i, a) => a.indexOf(k) === i);\n}\n\nexport function permissionRowsToRecord(\n rows: Array<{ entity: string; canCreate: boolean; canRead: boolean; canUpdate: boolean; canDelete: boolean }> | undefined\n): Record<string, EntityPermissionFlags> {\n const out: Record<string, EntityPermissionFlags> = {};\n if (!rows?.length) return out;\n for (const p of rows) {\n out[p.entity] = {\n c: !!p.canCreate,\n r: !!p.canRead,\n u: !!p.canUpdate,\n d: !!p.canDelete,\n };\n }\n return out;\n}\n\nexport function hasEntityPermission(\n record: Record<string, EntityPermissionFlags> | undefined,\n entity: string,\n action: EntityCrudAction\n): boolean {\n const p = record?.[entity];\n if (!p) return false;\n if (action === 'create') return p.c;\n if (action === 'read') return p.r;\n if (action === 'update') return p.u;\n return p.d;\n}\n","import type { EntityCrudAction, EntityPermissionFlags } from './permission-entities';\nimport { hasEntityPermission } from './permission-entities';\n\n/** isRBACAdmin bypasses entity checks only for these (users / roles plumbing). */\nexport const RBAC_ADMIN_ONLY_ENTITIES = new Set(['users', 'user_groups', 'permissions']);\n\nexport interface SessionUser {\n id?: string;\n email?: string | null;\n name?: string | null;\n groupId?: number;\n /** @deprecated use entityPerms / isRBACAdmin */\n permissions?: string[];\n /** Administrator group: full access only for users, user_groups, permissions */\n isRBACAdmin?: boolean;\n entityPerms?: Record<string, EntityPermissionFlags>;\n /** When false and not isRBACAdmin, admin API/UI is denied. */\n adminAccess?: boolean;\n}\n\nexport function sessionHasEntityAccess(\n user: SessionUser | null | undefined,\n entity: string,\n action: EntityCrudAction\n): boolean {\n if (!user?.email) return false;\n if (user.isRBACAdmin && RBAC_ADMIN_ONLY_ENTITIES.has(entity)) return true;\n return hasEntityPermission(user.entityPerms, entity, action);\n}\n\nexport function canManageRoles(user: SessionUser | null | undefined): boolean {\n return !!(user?.email && user.isRBACAdmin);\n}\n\nexport type GetSession = () => Promise<{ user?: SessionUser } | null>;\n\nexport const OPEN_ENDPOINTS: Array<Record<string, string[]>> = [\n { '/api/contacts': ['POST'] },\n { '/api/form-submissions': ['POST'] },\n { '/api/blogs': ['GET'] },\n];\n\nexport const PERMISSION_REQUIRED_ENDPOINTS: Record<string, string[]> = {};\n\nexport function isOpenEndpoint(pathname: string): boolean {\n return OPEN_ENDPOINTS.some((endpoint) => pathname.startsWith(Object.keys(endpoint)[0]));\n}\n\nexport function getRequiredPermission(pathname: string): string[] | null {\n return null;\n}\n\nexport function isPublicMethod(pathname: string, method: string): boolean {\n for (const endpoint of OPEN_ENDPOINTS) {\n const key = Object.keys(endpoint)[0];\n if (pathname.startsWith(key) && endpoint[key].includes(method)) return true;\n }\n return false;\n}\n\nexport interface AuthHelpers {\n requireAuth(req: Request): Promise<Response | null>;\n requirePermission(req: Request, permission: string): Promise<Response | null>;\n requireEntityPermission(req: Request, entity: string, action: EntityCrudAction): Promise<Response | null>;\n requireAdminAccess(req: Request): Promise<Response | null>;\n getAuthenticatedUser(): Promise<SessionUser | null>;\n}\n\nexport function createAuthHelpers(getSession: GetSession, NextResponse: { json: (body: unknown, init?: { status?: number }) => Response }): AuthHelpers {\n return {\n async requireAuth() {\n const session = await getSession();\n if (!session?.user?.email) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });\n }\n return null;\n },\n async requirePermission() {\n const session = await getSession();\n if (!session?.user?.email) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });\n }\n return null;\n },\n async requireEntityPermission(_req: Request, entity: string, action: EntityCrudAction) {\n const session = await getSession();\n if (!session?.user?.email) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });\n }\n const u = session.user as SessionUser;\n if (sessionHasEntityAccess(u, entity, action)) return null;\n return NextResponse.json({ error: 'Forbidden', entity, action }, { status: 403 });\n },\n async requireAdminAccess() {\n const session = await getSession();\n if (!session?.user?.email) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });\n }\n const u = session.user as SessionUser;\n if (u.isRBACAdmin) return null;\n if (u.adminAccess === false) return NextResponse.json({ error: 'Forbidden', reason: 'admin_access' }, { status: 403 });\n return null;\n },\n async getAuthenticatedUser() {\n const session = await getSession();\n return (session?.user as SessionUser) ?? null;\n },\n };\n}\n","import type { DataSource } from 'typeorm';\nimport type { EntityTarget } from 'typeorm';\nimport type { ObjectLiteral } from 'typeorm';\nimport { getPermissionableEntityKeys, ADMIN_GROUP_NAME } from './permission-entities';\n\n/**\n * Ensures the Administrator group has full CRUD on every permissionable entity\n * for the given entity map. Idempotent: upserts per entity.\n */\nexport async function seedAdministratorPermissions(\n dataSource: DataSource,\n entityMap: Record<string, unknown>\n): Promise<void> {\n const entities = getPermissionableEntityKeys(entityMap);\n const groupRepo = dataSource.getRepository(entityMap.user_groups as EntityTarget<ObjectLiteral>);\n const permRepo = dataSource.getRepository(entityMap.permissions as EntityTarget<ObjectLiteral>);\n\n const adminGroup = await groupRepo.findOne({ where: { name: ADMIN_GROUP_NAME, deleted: false } });\n if (!adminGroup) return;\n\n const fullCrud = { canCreate: true, canRead: true, canUpdate: true, canDelete: true };\n\n for (const entity of entities) {\n const existing = await permRepo.findOne({\n where: { groupId: adminGroup.id, entity },\n });\n if (existing) {\n existing.canCreate = true;\n existing.canRead = true;\n existing.canUpdate = true;\n existing.canDelete = true;\n await permRepo.save(existing);\n } else {\n await permRepo.save(\n permRepo.create({\n groupId: adminGroup.id,\n entity,\n ...fullCrud,\n })\n );\n }\n }\n}\n","export interface CmsMiddlewareConfig {\n publicAdminPaths?: string[];\n publicApiPaths?: string[];\n /** path -> allowed methods */\n publicApiMethods?: Record<string, string[]>;\n signInPath?: string;\n getSessionToken?: (request: { cookies: { get: (name: string) => { value?: string } | undefined } }) => string | undefined;\n}\n\n/** Default public API paths (no auth). Sites should extend this with their own routes. */\nexport const defaultPublicApiMethods: Record<string, string[]> = {\n '/api/contacts': ['POST'],\n '/api/form-submissions': ['POST'],\n '/api/blogs': ['GET'],\n '/api/forms': ['GET'],\n '/api/auth': ['GET', 'POST'],\n '/api/health': ['GET'],\n '/api/users/forgot-password': ['POST'],\n '/api/users/set-password': ['POST'],\n '/api/users/invite': ['POST'],\n};\n\nfunction defaultGetSessionToken(request: { cookies: { get: (name: string) => { value?: string } | undefined } }): string | undefined {\n return (\n request.cookies.get('__Secure-next-auth.session-token')?.value ??\n request.cookies.get('next-auth.session-token')?.value\n );\n}\n\nfunction isPublicMethod(pathname: string, method: string, publicApiMethods: Record<string, string[]>): boolean {\n for (const [endpoint, methods] of Object.entries(publicApiMethods)) {\n if (pathname.startsWith(endpoint) && methods.includes(method)) return true;\n }\n return false;\n}\n\n/**\n * Returns middleware logic. Use from Next.js middleware:\n * import { createCmsMiddleware } from '@infuro/cms-core';\n * export const middleware = createCmsMiddleware({ ... });\n * export const config = { matcher: ['/admin/:path*', '/api/:path*'] };\n */\nexport function createCmsMiddleware(config: CmsMiddlewareConfig = {}) {\n const {\n publicAdminPaths = ['/admin/signin', '/admin/forgot-password', '/admin/reset-password', '/admin/invite'],\n publicApiMethods = defaultPublicApiMethods,\n signInPath = '/admin/signin',\n getSessionToken = defaultGetSessionToken,\n } = config;\n\n return function cmsMiddleware(request: {\n nextUrl: { pathname: string };\n url: string;\n method: string;\n cookies: { get: (name: string) => { value?: string } | undefined };\n }): { type: 'next' } | { type: 'redirect'; url: string } | { type: 'json'; status: number; body: unknown } {\n const pathname = request.nextUrl.pathname;\n const method = request.method;\n\n if (publicAdminPaths.some((p) => pathname === p || pathname.startsWith(p + '/'))) {\n return { type: 'next' };\n }\n\n if (pathname.startsWith('/admin')) {\n const token = getSessionToken(request);\n if (!token) {\n return { type: 'redirect', url: new URL(signInPath, request.url).toString() };\n }\n }\n\n if (pathname.startsWith('/api')) {\n if (isPublicMethod(pathname, method, publicApiMethods)) {\n return { type: 'next' };\n }\n const token = getSessionToken(request);\n if (!token) {\n return { type: 'json', status: 401, body: { error: 'Unauthorized' } };\n }\n }\n\n return { type: 'next' };\n };\n}\n","/**\n * Build NextAuth options for credentials auth. App can extend/override via extend().\n */\nimport type { NextAuthOptions } from 'next-auth';\nimport _CredentialsProvider from 'next-auth/providers/credentials';\nconst CredentialsProvider = (_CredentialsProvider as unknown as { default: typeof _CredentialsProvider }).default ?? _CredentialsProvider;\nimport { isSuperAdminGroupName, permissionRowsToRecord } from './permission-entities';\n\nexport interface NextAuthUser {\n id: number;\n email: string;\n name: string | null;\n password: string | null;\n blocked?: boolean;\n deleted?: boolean;\n groupId?: number | null;\n adminAccess?: boolean;\n group?: {\n name?: string;\n permissions?: Array<{\n entity: string;\n canCreate: boolean;\n canRead: boolean;\n canUpdate: boolean;\n canDelete: boolean;\n }>;\n };\n}\n\nexport interface NextAuthOptionsConfig {\n /** Resolve user by email (e.g. from TypeORM). Return null if not found. */\n getUserByEmail: (email: string) => Promise<NextAuthUser | null>;\n comparePassword: (plain: string, hash: string) => Promise<boolean>;\n signInPage?: string;\n secret?: string;\n extend?: (options: NextAuthOptions) => NextAuthOptions;\n}\n\nexport function getNextAuthOptions(config: NextAuthOptionsConfig): NextAuthOptions {\n const { getUserByEmail, comparePassword, signInPage = '/admin/signin', secret, extend } = config;\n\n const options: NextAuthOptions = {\n secret: secret ?? process.env.NEXTAUTH_SECRET,\n providers: [\n CredentialsProvider({\n name: 'credentials',\n credentials: {\n email: { label: 'Email', type: 'email' },\n password: { label: 'Password', type: 'password' },\n },\n async authorize(credentials) {\n if (!credentials?.email || !credentials?.password) return null;\n try {\n const user = await getUserByEmail(credentials.email);\n if (!user || user.blocked || (user as { deleted?: boolean }).deleted || !user.password) return null;\n const valid = await comparePassword(credentials.password, user.password);\n if (!valid) return null;\n const g = user.group;\n const isRBACAdmin = isSuperAdminGroupName(g?.name);\n const entityPerms = permissionRowsToRecord(g?.permissions);\n const adminAccess = (user as NextAuthUser & { adminAccess?: boolean }).adminAccess === true;\n return {\n id: user.id.toString(),\n email: user.email,\n name: user.name,\n groupId: user.groupId ?? undefined,\n isRBACAdmin,\n entityPerms,\n adminAccess,\n };\n } catch {\n return null;\n }\n },\n }),\n ],\n session: { strategy: 'jwt' },\n pages: { signIn: signInPage },\n cookies: {\n sessionToken: {\n name: process.env.NEXTAUTH_URL?.startsWith('https')\n ? '__Secure-next-auth.session-token'\n : 'next-auth.session-token',\n options: {\n httpOnly: true,\n sameSite: 'lax',\n path: '/',\n secure: process.env.NEXTAUTH_URL?.startsWith('https') ?? false,\n },\n },\n },\n callbacks: {\n async jwt({ token, user }) {\n if (user) {\n const u = user as unknown as Record<string, unknown>;\n (token as Record<string, unknown>).id = u.id;\n (token as Record<string, unknown>).groupId = u.groupId;\n (token as Record<string, unknown>).isRBACAdmin = u.isRBACAdmin;\n (token as Record<string, unknown>).entityPerms = u.entityPerms;\n (token as Record<string, unknown>).adminAccess = u.adminAccess;\n }\n return token;\n },\n async session({ session, token }) {\n if (session.user) {\n const t = token as Record<string, unknown>;\n (session.user as Record<string, unknown>).id = t.id;\n (session.user as Record<string, unknown>).groupId = t.groupId;\n (session.user as Record<string, unknown>).isRBACAdmin = t.isRBACAdmin;\n (session.user as Record<string, unknown>).entityPerms = t.entityPerms;\n (session.user as Record<string, unknown>).adminAccess = t.adminAccess;\n }\n return session;\n },\n },\n };\n\n return extend ? extend(options) : options;\n}\n"],"mappings":"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","names":["isPublicMethod","_CredentialsProvider"]}
|
package/dist/auth.d.cts
CHANGED
|
@@ -1,30 +1,12 @@
|
|
|
1
|
+
export { A as ADMIN_GROUP_NAME, a as AuthHelpers, E as EntityCrudAction, b as EntityPermissionFlags, G as GetSession, O as OPEN_ENDPOINTS, P as PERMISSION_REQUIRED_ENDPOINTS, R as RBAC_ADMIN_ONLY_ENTITIES, S as SessionUser, c as canManageRoles, d as createAuthHelpers, g as getPermissionableEntityKeys, e as getRequiredPermission, h as hasEntityPermission, i as isOpenEndpoint, f as isPublicMethod, j as isSuperAdminGroupName, p as permissionRowsToRecord, s as sessionHasEntityAccess } from './helpers-dlrF_49e.cjs';
|
|
2
|
+
import { DataSource } from 'typeorm';
|
|
1
3
|
import { NextAuthOptions } from 'next-auth';
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
permissions?: string[];
|
|
9
|
-
}
|
|
10
|
-
type GetSession = () => Promise<{
|
|
11
|
-
user?: SessionUser;
|
|
12
|
-
} | null>;
|
|
13
|
-
declare const OPEN_ENDPOINTS: Array<Record<string, string[]>>;
|
|
14
|
-
declare const PERMISSION_REQUIRED_ENDPOINTS: Record<string, string[]>;
|
|
15
|
-
declare function isOpenEndpoint(pathname: string): boolean;
|
|
16
|
-
declare function getRequiredPermission(pathname: string): string[] | null;
|
|
17
|
-
declare function isPublicMethod(pathname: string, method: string): boolean;
|
|
18
|
-
interface AuthHelpers {
|
|
19
|
-
requireAuth(req: Request): Promise<Response | null>;
|
|
20
|
-
requirePermission(req: Request, permission: string): Promise<Response | null>;
|
|
21
|
-
getAuthenticatedUser(): Promise<SessionUser | null>;
|
|
22
|
-
}
|
|
23
|
-
declare function createAuthHelpers(getSession: GetSession, NextResponse: {
|
|
24
|
-
json: (body: unknown, init?: {
|
|
25
|
-
status?: number;
|
|
26
|
-
}) => Response;
|
|
27
|
-
}): AuthHelpers;
|
|
5
|
+
/**
|
|
6
|
+
* Ensures the Administrator group has full CRUD on every permissionable entity
|
|
7
|
+
* for the given entity map. Idempotent: upserts per entity.
|
|
8
|
+
*/
|
|
9
|
+
declare function seedAdministratorPermissions(dataSource: DataSource, entityMap: Record<string, unknown>): Promise<void>;
|
|
28
10
|
|
|
29
11
|
interface CmsMiddlewareConfig {
|
|
30
12
|
publicAdminPaths?: string[];
|
|
@@ -82,8 +64,16 @@ interface NextAuthUser {
|
|
|
82
64
|
blocked?: boolean;
|
|
83
65
|
deleted?: boolean;
|
|
84
66
|
groupId?: number | null;
|
|
67
|
+
adminAccess?: boolean;
|
|
85
68
|
group?: {
|
|
86
|
-
|
|
69
|
+
name?: string;
|
|
70
|
+
permissions?: Array<{
|
|
71
|
+
entity: string;
|
|
72
|
+
canCreate: boolean;
|
|
73
|
+
canRead: boolean;
|
|
74
|
+
canUpdate: boolean;
|
|
75
|
+
canDelete: boolean;
|
|
76
|
+
}>;
|
|
87
77
|
};
|
|
88
78
|
}
|
|
89
79
|
interface NextAuthOptionsConfig {
|
|
@@ -96,4 +86,4 @@ interface NextAuthOptionsConfig {
|
|
|
96
86
|
}
|
|
97
87
|
declare function getNextAuthOptions(config: NextAuthOptionsConfig): NextAuthOptions;
|
|
98
88
|
|
|
99
|
-
export { type
|
|
89
|
+
export { type CmsMiddlewareConfig, type NextAuthOptionsConfig, type NextAuthUser, createCmsMiddleware, defaultPublicApiMethods, getNextAuthOptions, seedAdministratorPermissions };
|
package/dist/auth.d.ts
CHANGED
|
@@ -1,30 +1,12 @@
|
|
|
1
|
+
export { A as ADMIN_GROUP_NAME, a as AuthHelpers, E as EntityCrudAction, b as EntityPermissionFlags, G as GetSession, O as OPEN_ENDPOINTS, P as PERMISSION_REQUIRED_ENDPOINTS, R as RBAC_ADMIN_ONLY_ENTITIES, S as SessionUser, c as canManageRoles, d as createAuthHelpers, g as getPermissionableEntityKeys, e as getRequiredPermission, h as hasEntityPermission, i as isOpenEndpoint, f as isPublicMethod, j as isSuperAdminGroupName, p as permissionRowsToRecord, s as sessionHasEntityAccess } from './helpers-dlrF_49e.js';
|
|
2
|
+
import { DataSource } from 'typeorm';
|
|
1
3
|
import { NextAuthOptions } from 'next-auth';
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
permissions?: string[];
|
|
9
|
-
}
|
|
10
|
-
type GetSession = () => Promise<{
|
|
11
|
-
user?: SessionUser;
|
|
12
|
-
} | null>;
|
|
13
|
-
declare const OPEN_ENDPOINTS: Array<Record<string, string[]>>;
|
|
14
|
-
declare const PERMISSION_REQUIRED_ENDPOINTS: Record<string, string[]>;
|
|
15
|
-
declare function isOpenEndpoint(pathname: string): boolean;
|
|
16
|
-
declare function getRequiredPermission(pathname: string): string[] | null;
|
|
17
|
-
declare function isPublicMethod(pathname: string, method: string): boolean;
|
|
18
|
-
interface AuthHelpers {
|
|
19
|
-
requireAuth(req: Request): Promise<Response | null>;
|
|
20
|
-
requirePermission(req: Request, permission: string): Promise<Response | null>;
|
|
21
|
-
getAuthenticatedUser(): Promise<SessionUser | null>;
|
|
22
|
-
}
|
|
23
|
-
declare function createAuthHelpers(getSession: GetSession, NextResponse: {
|
|
24
|
-
json: (body: unknown, init?: {
|
|
25
|
-
status?: number;
|
|
26
|
-
}) => Response;
|
|
27
|
-
}): AuthHelpers;
|
|
5
|
+
/**
|
|
6
|
+
* Ensures the Administrator group has full CRUD on every permissionable entity
|
|
7
|
+
* for the given entity map. Idempotent: upserts per entity.
|
|
8
|
+
*/
|
|
9
|
+
declare function seedAdministratorPermissions(dataSource: DataSource, entityMap: Record<string, unknown>): Promise<void>;
|
|
28
10
|
|
|
29
11
|
interface CmsMiddlewareConfig {
|
|
30
12
|
publicAdminPaths?: string[];
|
|
@@ -82,8 +64,16 @@ interface NextAuthUser {
|
|
|
82
64
|
blocked?: boolean;
|
|
83
65
|
deleted?: boolean;
|
|
84
66
|
groupId?: number | null;
|
|
67
|
+
adminAccess?: boolean;
|
|
85
68
|
group?: {
|
|
86
|
-
|
|
69
|
+
name?: string;
|
|
70
|
+
permissions?: Array<{
|
|
71
|
+
entity: string;
|
|
72
|
+
canCreate: boolean;
|
|
73
|
+
canRead: boolean;
|
|
74
|
+
canUpdate: boolean;
|
|
75
|
+
canDelete: boolean;
|
|
76
|
+
}>;
|
|
87
77
|
};
|
|
88
78
|
}
|
|
89
79
|
interface NextAuthOptionsConfig {
|
|
@@ -96,4 +86,4 @@ interface NextAuthOptionsConfig {
|
|
|
96
86
|
}
|
|
97
87
|
declare function getNextAuthOptions(config: NextAuthOptionsConfig): NextAuthOptions;
|
|
98
88
|
|
|
99
|
-
export { type
|
|
89
|
+
export { type CmsMiddlewareConfig, type NextAuthOptionsConfig, type NextAuthUser, createCmsMiddleware, defaultPublicApiMethods, getNextAuthOptions, seedAdministratorPermissions };
|
package/dist/auth.js
CHANGED
|
@@ -1,4 +1,69 @@
|
|
|
1
|
+
// src/auth/permission-entities.ts
|
|
2
|
+
var PERMISSION_ENTITY_INTERNAL_EXCLUDE = /* @__PURE__ */ new Set([
|
|
3
|
+
"users",
|
|
4
|
+
"password_reset_tokens",
|
|
5
|
+
"user_groups",
|
|
6
|
+
"permissions",
|
|
7
|
+
"comments",
|
|
8
|
+
"form_fields",
|
|
9
|
+
"configs",
|
|
10
|
+
"knowledge_base_chunks",
|
|
11
|
+
"carts",
|
|
12
|
+
"cart_items",
|
|
13
|
+
"wishlists",
|
|
14
|
+
"wishlist_items"
|
|
15
|
+
]);
|
|
16
|
+
var PERMISSION_LOGICAL_ENTITIES = [
|
|
17
|
+
"users",
|
|
18
|
+
"forms",
|
|
19
|
+
"form_submissions",
|
|
20
|
+
"dashboard",
|
|
21
|
+
"upload",
|
|
22
|
+
"settings",
|
|
23
|
+
"analytics",
|
|
24
|
+
"chat"
|
|
25
|
+
];
|
|
26
|
+
var ADMIN_GROUP_NAME = "Administrator";
|
|
27
|
+
function isSuperAdminGroupName(name) {
|
|
28
|
+
return name === ADMIN_GROUP_NAME;
|
|
29
|
+
}
|
|
30
|
+
function getPermissionableEntityKeys(entityMap) {
|
|
31
|
+
const fromMap = Object.keys(entityMap).filter((k) => !PERMISSION_ENTITY_INTERNAL_EXCLUDE.has(k));
|
|
32
|
+
const logical = PERMISSION_LOGICAL_ENTITIES.filter((k) => !fromMap.includes(k));
|
|
33
|
+
return [...fromMap.sort(), ...logical].filter((k, i, a) => a.indexOf(k) === i);
|
|
34
|
+
}
|
|
35
|
+
function permissionRowsToRecord(rows) {
|
|
36
|
+
const out = {};
|
|
37
|
+
if (!rows?.length) return out;
|
|
38
|
+
for (const p of rows) {
|
|
39
|
+
out[p.entity] = {
|
|
40
|
+
c: !!p.canCreate,
|
|
41
|
+
r: !!p.canRead,
|
|
42
|
+
u: !!p.canUpdate,
|
|
43
|
+
d: !!p.canDelete
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
return out;
|
|
47
|
+
}
|
|
48
|
+
function hasEntityPermission(record, entity, action) {
|
|
49
|
+
const p = record?.[entity];
|
|
50
|
+
if (!p) return false;
|
|
51
|
+
if (action === "create") return p.c;
|
|
52
|
+
if (action === "read") return p.r;
|
|
53
|
+
if (action === "update") return p.u;
|
|
54
|
+
return p.d;
|
|
55
|
+
}
|
|
56
|
+
|
|
1
57
|
// src/auth/helpers.ts
|
|
58
|
+
var RBAC_ADMIN_ONLY_ENTITIES = /* @__PURE__ */ new Set(["users", "user_groups", "permissions"]);
|
|
59
|
+
function sessionHasEntityAccess(user, entity, action) {
|
|
60
|
+
if (!user?.email) return false;
|
|
61
|
+
if (user.isRBACAdmin && RBAC_ADMIN_ONLY_ENTITIES.has(entity)) return true;
|
|
62
|
+
return hasEntityPermission(user.entityPerms, entity, action);
|
|
63
|
+
}
|
|
64
|
+
function canManageRoles(user) {
|
|
65
|
+
return !!(user?.email && user.isRBACAdmin);
|
|
66
|
+
}
|
|
2
67
|
var OPEN_ENDPOINTS = [
|
|
3
68
|
{ "/api/contacts": ["POST"] },
|
|
4
69
|
{ "/api/form-submissions": ["POST"] },
|
|
@@ -34,6 +99,25 @@ function createAuthHelpers(getSession, NextResponse) {
|
|
|
34
99
|
}
|
|
35
100
|
return null;
|
|
36
101
|
},
|
|
102
|
+
async requireEntityPermission(_req, entity, action) {
|
|
103
|
+
const session = await getSession();
|
|
104
|
+
if (!session?.user?.email) {
|
|
105
|
+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
106
|
+
}
|
|
107
|
+
const u = session.user;
|
|
108
|
+
if (sessionHasEntityAccess(u, entity, action)) return null;
|
|
109
|
+
return NextResponse.json({ error: "Forbidden", entity, action }, { status: 403 });
|
|
110
|
+
},
|
|
111
|
+
async requireAdminAccess() {
|
|
112
|
+
const session = await getSession();
|
|
113
|
+
if (!session?.user?.email) {
|
|
114
|
+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
115
|
+
}
|
|
116
|
+
const u = session.user;
|
|
117
|
+
if (u.isRBACAdmin) return null;
|
|
118
|
+
if (u.adminAccess === false) return NextResponse.json({ error: "Forbidden", reason: "admin_access" }, { status: 403 });
|
|
119
|
+
return null;
|
|
120
|
+
},
|
|
37
121
|
async getAuthenticatedUser() {
|
|
38
122
|
const session = await getSession();
|
|
39
123
|
return session?.user ?? null;
|
|
@@ -41,6 +125,36 @@ function createAuthHelpers(getSession, NextResponse) {
|
|
|
41
125
|
};
|
|
42
126
|
}
|
|
43
127
|
|
|
128
|
+
// src/auth/seed-permissions.ts
|
|
129
|
+
async function seedAdministratorPermissions(dataSource, entityMap) {
|
|
130
|
+
const entities = getPermissionableEntityKeys(entityMap);
|
|
131
|
+
const groupRepo = dataSource.getRepository(entityMap.user_groups);
|
|
132
|
+
const permRepo = dataSource.getRepository(entityMap.permissions);
|
|
133
|
+
const adminGroup = await groupRepo.findOne({ where: { name: ADMIN_GROUP_NAME, deleted: false } });
|
|
134
|
+
if (!adminGroup) return;
|
|
135
|
+
const fullCrud = { canCreate: true, canRead: true, canUpdate: true, canDelete: true };
|
|
136
|
+
for (const entity of entities) {
|
|
137
|
+
const existing = await permRepo.findOne({
|
|
138
|
+
where: { groupId: adminGroup.id, entity }
|
|
139
|
+
});
|
|
140
|
+
if (existing) {
|
|
141
|
+
existing.canCreate = true;
|
|
142
|
+
existing.canRead = true;
|
|
143
|
+
existing.canUpdate = true;
|
|
144
|
+
existing.canDelete = true;
|
|
145
|
+
await permRepo.save(existing);
|
|
146
|
+
} else {
|
|
147
|
+
await permRepo.save(
|
|
148
|
+
permRepo.create({
|
|
149
|
+
groupId: adminGroup.id,
|
|
150
|
+
entity,
|
|
151
|
+
...fullCrud
|
|
152
|
+
})
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
44
158
|
// src/auth/middleware.ts
|
|
45
159
|
var defaultPublicApiMethods = {
|
|
46
160
|
"/api/contacts": ["POST"],
|
|
@@ -115,12 +229,18 @@ function getNextAuthOptions(config) {
|
|
|
115
229
|
if (!user || user.blocked || user.deleted || !user.password) return null;
|
|
116
230
|
const valid = await comparePassword(credentials.password, user.password);
|
|
117
231
|
if (!valid) return null;
|
|
232
|
+
const g = user.group;
|
|
233
|
+
const isRBACAdmin = isSuperAdminGroupName(g?.name);
|
|
234
|
+
const entityPerms = permissionRowsToRecord(g?.permissions);
|
|
235
|
+
const adminAccess = user.adminAccess === true;
|
|
118
236
|
return {
|
|
119
237
|
id: user.id.toString(),
|
|
120
238
|
email: user.email,
|
|
121
239
|
name: user.name,
|
|
122
240
|
groupId: user.groupId ?? void 0,
|
|
123
|
-
|
|
241
|
+
isRBACAdmin,
|
|
242
|
+
entityPerms,
|
|
243
|
+
adminAccess
|
|
124
244
|
};
|
|
125
245
|
} catch {
|
|
126
246
|
return null;
|
|
@@ -144,17 +264,23 @@ function getNextAuthOptions(config) {
|
|
|
144
264
|
callbacks: {
|
|
145
265
|
async jwt({ token, user }) {
|
|
146
266
|
if (user) {
|
|
147
|
-
|
|
148
|
-
token.
|
|
149
|
-
token.
|
|
267
|
+
const u = user;
|
|
268
|
+
token.id = u.id;
|
|
269
|
+
token.groupId = u.groupId;
|
|
270
|
+
token.isRBACAdmin = u.isRBACAdmin;
|
|
271
|
+
token.entityPerms = u.entityPerms;
|
|
272
|
+
token.adminAccess = u.adminAccess;
|
|
150
273
|
}
|
|
151
274
|
return token;
|
|
152
275
|
},
|
|
153
276
|
async session({ session, token }) {
|
|
154
277
|
if (session.user) {
|
|
155
|
-
|
|
156
|
-
session.user.
|
|
157
|
-
session.user.
|
|
278
|
+
const t = token;
|
|
279
|
+
session.user.id = t.id;
|
|
280
|
+
session.user.groupId = t.groupId;
|
|
281
|
+
session.user.isRBACAdmin = t.isRBACAdmin;
|
|
282
|
+
session.user.entityPerms = t.entityPerms;
|
|
283
|
+
session.user.adminAccess = t.adminAccess;
|
|
158
284
|
}
|
|
159
285
|
return session;
|
|
160
286
|
}
|
|
@@ -163,14 +289,23 @@ function getNextAuthOptions(config) {
|
|
|
163
289
|
return extend ? extend(options) : options;
|
|
164
290
|
}
|
|
165
291
|
export {
|
|
292
|
+
ADMIN_GROUP_NAME,
|
|
166
293
|
OPEN_ENDPOINTS,
|
|
167
294
|
PERMISSION_REQUIRED_ENDPOINTS,
|
|
295
|
+
RBAC_ADMIN_ONLY_ENTITIES,
|
|
296
|
+
canManageRoles,
|
|
168
297
|
createAuthHelpers,
|
|
169
298
|
createCmsMiddleware,
|
|
170
299
|
defaultPublicApiMethods,
|
|
171
300
|
getNextAuthOptions,
|
|
301
|
+
getPermissionableEntityKeys,
|
|
172
302
|
getRequiredPermission,
|
|
303
|
+
hasEntityPermission,
|
|
173
304
|
isOpenEndpoint,
|
|
174
|
-
isPublicMethod
|
|
305
|
+
isPublicMethod,
|
|
306
|
+
isSuperAdminGroupName,
|
|
307
|
+
permissionRowsToRecord,
|
|
308
|
+
seedAdministratorPermissions,
|
|
309
|
+
sessionHasEntityAccess
|
|
175
310
|
};
|
|
176
311
|
//# sourceMappingURL=auth.js.map
|