88import de .bogenliga .application .business .wettkampf .api .types .WettkampfDO ;
99import de .bogenliga .application .business .wettkampf .api .WettkampfComponent ;
1010import de .bogenliga .application .springconfiguration .security .permissions .RequiresPermission ;
11+ import de .bogenliga .application .springconfiguration .security .permissions .RequiresOnePermissions ;
1112import de .bogenliga .application .springconfiguration .security .types .UserPermission ;
1213import org .slf4j .Logger ;
1314import org .slf4j .LoggerFactory ;
1819import org .springframework .http .ResponseEntity ;
1920import org .springframework .web .bind .annotation .*;
2021import de .bogenliga .application .business .bogenkontrollliste .api .BogenkontrolllisteComponent ;
22+ import de .bogenliga .application .business .dsbmannschaft .api .DsbMannschaftComponent ;
2123import de .bogenliga .application .business .meldezettel .api .MeldezettelComponent ;
2224import de .bogenliga .application .business .schusszettel .api .SchusszettelComponent ;
2325import de .bogenliga .application .business .setzliste .api .SetzlisteComponent ;
2426import de .bogenliga .application .business .lizenz .api .LizenzComponent ;
2527import de .bogenliga .application .business .rueckennummern .api .RueckennummernComponent ;
2628import de .bogenliga .application .common .errorhandling .ErrorCode ;
29+ import de .bogenliga .application .common .errorhandling .exception .BusinessException ;
2730import de .bogenliga .application .common .errorhandling .exception .TechnicalException ;
2831import de .bogenliga .application .common .service .ServiceFacade ;
2932import de .bogenliga .application .common .validation .Preconditions ;
3033import de .bogenliga .application .services .v1 .setzliste .service .SetzlisteService ;
34+ import de .bogenliga .application .springconfiguration .security .permissions .RequiresOnePermissionAspect ;
3135
3236
3337/**
@@ -65,6 +69,8 @@ public class DownloadService implements ServiceFacade {
6569 private final BogenkontrolllisteComponent bogenkontrolllisteComponent ;
6670 private final RueckennummernComponent rueckennummernComponent ;
6771 private final WettkampfComponent wettkampfComponent ;
72+ private final DsbMannschaftComponent dsbMannschaftComponent ;
73+ private final RequiresOnePermissionAspect requiresOnePermissionAspect ;
6874
6975
7076 /**
@@ -76,16 +82,20 @@ public DownloadService(final SetzlisteComponent setzlisteComponent, final Lizenz
7682 final MeldezettelComponent meldezettelComponent ,
7783 final BogenkontrolllisteComponent bogenkontrolllisteComponent ,
7884 final RueckennummernComponent rueckennummernComponent ,
79- final WettkampfComponent wettkampfComponent ) {
85+ final WettkampfComponent wettkampfComponent ,
86+ final DsbMannschaftComponent dsbMannschaftComponent ,
87+ final RequiresOnePermissionAspect requiresOnePermissionAspect ) {
8088 this .lizenzComponent = lizenzComponent ;
8189 this .setzlisteComponent = setzlisteComponent ;
8290 this .schusszettelComponent = schusszettelComponent ;
8391 this .meldezettelComponent = meldezettelComponent ;
8492 this .bogenkontrolllisteComponent = bogenkontrolllisteComponent ;
8593 this .rueckennummernComponent = rueckennummernComponent ;
8694 this .wettkampfComponent = wettkampfComponent ;
95+ this .dsbMannschaftComponent = dsbMannschaftComponent ;
96+ this .requiresOnePermissionAspect = requiresOnePermissionAspect ;
8797 }
88-
98+
8999 /**
90100 * returns the Setzliste as pdf file for client download
91101 * <p>
@@ -231,8 +241,8 @@ ResponseEntity<InputStreamResource> downloadbogenkontrolllistePdf(@RequestParam(
231241 */
232242 @ CrossOrigin (maxAge = 0 )
233243 @ GetMapping (
234- path = "pdf/rueckennummern" ,
235- produces = MediaType .APPLICATION_PDF_VALUE )
244+ path = "pdf/rueckennummern" ,
245+ produces = MediaType .APPLICATION_PDF_VALUE )
236246 @ RequiresPermission (UserPermission .CAN_READ_DEFAULT )
237247 public
238248 ResponseEntity <InputStreamResource > downloadRueckennummernPdf (@ RequestParam ("mannschaftid" ) final long mannschaftid ) {
@@ -291,11 +301,32 @@ private ResponseEntity<InputStreamResource> generateInputStream(byte[] fileBloB)
291301 @ GetMapping (
292302 path = "pdf/schuetzenlizenz/{dsbMitgliedId}/{teamId}" ,
293303 produces = MediaType .APPLICATION_PDF_VALUE )
294- @ RequiresPermission ( UserPermission .CAN_READ_WETTKAMPF )
304+ @ RequiresOnePermissions ( perm = { UserPermission .CAN_MODIFY_MY_VEREIN , UserPermission . CAN_MODIFY_MY_VERANSTALTUNG } )
295305 public ResponseEntity <InputStreamResource > downloadLizenz (@ PathVariable ("dsbMitgliedId" ) final long dsbMitgliedID ,
296- @ PathVariable ("teamId" ) final long teamID ) {
306+ @ PathVariable ("teamId" ) final long teamID ) {
297307 LOG .debug ("dsbMitgliedID: {}" , dsbMitgliedID );
298308 LOG .debug ("teamID: {}" , teamID );
309+
310+ // Data-specific authorization check
311+ var mannschaft = dsbMannschaftComponent .findById (teamID );
312+ if (mannschaft == null ) {
313+ throw new BusinessException (ErrorCode .NO_PERMISSION_ERROR ,
314+ "Team not found" );
315+ }
316+
317+ // Check if user has permission to modify their own club (Sportleiter)
318+ boolean isSportleiter = requiresOnePermissionAspect .hasSpecificPermissionSportleiter (
319+ UserPermission .CAN_MODIFY_MY_VEREIN , mannschaft .getVereinId ());
320+
321+ // Check if user has permission to modify event (Ligaleiter)
322+ boolean isLigaleiter = requiresOnePermissionAspect .hasSpecificPermissionLigaLeiterID (
323+ UserPermission .CAN_MODIFY_MY_VERANSTALTUNG , mannschaft .getVeranstaltungId ());
324+
325+ if (!isSportleiter && !isLigaleiter ) {
326+ throw new BusinessException (ErrorCode .NO_PERMISSION_ERROR ,
327+ String .format ("User does not have permission to download license for team %d" , teamID ));
328+ }
329+
299330 final byte [] fileBloB = lizenzComponent .getLizenzPDFasByteArray (dsbMitgliedID , teamID );
300331
301332 return generateInputStream (fileBloB );
@@ -314,10 +345,29 @@ public ResponseEntity<InputStreamResource> downloadLizenz(@PathVariable("dsbMitg
314345 @ GetMapping (
315346 path = "pdf/lizenzen" ,
316347 produces = MediaType .APPLICATION_PDF_VALUE )
317- @ RequiresPermission ( UserPermission .CAN_READ_DEFAULT )
348+ @ RequiresOnePermissions ( perm = { UserPermission .CAN_MODIFY_MY_VEREIN , UserPermission . CAN_MODIFY_MY_VERANSTALTUNG } )
318349 public
319350 ResponseEntity <InputStreamResource > downloadLizenzenPdf (@ RequestParam ("mannschaftid" ) final long mannschaftid ) {
320351
352+ // Data-specific authorization check
353+ var mannschaft = dsbMannschaftComponent .findById (mannschaftid );
354+ if (mannschaft == null ) {
355+ throw new BusinessException (ErrorCode .NO_PERMISSION_ERROR ,
356+ "Team not found" );
357+ }
358+
359+ // Check if user has permission to modify their own club (Sportleiter)
360+ boolean isSportleiter = requiresOnePermissionAspect .hasSpecificPermissionSportleiter (
361+ UserPermission .CAN_MODIFY_MY_VEREIN , mannschaft .getVereinId ());
362+
363+ // Check if user has permission to modify event (Ligaleiter)
364+ boolean isLigaleiter = requiresOnePermissionAspect .hasSpecificPermissionLigaLeiterID (
365+ UserPermission .CAN_MODIFY_MY_VERANSTALTUNG , mannschaft .getVeranstaltungId ());
366+
367+ if (!isSportleiter && !isLigaleiter ) {
368+ throw new BusinessException (ErrorCode .NO_PERMISSION_ERROR ,
369+ String .format ("User does not have permission to download licenses for team %d" , mannschaftid ));
370+ }
321371
322372 final byte [] fileBloB = lizenzComponent .getMannschaftsLizenzenPDFasByteArray (mannschaftid );
323373
@@ -334,16 +384,16 @@ ResponseEntity<InputStreamResource> downloadLizenzenPdf(@RequestParam("mannschaf
334384 * <pre>{@code Request: GET /v1/download/pdf/Einzelstatistik/?werte=x}</pre>
335385 *
336386 * @return pdf as InputStreamRessource
337- */
387+ */
338388 @ CrossOrigin (maxAge = 0 )
339389 @ GetMapping (
340390 path = "pdf/Einzelstatistik" ,
341391 produces = MediaType .APPLICATION_PDF_VALUE )
342392 @ RequiresPermission (UserPermission .CAN_READ_DEFAULT )
343393 public
344394 ResponseEntity <InputStreamResource > downloadEinzelstatistikPdf (@ RequestParam ("veranstaltungsid" ) final long veranstaltungsid ,
345- @ RequestParam ("manschaftsid" ) final long manschaftsid ,
346- @ RequestParam ("jahr" ) final int jahr )
395+ @ RequestParam ("manschaftsid" ) final long manschaftsid ,
396+ @ RequestParam ("jahr" ) final int jahr )
347397 {
348398
349399 final byte [] fileBloB = wettkampfComponent .getPDFasByteArray ("Einzelstatistik" ,veranstaltungsid ,manschaftsid ,jahr );
0 commit comments