Fix lỗi set privacy chéo

This commit is contained in:
2026-06-10 15:00:40 +07:00
parent 727bda9b48
commit ec7a9186b6
9 changed files with 190 additions and 76 deletions
+17 -5
View File
@@ -37,8 +37,14 @@ router.post('/', protect, uploadSinglePanorama, checkQuota, async (req, res) =>
const { title, lat, lng, privacy, sharedWithUsers, tourId } = req.body;
if (!req.file) return res.status(400).json({ message: 'Please upload a panorama image' });
// Xử lý tourId từ FormData an toàn
// [BẢO MẬT] Làm sạch tourId từ client gửi lên
const cleanedTourId = (tourId && tourId !== 'null' && tourId !== '') ? tourId : undefined;
// [BẢO MẬT] Xác thực tourId nếu được cung cấp
if (cleanedTourId) {
const tourExists = await Scene.exists({ _id: cleanedTourId });
if (!tourExists) return res.status(400).json({ message: 'Tour gốc không tồn tại hoặc đã bị xóa.' });
}
const latitude = Number(lat) || 0;
const longitude = Number(lng) || 0;
@@ -164,6 +170,10 @@ router.put('/:id', protect, uploadSinglePanorama, async (req, res) => {
if (lat) scene.gps.lat = parseFloat(lat);
if (lng) scene.gps.lng = parseFloat(lng);
// [BẢO MẬT] Tuyệt đối không cho phép thay đổi tourId qua API cập nhật Metadata
// Một cảnh khi đã thuộc về một Tour thì không thể bị "chuyển hộ khẩu" sang Tour khác.
// (Trường tourId không có trong danh sách bóc tách req.body ở trên)
// [BẢO MẬT] Chỉ duy trì shareToken ở chế độ 'shared'.
// Gán undefined để Mongoose xóa trường này khỏi DB khi save.
if (scene.privacy !== 'shared') {
@@ -213,9 +223,11 @@ router.put('/:id', protect, uploadSinglePanorama, async (req, res) => {
if (!scene.tourId) scene.tourId = scene._id;
await scene.save();
// [CASCADING] Lan truyền Privacy xuống các cảnh con nếu đây là cảnh cha
if (!isChild) {
await propagateScenePrivacy(scene.tourId || scene._id, scene);
// [BẢO MẬT] Lan truyền Privacy xuống các cảnh con nếu đây là cảnh gốc của Tour.
const isRoot = scene.tourId && scene.tourId.toString() === scene._id.toString();
if (isRoot) {
await propagateScenePrivacy(scene._id, scene, req.user._id);
}
res.json({ message: 'Cập nhật thành công và đã đồng bộ quyền riêng tư cho các cảnh liên quan.', scene });
@@ -235,7 +247,7 @@ router.delete('/:id', protect, async (req, res) => {
return res.status(403).json({ message: 'Forbidden' });
}
const { deletedCount } = await deleteSceneCascade(rootSceneId, req.user.username);
const { deletedCount } = await deleteSceneCascade(rootSceneId, req.user._id);
res.json({
message: deletedCount > 1