s3
This commit is contained in:
98
backend/services/s3OwnershipService.js
Normal file
98
backend/services/s3OwnershipService.js
Normal file
@@ -0,0 +1,98 @@
|
||||
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;
|
||||
Reference in New Issue
Block a user