diff --git a/backend/services/permissionsService.js b/backend/services/permissionsService.js index f55935e..38ed064 100644 --- a/backend/services/permissionsService.js +++ b/backend/services/permissionsService.js @@ -96,7 +96,7 @@ async function getAccess(userId, resourceType, resourceUid) { } async function ownershipOrPermissionWhere(resourceType, userId) { - // Admin users can see all resources + // Build WHERE clause for resource queries based on ownership and sharing permissions // Note: isAdmin expects a UID, but we might receive a numeric ID // Get the user's UID if we received a numeric ID let userUid = userId; @@ -118,12 +118,15 @@ async function ownershipOrPermissionWhere(resourceType, userId) { `[PERMISSIONS DEBUG] Resource: ${resourceType}, UserId: ${userId}, IsAdmin: ${isUserAdmin}` ); - if (isUserAdmin) { - console.log( - `[PERMISSIONS DEBUG] User is admin, returning empty where clause (all resources visible)` - ); - return {}; // empty where clause = no restriction - } + // Admin users should NOT see all resources automatically + // They should only see their own resources and shared resources, like regular users + // If admin-level system-wide visibility is needed, it should be via dedicated admin endpoints + // if (isUserAdmin) { + // console.log( + // `[PERMISSIONS DEBUG] User is admin, returning empty where clause (all resources visible)` + // ); + // return {}; // empty where clause = no restriction + // } const sharedUids = await getSharedUidsForUser(resourceType, userId); console.log( diff --git a/backend/tests/integration/permissions-admin.test.js b/backend/tests/integration/permissions-admin.test.js new file mode 100644 index 0000000..3a10a92 --- /dev/null +++ b/backend/tests/integration/permissions-admin.test.js @@ -0,0 +1,195 @@ +const request = require('supertest'); +const app = require('../../app'); +const { Task, Project, Note, Role } = require('../../models'); +const { createTestUser } = require('../helpers/testUtils'); + +describe('Admin Permissions - Resource Visibility', () => { + let adminUser, regularUser, adminAgent, regularAgent; + + async function makeAdmin(userId) { + await Role.findOrCreate({ + where: { user_id: userId }, + defaults: { user_id: userId, is_admin: true }, + }); + } + + async function loginAgent(email) { + const agent = request.agent(app); + await agent.post('/api/login').send({ email, password: 'password123' }); + return agent; + } + + beforeEach(async () => { + // Create admin user + adminUser = await createTestUser({ + email: `admin_${Date.now()}@example.com`, + }); + await makeAdmin(adminUser.id); + + // Create regular user + regularUser = await createTestUser({ + email: `regular_${Date.now()}@example.com`, + }); + + // Login both users + adminAgent = await loginAgent(adminUser.email); + regularAgent = await loginAgent(regularUser.email); + }); + + describe('Tasks visibility', () => { + it('admin should only see their own tasks, not all tasks', async () => { + // Create tasks for both users + const adminTask = await Task.create({ + name: 'Admin Task', + user_id: adminUser.id, + }); + + const regularTask = await Task.create({ + name: 'Regular User Task', + user_id: regularUser.id, + }); + + // Admin fetches all tasks + const res = await adminAgent.get('/api/tasks'); + expect(res.status).toBe(200); + + const taskIds = res.body.tasks.map((t) => t.id); + + // Admin should see their own task + expect(taskIds).toContain(adminTask.id); + + // Admin should NOT see other user's task (THIS IS THE KEY FIX) + expect(taskIds).not.toContain(regularTask.id); + }); + }); + + describe('Projects visibility', () => { + it('admin should only see their own projects, not all projects', async () => { + // Create projects for both users + const adminProject = await Project.create({ + name: 'Admin Project', + user_id: adminUser.id, + }); + + const regularProject = await Project.create({ + name: 'Regular User Project', + user_id: regularUser.id, + }); + + // Admin fetches all projects + const res = await adminAgent.get('/api/projects'); + expect(res.status).toBe(200); + + const projectIds = res.body.projects.map((p) => p.id); + + // Admin should see their own project + expect(projectIds).toContain(adminProject.id); + + // Admin should NOT see other user's project (THIS IS THE KEY FIX) + expect(projectIds).not.toContain(regularProject.id); + }); + }); + + describe('Notes visibility', () => { + it('admin should only see their own notes, not all notes', async () => { + // Create notes for both users + const adminNote = await Note.create({ + name: 'Admin Note', + content: 'Admin content', + user_id: adminUser.id, + }); + + const regularNote = await Note.create({ + name: 'Regular User Note', + content: 'Regular content', + user_id: regularUser.id, + }); + + // Admin fetches all notes + const res = await adminAgent.get('/api/notes'); + expect(res.status).toBe(200); + + const noteIds = res.body.map((n) => n.id); + + // Admin should see their own note + expect(noteIds).toContain(adminNote.id); + + // Admin should NOT see other user's note (THIS IS THE KEY FIX) + expect(noteIds).not.toContain(regularNote.id); + }); + }); + + describe('Regular user behavior unchanged', () => { + it('regular user should only see their own tasks', async () => { + // Create tasks for both users + const adminTask = await Task.create({ + name: 'Admin Task', + user_id: adminUser.id, + }); + + const regularTask = await Task.create({ + name: 'Regular User Task', + user_id: regularUser.id, + }); + + // Regular user fetches all tasks + const res = await regularAgent.get('/api/tasks'); + expect(res.status).toBe(200); + + const taskIds = res.body.tasks.map((t) => t.id); + + // Regular user should see their own task + expect(taskIds).toContain(regularTask.id); + + // Regular user should NOT see admin's task + expect(taskIds).not.toContain(adminTask.id); + }); + }); + + describe('Task metrics visibility', () => { + it('admin task metrics should only include own tasks', async () => { + // Create tasks for admin only (to avoid confusion) + const adminTask1 = await Task.create({ + name: 'Admin Task 1', + user_id: adminUser.id, + is_completed: false, + }); + + const adminTask2 = await Task.create({ + name: 'Admin Task 2', + user_id: adminUser.id, + is_completed: true, + }); + + // Create tasks for regular user (should not appear in admin's metrics) + await Task.create({ + name: 'Regular Task 1', + user_id: regularUser.id, + is_completed: false, + }); + + await Task.create({ + name: 'Regular Task 2', + user_id: regularUser.id, + is_completed: false, + }); + + // Admin fetches tasks with metrics + const res = await adminAgent.get('/api/tasks?compute_metrics=true'); + expect(res.status).toBe(200); + + // Admin should only see their own tasks in the response + expect(res.body.tasks.length).toBe(2); + + // Verify the admin's task IDs are present + const taskIds = res.body.tasks.map((t) => t.id); + expect(taskIds).toContain(adminTask1.id); + expect(taskIds).toContain(adminTask2.id); + + // Verify metrics exist and only count admin's tasks + expect(res.body.metrics).toBeDefined(); + // Admin has 2 tasks total (other users' tasks should not be counted) + expect(res.body.metrics.total_open_tasks).toBeLessThanOrEqual(2); + }); + }); +});