aws-service-stack 0.18.395 → 0.18.397
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/controller/controller-role.js +1 -1
- package/dist/controller/controller-role.js.map +1 -1
- package/dist/model/role.model.d.ts +0 -6
- package/dist/model/role.model.js +0 -9
- package/dist/model/role.model.js.map +1 -1
- package/dist/service/permission.service.js +1 -1
- package/dist/service/permission.service.js.map +1 -1
- package/package.json +1 -1
|
@@ -170,7 +170,7 @@ class ControllerRole {
|
|
|
170
170
|
return req.identity?.groups ?? [];
|
|
171
171
|
}
|
|
172
172
|
getScope(req, scopeMap) {
|
|
173
|
-
return req.queryStringParameters?.scope || req.customQueryParameters?.scope || scopeMap.keys().
|
|
173
|
+
return req.queryStringParameters?.scope || req.customQueryParameters?.scope || Array.from(scopeMap.keys()).pop();
|
|
174
174
|
}
|
|
175
175
|
async hasAnyRolePermission(roles, resource, scope, method) {
|
|
176
176
|
for (const role of roles) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"controller-role.js","sourceRoot":"","sources":["../../src/controller/controller-role.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from \"zod\";\nimport { ErrorHttp } from \"../exception\";\nimport { Permission } from \"../model/role.model\";\nimport { List, PermissionMap, ScopeMap } from \"../model/base.model\";\nimport { HttpRequest } from \"../utils/http/http.util\";\nimport { PermissionService } from \"../service/permission.service\";\nimport { createGroup, deleteGroup, addUserToGroup, removeUserFromGroup } from \"../function/cognito\";\nimport { validateWithSchema, formatErrors } from \"../utils/validation.util\";\nimport { normalizeLower, trimSpecialChar } from \"../utils/string.util\";\nimport { Filter } from \"@chinggis/types\";\n\n// ---------------------------------------------------------------------------\n// DTO Schemas\n// ---------------------------------------------------------------------------\n\nconst MethodSchema = z.object({\n GET: z.boolean().optional(),\n POST: z.boolean().optional(),\n PATCH: z.boolean().optional(),\n PUT: z.boolean().optional(),\n DELETE: z.boolean().optional(),\n});\n\nconst AddPermissionDto = z.object({\n role: z.string().min(1, \"Role is required\"),\n resource: z.string().min(1, \"Resource is required\"),\n scope: z.string().min(1, \"Scope is required\"),\n method: MethodSchema,\n});\n\nconst UpdatePermissionDto = z.object({\n role: z.string().min(1, \"Role is required\").optional(),\n resource: z.string().min(1, \"Resource is required\").optional(),\n scope: z.string().min(1, \"Scope is required\").optional(),\n method: MethodSchema.optional(),\n});\n\nconst IdParamDto = z.string().min(1, \"Id is required\");\n\nconst GroupDto = z.object({\n userPoolId: z.string().min(1, \"UserPoolId is required\"),\n groupName: z.string().min(1, \"GroupName is required\"),\n description: z.string().optional(),\n});\n\nconst UserGroupDto = z.object({\n userPoolId: z.string().min(1, \"UserPoolId is required\"),\n username: z.string().min(1, \"Username is required\"),\n groupName: z.string().min(1, \"GroupName is required\"),\n});\n\n// ---------------------------------------------------------------------------\n\nconst MUTATING_METHODS = new Set([\"PATCH\", \"PUT\", \"DELETE\"]);\n\ntype RouteHandler = (permissionMap: PermissionMap, body: any) => Promise<any>;\n\nexport class ControllerRole {\n protected readonly permissionService: PermissionService;\n\n private readonly subRoutes: Record<string, RouteHandler> = {\n \"/add-group\": (pm, body) => this.addRole(pm.userPoolId, body.groupName, body.description),\n \"/add-user-group\": (pm, body) => this.assignRole(pm.userPoolId, body.username, body.groupName),\n \"/remove-group\": (pm, body) => this.removeRole(pm.userPoolId, body.groupName),\n \"/remove-user-group\": (pm, body) => this.unassignRole(pm.userPoolId, body.username, body.groupName),\n };\n\n constructor(roleTableName: string) {\n this.permissionService = new PermissionService(roleTableName);\n }\n\n // ---------------------------------------------------------------------------\n // Permission CRUD\n // ---------------------------------------------------------------------------\n\n async addPermission(input: {\n role: string;\n resource: string;\n scope: string;\n method: Permission[\"method\"];\n }): Promise<Permission> {\n this.validate(AddPermissionDto, input);\n return this.permissionService.createPermission(input);\n }\n\n async updatePermission(\n id: string,\n updates: Partial<Pick<Permission, \"role\" | \"resource\" | \"scope\" | \"method\">>,\n ): Promise<Permission> {\n this.validate(IdParamDto, id);\n this.validate(UpdatePermissionDto, updates);\n return this.permissionService.updatePermission(id, updates);\n }\n\n async deletePermission(id: string): Promise<boolean> {\n this.validate(IdParamDto, id);\n return this.permissionService.deletePermission(id);\n }\n\n async getPermission(id: string): Promise<Permission | null> {\n this.validate(IdParamDto, id);\n return this.permissionService.getPermissionById(id);\n }\n\n async listPermissions(filter?: Filter): Promise<List<Partial<Permission>>> {\n return this.permissionService.listPermissions(filter);\n }\n\n // ---------------------------------------------------------------------------\n // Role (Cognito group)\n // ---------------------------------------------------------------------------\n\n async addRole(userPoolId: string, groupName: string, description?: string) {\n this.validate(GroupDto, { userPoolId, groupName, description });\n return createGroup(userPoolId, groupName, description);\n }\n\n async removeRole(userPoolId: string, groupName: string) {\n this.validate(GroupDto, { userPoolId, groupName });\n return deleteGroup(userPoolId, groupName);\n }\n\n async assignRole(userPoolId: string, username: string, groupName: string) {\n this.validate(UserGroupDto, { userPoolId, username, groupName });\n return addUserToGroup(userPoolId, username, groupName);\n }\n\n async unassignRole(userPoolId: string, username: string, groupName: string) {\n this.validate(UserGroupDto, { userPoolId, username, groupName });\n return removeUserFromGroup(userPoolId, username, groupName);\n }\n\n // ---------------------------------------------------------------------------\n // Permission queries\n // ---------------------------------------------------------------------------\n\n async hasPermission(role: string, resource: string, scope: string, method: string): Promise<boolean> {\n return this.permissionService.hasPermission(role, resource, scope, method);\n }\n\n async getPermissionByKey(permissionKey: string): Promise<Permission | null> {\n return this.permissionService.getPermissionByKey(permissionKey);\n }\n\n // ---------------------------------------------------------------------------\n // Permission request handler (routes from ControllerApi)\n // ---------------------------------------------------------------------------\n\n async handlePermissionRequest(\n req: HttpRequest,\n path: string,\n permissionMap: PermissionMap,\n adminGroups: string[],\n ): Promise<any> {\n this.checkAdminPermission(req.identity.groups, adminGroups);\n\n if (req.methode === \"POST\") {\n const normalizedPath = trimSpecialChar(path);\n const rolePath = trimSpecialChar(permissionMap.rolePath ?? \"\");\n\n for (const [suffix, handler] of Object.entries(this.subRoutes)) {\n if (normalizedPath === `${rolePath}${suffix}`) {\n return handler(permissionMap, req.body);\n }\n }\n\n return this.addPermission(req.body);\n }\n\n switch (req.methode) {\n case \"GET\":\n return this.listPermissions(req.filter);\n case \"PATCH\":\n return this.updatePermission(req.entityId, req.body);\n case \"DELETE\":\n return this.deletePermission(req.entityId);\n default:\n throw new ErrorHttp({ code: 400, error: \"BadRequest\" }, \"[CORE] unsupported method\");\n }\n }\n\n // ---------------------------------------------------------------------------\n // RBAC access enforcement\n // ---------------------------------------------------------------------------\n\n async checkAccess(\n req: HttpRequest,\n resource: string,\n scopeMap: ScopeMap,\n adminGroups?: string[],\n findById?: (id: string) => Promise<any>,\n ): Promise<boolean> {\n const roles = this.extractRoles(req);\n if (!roles.length) this.deny();\n\n if (adminGroups?.length && this.isAdmin(roles, adminGroups)) return true;\n\n const method = req.methode.toUpperCase();\n const scope = this.getScope(req, scopeMap);\n\n this.applyMethodFilters(req, method, scope, scopeMap);\n\n const permitted = await this.hasAnyRolePermission(roles, resource, scope, method);\n if (!permitted) {\n this.deny(`Permission denied: ${roles.join(\",\")}: \"${resource}#${scope}.${method}\"`);\n }\n\n if (MUTATING_METHODS.has(method)) {\n await this.verifyScopeOwnership(req, scope, scopeMap, findById);\n }\n\n return true;\n }\n\n checkAdminPermission(cognitoGroups: string[], adminGroups: string[]) {\n if (this.isAdmin(cognitoGroups, adminGroups)) return true;\n this.deny(\"Permission Denied\");\n }\n\n // ---------------------------------------------------------------------------\n // Private — error + auth helpers\n // ---------------------------------------------------------------------------\n\n private deny(message = \"Permission denied: no role accessible\"): never {\n throw new ErrorHttp({ code: 403, error: \"PermissionDenied\" }, message);\n }\n\n private isAdmin(cognitoGroups: string[] | undefined, adminGroups: string[]): boolean {\n adminGroups = normalizeLower(adminGroups);\n return cognitoGroups?.some((group) => adminGroups.includes(group)) ?? false;\n }\n\n private extractRoles(req: HttpRequest): string[] {\n return req.identity?.groups ?? [];\n }\n\n private getScope(req: HttpRequest, scopeMap: ScopeMap): string {\n return req.queryStringParameters?.scope || req.customQueryParameters?.scope || scopeMap.keys().next().value!;\n }\n\n private async hasAnyRolePermission(\n roles: string[],\n resource: string,\n scope: string,\n method: string,\n ): Promise<boolean> {\n for (const role of roles) {\n if (await this.permissionService.hasPermission(role, resource, scope, method)) return true;\n }\n return false;\n }\n\n private validate<T extends z.ZodType>(schema: T, data: unknown): z.infer<T> {\n try {\n return validateWithSchema(schema, data);\n } catch (error) {\n const formattedErrors = formatErrors(error);\n throw new ErrorHttp({ code: 400, error: \"BadRequest\" }, formattedErrors);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Private — scope ownership verification\n // ---------------------------------------------------------------------------\n\n private async verifyScopeOwnership(\n req: HttpRequest,\n scope: string,\n scopeMap: ScopeMap,\n findById?: (id: string) => Promise<any>,\n ): Promise<void> {\n const mapping = scopeMap.get(scope);\n if (!mapping) return;\n\n const claimValue = this.resolveClaim(req, mapping);\n if (!claimValue) this.deny(`Missing claim for scope \"${scope}\"`);\n\n if (!findById) return;\n\n if (!req.entityId) {\n throw new ErrorHttp({ code: 400, error: \"BadRequest\" }, \"Missing resource id\");\n }\n\n const entity = await findById(req.entityId);\n if (!entity) {\n throw new ErrorHttp({ code: 404, error: \"NotFound\" }, `Resource with id \"${req.entityId}\" not found`);\n }\n\n if (entity[mapping.filterField] !== claimValue) {\n this.deny(`Scope mismatch: no access to resource \"${req.entityId}\"`);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Private — claim resolution\n // ---------------------------------------------------------------------------\n\n private resolveClaim(req: HttpRequest, mapping: { claimKey?: string }): string | undefined {\n return req.identity?.[mapping.claimKey] || req.identity?.attributes?.[mapping.claimKey];\n }\n\n private resolveClaimWithFallback(\n req: HttpRequest,\n mapping: { claimKey?: string },\n scopeName: string,\n ): string | undefined {\n const claimKey = mapping.claimKey ?? \"custom:\" + scopeName;\n return req.identity?.[claimKey] || req.identity?.attributes?.[claimKey] || req.identity?.sub;\n }\n\n // ---------------------------------------------------------------------------\n // Private — filter application\n // ---------------------------------------------------------------------------\n\n private applyMethodFilters(req: HttpRequest, method: string, scope: string, scopeMap: ScopeMap): void {\n if (method === \"POST\") {\n this.setDefaultIndexFields(req, scopeMap);\n } else if (method === \"GET\" && this.isOpenSearchPath(req)) {\n this.setDefaultFilters(req, scopeMap);\n } else if (method === \"GET\") {\n this.applyScopeFilter(req, scope, scopeMap);\n }\n }\n\n private applyScopeFilter(req: HttpRequest, scope: string, scopeMap: ScopeMap): void {\n if (!req.filter) req.filter = {};\n this.clearScopeFilters(req, scopeMap);\n\n const mapping = scopeMap.get(scope);\n if (!mapping) return;\n\n const claimValue = this.resolveClaim(req, mapping);\n if (claimValue || !req.filter[mapping.filterField]) {\n req.filter[mapping.filterField] = claimValue;\n }\n delete req.filter.scope;\n }\n\n private setDefaultIndexFields(req: HttpRequest, scopeMap: ScopeMap): void {\n if (!req.body) req.body = {};\n if (this.isUnset(req.body.ownerId)) req.body.ownerId = req.identity?.sub;\n this.applyAllScopeValues(req, scopeMap, true);\n }\n\n private setDefaultFilters(req: HttpRequest, scopeMap: ScopeMap): void {\n if (!req.filter) req.filter = {};\n\n const requestedScope = this.resolveRequestedScope(req);\n const levelLimit = this.getScopeLevelLimit(requestedScope, scopeMap);\n\n for (const [scopeName, mapping] of scopeMap) {\n if (levelLimit !== undefined && mapping.level !== undefined && mapping.level > levelLimit) continue;\n\n const claimValue = this.resolveClaimWithFallback(req, mapping, scopeName);\n const eqField = mapping.filterField + \".keyword__eq\";\n if (claimValue && this.isUnset(req.filter[eqField])) {\n req.filter[eqField] = claimValue;\n }\n }\n\n delete req.filter.scope;\n }\n\n private applyAllScopeValues(req: HttpRequest, scopeMap: ScopeMap, defaultsOnly = false): void {\n for (const [scopeName, mapping] of scopeMap) {\n const claimValue = this.resolveClaimWithFallback(req, mapping, scopeName);\n if (claimValue && (!defaultsOnly || this.isUnset(req.body[mapping.filterField]))) {\n req.body[mapping.filterField] = claimValue;\n }\n }\n }\n\n private isOpenSearchPath(req: HttpRequest): boolean {\n const path = trimSpecialChar(req.event?.requestContext?.resourcePath ?? req.resourcePath ?? \"\");\n return path.endsWith(\"search/query\");\n }\n\n private resolveRequestedScope(req: HttpRequest): string | undefined {\n const scopeRaw = req.filter?.scope || req.queryStringParameters?.scope || req.customQueryParameters?.scope;\n if (typeof scopeRaw !== \"string\") return undefined;\n const normalized = scopeRaw.trim().toLowerCase();\n return normalized.length ? normalized : undefined;\n }\n\n private getScopeLevelLimit(requestedScope: string | undefined, scopeMap: ScopeMap): number | undefined {\n if (!requestedScope) return undefined;\n for (const [scopeName, mapping] of scopeMap) {\n if (scopeName.toLowerCase() === requestedScope) return mapping.level;\n }\n return undefined;\n }\n\n private isUnset(value: unknown): boolean {\n return value === undefined || value === null || value === \"\";\n }\n\n private clearScopeFilters(req: HttpRequest, scopeMap: ScopeMap): void {\n for (const [, entry] of scopeMap) {\n delete req.filter[entry.filterField];\n }\n delete req.filter[\"profileId\"];\n delete req.filter[\"ownerId\"];\n delete req.filter[\"scope\"];\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"controller-role.js","sourceRoot":"","sources":["../../src/controller/controller-role.ts"],"names":[],"mappings":";;;AAAA,6BAAwB;AACxB,4CAAyC;AAIzC,sEAAkE;AAClE,iDAAoG;AACpG,8DAA4E;AAC5E,sDAAuE;AAGvE,8EAA8E;AAC9E,cAAc;AACd,8EAA8E;AAE9E,MAAM,YAAY,GAAG,OAAC,CAAC,MAAM,CAAC;IAC5B,GAAG,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IAC3B,IAAI,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IAC5B,KAAK,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IAC7B,GAAG,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IAC3B,MAAM,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;CAC/B,CAAC,CAAC;AAEH,MAAM,gBAAgB,GAAG,OAAC,CAAC,MAAM,CAAC;IAChC,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,kBAAkB,CAAC;IAC3C,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,sBAAsB,CAAC;IACnD,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,mBAAmB,CAAC;IAC7C,MAAM,EAAE,YAAY;CACrB,CAAC,CAAC;AAEH,MAAM,mBAAmB,GAAG,OAAC,CAAC,MAAM,CAAC;IACnC,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,kBAAkB,CAAC,CAAC,QAAQ,EAAE;IACtD,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,sBAAsB,CAAC,CAAC,QAAQ,EAAE;IAC9D,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,mBAAmB,CAAC,CAAC,QAAQ,EAAE;IACxD,MAAM,EAAE,YAAY,CAAC,QAAQ,EAAE;CAChC,CAAC,CAAC;AAEH,MAAM,UAAU,GAAG,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC;AAEvD,MAAM,QAAQ,GAAG,OAAC,CAAC,MAAM,CAAC;IACxB,UAAU,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,wBAAwB,CAAC;IACvD,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,uBAAuB,CAAC;IACrD,WAAW,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACnC,CAAC,CAAC;AAEH,MAAM,YAAY,GAAG,OAAC,CAAC,MAAM,CAAC;IAC5B,UAAU,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,wBAAwB,CAAC;IACvD,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,sBAAsB,CAAC;IACnD,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,uBAAuB,CAAC;CACtD,CAAC,CAAC;AAEH,8EAA8E;AAE9E,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC;AAI7D,MAAa,cAAc;IACN,iBAAiB,CAAoB;IAEvC,SAAS,GAAiC;QACzD,YAAY,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,WAAW,CAAC;QACzF,iBAAiB,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,UAAU,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC;QAC9F,eAAe,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC;QAC7E,oBAAoB,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,UAAU,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC;KACpG,CAAC;IAEF,YAAY,aAAqB;QAC/B,IAAI,CAAC,iBAAiB,GAAG,IAAI,sCAAiB,CAAC,aAAa,CAAC,CAAC;IAChE,CAAC;IAED,8EAA8E;IAC9E,kBAAkB;IAClB,8EAA8E;IAE9E,KAAK,CAAC,aAAa,CAAC,KAKnB;QACC,IAAI,CAAC,QAAQ,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAC;QACvC,OAAO,IAAI,CAAC,iBAAiB,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;IACxD,CAAC;IAED,KAAK,CAAC,gBAAgB,CACpB,EAAU,EACV,OAA4E;QAE5E,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;QAC9B,IAAI,CAAC,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC,CAAC;QAC5C,OAAO,IAAI,CAAC,iBAAiB,CAAC,gBAAgB,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;IAC9D,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,EAAU;QAC/B,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;QAC9B,OAAO,IAAI,CAAC,iBAAiB,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC;IACrD,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,EAAU;QAC5B,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;QAC9B,OAAO,IAAI,CAAC,iBAAiB,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC;IACtD,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,MAAe;QACnC,OAAO,IAAI,CAAC,iBAAiB,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;IACxD,CAAC;IAED,8EAA8E;IAC9E,uBAAuB;IACvB,8EAA8E;IAE9E,KAAK,CAAC,OAAO,CAAC,UAAkB,EAAE,SAAiB,EAAE,WAAoB;QACvE,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC,CAAC;QAChE,OAAO,IAAA,qBAAW,EAAC,UAAU,EAAE,SAAS,EAAE,WAAW,CAAC,CAAC;IACzD,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,UAAkB,EAAE,SAAiB;QACpD,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC,CAAC;QACnD,OAAO,IAAA,qBAAW,EAAC,UAAU,EAAE,SAAS,CAAC,CAAC;IAC5C,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,UAAkB,EAAE,QAAgB,EAAE,SAAiB;QACtE,IAAI,CAAC,QAAQ,CAAC,YAAY,EAAE,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;QACjE,OAAO,IAAA,wBAAc,EAAC,UAAU,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;IACzD,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,UAAkB,EAAE,QAAgB,EAAE,SAAiB;QACxE,IAAI,CAAC,QAAQ,CAAC,YAAY,EAAE,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;QACjE,OAAO,IAAA,6BAAmB,EAAC,UAAU,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;IAC9D,CAAC;IAED,8EAA8E;IAC9E,qBAAqB;IACrB,8EAA8E;IAE9E,KAAK,CAAC,aAAa,CAAC,IAAY,EAAE,QAAgB,EAAE,KAAa,EAAE,MAAc;QAC/E,OAAO,IAAI,CAAC,iBAAiB,CAAC,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;IAC7E,CAAC;IAED,KAAK,CAAC,kBAAkB,CAAC,aAAqB;QAC5C,OAAO,IAAI,CAAC,iBAAiB,CAAC,kBAAkB,CAAC,aAAa,CAAC,CAAC;IAClE,CAAC;IAED,8EAA8E;IAC9E,yDAAyD;IACzD,8EAA8E;IAE9E,KAAK,CAAC,uBAAuB,CAC3B,GAAgB,EAChB,IAAY,EACZ,aAA4B,EAC5B,WAAqB;QAErB,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;QAE5D,IAAI,GAAG,CAAC,OAAO,KAAK,MAAM,EAAE,CAAC;YAC3B,MAAM,cAAc,GAAG,IAAA,6BAAe,EAAC,IAAI,CAAC,CAAC;YAC7C,MAAM,QAAQ,GAAG,IAAA,6BAAe,EAAC,aAAa,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;YAE/D,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC/D,IAAI,cAAc,KAAK,GAAG,QAAQ,GAAG,MAAM,EAAE,EAAE,CAAC;oBAC9C,OAAO,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;gBAC1C,CAAC;YACH,CAAC;YAED,OAAO,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACtC,CAAC;QAED,QAAQ,GAAG,CAAC,OAAO,EAAE,CAAC;YACpB,KAAK,KAAK;gBACR,OAAO,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAC1C,KAAK,OAAO;gBACV,OAAO,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;YACvD,KAAK,QAAQ;gBACX,OAAO,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAC7C;gBACE,MAAM,IAAI,qBAAS,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,2BAA2B,CAAC,CAAC;QACzF,CAAC;IACH,CAAC;IAED,8EAA8E;IAC9E,0BAA0B;IAC1B,8EAA8E;IAE9E,KAAK,CAAC,WAAW,CACf,GAAgB,EAChB,QAAgB,EAChB,QAAkB,EAClB,WAAsB,EACtB,QAAuC;QAEvC,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;QACrC,IAAI,CAAC,KAAK,CAAC,MAAM;YAAE,IAAI,CAAC,IAAI,EAAE,CAAC;QAE/B,IAAI,WAAW,EAAE,MAAM,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,WAAW,CAAC;YAAE,OAAO,IAAI,CAAC;QAEzE,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;QACzC,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QAE3C,IAAI,CAAC,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;QAEtD,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;QAClF,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,IAAI,CAAC,IAAI,CAAC,sBAAsB,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,QAAQ,IAAI,KAAK,IAAI,MAAM,GAAG,CAAC,CAAC;QACvF,CAAC;QAED,IAAI,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YACjC,MAAM,IAAI,CAAC,oBAAoB,CAAC,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAClE,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED,oBAAoB,CAAC,aAAuB,EAAE,WAAqB;QACjE,IAAI,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,WAAW,CAAC;YAAE,OAAO,IAAI,CAAC;QAC1D,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IACjC,CAAC;IAED,8EAA8E;IAC9E,iCAAiC;IACjC,8EAA8E;IAEtE,IAAI,CAAC,OAAO,GAAG,uCAAuC;QAC5D,MAAM,IAAI,qBAAS,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,kBAAkB,EAAE,EAAE,OAAO,CAAC,CAAC;IACzE,CAAC;IAEO,OAAO,CAAC,aAAmC,EAAE,WAAqB;QACxE,WAAW,GAAG,IAAA,4BAAc,EAAC,WAAW,CAAC,CAAC;QAC1C,OAAO,aAAa,EAAE,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC;IAC9E,CAAC;IAEO,YAAY,CAAC,GAAgB;QACnC,OAAO,GAAG,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,CAAC;IACpC,CAAC;IAEO,QAAQ,CAAC,GAAgB,EAAE,QAAkB;QACnD,OAAO,GAAG,CAAC,qBAAqB,EAAE,KAAK,IAAI,GAAG,CAAC,qBAAqB,EAAE,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC;IACnH,CAAC;IAEO,KAAK,CAAC,oBAAoB,CAChC,KAAe,EACf,QAAgB,EAChB,KAAa,EACb,MAAc;QAEd,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,MAAM,IAAI,CAAC,iBAAiB,CAAC,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC;gBAAE,OAAO,IAAI,CAAC;QAC7F,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAEO,QAAQ,CAAsB,MAAS,EAAE,IAAa;QAC5D,IAAI,CAAC;YACH,OAAO,IAAA,oCAAkB,EAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC1C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,eAAe,GAAG,IAAA,8BAAY,EAAC,KAAK,CAAC,CAAC;YAC5C,MAAM,IAAI,qBAAS,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,eAAe,CAAC,CAAC;QAC3E,CAAC;IACH,CAAC;IAED,8EAA8E;IAC9E,yCAAyC;IACzC,8EAA8E;IAEtE,KAAK,CAAC,oBAAoB,CAChC,GAAgB,EAChB,KAAa,EACb,QAAkB,EAClB,QAAuC;QAEvC,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACpC,IAAI,CAAC,OAAO;YAAE,OAAO;QAErB,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QACnD,IAAI,CAAC,UAAU;YAAE,IAAI,CAAC,IAAI,CAAC,4BAA4B,KAAK,GAAG,CAAC,CAAC;QAEjE,IAAI,CAAC,QAAQ;YAAE,OAAO;QAEtB,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;YAClB,MAAM,IAAI,qBAAS,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,qBAAqB,CAAC,CAAC;QACjF,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC5C,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,qBAAS,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,qBAAqB,GAAG,CAAC,QAAQ,aAAa,CAAC,CAAC;QACxG,CAAC;QAED,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,KAAK,UAAU,EAAE,CAAC;YAC/C,IAAI,CAAC,IAAI,CAAC,0CAA0C,GAAG,CAAC,QAAQ,GAAG,CAAC,CAAC;QACvE,CAAC;IACH,CAAC;IAED,8EAA8E;IAC9E,6BAA6B;IAC7B,8EAA8E;IAEtE,YAAY,CAAC,GAAgB,EAAE,OAA8B;QACnE,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,QAAQ,EAAE,UAAU,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC1F,CAAC;IAEO,wBAAwB,CAC9B,GAAgB,EAChB,OAA8B,EAC9B,SAAiB;QAEjB,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,SAAS,GAAG,SAAS,CAAC;QAC3D,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,QAAQ,EAAE,UAAU,EAAE,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC;IAC/F,CAAC;IAED,8EAA8E;IAC9E,+BAA+B;IAC/B,8EAA8E;IAEtE,kBAAkB,CAAC,GAAgB,EAAE,MAAc,EAAE,KAAa,EAAE,QAAkB;QAC5F,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;YACtB,IAAI,CAAC,qBAAqB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QAC5C,CAAC;aAAM,IAAI,MAAM,KAAK,KAAK,IAAI,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1D,IAAI,CAAC,iBAAiB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QACxC,CAAC;aAAM,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;YAC5B,IAAI,CAAC,gBAAgB,CAAC,GAAG,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC;IAEO,gBAAgB,CAAC,GAAgB,EAAE,KAAa,EAAE,QAAkB;QAC1E,IAAI,CAAC,GAAG,CAAC,MAAM;YAAE,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC;QACjC,IAAI,CAAC,iBAAiB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QAEtC,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACpC,IAAI,CAAC,OAAO;YAAE,OAAO;QAErB,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QACnD,IAAI,UAAU,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;YACnD,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,GAAG,UAAU,CAAC;QAC/C,CAAC;QACD,OAAO,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC;IAC1B,CAAC;IAEO,qBAAqB,CAAC,GAAgB,EAAE,QAAkB;QAChE,IAAI,CAAC,GAAG,CAAC,IAAI;YAAE,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC;QAC7B,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC;YAAE,GAAG,CAAC,IAAI,CAAC,OAAO,GAAG,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC;QACzE,IAAI,CAAC,mBAAmB,CAAC,GAAG,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;IAChD,CAAC;IAEO,iBAAiB,CAAC,GAAgB,EAAE,QAAkB;QAC5D,IAAI,CAAC,GAAG,CAAC,MAAM;YAAE,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC;QAEjC,MAAM,cAAc,GAAG,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAAC,CAAC;QACvD,MAAM,UAAU,GAAG,IAAI,CAAC,kBAAkB,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC;QAErE,KAAK,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,IAAI,QAAQ,EAAE,CAAC;YAC5C,IAAI,UAAU,KAAK,SAAS,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,IAAI,OAAO,CAAC,KAAK,GAAG,UAAU;gBAAE,SAAS;YAEpG,MAAM,UAAU,GAAG,IAAI,CAAC,wBAAwB,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;YAC1E,MAAM,OAAO,GAAG,OAAO,CAAC,WAAW,GAAG,cAAc,CAAC;YACrD,IAAI,UAAU,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;gBACpD,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,UAAU,CAAC;YACnC,CAAC;QACH,CAAC;QAED,OAAO,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC;IAC1B,CAAC;IAEO,mBAAmB,CAAC,GAAgB,EAAE,QAAkB,EAAE,YAAY,GAAG,KAAK;QACpF,KAAK,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,IAAI,QAAQ,EAAE,CAAC;YAC5C,MAAM,UAAU,GAAG,IAAI,CAAC,wBAAwB,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;YAC1E,IAAI,UAAU,IAAI,CAAC,CAAC,YAAY,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;gBACjF,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,GAAG,UAAU,CAAC;YAC7C,CAAC;QACH,CAAC;IACH,CAAC;IAEO,gBAAgB,CAAC,GAAgB;QACvC,MAAM,IAAI,GAAG,IAAA,6BAAe,EAAC,GAAG,CAAC,KAAK,EAAE,cAAc,EAAE,YAAY,IAAI,GAAG,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC;QAChG,OAAO,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;IACvC,CAAC;IAEO,qBAAqB,CAAC,GAAgB;QAC5C,MAAM,QAAQ,GAAG,GAAG,CAAC,MAAM,EAAE,KAAK,IAAI,GAAG,CAAC,qBAAqB,EAAE,KAAK,IAAI,GAAG,CAAC,qBAAqB,EAAE,KAAK,CAAC;QAC3G,IAAI,OAAO,QAAQ,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAC;QACnD,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACjD,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;IACpD,CAAC;IAEO,kBAAkB,CAAC,cAAkC,EAAE,QAAkB;QAC/E,IAAI,CAAC,cAAc;YAAE,OAAO,SAAS,CAAC;QACtC,KAAK,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,IAAI,QAAQ,EAAE,CAAC;YAC5C,IAAI,SAAS,CAAC,WAAW,EAAE,KAAK,cAAc;gBAAE,OAAO,OAAO,CAAC,KAAK,CAAC;QACvE,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAEO,OAAO,CAAC,KAAc;QAC5B,OAAO,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;IAC/D,CAAC;IAEO,iBAAiB,CAAC,GAAgB,EAAE,QAAkB;QAC5D,KAAK,MAAM,CAAC,EAAE,KAAK,CAAC,IAAI,QAAQ,EAAE,CAAC;YACjC,OAAO,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QACvC,CAAC;QACD,OAAO,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QAC/B,OAAO,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC7B,OAAO,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;CACF;AA3VD,wCA2VC","sourcesContent":["import { z } from \"zod\";\nimport { ErrorHttp } from \"../exception\";\nimport { Permission } from \"../model/role.model\";\nimport { List, PermissionMap, ScopeMap } from \"../model/base.model\";\nimport { HttpRequest } from \"../utils/http/http.util\";\nimport { PermissionService } from \"../service/permission.service\";\nimport { createGroup, deleteGroup, addUserToGroup, removeUserFromGroup } from \"../function/cognito\";\nimport { validateWithSchema, formatErrors } from \"../utils/validation.util\";\nimport { normalizeLower, trimSpecialChar } from \"../utils/string.util\";\nimport { Filter } from \"@chinggis/types\";\n\n// ---------------------------------------------------------------------------\n// DTO Schemas\n// ---------------------------------------------------------------------------\n\nconst MethodSchema = z.object({\n GET: z.boolean().optional(),\n POST: z.boolean().optional(),\n PATCH: z.boolean().optional(),\n PUT: z.boolean().optional(),\n DELETE: z.boolean().optional(),\n});\n\nconst AddPermissionDto = z.object({\n role: z.string().min(1, \"Role is required\"),\n resource: z.string().min(1, \"Resource is required\"),\n scope: z.string().min(1, \"Scope is required\"),\n method: MethodSchema,\n});\n\nconst UpdatePermissionDto = z.object({\n role: z.string().min(1, \"Role is required\").optional(),\n resource: z.string().min(1, \"Resource is required\").optional(),\n scope: z.string().min(1, \"Scope is required\").optional(),\n method: MethodSchema.optional(),\n});\n\nconst IdParamDto = z.string().min(1, \"Id is required\");\n\nconst GroupDto = z.object({\n userPoolId: z.string().min(1, \"UserPoolId is required\"),\n groupName: z.string().min(1, \"GroupName is required\"),\n description: z.string().optional(),\n});\n\nconst UserGroupDto = z.object({\n userPoolId: z.string().min(1, \"UserPoolId is required\"),\n username: z.string().min(1, \"Username is required\"),\n groupName: z.string().min(1, \"GroupName is required\"),\n});\n\n// ---------------------------------------------------------------------------\n\nconst MUTATING_METHODS = new Set([\"PATCH\", \"PUT\", \"DELETE\"]);\n\ntype RouteHandler = (permissionMap: PermissionMap, body: any) => Promise<any>;\n\nexport class ControllerRole {\n protected readonly permissionService: PermissionService;\n\n private readonly subRoutes: Record<string, RouteHandler> = {\n \"/add-group\": (pm, body) => this.addRole(pm.userPoolId, body.groupName, body.description),\n \"/add-user-group\": (pm, body) => this.assignRole(pm.userPoolId, body.username, body.groupName),\n \"/remove-group\": (pm, body) => this.removeRole(pm.userPoolId, body.groupName),\n \"/remove-user-group\": (pm, body) => this.unassignRole(pm.userPoolId, body.username, body.groupName),\n };\n\n constructor(roleTableName: string) {\n this.permissionService = new PermissionService(roleTableName);\n }\n\n // ---------------------------------------------------------------------------\n // Permission CRUD\n // ---------------------------------------------------------------------------\n\n async addPermission(input: {\n role: string;\n resource: string;\n scope: string;\n method: Permission[\"method\"];\n }): Promise<Permission> {\n this.validate(AddPermissionDto, input);\n return this.permissionService.createPermission(input);\n }\n\n async updatePermission(\n id: string,\n updates: Partial<Pick<Permission, \"role\" | \"resource\" | \"scope\" | \"method\">>,\n ): Promise<Permission> {\n this.validate(IdParamDto, id);\n this.validate(UpdatePermissionDto, updates);\n return this.permissionService.updatePermission(id, updates);\n }\n\n async deletePermission(id: string): Promise<boolean> {\n this.validate(IdParamDto, id);\n return this.permissionService.deletePermission(id);\n }\n\n async getPermission(id: string): Promise<Permission | null> {\n this.validate(IdParamDto, id);\n return this.permissionService.getPermissionById(id);\n }\n\n async listPermissions(filter?: Filter): Promise<List<Partial<Permission>>> {\n return this.permissionService.listPermissions(filter);\n }\n\n // ---------------------------------------------------------------------------\n // Role (Cognito group)\n // ---------------------------------------------------------------------------\n\n async addRole(userPoolId: string, groupName: string, description?: string) {\n this.validate(GroupDto, { userPoolId, groupName, description });\n return createGroup(userPoolId, groupName, description);\n }\n\n async removeRole(userPoolId: string, groupName: string) {\n this.validate(GroupDto, { userPoolId, groupName });\n return deleteGroup(userPoolId, groupName);\n }\n\n async assignRole(userPoolId: string, username: string, groupName: string) {\n this.validate(UserGroupDto, { userPoolId, username, groupName });\n return addUserToGroup(userPoolId, username, groupName);\n }\n\n async unassignRole(userPoolId: string, username: string, groupName: string) {\n this.validate(UserGroupDto, { userPoolId, username, groupName });\n return removeUserFromGroup(userPoolId, username, groupName);\n }\n\n // ---------------------------------------------------------------------------\n // Permission queries\n // ---------------------------------------------------------------------------\n\n async hasPermission(role: string, resource: string, scope: string, method: string): Promise<boolean> {\n return this.permissionService.hasPermission(role, resource, scope, method);\n }\n\n async getPermissionByKey(permissionKey: string): Promise<Permission | null> {\n return this.permissionService.getPermissionByKey(permissionKey);\n }\n\n // ---------------------------------------------------------------------------\n // Permission request handler (routes from ControllerApi)\n // ---------------------------------------------------------------------------\n\n async handlePermissionRequest(\n req: HttpRequest,\n path: string,\n permissionMap: PermissionMap,\n adminGroups: string[],\n ): Promise<any> {\n this.checkAdminPermission(req.identity.groups, adminGroups);\n\n if (req.methode === \"POST\") {\n const normalizedPath = trimSpecialChar(path);\n const rolePath = trimSpecialChar(permissionMap.rolePath ?? \"\");\n\n for (const [suffix, handler] of Object.entries(this.subRoutes)) {\n if (normalizedPath === `${rolePath}${suffix}`) {\n return handler(permissionMap, req.body);\n }\n }\n\n return this.addPermission(req.body);\n }\n\n switch (req.methode) {\n case \"GET\":\n return this.listPermissions(req.filter);\n case \"PATCH\":\n return this.updatePermission(req.entityId, req.body);\n case \"DELETE\":\n return this.deletePermission(req.entityId);\n default:\n throw new ErrorHttp({ code: 400, error: \"BadRequest\" }, \"[CORE] unsupported method\");\n }\n }\n\n // ---------------------------------------------------------------------------\n // RBAC access enforcement\n // ---------------------------------------------------------------------------\n\n async checkAccess(\n req: HttpRequest,\n resource: string,\n scopeMap: ScopeMap,\n adminGroups?: string[],\n findById?: (id: string) => Promise<any>,\n ): Promise<boolean> {\n const roles = this.extractRoles(req);\n if (!roles.length) this.deny();\n\n if (adminGroups?.length && this.isAdmin(roles, adminGroups)) return true;\n\n const method = req.methode.toUpperCase();\n const scope = this.getScope(req, scopeMap);\n\n this.applyMethodFilters(req, method, scope, scopeMap);\n\n const permitted = await this.hasAnyRolePermission(roles, resource, scope, method);\n if (!permitted) {\n this.deny(`Permission denied: ${roles.join(\",\")}: \"${resource}#${scope}.${method}\"`);\n }\n\n if (MUTATING_METHODS.has(method)) {\n await this.verifyScopeOwnership(req, scope, scopeMap, findById);\n }\n\n return true;\n }\n\n checkAdminPermission(cognitoGroups: string[], adminGroups: string[]) {\n if (this.isAdmin(cognitoGroups, adminGroups)) return true;\n this.deny(\"Permission Denied\");\n }\n\n // ---------------------------------------------------------------------------\n // Private — error + auth helpers\n // ---------------------------------------------------------------------------\n\n private deny(message = \"Permission denied: no role accessible\"): never {\n throw new ErrorHttp({ code: 403, error: \"PermissionDenied\" }, message);\n }\n\n private isAdmin(cognitoGroups: string[] | undefined, adminGroups: string[]): boolean {\n adminGroups = normalizeLower(adminGroups);\n return cognitoGroups?.some((group) => adminGroups.includes(group)) ?? false;\n }\n\n private extractRoles(req: HttpRequest): string[] {\n return req.identity?.groups ?? [];\n }\n\n private getScope(req: HttpRequest, scopeMap: ScopeMap): string {\n return req.queryStringParameters?.scope || req.customQueryParameters?.scope || Array.from(scopeMap.keys()).pop();\n }\n\n private async hasAnyRolePermission(\n roles: string[],\n resource: string,\n scope: string,\n method: string,\n ): Promise<boolean> {\n for (const role of roles) {\n if (await this.permissionService.hasPermission(role, resource, scope, method)) return true;\n }\n return false;\n }\n\n private validate<T extends z.ZodType>(schema: T, data: unknown): z.infer<T> {\n try {\n return validateWithSchema(schema, data);\n } catch (error) {\n const formattedErrors = formatErrors(error);\n throw new ErrorHttp({ code: 400, error: \"BadRequest\" }, formattedErrors);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Private — scope ownership verification\n // ---------------------------------------------------------------------------\n\n private async verifyScopeOwnership(\n req: HttpRequest,\n scope: string,\n scopeMap: ScopeMap,\n findById?: (id: string) => Promise<any>,\n ): Promise<void> {\n const mapping = scopeMap.get(scope);\n if (!mapping) return;\n\n const claimValue = this.resolveClaim(req, mapping);\n if (!claimValue) this.deny(`Missing claim for scope \"${scope}\"`);\n\n if (!findById) return;\n\n if (!req.entityId) {\n throw new ErrorHttp({ code: 400, error: \"BadRequest\" }, \"Missing resource id\");\n }\n\n const entity = await findById(req.entityId);\n if (!entity) {\n throw new ErrorHttp({ code: 404, error: \"NotFound\" }, `Resource with id \"${req.entityId}\" not found`);\n }\n\n if (entity[mapping.filterField] !== claimValue) {\n this.deny(`Scope mismatch: no access to resource \"${req.entityId}\"`);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Private — claim resolution\n // ---------------------------------------------------------------------------\n\n private resolveClaim(req: HttpRequest, mapping: { claimKey?: string }): string | undefined {\n return req.identity?.[mapping.claimKey] || req.identity?.attributes?.[mapping.claimKey];\n }\n\n private resolveClaimWithFallback(\n req: HttpRequest,\n mapping: { claimKey?: string },\n scopeName: string,\n ): string | undefined {\n const claimKey = mapping.claimKey ?? \"custom:\" + scopeName;\n return req.identity?.[claimKey] || req.identity?.attributes?.[claimKey] || req.identity?.sub;\n }\n\n // ---------------------------------------------------------------------------\n // Private — filter application\n // ---------------------------------------------------------------------------\n\n private applyMethodFilters(req: HttpRequest, method: string, scope: string, scopeMap: ScopeMap): void {\n if (method === \"POST\") {\n this.setDefaultIndexFields(req, scopeMap);\n } else if (method === \"GET\" && this.isOpenSearchPath(req)) {\n this.setDefaultFilters(req, scopeMap);\n } else if (method === \"GET\") {\n this.applyScopeFilter(req, scope, scopeMap);\n }\n }\n\n private applyScopeFilter(req: HttpRequest, scope: string, scopeMap: ScopeMap): void {\n if (!req.filter) req.filter = {};\n this.clearScopeFilters(req, scopeMap);\n\n const mapping = scopeMap.get(scope);\n if (!mapping) return;\n\n const claimValue = this.resolveClaim(req, mapping);\n if (claimValue || !req.filter[mapping.filterField]) {\n req.filter[mapping.filterField] = claimValue;\n }\n delete req.filter.scope;\n }\n\n private setDefaultIndexFields(req: HttpRequest, scopeMap: ScopeMap): void {\n if (!req.body) req.body = {};\n if (this.isUnset(req.body.ownerId)) req.body.ownerId = req.identity?.sub;\n this.applyAllScopeValues(req, scopeMap, true);\n }\n\n private setDefaultFilters(req: HttpRequest, scopeMap: ScopeMap): void {\n if (!req.filter) req.filter = {};\n\n const requestedScope = this.resolveRequestedScope(req);\n const levelLimit = this.getScopeLevelLimit(requestedScope, scopeMap);\n\n for (const [scopeName, mapping] of scopeMap) {\n if (levelLimit !== undefined && mapping.level !== undefined && mapping.level > levelLimit) continue;\n\n const claimValue = this.resolveClaimWithFallback(req, mapping, scopeName);\n const eqField = mapping.filterField + \".keyword__eq\";\n if (claimValue && this.isUnset(req.filter[eqField])) {\n req.filter[eqField] = claimValue;\n }\n }\n\n delete req.filter.scope;\n }\n\n private applyAllScopeValues(req: HttpRequest, scopeMap: ScopeMap, defaultsOnly = false): void {\n for (const [scopeName, mapping] of scopeMap) {\n const claimValue = this.resolveClaimWithFallback(req, mapping, scopeName);\n if (claimValue && (!defaultsOnly || this.isUnset(req.body[mapping.filterField]))) {\n req.body[mapping.filterField] = claimValue;\n }\n }\n }\n\n private isOpenSearchPath(req: HttpRequest): boolean {\n const path = trimSpecialChar(req.event?.requestContext?.resourcePath ?? req.resourcePath ?? \"\");\n return path.endsWith(\"search/query\");\n }\n\n private resolveRequestedScope(req: HttpRequest): string | undefined {\n const scopeRaw = req.filter?.scope || req.queryStringParameters?.scope || req.customQueryParameters?.scope;\n if (typeof scopeRaw !== \"string\") return undefined;\n const normalized = scopeRaw.trim().toLowerCase();\n return normalized.length ? normalized : undefined;\n }\n\n private getScopeLevelLimit(requestedScope: string | undefined, scopeMap: ScopeMap): number | undefined {\n if (!requestedScope) return undefined;\n for (const [scopeName, mapping] of scopeMap) {\n if (scopeName.toLowerCase() === requestedScope) return mapping.level;\n }\n return undefined;\n }\n\n private isUnset(value: unknown): boolean {\n return value === undefined || value === null || value === \"\";\n }\n\n private clearScopeFilters(req: HttpRequest, scopeMap: ScopeMap): void {\n for (const [, entry] of scopeMap) {\n delete req.filter[entry.filterField];\n }\n delete req.filter[\"profileId\"];\n delete req.filter[\"ownerId\"];\n delete req.filter[\"scope\"];\n }\n}\n"]}
|
|
@@ -12,9 +12,3 @@ export interface Permission extends BaseEntity {
|
|
|
12
12
|
};
|
|
13
13
|
permissionKey: string;
|
|
14
14
|
}
|
|
15
|
-
/**
|
|
16
|
-
* Build a fully-qualified permission key.
|
|
17
|
-
* Format: `${role}.${resource}.${scope}.${method}`
|
|
18
|
-
* Example: `OwnerRole.PropertyTable.BranchScope.GET`
|
|
19
|
-
*/
|
|
20
|
-
export declare function buildPermissionKey(role: string, resource: string, scope: string, method: string): string;
|
package/dist/model/role.model.js
CHANGED
|
@@ -1,12 +1,3 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.buildPermissionKey = buildPermissionKey;
|
|
4
|
-
/**
|
|
5
|
-
* Build a fully-qualified permission key.
|
|
6
|
-
* Format: `${role}.${resource}.${scope}.${method}`
|
|
7
|
-
* Example: `OwnerRole.PropertyTable.BranchScope.GET`
|
|
8
|
-
*/
|
|
9
|
-
function buildPermissionKey(role, resource, scope, method) {
|
|
10
|
-
return `${role}.${resource}.${scope}.${method}`;
|
|
11
|
-
}
|
|
12
3
|
//# sourceMappingURL=role.model.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"role.model.js","sourceRoot":"","sources":["../../src/model/role.model.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"role.model.js","sourceRoot":"","sources":["../../src/model/role.model.ts"],"names":[],"mappings":"","sourcesContent":["import { BaseEntity } from \"./base.model\";\n\nexport interface Permission extends BaseEntity {\n role: string; // DB Index: Manager | User etc\n resource: string; // DB Index: Property | Profile etc\n scope: string; // DB Index: Organization | Branch | Agent\n method: {\n get?: boolean;\n post?: boolean;\n patch?: boolean;\n put?: boolean;\n delete?: boolean;\n };\n permissionKey: string; // DB Index: role#resourse#scope Manager#Property#Branch\n}\n"]}
|
|
@@ -50,7 +50,7 @@ class PermissionService {
|
|
|
50
50
|
async hasPermission(role, resource, scope, method) {
|
|
51
51
|
if (!role || !resource || !scope || !method)
|
|
52
52
|
return false;
|
|
53
|
-
const permissionKey = `${role}
|
|
53
|
+
const permissionKey = `${role}#${resource}#${scope}`;
|
|
54
54
|
const permission = await this.getPermissionByKey(permissionKey);
|
|
55
55
|
if (!permission?.method)
|
|
56
56
|
return false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permission.service.js","sourceRoot":"","sources":["../../src/service/permission.service.ts"],"names":[],"mappings":";;;AACA,4CAAyC;AACzC,yDAAqD;AACrD,uDAAmD;AAGnD;;;;;;;;;GASG;AACH,MAAa,iBAAiB;IACX,IAAI,CAAiB;IACrB,KAAK,CAAkB;IAExC,YAAY,SAAiB,EAAE,UAAmB;QAChD,IAAI,CAAC,IAAI,GAAG,IAAI,gCAAc,CAAC,SAAS,CAAC,CAAC;QAC1C,IAAI,CAAC,KAAK,GAAG,IAAI,kCAAe,CAAC,UAAU,CAAC,CAAC;IAC/C,CAAC;IAED,8EAA8E;IAC9E,cAAc;IACd,8EAA8E;IAE9E,oFAAoF;IACpF,KAAK,CAAC,kBAAkB,CAAC,aAAqB;QAC5C,IAAI,CAAC,aAAa;YAAE,OAAO,IAAI,CAAC;QAEhC,OAAO,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,QAAQ,aAAa,EAAE,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,aAAa,CAAC,CAAC,CAAC;IAC5G,CAAC;IAED,mDAAmD;IACnD,KAAK,CAAC,iBAAiB,CAAC,EAAU;QAChC,IAAI,CAAC,EAAE;YAAE,OAAO,IAAI,CAAC;QAErB,OAAO,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;IACzE,CAAC;IAED,8EAA8E;IAC9E,mBAAmB;IACnB,8EAA8E;IAE9E;;;;;;;OAOG;IACH,KAAK,CAAC,aAAa,CAAC,IAAY,EAAE,QAAgB,EAAE,KAAa,EAAE,MAAc;QAC/E,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,IAAI,CAAC,KAAK,IAAI,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAE1D,MAAM,aAAa,GAAG,GAAG,IAAI,IAAI,QAAQ,IAAI,KAAK,IAAI,MAAM,EAAE,CAAC;QAC/D,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,aAAa,CAAC,CAAC;QAChE,IAAI,CAAC,UAAU,EAAE,MAAM;YAAE,OAAO,KAAK,CAAC;QAEtC,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,EAAgC,CAAC;QAEvE,OAAO,UAAU,CAAC,MAAM,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;IACjD,CAAC;IAED,8EAA8E;IAC9E,gBAAgB;IAChB,8EAA8E;IAE9E,KAAK,CAAC,gBAAgB,CAAC,KAKtB;QACC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC;YACxB,MAAM,IAAI,qBAAS,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,kBAAkB,CAAC,CAAC;QAC9E,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC;YAC5B,MAAM,IAAI,qBAAS,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,sBAAsB,CAAC,CAAC;QAClF,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC;YACzB,MAAM,IAAI,qBAAS,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,mBAAmB,CAAC,CAAC;QAC/E,CAAC;QAED,kBAAkB;QAClB,MAAM,aAAa,GAAG,GAAG,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QACvE,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,aAAa,CAAC,CAAC;QACpE,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,IAAI,qBAAS,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,eAAe,aAAa,kBAAkB,CAAC,CAAC;QACxG,CAAC;QAED,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;YACjC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE;YACvB,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE;YAC/B,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE;YACzB,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,EAAE;YAC1B,aAAa;SACd,CAAC,CAAC;QAEH,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACnB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,8EAA8E;IAC9E,gBAAgB;IAChB,8EAA8E;IAE9E,KAAK,CAAC,gBAAgB,CACpB,EAAU,EACV,OAA4E;QAE5E,IAAI,CAAC,EAAE;YAAE,MAAM,IAAI,qBAAS,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,2BAA2B,CAAC,CAAC;QAE9F,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC9C,IAAI,CAAC,QAAQ;YAAE,MAAM,IAAI,qBAAS,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,eAAe,EAAE,aAAa,CAAC,CAAC;QAErG,MAAM,IAAI,GAAwB,EAAE,EAAE,EAAE,CAAC;QACzC,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS;YAAE,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QAChE,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS;YAAE,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QAC5E,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;YAAE,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACnE,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS;YAAE,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAE/D,2CAA2C;QAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,QAAQ,CAAC,IAAI,CAAC;QACxC,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,QAAQ,CAAC;QACpD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,QAAQ,CAAC,KAAK,CAAC;QAC3C,IAAI,CAAC,aAAa,GAAG,GAAG,IAAI,IAAI,QAAQ,IAAI,KAAK,EAAE,CAAC;QAEpD,gDAAgD;QAChD,IAAI,IAAI,CAAC,aAAa,KAAK,QAAQ,CAAC,aAAa,EAAE,CAAC;YAClD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YACzE,IAAI,QAAQ,EAAE,CAAC;gBACb,MAAM,IAAI,qBAAS,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,eAAe,IAAI,CAAC,aAAa,kBAAkB,CAAC,CAAC;YAC7G,CAAC;QACH,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAE7C,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACnB,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,8EAA8E;IAC9E,gBAAgB;IAChB,8EAA8E;IAE9E,KAAK,CAAC,gBAAgB,CAAC,EAAU;QAC/B,IAAI,CAAC,EAAE;YAAE,MAAM,IAAI,qBAAS,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,2BAA2B,CAAC,CAAC;QAE9F,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC9C,IAAI,CAAC,QAAQ;YAAE,MAAM,IAAI,qBAAS,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,eAAe,EAAE,aAAa,CAAC,CAAC;QAErG,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAE1C,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACnB,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,8EAA8E;IAC9E,cAAc;IACd,8EAA8E;IAE9E,KAAK,CAAC,eAAe,CAAC,MAAe;QACnC,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IACvC,CAAC;IAED,8EAA8E;IAC9E,8BAA8B;IAC9B,8EAA8E;IAE9E,UAAU;QACR,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;CACF;AAhKD,8CAgKC","sourcesContent":["import { Permission } from \"../model/role.model\";\nimport { ErrorHttp } from \"../exception\";\nimport { PermissionCache } from \"./permission.cache\";\nimport { PermissionRepo } from \"./permission.repo\";\nimport { Filter, List } from \"@chinggis/types\";\n\n/**\n * PermissionService — cache + orchestration layer for Permission entities.\n *\n * Design decisions:\n * - Read-through cache via PermissionCache (in-flight dedup, TTL).\n * - Repository handles only DB I/O.\n * - Cache invalidation on every mutation.\n * - ~100 roles total, read-heavy → caching makes sense.\n * - Cache keyed by `perm:{permissionKey}` for deterministic, namespaced lookup.\n */\nexport class PermissionService {\n private readonly repo: PermissionRepo;\n private readonly cache: PermissionCache;\n\n constructor(tableName: string, cacheTtlMs?: number) {\n this.repo = new PermissionRepo(tableName);\n this.cache = new PermissionCache(cacheTtlMs);\n }\n\n // ---------------------------------------------------------------------------\n // Core lookup\n // ---------------------------------------------------------------------------\n\n /** Fetch Permission by composite key (role#resource#scope). Read-through cached. */\n async getPermissionByKey(permissionKey: string): Promise<Permission | null> {\n if (!permissionKey) return null;\n\n return this.cache.getOrFetch(`perm:${permissionKey}`, () => this.repo.findByPermissionKey(permissionKey));\n }\n\n /** Fetch Permission by id. Read-through cached. */\n async getPermissionById(id: string): Promise<Permission | null> {\n if (!id) return null;\n\n return this.cache.getOrFetch(`id:${id}`, () => this.repo.findById(id));\n }\n\n // ---------------------------------------------------------------------------\n // Permission check\n // ---------------------------------------------------------------------------\n\n /**\n * Check if the given role+resource+scope has a specific HTTP method allowed.\n *\n * Builds the permissionKey `role#resource#scope`, fetches the Permission,\n * and checks the method map.\n *\n * @returns `true` if allowed, `false` otherwise.\n */\n async hasPermission(role: string, resource: string, scope: string, method: string): Promise<boolean> {\n if (!role || !resource || !scope || !method) return false;\n\n const permissionKey = `${role}.${resource}.${scope}.${method}`;\n const permission = await this.getPermissionByKey(permissionKey);\n if (!permission?.method) return false;\n\n const methodLower = method.toUpperCase() as keyof Permission[\"method\"];\n\n return permission.method[methodLower] === true;\n }\n\n // ---------------------------------------------------------------------------\n // CRUD — create\n // ---------------------------------------------------------------------------\n\n async createPermission(input: {\n role: string;\n resource: string;\n scope: string;\n method: Permission[\"method\"];\n }): Promise<Permission> {\n if (!input.role?.trim()) {\n throw new ErrorHttp({ code: 400, error: \"BadRequest\" }, \"Role is required\");\n }\n if (!input.resource?.trim()) {\n throw new ErrorHttp({ code: 400, error: \"BadRequest\" }, \"Resource is required\");\n }\n if (!input.scope?.trim()) {\n throw new ErrorHttp({ code: 400, error: \"BadRequest\" }, \"Scope is required\");\n }\n\n // Duplicate check\n const permissionKey = `${input.role}#${input.resource}#${input.scope}`;\n const existing = await this.repo.findByPermissionKey(permissionKey);\n if (existing) {\n throw new ErrorHttp({ code: 409, error: \"Conflict\" }, `Permission \"${permissionKey}\" already exists`);\n }\n\n const saved = await this.repo.save({\n role: input.role.trim(),\n resource: input.resource.trim(),\n scope: input.scope.trim(),\n method: input.method ?? {},\n permissionKey,\n });\n\n this.cache.clear();\n return saved;\n }\n\n // ---------------------------------------------------------------------------\n // CRUD — update\n // ---------------------------------------------------------------------------\n\n async updatePermission(\n id: string,\n updates: Partial<Pick<Permission, \"role\" | \"resource\" | \"scope\" | \"method\">>,\n ): Promise<Permission> {\n if (!id) throw new ErrorHttp({ code: 400, error: \"BadRequest\" }, \"Permission id is required\");\n\n const existing = await this.repo.findById(id);\n if (!existing) throw new ErrorHttp({ code: 404, error: \"NotFound\" }, `Permission \"${id}\" not found`);\n\n const data: Partial<Permission> = { id };\n if (updates.role !== undefined) data.role = updates.role.trim();\n if (updates.resource !== undefined) data.resource = updates.resource.trim();\n if (updates.scope !== undefined) data.scope = updates.scope.trim();\n if (updates.method !== undefined) data.method = updates.method;\n\n // Rebuild key if any key component changed\n const role = data.role ?? existing.role;\n const resource = data.resource ?? existing.resource;\n const scope = data.scope ?? existing.scope;\n data.permissionKey = `${role}#${resource}#${scope}`;\n\n // Check for duplicate key if components changed\n if (data.permissionKey !== existing.permissionKey) {\n const conflict = await this.repo.findByPermissionKey(data.permissionKey);\n if (conflict) {\n throw new ErrorHttp({ code: 409, error: \"Conflict\" }, `Permission \"${data.permissionKey}\" already exists`);\n }\n }\n\n const updated = await this.repo.update(data);\n\n this.cache.clear();\n return updated;\n }\n\n // ---------------------------------------------------------------------------\n // CRUD — delete\n // ---------------------------------------------------------------------------\n\n async deletePermission(id: string): Promise<boolean> {\n if (!id) throw new ErrorHttp({ code: 400, error: \"BadRequest\" }, \"Permission id is required\");\n\n const existing = await this.repo.findById(id);\n if (!existing) throw new ErrorHttp({ code: 404, error: \"NotFound\" }, `Permission \"${id}\" not found`);\n\n const result = await this.repo.delete(id);\n\n this.cache.clear();\n return result;\n }\n\n // ---------------------------------------------------------------------------\n // CRUD — list\n // ---------------------------------------------------------------------------\n\n async listPermissions(filter?: Filter): Promise<List<Partial<Permission>>> {\n return this.repo.findByIndex(filter);\n }\n\n // ---------------------------------------------------------------------------\n // Cache control (for testing)\n // ---------------------------------------------------------------------------\n\n clearCache(): void {\n this.cache.clear();\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"permission.service.js","sourceRoot":"","sources":["../../src/service/permission.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Permission } from \"../model/role.model\";\nimport { ErrorHttp } from \"../exception\";\nimport { PermissionCache } from \"./permission.cache\";\nimport { PermissionRepo } from \"./permission.repo\";\nimport { Filter, List } from \"@chinggis/types\";\n\n/**\n * PermissionService — cache + orchestration layer for Permission entities.\n *\n * Design decisions:\n * - Read-through cache via PermissionCache (in-flight dedup, TTL).\n * - Repository handles only DB I/O.\n * - Cache invalidation on every mutation.\n * - ~100 roles total, read-heavy → caching makes sense.\n * - Cache keyed by `perm:{permissionKey}` for deterministic, namespaced lookup.\n */\nexport class PermissionService {\n private readonly repo: PermissionRepo;\n private readonly cache: PermissionCache;\n\n constructor(tableName: string, cacheTtlMs?: number) {\n this.repo = new PermissionRepo(tableName);\n this.cache = new PermissionCache(cacheTtlMs);\n }\n\n // ---------------------------------------------------------------------------\n // Core lookup\n // ---------------------------------------------------------------------------\n\n /** Fetch Permission by composite key (role#resource#scope). Read-through cached. */\n async getPermissionByKey(permissionKey: string): Promise<Permission | null> {\n if (!permissionKey) return null;\n\n return this.cache.getOrFetch(`perm:${permissionKey}`, () => this.repo.findByPermissionKey(permissionKey));\n }\n\n /** Fetch Permission by id. Read-through cached. */\n async getPermissionById(id: string): Promise<Permission | null> {\n if (!id) return null;\n\n return this.cache.getOrFetch(`id:${id}`, () => this.repo.findById(id));\n }\n\n // ---------------------------------------------------------------------------\n // Permission check\n // ---------------------------------------------------------------------------\n\n /**\n * Check if the given role+resource+scope has a specific HTTP method allowed.\n *\n * Builds the permissionKey `role#resource#scope`, fetches the Permission,\n * and checks the method map.\n *\n * @returns `true` if allowed, `false` otherwise.\n */\n async hasPermission(role: string, resource: string, scope: string, method: string): Promise<boolean> {\n if (!role || !resource || !scope || !method) return false;\n\n const permissionKey = `${role}#${resource}#${scope}`;\n const permission = await this.getPermissionByKey(permissionKey);\n if (!permission?.method) return false;\n\n const methodLower = method.toUpperCase() as keyof Permission[\"method\"];\n\n return permission.method[methodLower] === true;\n }\n\n // ---------------------------------------------------------------------------\n // CRUD — create\n // ---------------------------------------------------------------------------\n\n async createPermission(input: {\n role: string;\n resource: string;\n scope: string;\n method: Permission[\"method\"];\n }): Promise<Permission> {\n if (!input.role?.trim()) {\n throw new ErrorHttp({ code: 400, error: \"BadRequest\" }, \"Role is required\");\n }\n if (!input.resource?.trim()) {\n throw new ErrorHttp({ code: 400, error: \"BadRequest\" }, \"Resource is required\");\n }\n if (!input.scope?.trim()) {\n throw new ErrorHttp({ code: 400, error: \"BadRequest\" }, \"Scope is required\");\n }\n\n // Duplicate check\n const permissionKey = `${input.role}#${input.resource}#${input.scope}`;\n const existing = await this.repo.findByPermissionKey(permissionKey);\n if (existing) {\n throw new ErrorHttp({ code: 409, error: \"Conflict\" }, `Permission \"${permissionKey}\" already exists`);\n }\n\n const saved = await this.repo.save({\n role: input.role.trim(),\n resource: input.resource.trim(),\n scope: input.scope.trim(),\n method: input.method ?? {},\n permissionKey,\n });\n\n this.cache.clear();\n return saved;\n }\n\n // ---------------------------------------------------------------------------\n // CRUD — update\n // ---------------------------------------------------------------------------\n\n async updatePermission(\n id: string,\n updates: Partial<Pick<Permission, \"role\" | \"resource\" | \"scope\" | \"method\">>,\n ): Promise<Permission> {\n if (!id) throw new ErrorHttp({ code: 400, error: \"BadRequest\" }, \"Permission id is required\");\n\n const existing = await this.repo.findById(id);\n if (!existing) throw new ErrorHttp({ code: 404, error: \"NotFound\" }, `Permission \"${id}\" not found`);\n\n const data: Partial<Permission> = { id };\n if (updates.role !== undefined) data.role = updates.role.trim();\n if (updates.resource !== undefined) data.resource = updates.resource.trim();\n if (updates.scope !== undefined) data.scope = updates.scope.trim();\n if (updates.method !== undefined) data.method = updates.method;\n\n // Rebuild key if any key component changed\n const role = data.role ?? existing.role;\n const resource = data.resource ?? existing.resource;\n const scope = data.scope ?? existing.scope;\n data.permissionKey = `${role}#${resource}#${scope}`;\n\n // Check for duplicate key if components changed\n if (data.permissionKey !== existing.permissionKey) {\n const conflict = await this.repo.findByPermissionKey(data.permissionKey);\n if (conflict) {\n throw new ErrorHttp({ code: 409, error: \"Conflict\" }, `Permission \"${data.permissionKey}\" already exists`);\n }\n }\n\n const updated = await this.repo.update(data);\n\n this.cache.clear();\n return updated;\n }\n\n // ---------------------------------------------------------------------------\n // CRUD — delete\n // ---------------------------------------------------------------------------\n\n async deletePermission(id: string): Promise<boolean> {\n if (!id) throw new ErrorHttp({ code: 400, error: \"BadRequest\" }, \"Permission id is required\");\n\n const existing = await this.repo.findById(id);\n if (!existing) throw new ErrorHttp({ code: 404, error: \"NotFound\" }, `Permission \"${id}\" not found`);\n\n const result = await this.repo.delete(id);\n\n this.cache.clear();\n return result;\n }\n\n // ---------------------------------------------------------------------------\n // CRUD — list\n // ---------------------------------------------------------------------------\n\n async listPermissions(filter?: Filter): Promise<List<Partial<Permission>>> {\n return this.repo.findByIndex(filter);\n }\n\n // ---------------------------------------------------------------------------\n // Cache control (for testing)\n // ---------------------------------------------------------------------------\n\n clearCache(): void {\n this.cache.clear();\n }\n}\n"]}
|