UNPKG

@lucidcms/core

Version:

The core of the Lucid CMS. It's responsible for spinning up the API and serving the CMS.

1 lines 9.15 kB
{"version":3,"file":"prepare-update-single-audit-logs.mjs","names":["formatter","constants","securityAuditServices.serializeRoleValue"],"sources":["../../../../src/services/users/helpers/prepare-update-single-audit-logs.ts"],"sourcesContent":["import constants from \"../../../constants/constants.js\";\nimport type { BooleanInt } from \"../../../libs/db/types.js\";\nimport formatter from \"../../../libs/formatters/index.js\";\nimport { RoleTranslationsRepository } from \"../../../libs/repositories/index.js\";\nimport type {\n\tSecurityAuditAction,\n\tSecurityAuditRoleSnapshot,\n} from \"../../../types/security-audit.js\";\nimport { sortRoleSnapshot } from \"../../../utils/security-audit/sort-role-snapshot.js\";\nimport type {\n\tServiceContext,\n\tServiceFn,\n\tServiceResponse,\n} from \"../../../utils/services/types.js\";\nimport * as securityAuditServices from \"../../security-audit/index.js\";\n\ntype SecurityAuditLogData = {\n\tuserId: number;\n\taction: SecurityAuditAction;\n\tperformedBy: number;\n\tpreviousValue: string;\n\tnewValue: string;\n};\n\ntype RoleAuditState = {\n\tpreviousRoles: SecurityAuditRoleSnapshot[];\n\tnextRoleIds: number[];\n\trolesChanged: boolean;\n\tpreviousSuperAdmin: boolean;\n\tnextSuperAdmin: boolean;\n};\n\ntype EmailChange = {\n\tpreviousValue: string;\n\tnewValue: string;\n};\n\nconst sortRoleIds = (roleIds: number[]) => [...roleIds].sort((a, b) => a - b);\n\nconst roleIdsMatch = (previousRoleIds: number[], nextRoleIds: number[]) =>\n\tpreviousRoleIds.length === nextRoleIds.length &&\n\tpreviousRoleIds.every((roleId, index) => roleId === nextRoleIds[index]);\n\nconst getUpdateAuditState = (props: {\n\tcurrentEmail: string;\n\tcurrentRoles: SecurityAuditRoleSnapshot[];\n\tcurrentSuperAdmin: boolean;\n\tnormalizedEmail?: string;\n\troleIds?: number[];\n\tsuperAdmin?: boolean;\n\tcanUpdateSuperAdmin: boolean;\n}) => {\n\tconst previousRoles = sortRoleSnapshot(props.currentRoles);\n\tconst previousRoleIds = sortRoleIds(previousRoles.map((role) => role.id));\n\tconst nextRoleIds =\n\t\tprops.roleIds !== undefined ? sortRoleIds(props.roleIds) : previousRoleIds;\n\tconst rolesChanged =\n\t\tprops.roleIds !== undefined &&\n\t\troleIdsMatch(previousRoleIds, nextRoleIds) === false;\n\tconst nextSuperAdmin =\n\t\tprops.canUpdateSuperAdmin && props.superAdmin !== undefined\n\t\t\t? props.superAdmin\n\t\t\t: props.currentSuperAdmin;\n\tconst superAdminChanged = props.currentSuperAdmin !== nextSuperAdmin;\n\n\treturn {\n\t\temailChange:\n\t\t\tprops.normalizedEmail !== undefined &&\n\t\t\tprops.normalizedEmail !== props.currentEmail\n\t\t\t\t? {\n\t\t\t\t\t\tpreviousValue: props.currentEmail,\n\t\t\t\t\t\tnewValue: props.normalizedEmail,\n\t\t\t\t\t}\n\t\t\t\t: undefined,\n\t\troleChange:\n\t\t\trolesChanged || superAdminChanged\n\t\t\t\t? {\n\t\t\t\t\t\tpreviousRoles,\n\t\t\t\t\t\tnextRoleIds,\n\t\t\t\t\t\trolesChanged,\n\t\t\t\t\t\tpreviousSuperAdmin: props.currentSuperAdmin,\n\t\t\t\t\t\tnextSuperAdmin,\n\t\t\t\t\t}\n\t\t\t\t: undefined,\n\t};\n};\n\nconst resolveNextRoleSnapshot = async (\n\tcontext: ServiceContext,\n\troleChange: RoleAuditState,\n): ServiceResponse<SecurityAuditRoleSnapshot[]> => {\n\tif (roleChange.rolesChanged === false) {\n\t\treturn {\n\t\t\terror: undefined,\n\t\t\tdata: roleChange.previousRoles,\n\t\t};\n\t}\n\n\tif (roleChange.nextRoleIds.length === 0) {\n\t\treturn {\n\t\t\terror: undefined,\n\t\t\tdata: [],\n\t\t};\n\t}\n\n\tconst RoleTranslations = new RoleTranslationsRepository(\n\t\tcontext.db.client,\n\t\tcontext.config.db,\n\t);\n\tconst rolesRes = await RoleTranslations.selectMultiple({\n\t\tselect: [\"role_id\", \"name\"],\n\t\twhere: [\n\t\t\t{\n\t\t\t\tkey: \"role_id\",\n\t\t\t\toperator: \"in\",\n\t\t\t\tvalue: roleChange.nextRoleIds,\n\t\t\t},\n\t\t\t{\n\t\t\t\tkey: \"locale_code\",\n\t\t\t\toperator: \"=\",\n\t\t\t\tvalue: context.config.localization.defaultLocale,\n\t\t\t},\n\t\t],\n\t\tvalidation: {\n\t\t\tenabled: true,\n\t\t},\n\t});\n\tif (rolesRes.error) return rolesRes;\n\n\treturn {\n\t\terror: undefined,\n\t\tdata: sortRoleSnapshot(\n\t\t\trolesRes.data.map((role) => ({\n\t\t\t\tid: role.role_id,\n\t\t\t\tname: role.name ?? \"\",\n\t\t\t})),\n\t\t),\n\t};\n};\n\nconst prepareUpdateSingleAuditLogs: ServiceFn<\n\t[\n\t\t{\n\t\t\tuserId: number;\n\t\t\tperformedBy: number;\n\t\t\tcurrentUser: {\n\t\t\t\temail: string;\n\t\t\t\tsuperAdmin: BooleanInt;\n\t\t\t\troles: SecurityAuditRoleSnapshot[];\n\t\t\t};\n\t\t\tnormalizedEmail?: string;\n\t\t\tpasswordChanged: boolean;\n\t\t\troleIds?: number[];\n\t\t\tsuperAdmin?: boolean;\n\t\t\tcanUpdateSuperAdmin: boolean;\n\t\t},\n\t],\n\t{\n\t\temailChange?: EmailChange;\n\t\tlogs: SecurityAuditLogData[];\n\t}\n> = async (context, data) => {\n\tconst auditState = getUpdateAuditState({\n\t\tcurrentEmail: data.currentUser.email,\n\t\tcurrentRoles: data.currentUser.roles,\n\t\tcurrentSuperAdmin: formatter.formatBoolean(data.currentUser.superAdmin),\n\t\tnormalizedEmail: data.normalizedEmail,\n\t\troleIds: data.roleIds,\n\t\tsuperAdmin: data.superAdmin,\n\t\tcanUpdateSuperAdmin: data.canUpdateSuperAdmin,\n\t});\n\tconst logs: SecurityAuditLogData[] = [];\n\n\tif (auditState.emailChange) {\n\t\tlogs.push({\n\t\t\tuserId: data.userId,\n\t\t\taction: constants.securityAudit.actions.emailChange,\n\t\t\tperformedBy: data.performedBy,\n\t\t\tpreviousValue: auditState.emailChange.previousValue,\n\t\t\tnewValue: auditState.emailChange.newValue,\n\t\t});\n\t}\n\n\tif (data.passwordChanged) {\n\t\tlogs.push({\n\t\t\tuserId: data.userId,\n\t\t\taction: constants.securityAudit.actions.passwordChange,\n\t\t\tperformedBy: data.performedBy,\n\t\t\tpreviousValue: constants.securityAudit.redactedValue,\n\t\t\tnewValue: constants.securityAudit.redactedValue,\n\t\t});\n\t}\n\n\tif (auditState.roleChange) {\n\t\tconst nextRolesRes = await resolveNextRoleSnapshot(\n\t\t\tcontext,\n\t\t\tauditState.roleChange,\n\t\t);\n\t\tif (nextRolesRes.error) return nextRolesRes;\n\n\t\tconst [previousValueRes, newValueRes] = await Promise.all([\n\t\t\tsecurityAuditServices.serializeRoleValue(context, {\n\t\t\t\troles: auditState.roleChange.previousRoles,\n\t\t\t\tsuperAdmin: auditState.roleChange.previousSuperAdmin,\n\t\t\t}),\n\t\t\tsecurityAuditServices.serializeRoleValue(context, {\n\t\t\t\troles: nextRolesRes.data,\n\t\t\t\tsuperAdmin: auditState.roleChange.nextSuperAdmin,\n\t\t\t}),\n\t\t]);\n\t\tif (previousValueRes.error) return previousValueRes;\n\t\tif (newValueRes.error) return newValueRes;\n\n\t\tlogs.push({\n\t\t\tuserId: data.userId,\n\t\t\taction: constants.securityAudit.actions.roleChange,\n\t\t\tperformedBy: data.performedBy,\n\t\t\tpreviousValue: previousValueRes.data,\n\t\t\tnewValue: newValueRes.data,\n\t\t});\n\t}\n\n\treturn {\n\t\terror: undefined,\n\t\tdata: {\n\t\t\temailChange: auditState.emailChange,\n\t\t\tlogs,\n\t\t},\n\t};\n};\n\nexport default prepareUpdateSingleAuditLogs;\n"],"mappings":"mSAqCA,MAAM,EAAe,GAAsB,CAAC,GAAG,CAAO,CAAC,CAAC,MAAM,EAAG,IAAM,EAAI,CAAC,EAEtE,GAAgB,EAA2B,IAChD,EAAgB,SAAW,EAAY,QACvC,EAAgB,OAAO,EAAQ,IAAU,IAAW,EAAY,EAAM,EAEjE,EAAuB,GAQvB,CACL,IAAM,EAAgB,EAAiB,EAAM,YAAY,EACnD,EAAkB,EAAY,EAAc,IAAK,GAAS,EAAK,EAAE,CAAC,EAClE,EACL,EAAM,UAAY,IAAA,GAAyC,EAA7B,EAAY,EAAM,OAAO,EAClD,EACL,EAAM,UAAY,IAAA,IAClB,EAAa,EAAiB,CAAW,IAAM,GAC1C,EACL,EAAM,qBAAuB,EAAM,aAAe,IAAA,GAC/C,EAAM,WACN,EAAM,kBACJ,EAAoB,EAAM,oBAAsB,EAEtD,MAAO,CACN,YACC,EAAM,kBAAoB,IAAA,IAC1B,EAAM,kBAAoB,EAAM,aAC7B,CACA,cAAe,EAAM,aACrB,SAAU,EAAM,eACjB,EACC,IAAA,GACJ,WACC,GAAgB,EACb,CACA,gBACA,cACA,eACA,mBAAoB,EAAM,kBAC1B,gBACD,EACC,IAAA,EACL,CACD,EAEM,EAA0B,MAC/B,EACA,IACkD,CAClD,GAAI,EAAW,eAAiB,GAC/B,MAAO,CACN,MAAO,IAAA,GACP,KAAM,EAAW,aAClB,EAGD,GAAI,EAAW,YAAY,SAAW,EACrC,MAAO,CACN,MAAO,IAAA,GACP,KAAM,CAAC,CACR,EAOD,IAAM,EAAW,MAAM,IAJM,EAC5B,EAAQ,GAAG,OACX,EAAQ,OAAO,EAEsB,CAAC,CAAC,eAAe,CACtD,OAAQ,CAAC,UAAW,MAAM,EAC1B,MAAO,CACN,CACC,IAAK,UACL,SAAU,KACV,MAAO,EAAW,WACnB,EACA,CACC,IAAK,cACL,SAAU,IACV,MAAO,EAAQ,OAAO,aAAa,aACpC,CACD,EACA,WAAY,CACX,QAAS,EACV,CACD,CAAC,EAGD,OAFI,EAAS,MAAc,EAEpB,CACN,MAAO,IAAA,GACP,KAAM,EACL,EAAS,KAAK,IAAK,IAAU,CAC5B,GAAI,EAAK,QACT,KAAM,EAAK,MAAQ,EACpB,EAAE,CACH,CACD,CACD,EAEM,EAqBF,MAAO,EAAS,IAAS,CAC5B,IAAM,EAAa,EAAoB,CACtC,aAAc,EAAK,YAAY,MAC/B,aAAc,EAAK,YAAY,MAC/B,kBAAmBA,EAAU,cAAc,EAAK,YAAY,UAAU,EACtE,gBAAiB,EAAK,gBACtB,QAAS,EAAK,QACd,WAAY,EAAK,WACjB,oBAAqB,EAAK,mBAC3B,CAAC,EACK,EAA+B,CAAC,EAsBtC,GApBI,EAAW,aACd,EAAK,KAAK,CACT,OAAQ,EAAK,OACb,OAAQC,EAAU,cAAc,QAAQ,YACxC,YAAa,EAAK,YAClB,cAAe,EAAW,YAAY,cACtC,SAAU,EAAW,YAAY,QAClC,CAAC,EAGE,EAAK,iBACR,EAAK,KAAK,CACT,OAAQ,EAAK,OACb,OAAQA,EAAU,cAAc,QAAQ,eACxC,YAAa,EAAK,YAClB,cAAeA,EAAU,cAAc,cACvC,SAAUA,EAAU,cAAc,aACnC,CAAC,EAGE,EAAW,WAAY,CAC1B,IAAM,EAAe,MAAM,EAC1B,EACA,EAAW,UACZ,EACA,GAAI,EAAa,MAAO,OAAO,EAE/B,GAAM,CAAC,EAAkB,GAAe,MAAM,QAAQ,IAAI,CACzDC,EAAyC,EAAS,CACjD,MAAO,EAAW,WAAW,cAC7B,WAAY,EAAW,WAAW,kBACnC,CAAC,EACDA,EAAyC,EAAS,CACjD,MAAO,EAAa,KACpB,WAAY,EAAW,WAAW,cACnC,CAAC,CACF,CAAC,EACD,GAAI,EAAiB,MAAO,OAAO,EACnC,GAAI,EAAY,MAAO,OAAO,EAE9B,EAAK,KAAK,CACT,OAAQ,EAAK,OACb,OAAQD,EAAU,cAAc,QAAQ,WACxC,YAAa,EAAK,YAClB,cAAe,EAAiB,KAChC,SAAU,EAAY,IACvB,CAAC,CACF,CAEA,MAAO,CACN,MAAO,IAAA,GACP,KAAM,CACL,YAAa,EAAW,YACxB,MACD,CACD,CACD"}