- Added debug logging to getUserFromRequest function - Log when no auth header is found vs when token verification fails - Log token verification attempts and decoded results - This will help identify why 'All My Cards' collection lookup is failing The issue appears to be that the development fallback always returns admin user, but users are trying to access collections belonging to other users (like Bob). This debugging will help us see if tokens are being sent properly.
162 lines
4.7 KiB
JavaScript
162 lines
4.7 KiB
JavaScript
import { sql } from '@vercel/postgres';
|
|
import jwt from 'jsonwebtoken';
|
|
|
|
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key-change-in-production';
|
|
|
|
/**
|
|
* Get user ID from request headers
|
|
*/
|
|
export async function getUserFromRequest(req) {
|
|
try {
|
|
const authHeader = req.headers.authorization;
|
|
|
|
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
|
// For development, return user ID 1 if no token (should be removed in production)
|
|
console.warn('⚠️ Development mode: Using fallback user authentication');
|
|
console.warn('⚠️ No auth header found:', authHeader);
|
|
return { userId: 1, email: 'admin@tcgvault.com', role: 'admin' };
|
|
}
|
|
|
|
const token = authHeader.substring(7);
|
|
console.log('🔍 Attempting to verify token:', token.substring(0, 20) + '...');
|
|
const decoded = jwt.verify(token, JWT_SECRET);
|
|
console.log('✅ Token decoded successfully:', decoded);
|
|
|
|
// Get user data from database
|
|
const result = await sql`
|
|
SELECT id, email, role
|
|
FROM users
|
|
WHERE id = ${decoded.userId}
|
|
`;
|
|
|
|
if (result.rows.length === 0) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
userId: result.rows[0].id,
|
|
email: result.rows[0].email,
|
|
role: result.rows[0].role
|
|
};
|
|
} catch (error) {
|
|
console.error('Error getting user from request:', error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if user has permission to access a collection
|
|
*/
|
|
export async function checkCollectionPermission(collectionId, userId, requiredPermission = 'viewer') {
|
|
try {
|
|
// Get collection and user permission
|
|
const result = await sql`
|
|
SELECT
|
|
c.id,
|
|
c.user_id as owner_id,
|
|
c.is_public,
|
|
cp.role,
|
|
cp.status
|
|
FROM collections c
|
|
LEFT JOIN collection_permissions cp ON c.id = cp.collection_id AND cp.user_id = ${userId}
|
|
WHERE c.id = ${collectionId}
|
|
`;
|
|
|
|
if (result.rows.length === 0) {
|
|
return { hasAccess: false, reason: 'Collection not found' };
|
|
}
|
|
|
|
const collection = result.rows[0];
|
|
|
|
// Owner always has access
|
|
if (collection.owner_id === userId) {
|
|
return { hasAccess: true, role: 'owner', collection };
|
|
}
|
|
|
|
// Public collections - everyone can view (but not edit)
|
|
if (collection.is_public && requiredPermission === 'viewer') {
|
|
return { hasAccess: true, role: 'viewer', collection };
|
|
}
|
|
|
|
// Check explicit permissions
|
|
if (collection.role && collection.status === 'active') {
|
|
const hasRequiredPermission = checkRolePermission(collection.role, requiredPermission);
|
|
if (hasRequiredPermission) {
|
|
return { hasAccess: true, role: collection.role, collection };
|
|
}
|
|
}
|
|
|
|
// Collections without explicit permission
|
|
return { hasAccess: false, reason: 'Access denied' };
|
|
|
|
} catch (error) {
|
|
console.error('Error checking collection permission:', error);
|
|
return { hasAccess: false, reason: 'Internal error' };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if a role has the required permission level
|
|
*/
|
|
function checkRolePermission(userRole, requiredPermission) {
|
|
const roleHierarchy = {
|
|
viewer: 1,
|
|
editor: 2,
|
|
owner: 3
|
|
};
|
|
|
|
const userLevel = roleHierarchy[userRole] || 0;
|
|
const requiredLevel = roleHierarchy[requiredPermission] || 0;
|
|
|
|
return userLevel >= requiredLevel;
|
|
}
|
|
|
|
/**
|
|
* Middleware to protect collection routes
|
|
*/
|
|
export function withCollectionPermission(requiredPermission = 'viewer') {
|
|
return function(handler) {
|
|
return async function(req, res) {
|
|
try {
|
|
const { id: collectionId } = req.query;
|
|
|
|
if (!collectionId) {
|
|
return res.status(400).json({ error: 'Collection ID is required' });
|
|
}
|
|
|
|
const user = await getUserFromRequest(req);
|
|
if (!user) {
|
|
return res.status(401).json({ error: 'Authentication required' });
|
|
}
|
|
|
|
const permission = await checkCollectionPermission(collectionId, user.userId, requiredPermission);
|
|
if (!permission.hasAccess) {
|
|
return res.status(403).json({ error: permission.reason || 'Access denied' });
|
|
}
|
|
|
|
// Add user and permission info to request
|
|
req.user = user;
|
|
req.permission = permission;
|
|
|
|
return handler(req, res);
|
|
} catch (error) {
|
|
console.error('Permission middleware error:', error);
|
|
return res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
};
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Log collection activity
|
|
*/
|
|
export async function logCollectionActivity(collectionId, userId, action, details = {}) {
|
|
try {
|
|
await sql`
|
|
INSERT INTO collection_activity (collection_id, user_id, action, details)
|
|
VALUES (${collectionId}, ${userId}, ${action}, ${JSON.stringify(details)})
|
|
`;
|
|
} catch (error) {
|
|
console.error('Error logging collection activity:', error);
|
|
}
|
|
}
|