Skip to content

Commit bc40273

Browse files
Create PublicAthenaResource, remove all corresponding code to it
1 parent 8d76e06 commit bc40273

File tree

2 files changed

+127
-97
lines changed

2 files changed

+127
-97
lines changed

src/main/java/de/tum/cit/aet/artemis/athena/web/AthenaResource.java

Lines changed: 1 addition & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,18 @@
11
package de.tum.cit.aet.artemis.athena.web;
22

33
import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_ATHENA;
4-
import static de.tum.cit.aet.artemis.programming.service.localvc.ssh.HashUtils.hashSha256;
54

6-
import java.io.IOException;
7-
import java.security.MessageDigest;
85
import java.util.List;
96
import java.util.Optional;
107
import java.util.function.Function;
118

129
import org.slf4j.Logger;
1310
import org.slf4j.LoggerFactory;
14-
import org.springframework.beans.factory.annotation.Value;
1511
import org.springframework.context.annotation.Profile;
16-
import org.springframework.core.io.Resource;
1712
import org.springframework.http.HttpStatus;
1813
import org.springframework.http.ResponseEntity;
1914
import org.springframework.web.bind.annotation.GetMapping;
2015
import org.springframework.web.bind.annotation.PathVariable;
21-
import org.springframework.web.bind.annotation.RequestHeader;
2216
import org.springframework.web.bind.annotation.RequestMapping;
2317
import org.springframework.web.bind.annotation.RestController;
2418

@@ -27,25 +21,19 @@
2721
import de.tum.cit.aet.artemis.athena.dto.TextFeedbackDTO;
2822
import de.tum.cit.aet.artemis.athena.service.AthenaFeedbackSuggestionsService;
2923
import de.tum.cit.aet.artemis.athena.service.AthenaModuleService;
30-
import de.tum.cit.aet.artemis.athena.service.AthenaRepositoryExportService;
3124
import de.tum.cit.aet.artemis.core.domain.Course;
32-
import de.tum.cit.aet.artemis.core.exception.AccessForbiddenException;
3325
import de.tum.cit.aet.artemis.core.exception.InternalServerErrorException;
3426
import de.tum.cit.aet.artemis.core.exception.NetworkingException;
3527
import de.tum.cit.aet.artemis.core.repository.CourseRepository;
3628
import de.tum.cit.aet.artemis.core.security.Role;
3729
import de.tum.cit.aet.artemis.core.security.annotations.EnforceAtLeastEditor;
3830
import de.tum.cit.aet.artemis.core.security.annotations.EnforceAtLeastTutor;
39-
import de.tum.cit.aet.artemis.core.security.annotations.EnforceNothing;
40-
import de.tum.cit.aet.artemis.core.security.annotations.ManualConfig;
4131
import de.tum.cit.aet.artemis.core.service.AuthorizationCheckService;
42-
import de.tum.cit.aet.artemis.core.util.ResponseUtil;
4332
import de.tum.cit.aet.artemis.exercise.domain.Exercise;
4433
import de.tum.cit.aet.artemis.exercise.domain.ExerciseType;
4534
import de.tum.cit.aet.artemis.exercise.domain.Submission;
4635
import de.tum.cit.aet.artemis.modeling.repository.ModelingExerciseRepository;
4736
import de.tum.cit.aet.artemis.modeling.repository.ModelingSubmissionRepository;
48-
import de.tum.cit.aet.artemis.programming.domain.RepositoryType;
4937
import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseRepository;
5038
import de.tum.cit.aet.artemis.programming.repository.ProgrammingSubmissionRepository;
5139
import de.tum.cit.aet.artemis.text.api.TextApi;
@@ -81,20 +69,15 @@ public class AthenaResource {
8169

8270
private final AthenaFeedbackSuggestionsService athenaFeedbackSuggestionsService;
8371

84-
private final AthenaRepositoryExportService athenaRepositoryExportService;
85-
8672
private final AthenaModuleService athenaModuleService;
8773

88-
private final byte[] athenaSecretHash;
89-
9074
/**
9175
* The AthenaResource provides an endpoint for the client to fetch feedback suggestions from Athena.
9276
*/
9377
public AthenaResource(CourseRepository courseRepository, Optional<TextRepositoryApi> textRepositoryApi, Optional<TextSubmissionApi> textSubmissionApi,
9478
ProgrammingExerciseRepository programmingExerciseRepository, ProgrammingSubmissionRepository programmingSubmissionRepository,
9579
ModelingExerciseRepository modelingExerciseRepository, ModelingSubmissionRepository modelingSubmissionRepository, AuthorizationCheckService authCheckService,
96-
AthenaFeedbackSuggestionsService athenaFeedbackSuggestionsService, AthenaRepositoryExportService athenaRepositoryExportService, AthenaModuleService athenaModuleService,
97-
@Value("${artemis.athena.secret}") String athenaSecret) {
80+
AthenaFeedbackSuggestionsService athenaFeedbackSuggestionsService, AthenaModuleService athenaModuleService) {
9881
this.courseRepository = courseRepository;
9982
this.textRepositoryApi = textRepositoryApi;
10083
this.textSubmissionApi = textSubmissionApi;
@@ -104,9 +87,7 @@ public AthenaResource(CourseRepository courseRepository, Optional<TextRepository
10487
this.modelingSubmissionRepository = modelingSubmissionRepository;
10588
this.authCheckService = authCheckService;
10689
this.athenaFeedbackSuggestionsService = athenaFeedbackSuggestionsService;
107-
this.athenaRepositoryExportService = athenaRepositoryExportService;
10890
this.athenaModuleService = athenaModuleService;
109-
this.athenaSecretHash = hashSha256(athenaSecret);
11091
}
11192

11293
@FunctionalInterface
@@ -237,81 +218,4 @@ public ResponseEntity<List<String>> getAvailableModulesForProgrammingExercises(@
237218
public ResponseEntity<List<String>> getAvailableModulesForModelingExercises(@PathVariable long courseId) {
238219
return this.getAvailableModules(courseId, ExerciseType.MODELING);
239220
}
240-
241-
/**
242-
* Check if the given auth header is valid for Athena, otherwise throw an exception.
243-
*
244-
* @param incomingSecret the auth header value to check
245-
*/
246-
private void checkAthenaSecret(String incomingSecret) {
247-
if (!MessageDigest.isEqual(athenaSecretHash, hashSha256(incomingSecret))) {
248-
log.error("Athena secret does not match");
249-
throw new AccessForbiddenException("Athena secret does not match");
250-
}
251-
}
252-
253-
/**
254-
* GET public/programming-exercises/:exerciseId/submissions/:submissionId/repository : Get the repository as a zip file download
255-
*
256-
* @param exerciseId the id of the exercise the submission belongs to
257-
* @param submissionId the id of the submission to get the repository for
258-
* @param auth the auth header value to check
259-
* @return 200 Ok with the zip file as body if successful
260-
*/
261-
@GetMapping("public/programming-exercises/{exerciseId}/submissions/{submissionId}/repository")
262-
@EnforceNothing // We check the Athena secret instead
263-
@ManualConfig
264-
public ResponseEntity<Resource> getRepository(@PathVariable long exerciseId, @PathVariable long submissionId, @RequestHeader("Authorization") String auth) throws IOException {
265-
log.debug("REST call to get student repository for exercise {}, submission {}", exerciseId, submissionId);
266-
checkAthenaSecret(auth);
267-
return ResponseUtil.ok(athenaRepositoryExportService.exportRepository(exerciseId, submissionId, null));
268-
}
269-
270-
/**
271-
* GET public/programming-exercises/:exerciseId/repository/template : Get the template repository as a zip file download
272-
*
273-
* @param exerciseId the id of the exercise
274-
* @param auth the auth header value to check
275-
* @return 200 Ok with the zip file as body if successful
276-
*/
277-
@GetMapping("public/programming-exercises/{exerciseId}/repository/template")
278-
@EnforceNothing // We check the Athena secret instead
279-
@ManualConfig
280-
public ResponseEntity<Resource> getTemplateRepository(@PathVariable long exerciseId, @RequestHeader("Authorization") String auth) throws IOException {
281-
log.debug("REST call to get template repository for exercise {}", exerciseId);
282-
checkAthenaSecret(auth);
283-
return ResponseUtil.ok(athenaRepositoryExportService.exportRepository(exerciseId, null, RepositoryType.TEMPLATE));
284-
}
285-
286-
/**
287-
* GET public/programming-exercises/:exerciseId/repository/solution : Get the solution repository as a zip file download
288-
*
289-
* @param exerciseId the id of the exercise
290-
* @param auth the auth header value to check
291-
* @return 200 Ok with the zip file as body if successful
292-
*/
293-
@GetMapping("public/programming-exercises/{exerciseId}/repository/solution")
294-
@EnforceNothing // We check the Athena secret instead
295-
@ManualConfig
296-
public ResponseEntity<Resource> getSolutionRepository(@PathVariable long exerciseId, @RequestHeader("Authorization") String auth) throws IOException {
297-
log.debug("REST call to get solution repository for exercise {}", exerciseId);
298-
checkAthenaSecret(auth);
299-
return ResponseUtil.ok(athenaRepositoryExportService.exportRepository(exerciseId, null, RepositoryType.SOLUTION));
300-
}
301-
302-
/**
303-
* GET public/programming-exercises/:exerciseId/repository/tests : Get the test repository as a zip file download
304-
*
305-
* @param exerciseId the id of the exercise
306-
* @param auth the auth header value to check
307-
* @return 200 Ok with the zip file as body if successful
308-
*/
309-
@GetMapping("public/programming-exercises/{exerciseId}/repository/tests")
310-
@EnforceNothing // We check the Athena secret instead
311-
@ManualConfig
312-
public ResponseEntity<Resource> getTestRepository(@PathVariable long exerciseId, @RequestHeader("Authorization") String auth) throws IOException {
313-
log.debug("REST call to get test repository for exercise {}", exerciseId);
314-
checkAthenaSecret(auth);
315-
return ResponseUtil.ok(athenaRepositoryExportService.exportRepository(exerciseId, null, RepositoryType.TESTS));
316-
}
317221
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
package de.tum.cit.aet.artemis.athena.web.open;
2+
3+
import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_ATHENA;
4+
import static de.tum.cit.aet.artemis.programming.service.localvc.ssh.HashUtils.hashSha256;
5+
6+
import java.io.IOException;
7+
import java.security.MessageDigest;
8+
9+
import org.slf4j.Logger;
10+
import org.slf4j.LoggerFactory;
11+
import org.springframework.beans.factory.annotation.Value;
12+
import org.springframework.context.annotation.Profile;
13+
import org.springframework.core.io.Resource;
14+
import org.springframework.http.ResponseEntity;
15+
import org.springframework.web.bind.annotation.GetMapping;
16+
import org.springframework.web.bind.annotation.PathVariable;
17+
import org.springframework.web.bind.annotation.RequestHeader;
18+
import org.springframework.web.bind.annotation.RequestMapping;
19+
import org.springframework.web.bind.annotation.RestController;
20+
21+
import de.tum.cit.aet.artemis.athena.service.AthenaRepositoryExportService;
22+
import de.tum.cit.aet.artemis.core.exception.AccessForbiddenException;
23+
import de.tum.cit.aet.artemis.core.security.annotations.EnforceNothing;
24+
import de.tum.cit.aet.artemis.core.security.annotations.ManualConfig;
25+
import de.tum.cit.aet.artemis.core.util.ResponseUtil;
26+
import de.tum.cit.aet.artemis.programming.domain.RepositoryType;
27+
28+
/**
29+
* REST controller for providing Athena access to the repositories of programming exercises.
30+
*/
31+
@Profile(PROFILE_ATHENA)
32+
@RestController
33+
@RequestMapping("api/athena/public/")
34+
public class PublicAthenaResource {
35+
36+
private static final Logger log = LoggerFactory.getLogger(PublicAthenaResource.class);
37+
38+
private final AthenaRepositoryExportService athenaRepositoryExportService;
39+
40+
private final byte[] athenaSecretHash;
41+
42+
/**
43+
* The PublicAthenaResource provides endpoints for Athena to get the repositories from Artemis.
44+
*/
45+
public PublicAthenaResource(AthenaRepositoryExportService athenaRepositoryExportService, @Value("${artemis.athena.secret}") String athenaSecret) {
46+
this.athenaRepositoryExportService = athenaRepositoryExportService;
47+
this.athenaSecretHash = hashSha256(athenaSecret);
48+
}
49+
50+
/**
51+
* Check if the given auth header is valid for Athena, otherwise throw an exception.
52+
*
53+
* @param incomingSecret the auth header value to check
54+
*/
55+
private void checkAthenaSecret(String incomingSecret) {
56+
if (!MessageDigest.isEqual(athenaSecretHash, hashSha256(incomingSecret))) {
57+
log.error("Athena secret does not match");
58+
throw new AccessForbiddenException("Athena secret does not match");
59+
}
60+
}
61+
62+
/**
63+
* GET public/programming-exercises/:exerciseId/submissions/:submissionId/repository : Get the repository as a zip file download
64+
*
65+
* @param exerciseId the id of the exercise the submission belongs to
66+
* @param submissionId the id of the submission to get the repository for
67+
* @param auth the auth header value to check
68+
* @return 200 Ok with the zip file as body if successful
69+
*/
70+
@GetMapping("programming-exercises/{exerciseId}/submissions/{submissionId}/repository")
71+
@EnforceNothing // We check the Athena secret instead
72+
@ManualConfig
73+
public ResponseEntity<Resource> getRepository(@PathVariable long exerciseId, @PathVariable long submissionId, @RequestHeader("Authorization") String auth) throws IOException {
74+
log.debug("REST call to get student repository for exercise {}, submission {}", exerciseId, submissionId);
75+
checkAthenaSecret(auth);
76+
return ResponseUtil.ok(athenaRepositoryExportService.exportRepository(exerciseId, submissionId, null));
77+
}
78+
79+
/**
80+
* GET public/programming-exercises/:exerciseId/repository/template : Get the template repository as a zip file download
81+
*
82+
* @param exerciseId the id of the exercise
83+
* @param auth the auth header value to check
84+
* @return 200 Ok with the zip file as body if successful
85+
*/
86+
@GetMapping("programming-exercises/{exerciseId}/repository/template")
87+
@EnforceNothing // We check the Athena secret instead
88+
@ManualConfig
89+
public ResponseEntity<Resource> getTemplateRepository(@PathVariable long exerciseId, @RequestHeader("Authorization") String auth) throws IOException {
90+
log.debug("REST call to get template repository for exercise {}", exerciseId);
91+
checkAthenaSecret(auth);
92+
return ResponseUtil.ok(athenaRepositoryExportService.exportRepository(exerciseId, null, RepositoryType.TEMPLATE));
93+
}
94+
95+
/**
96+
* GET public/programming-exercises/:exerciseId/repository/solution : Get the solution repository as a zip file download
97+
*
98+
* @param exerciseId the id of the exercise
99+
* @param auth the auth header value to check
100+
* @return 200 Ok with the zip file as body if successful
101+
*/
102+
@GetMapping("programming-exercises/{exerciseId}/repository/solution")
103+
@EnforceNothing // We check the Athena secret instead
104+
@ManualConfig
105+
public ResponseEntity<Resource> getSolutionRepository(@PathVariable long exerciseId, @RequestHeader("Authorization") String auth) throws IOException {
106+
log.debug("REST call to get solution repository for exercise {}", exerciseId);
107+
checkAthenaSecret(auth);
108+
return ResponseUtil.ok(athenaRepositoryExportService.exportRepository(exerciseId, null, RepositoryType.SOLUTION));
109+
}
110+
111+
/**
112+
* GET public/programming-exercises/:exerciseId/repository/tests : Get the test repository as a zip file download
113+
*
114+
* @param exerciseId the id of the exercise
115+
* @param auth the auth header value to check
116+
* @return 200 Ok with the zip file as body if successful
117+
*/
118+
@GetMapping("programming-exercises/{exerciseId}/repository/tests")
119+
@EnforceNothing // We check the Athena secret instead
120+
@ManualConfig
121+
public ResponseEntity<Resource> getTestRepository(@PathVariable long exerciseId, @RequestHeader("Authorization") String auth) throws IOException {
122+
log.debug("REST call to get test repository for exercise {}", exerciseId);
123+
checkAthenaSecret(auth);
124+
return ResponseUtil.ok(athenaRepositoryExportService.exportRepository(exerciseId, null, RepositoryType.TESTS));
125+
}
126+
}

0 commit comments

Comments
 (0)