66
77namespace Chamilo \CoreBundle \Controller ;
88
9+ use Chamilo \CoreBundle \Entity \Course as CourseEntity ;
910use Chamilo \CoreBundle \Repository \Node \UserRepository ;
11+ use Chamilo \CoreBundle \Security \Authorization \Voter \CourseVoter ;
1012use Chamilo \CourseBundle \Component \CourseCopy \CommonCartridge \Builder \Cc13Capabilities ;
1113use Chamilo \CourseBundle \Component \CourseCopy \CommonCartridge \Builder \Cc13Export ;
1214use Chamilo \CourseBundle \Component \CourseCopy \CommonCartridge \Import \Imscc13Import ;
2426use Symfony \Component \HttpFoundation \JsonResponse ;
2527use Symfony \Component \HttpFoundation \Request ;
2628use Symfony \Component \HttpFoundation \ResponseHeaderBag ;
29+ use Symfony \Component \HttpKernel \Exception \NotFoundHttpException ;
2730use Symfony \Component \Routing \Attribute \Route ;
2831use Symfony \Component \Routing \Generator \UrlGeneratorInterface ;
2932use Symfony \Component \Security \Http \Attribute \IsGranted ;
@@ -369,7 +372,7 @@ public function copyOptions(int $node, Request $req): JsonResponse
369372 }
370373
371374 #[Route('/copy/resources ' , name: 'copy_resources ' , methods: ['GET ' ])]
372- public function copyResources (int $ node , Request $ req ): JsonResponse
375+ public function copyResources (int $ node , Request $ req, EntityManagerInterface $ em ): JsonResponse
373376 {
374377 $ this ->setDebugFromRequest ($ req );
375378
@@ -378,6 +381,8 @@ public function copyResources(int $node, Request $req): JsonResponse
378381 return $ this ->jsonError ('Missing sourceCourseId ' , 400 );
379382 }
380383
384+ $ this ->assertSourceCourseAccess ($ sourceCourseCode , $ em );
385+
381386 $ cb = new CourseBuilder ();
382387 $ cb ->set_tools_to_build (self ::LEGACY_SNAPSHOT_TOOLS );
383388
@@ -391,22 +396,24 @@ public function copyResources(int $node, Request $req): JsonResponse
391396 }
392397
393398 #[Route('/copy/execute ' , name: 'copy_execute ' , methods: ['POST ' ])]
394- public function copyExecute (int $ node , Request $ req ): JsonResponse
399+ public function copyExecute (int $ node , Request $ req, EntityManagerInterface $ em ): JsonResponse
395400 {
396401 $ this ->setDebugFromRequest ($ req );
397402
398- try {
399- $ payload = $ this ->getJsonPayload ($ req );
403+ $ payload = $ this ->getJsonPayload ($ req );
400404
401- $ sourceCourseId = (string ) ($ payload ['sourceCourseId ' ] ?? '' );
405+ $ sourceCourseId = (string ) ($ payload ['sourceCourseId ' ] ?? '' );
406+ if ('' === $ sourceCourseId ) {
407+ return $ this ->jsonError ('Missing sourceCourseId ' , 400 );
408+ }
409+
410+ $ this ->assertSourceCourseAccess ($ sourceCourseId , $ em );
411+
412+ try {
402413 $ copyOption = (string ) ($ payload ['copyOption ' ] ?? 'full_copy ' ); // full_copy | select_items
403414 $ sameFileNameOption = (int ) ($ payload ['sameFileNameOption ' ] ?? 2 );
404415 $ selectedResourcesMap = (array ) ($ payload ['resources ' ] ?? []);
405416
406- if ('' === $ sourceCourseId ) {
407- return $ this ->jsonError ('Missing sourceCourseId ' , 400 );
408- }
409-
410417 $ cb = new CourseBuilder ('partial ' );
411418 $ cb ->set_tools_to_build (self ::LEGACY_SNAPSHOT_TOOLS );
412419
@@ -1131,6 +1138,22 @@ public function importDiagnose(int $node, string $backupId, Request $req): JsonR
11311138 }
11321139 }
11331140
1141+ /**
1142+ * Resolve the source course by its code and ensure the current user is
1143+ * authorized to view (and therefore copy from) it. The request listener
1144+ * only gates the destination course via cid; the user-supplied source
1145+ * course must be authorized explicitly to prevent cross-course access.
1146+ */
1147+ private function assertSourceCourseAccess (string $ courseCode , EntityManagerInterface $ em ): void
1148+ {
1149+ $ course = $ em ->getRepository (CourseEntity::class)->findOneBy (['code ' => $ courseCode ]);
1150+ if (!$ course instanceof CourseEntity) {
1151+ throw new NotFoundHttpException ('Source course not found ' );
1152+ }
1153+
1154+ $ this ->denyAccessUnlessGranted (CourseVoter::VIEW , $ course );
1155+ }
1156+
11341157 /**
11351158 * Map UI options (1/2/3) to legacy file policy.
11361159 */
0 commit comments