Files
rentall-app/backend/services/s3OwnershipService.js
jackiettran b0268a2fb7 s3
2025-12-11 20:05:18 -05:00

99 lines
2.8 KiB
JavaScript

const { Message, ConditionCheck, Rental } = require("../models");
const { Op } = require("sequelize");
/**
* Service for verifying ownership/access to S3 files
* Used to authorize signed URL requests for private content
*/
class S3OwnershipService {
/**
* Extract file type from S3 key
* @param {string} key - S3 key like "messages/uuid.jpg"
* @returns {string|null} - File type or null if unknown
*/
static getFileTypeFromKey(key) {
if (!key) return null;
const folder = key.split("/")[0];
const folderMap = {
profiles: "profile",
items: "item",
messages: "message",
forum: "forum",
"condition-checks": "condition-check",
};
return folderMap[folder] || null;
}
/**
* Verify if a user can access a file
* @param {string} key - S3 key
* @param {string} userId - User ID making the request
* @returns {Promise<{authorized: boolean, reason?: string}>}
*/
static async canAccessFile(key, userId) {
const fileType = this.getFileTypeFromKey(key);
switch (fileType) {
case "profile":
case "item":
case "forum":
// Public folders - anyone can access
return { authorized: true };
case "message":
return this.verifyMessageAccess(key, userId);
case "condition-check":
return this.verifyConditionCheckAccess(key, userId);
default:
return { authorized: false, reason: "Unknown file type" };
}
}
/**
* Verify message image access - user must be sender OR receiver
* @param {string} key - S3 key
* @param {string} userId - User ID making the request
* @returns {Promise<{authorized: boolean, reason?: string}>}
*/
static async verifyMessageAccess(key, userId) {
const message = await Message.findOne({
where: {
imageFilename: key,
[Op.or]: [{ senderId: userId }, { receiverId: userId }],
},
});
return {
authorized: !!message,
reason: message ? null : "Not a participant in this message",
};
}
/**
* Verify condition check image access - user must be rental owner OR renter
* @param {string} key - S3 key
* @param {string} userId - User ID making the request
* @returns {Promise<{authorized: boolean, reason?: string}>}
*/
static async verifyConditionCheckAccess(key, userId) {
const check = await ConditionCheck.findOne({
where: {
imageFilenames: { [Op.contains]: [key] },
},
include: [
{
model: Rental,
as: "rental",
where: {
[Op.or]: [{ ownerId: userId }, { renterId: userId }],
},
},
],
});
return {
authorized: !!check,
reason: check ? null : "Not a participant in this rental",
};
}
}
module.exports = S3OwnershipService;