diff --git a/backend/middleware/auth.js b/backend/middleware/auth.js index 58c5415..f2ada0e 100644 --- a/backend/middleware/auth.js +++ b/backend/middleware/auth.js @@ -18,6 +18,13 @@ const requireAuth = async (req, res, next) => { return res.status(401).json({ error: 'User not found' }); } + // Debug logging to verify correct user is authenticated + if (req.path.includes('/tasks') && req.method === 'GET') { + console.log( + `[AUTH DEBUG] ${req.method} ${req.path} - User: ${user.email} (ID: ${user.id})` + ); + } + req.currentUser = user; next(); } catch (error) { diff --git a/backend/routes/tasks.js b/backend/routes/tasks.js index 445dedc..52bebcb 100644 --- a/backend/routes/tasks.js +++ b/backend/routes/tasks.js @@ -838,24 +838,28 @@ async function computeTaskMetrics(userId, userTimezone = 'UTC') { const tasksDueToday = await Task.findAll({ where: { - ...visibleTasksWhere, - status: { - [Op.notIn]: [ - Task.STATUS.DONE, - Task.STATUS.ARCHIVED, - 'done', - 'archived', - ], - }, - parent_task_id: null, // Exclude subtasks - recurring_parent_id: null, // Exclude recurring instances - [Op.or]: [ - { due_date: { [Op.lte]: todayBounds.end } }, - sequelize.literal(`EXISTS ( - SELECT 1 FROM projects - WHERE projects.id = Task.project_id + [Op.and]: [ + visibleTasksWhere, + { + status: { + [Op.notIn]: [ + Task.STATUS.DONE, + Task.STATUS.ARCHIVED, + 'done', + 'archived', + ], + }, + parent_task_id: null, // Exclude subtasks + recurring_parent_id: null, // Exclude recurring instances + [Op.or]: [ + { due_date: { [Op.lte]: todayBounds.end } }, + sequelize.literal(`EXISTS ( + SELECT 1 FROM projects + WHERE projects.id = Task.project_id AND projects.due_date_at <= '${todayBounds.end.toISOString()}' )`), + ], + }, ], }, include: [ diff --git a/backend/services/permissionsService.js b/backend/services/permissionsService.js index d3d5333..f55935e 100644 --- a/backend/services/permissionsService.js +++ b/backend/services/permissionsService.js @@ -97,15 +97,47 @@ async function getAccess(userId, resourceType, resourceUid) { async function ownershipOrPermissionWhere(resourceType, userId) { // Admin users can see all resources - if (await isAdmin(userId)) { + // 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; + if (typeof userId === 'number' || !isNaN(parseInt(userId))) { + const { User } = require('../models'); + const user = await User.findByPk(userId, { + attributes: ['uid', 'email'], + }); + if (user) { + userUid = user.uid; + console.log( + `[PERMISSIONS DEBUG] User lookup: ID=${userId}, UID=${userUid}, Email=${user.email}` + ); + } + } + + const isUserAdmin = await isAdmin(userUid); + console.log( + `[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 } const sharedUids = await getSharedUidsForUser(resourceType, userId); + console.log( + `[PERMISSIONS DEBUG] Shared ${resourceType} UIDs for user ${userId}:`, + sharedUids + ); // For tasks and notes, also include items from shared projects if (resourceType === 'task' || resourceType === 'note') { const sharedProjectUids = await getSharedUidsForUser('project', userId); + console.log( + `[PERMISSIONS DEBUG] Shared project UIDs for user ${userId}:`, + sharedProjectUids + ); // Get the project IDs for shared projects let sharedProjectIds = []; @@ -116,6 +148,10 @@ async function ownershipOrPermissionWhere(resourceType, userId) { raw: true, }); sharedProjectIds = projects.map((p) => p.id); + console.log( + `[PERMISSIONS DEBUG] Shared project IDs for user ${userId}:`, + sharedProjectIds + ); } const conditions = [ @@ -130,6 +166,10 @@ async function ownershipOrPermissionWhere(resourceType, userId) { conditions.push({ project_id: { [Op.in]: sharedProjectIds } }); // Items in shared projects } + console.log( + `[PERMISSIONS DEBUG] Final where conditions for ${resourceType}:`, + JSON.stringify(conditions) + ); return { [Op.or]: conditions }; }