From 11d94322183c5c348240c70721b45c86678398b4 Mon Sep 17 00:00:00 2001 From: Tetardtek Date: Sat, 14 Mar 2026 08:12:11 +0100 Subject: [PATCH] =?UTF-8?q?fix(routes):=20resolve=20superOAuthId=20?= =?UTF-8?q?=E2=86=92=20DB=20userId=20=E2=80=94=20critical=20auth=20bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit req.user.id = SuperOAuth UUID, pas l'UUID TypeORM en DB. Sans ce fix : getUserPlanLevel retourne toujours 0, ownerId ne matche jamais. - video.routes: resolveDbUserId avant getUserPlanLevel - playlist.routes: resolveDbUserId sur toutes les opérations owner/member --- backend/src/routes/playlist.routes.ts | 32 +++++++++++++++++++++------ backend/src/routes/video.routes.ts | 13 +++++++++-- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/backend/src/routes/playlist.routes.ts b/backend/src/routes/playlist.routes.ts index f1b63a6..1b892c6 100644 --- a/backend/src/routes/playlist.routes.ts +++ b/backend/src/routes/playlist.routes.ts @@ -2,25 +2,34 @@ import { Router, Request, Response } from "express"; import { AppDataSource } from "../config/data-source"; import { Playlist } from "../entities/Playlist"; import { PlaylistShare } from "../entities/PlaylistShare"; +import { User } from "../entities/User"; import { requireAuth, AuthenticatedRequest } from "../middleware/auth.middleware"; const router = Router(); +/** Résout le superOAuthId vers l'UUID DB, crée le user si inexistant */ +async function resolveDbUserId(superOAuthId: string): Promise { + const user = await AppDataSource.getRepository(User).findOne({ where: { superOAuthId } }); + return user?.id ?? null; +} + /** * GET /api/playlists * Playlists publiques + playlists partagées avec l'utilisateur connecté. */ router.get("/", requireAuth, async (req: Request, res: Response): Promise => { const { user } = req as AuthenticatedRequest; + const dbUserId = await resolveDbUserId(user.id); + if (!dbUserId) { res.status(401).json({ success: false, error: "USER_NOT_FOUND" }); return; } try { const owned = await AppDataSource.getRepository(Playlist).find({ - where: { ownerId: user.id }, + where: { ownerId: dbUserId }, order: { createdAt: "DESC" }, }); const shared = await AppDataSource.getRepository(PlaylistShare).find({ - where: { userId: user.id, status: "active" }, + where: { userId: dbUserId, status: "active" }, relations: ["playlist"], }); @@ -53,10 +62,13 @@ router.post("/", requireAuth, async (req: Request, res: Response): Promise return; } + const dbUserId = await resolveDbUserId(user.id); + if (!dbUserId) { res.status(401).json({ success: false, error: "USER_NOT_FOUND" }); return; } + try { const playlist = AppDataSource.getRepository(Playlist).create({ id: require("crypto").randomUUID(), - ownerId: user.id, + ownerId: dbUserId, title: title.trim(), description: description ?? null, visibility: visibility ?? "private", @@ -76,6 +88,8 @@ router.post("/", requireAuth, async (req: Request, res: Response): Promise */ router.get("/:id", requireAuth, async (req: Request, res: Response): Promise => { const { user } = req as AuthenticatedRequest; + const dbUserId = await resolveDbUserId(user.id); + if (!dbUserId) { res.status(401).json({ success: false, error: "USER_NOT_FOUND" }); return; } try { const playlist = await AppDataSource.getRepository(Playlist).findOne({ @@ -88,8 +102,8 @@ router.get("/:id", requireAuth, async (req: Request, res: Response): Promise s.userId === user.id && s.status === "active"); + const isOwner = playlist.ownerId === dbUserId; + const share = playlist.shares.find((s) => s.userId === dbUserId && s.status === "active"); const isPublic = playlist.visibility === "public"; if (!isOwner && !share && !isPublic) { @@ -121,6 +135,8 @@ router.get("/:id", requireAuth, async (req: Request, res: Response): Promise => { const { user } = req as AuthenticatedRequest; const { userId, permission } = req.body as { userId?: string; permission?: "view" | "edit" }; + const dbUserId = await resolveDbUserId(user.id); + if (!dbUserId) { res.status(401).json({ success: false, error: "USER_NOT_FOUND" }); return; } try { const playlist = await AppDataSource.getRepository(Playlist).findOneBy({ id: req.params.id }); @@ -130,7 +146,7 @@ router.post("/:id/share", requireAuth, async (req: Request, res: Response): Prom return; } - if (playlist.ownerId !== user.id) { + if (playlist.ownerId !== dbUserId) { res.status(403).json({ success: false, error: "FORBIDDEN" }); return; } @@ -165,11 +181,13 @@ router.patch("/:id/share/:shareId", requireAuth, async (req: Request, res: Respo permission?: "view" | "edit"; status?: "active" | "revoked"; }; + const dbUserId = await resolveDbUserId(user.id); + if (!dbUserId) { res.status(401).json({ success: false, error: "USER_NOT_FOUND" }); return; } try { const playlist = await AppDataSource.getRepository(Playlist).findOneBy({ id: req.params.id }); - if (!playlist || playlist.ownerId !== user.id) { + if (!playlist || playlist.ownerId !== dbUserId) { res.status(403).json({ success: false, error: "FORBIDDEN" }); return; } diff --git a/backend/src/routes/video.routes.ts b/backend/src/routes/video.routes.ts index ce394e7..dea1d67 100644 --- a/backend/src/routes/video.routes.ts +++ b/backend/src/routes/video.routes.ts @@ -1,15 +1,24 @@ import { Router, Request, Response } from "express"; import { AppDataSource } from "../config/data-source"; import { Video } from "../entities/Video"; +import { User } from "../entities/User"; import { requireAuth, AuthenticatedRequest, AuthenticatedUser } from "../middleware/auth.middleware"; import { UserSubscription } from "../entities/UserSubscription"; const router = Router(); +/** Résout le superOAuthId vers l'UUID DB, retourne null si user inconnu */ +async function resolveDbUserId(superOAuthId: string): Promise { + const user = await AppDataSource.getRepository(User).findOne({ where: { superOAuthId } }); + return user?.id ?? null; +} + /** Récupère le niveau de plan actif d'un user (0 = free si aucun abonnement actif) */ -async function getUserPlanLevel(userId: string): Promise { +async function getUserPlanLevel(superOAuthId: string): Promise { + const dbUserId = await resolveDbUserId(superOAuthId); + if (!dbUserId) return 0; const sub = await AppDataSource.getRepository(UserSubscription).findOne({ - where: { userId, status: "active" }, + where: { userId: dbUserId, status: "active" }, relations: ["plan"], order: { startsAt: "DESC" }, });