const db = require('../db/models'); class AccessPolicyAuditService { static async findViolations(options = {}) { const transaction = options.transaction; const publicRoles = await db.roles.findAll({ where: { name: 'Public' }, include: [{ association: 'permissions' }], transaction, }); const publicRolePermissions = publicRoles.flatMap((role) => (role.permissions || []).map((permission) => ({ roleId: role.id, permissionId: permission.id, permissionName: permission.name, })), ); const publicUsersWithCustomPermissions = await db.users.findAll({ attributes: ['id', 'email'], include: [ { association: 'app_role', attributes: ['id', 'name'], where: { name: 'Public' }, required: true, }, { association: 'custom_permissions', attributes: ['id', 'name'], required: true, }, ], transaction, }); const productionPresentationAccessForNonPublicUsers = await db.production_presentation_access.findAll({ attributes: ['id', 'userId', 'projectId'], include: [ { association: 'user', attributes: ['id', 'email'], required: true, include: [ { association: 'app_role', attributes: ['id', 'name'], required: false, }, ], }, { association: 'project', attributes: ['id', 'name', 'slug'], required: false, }, ], transaction, }); const nonPublicGrants = productionPresentationAccessForNonPublicUsers.filter( (grant) => grant.user?.app_role?.name !== 'Public', ); return { publicRolePermissions: publicRolePermissions.map((entry) => ({ roleId: entry.roleId, id: entry.permissionId, name: entry.permissionName, })), publicUsersWithCustomPermissions: publicUsersWithCustomPermissions.map( (user) => ({ id: user.id, email: user.email, customPermissions: (user.custom_permissions || []).map( (permission) => ({ id: permission.id, name: permission.name, }), ), }), ), productionPresentationAccessForNonPublicUsers: nonPublicGrants.map( (grant) => ({ id: grant.id, userId: grant.userId, userEmail: grant.user?.email || null, userRole: grant.user?.app_role?.name || null, projectId: grant.projectId, projectSlug: grant.project?.slug || null, }), ), }; } static hasViolations(report) { return ( report.publicRolePermissions.length > 0 || report.publicUsersWithCustomPermissions.length > 0 || report.productionPresentationAccessForNonPublicUsers.length > 0 ); } static async cleanupViolations(options = {}) { const transaction = options.transaction; const report = await this.findViolations({ transaction }); const publicRoleIds = [ ...new Set( report.publicRolePermissions .map((permission) => permission.roleId) .filter(Boolean), ), ]; for (const publicRoleId of publicRoleIds) { const publicRole = await db.roles.findByPk(publicRoleId, { transaction }); if (publicRole) { await publicRole.setPermissions([], { transaction }); } } for (const userReport of report.publicUsersWithCustomPermissions) { const user = await db.users.findByPk(userReport.id, { transaction }); await user.setCustom_permissions([], { transaction }); } const grantIds = report.productionPresentationAccessForNonPublicUsers.map( (grant) => grant.id, ); if (grantIds.length > 0) { await db.production_presentation_access.destroy({ where: { id: { [db.Sequelize.Op.in]: grantIds } }, transaction, }); } return { before: report, removedPublicRolePermissions: report.publicRolePermissions.length, clearedPublicUserCustomPermissions: report.publicUsersWithCustomPermissions.length, removedNonPublicProductionPresentationGrants: grantIds.length, }; } } module.exports = AccessPolicyAuditService;