Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Programming exercises: Provide theia clone information on redirect #9379

Open
wants to merge 72 commits into
base: feature/re-key
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
a0497f3
Move Theia Button to Code Button
iyannsch Sep 23, 2024
2ab444e
Add functionality to pull build config on demand from server
iyannsch Sep 24, 2024
389f5fd
Merge branch 'develop' into feature/programming-exercises/provide-the…
iyannsch Sep 28, 2024
7ce3a49
Add Uri, Token, and landing-page URL to Theia Button
iyannsch Sep 28, 2024
4160302
Add Form test and re-key endpoint
iyannsch Oct 3, 2024
eae2c89
Clean up initTheia method
iyannsch Oct 3, 2024
0f18104
Fix formatting
iyannsch Oct 3, 2024
486b2c5
Fix implicit type expressions
iyannsch Oct 4, 2024
d9bf634
Fix rekey endpoint to be reachable
iyannsch Oct 4, 2024
a643b42
Merge upstream key endpoint
iyannsch Oct 4, 2024
34a84d7
Merge branch 'feature/re-key' into feature/programming-exercises/prov…
iyannsch Oct 4, 2024
4f1a99b
Fix endpoint theia-token connection
iyannsch Oct 4, 2024
4aa5171
Merge branch 'develop' into feature/programming-exercises/provide-the…
iyannsch Oct 8, 2024
7abb02c
Merge branch 'feature/re-key' into feature/programming-exercises/prov…
iyannsch Oct 8, 2024
b7ebea0
Merge upstream bearer branch
iyannsch Oct 8, 2024
685a483
Get Token unconditionally
iyannsch Oct 8, 2024
e8be59c
Merge branch 'develop' into feature/programming-exercises/provide-the…
iyannsch Oct 29, 2024
575379b
Fix failing correct data submission test
iyannsch Oct 29, 2024
2d2a5b7
Remove minus from appDef to align to TS configuration
iyannsch Oct 29, 2024
6207ce8
Fix configuration values to align with new system
iyannsch Nov 7, 2024
a6cc06e
Merge branch 'develop' into feature/programming-exercises/provide-the…
iyannsch Nov 7, 2024
f82d6fb
Merge branch 'feature/re-key' into feature/programming-exercises/prov…
iyannsch Nov 7, 2024
beb3d48
Merge branch 'feature/re-key' of github.com:ls1intum/Artemis into fea…
iyannsch Nov 7, 2024
625216e
Rename endpoint to theia-token
iyannsch Nov 7, 2024
b42126a
Quiz exercises: Disable practice mode for imported exercises (#9683)
Hialus Nov 7, 2024
63f9cca
Programming exercises: Add C# programming language template (#9626)
magaupp Nov 7, 2024
a8ec277
Communication: Add additional input formatting options (#9657)
asliayk Nov 7, 2024
a140b11
Communication: Fix element height in announcement channel (#9664)
PaRangger Nov 7, 2024
d3357f8
Development: Use signals in lecture online and text unit (#9658)
florian-glombik Nov 7, 2024
4d34faa
Development: Use signals in lecture add attachment form (#9656)
florian-glombik Nov 7, 2024
9ad7a35
Programming exercises: Upgrade Haskell container image (#9687)
b-fein Nov 7, 2024
d9a1f91
Development: Update the editor-related screenshots in the documentati…
pzdr7 Nov 7, 2024
3bff129
Programming exercises: Decrease space between lines in the code edito…
chrisknedl Nov 7, 2024
25146ba
Development: Fix client test coverage (#9703)
florian-glombik Nov 8, 2024
bfebf27
Development: Use signals in video unit form component (#9692)
florian-glombik Nov 8, 2024
7a18571
Communication: Group consecutive messages (#9456)
asliayk Nov 8, 2024
e64adb2
Development: Update client dependencies
krusche Nov 9, 2024
c9f0c0d
Development: Update server dependencies
krusche Nov 9, 2024
0d01a40
Development: Use signals in date time picker (#9694)
florian-glombik Nov 9, 2024
64f95cd
Development: Fix course messages e2e tests (#9720)
asliayk Nov 9, 2024
6e37a8f
Communication: Remeber last scroll position when switching conversati…
cremertim Nov 9, 2024
bf2fbbd
Lectures: Fix lecture unit file attachment names (#9721)
SimonEntholzer Nov 10, 2024
cec3866
Communication: Add profile picture to sidebar element and conversatio…
asliayk Nov 10, 2024
afa8543
Programming exercises: Fix code button showing HTTPS link when it sho…
SimonEntholzer Nov 10, 2024
e89f035
Programming exercises: Add information box to exercise details page (…
rabeatwork Nov 10, 2024
395e8df
Programming exercises: Add error categories and categorize feedback i…
az108 Nov 10, 2024
0cc6620
General: Add cleanup service for admins (#9296)
coolchock Nov 10, 2024
5a3ae27
Development: Improve spotless config for pre commit hook (#9600)
krusche Nov 10, 2024
43446ae
Adaptive learning: Fix bulk creation of competencies (#9682)
JohannesStoehr Nov 10, 2024
f75b379
Adaptive learning: Fix import all competencies with relations (#9713)
MaximilianAnzinger Nov 10, 2024
36a182b
Lectures: Add editing functionality to attachment PDF preview (#9265)
eceeeren Nov 10, 2024
110d047
Development: Convert DTOs to records (#9385)
krusche Nov 10, 2024
b932844
Development: Fix issues with admin cleanup service and improve logging
krusche Nov 10, 2024
d5e3b70
Development: Fix typo in jest.config.js
krusche Nov 11, 2024
de36813
Development: Bump version to 7.7.1
krusche Nov 11, 2024
8205b58
Adaptive learning: Visualize competencies linked to exercises correct…
MaximilianAnzinger Nov 12, 2024
7876792
Iris: Fix an error related to Iris settings when tutors navigate to c…
alexjoham Nov 12, 2024
2946389
Merge branch 'feature/re-key' of github.com:ls1intum/Artemis into fea…
iyannsch Nov 12, 2024
c7c5322
Rename endpoint to tool-token
iyannsch Nov 12, 2024
9b0bcc3
Add ArtemisURL to theia query params
iyannsch Nov 12, 2024
c907b0a
Integrated code lifecycle: Add auxiliary repositories in exercise exp…
SimonEntholzer Nov 12, 2024
65bc890
Programming exercises: Fix a performance issue with build log statistics
krusche Nov 12, 2024
749b301
Programming exercise: Speed up version control access log queries wit…
krusche Nov 12, 2024
f2d7aaa
Adaptive learning: Fix an issue with competency progress calculation
krusche Nov 12, 2024
f485527
Lectures: Fix an issue when saving lecture units in guided mode witho…
florian-glombik Nov 12, 2024
1b50466
Adaptive learning: Fix linking attachment units to competencies (#9739)
JohannesStoehr Nov 12, 2024
70ccfc3
Development: Fix server tests related to build log statistics
krusche Nov 13, 2024
054476c
Development: Improve slow query performance (#9727)
krusche Nov 13, 2024
d3817c8
Add gitUser and gitMail to Theia LP
iyannsch Nov 13, 2024
383945a
Development: Refactor competencies management page to signals (#9629)
JohannesWt Nov 13, 2024
ac87b07
Remove debug for artemisUrl
iyannsch Nov 13, 2024
10648b0
Merge branch 'develop' of github.com:ls1intum/Artemis into feature/pr…
iyannsch Nov 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import java.util.regex.Pattern;

import jakarta.annotation.Nullable;
import jakarta.servlet.http.Cookie;
import jakarta.validation.constraints.NotNull;

import org.slf4j.Logger;
Expand Down Expand Up @@ -52,7 +51,6 @@
import org.springframework.web.socket.server.HandshakeInterceptor;
import org.springframework.web.socket.server.support.DefaultHandshakeHandler;
import org.springframework.web.socket.sockjs.transport.handler.WebSocketTransportHandler;
import org.springframework.web.util.WebUtils;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Iterators;
Expand Down Expand Up @@ -202,8 +200,7 @@ public boolean beforeHandshake(@NotNull ServerHttpRequest request, @NotNull Serv
@NotNull Map<String, Object> attributes) {
if (request instanceof ServletServerHttpRequest servletRequest) {
attributes.put(IP_ADDRESS, servletRequest.getRemoteAddress());
Cookie jwtCookie = WebUtils.getCookie(servletRequest.getServletRequest(), JWTFilter.JWT_COOKIE_NAME);
return JWTFilter.isJwtCookieValid(tokenProvider, jwtCookie);
return JWTFilter.extractValidJwt(servletRequest.getServletRequest(), tokenProvider) != null;
}
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,17 @@ public ResponseCookie buildLoginCookie(boolean rememberMe) {
return buildJWTCookie(jwt, duration);
}

/**
* Builds the cookie with Theia flag
*
* @param duration the duration of the cookie and the jwt
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @param duration the duration of the cookie and the jwt
* @param duration the duration of the cookie and the jwt in milliseconds

* @return the login ResponseCookie containing the JWT
*/
public ResponseCookie buildTheiaCookie(long duration) {
String jwt = tokenProvider.createToken(SecurityContextHolder.getContext().getAuthentication(), duration, "THEIA");
return buildJWTCookie(jwt, Duration.of(duration, ChronoUnit.MILLIS));
}
iyannsch marked this conversation as resolved.
Show resolved Hide resolved

/**
* Builds the cookie containing the jwt for a logout and sets it in the response
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.io.IOException;

import jakarta.annotation.Nullable;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
Expand Down Expand Up @@ -31,26 +32,70 @@ public JWTFilter(TokenProvider tokenProvider) {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
Cookie jwtCookie = WebUtils.getCookie(httpServletRequest, JWT_COOKIE_NAME);
if (isJwtCookieValid(this.tokenProvider, jwtCookie)) {
Authentication authentication = this.tokenProvider.getAuthentication(jwtCookie.getValue());

String jwtToken = extractValidJwt(httpServletRequest, this.tokenProvider);
if (jwtToken != null) {
Authentication authentication = this.tokenProvider.getAuthentication(jwtToken);
SecurityContextHolder.getContext().setAuthentication(authentication);
}

filterChain.doFilter(servletRequest, servletResponse);
}

/**
* Checks if the cookie containing the jwt is valid
* Extracts the first valid jwt found in the cookie or the Authorization header
*
* @param tokenProvider the artemis token provider used to generate and validate jwt's
* @param jwtCookie the cookie containing the jwt
* @return true if the jwt is valid, false if missing or invalid
* @param httpServletRequest the http request
* @param tokenProvider the Artemis token provider used to generate and validate jwt's
* @return the valid jwt or null if not found or invalid
*/
public static @Nullable String extractValidJwt(HttpServletRequest httpServletRequest, TokenProvider tokenProvider) {
String jwtToken = getJwtFromCookie(WebUtils.getCookie(httpServletRequest, JWT_COOKIE_NAME));
if (isJwtValid(tokenProvider, jwtToken)) {
return jwtToken;
}
jwtToken = getJwtFromBearer(httpServletRequest.getHeader("Authorization"));
if (isJwtValid(tokenProvider, jwtToken)) {
return jwtToken;
}
return null;
}
iyannsch marked this conversation as resolved.
Show resolved Hide resolved

/**
* Extracts the jwt from the cookie
*
* @param jwtCookie the cookie with Key "jwt"
* @return the jwt or null if not found
*/
public static boolean isJwtCookieValid(TokenProvider tokenProvider, Cookie jwtCookie) {
private static @Nullable String getJwtFromCookie(Cookie jwtCookie) {
if (jwtCookie == null) {
return false;
return null;
}
String jwt = jwtCookie.getValue();
return StringUtils.hasText(jwt) && tokenProvider.validateTokenForAuthority(jwt);
return jwtCookie.getValue();
}

/**
* Extracts the jwt from the Authorization header
*
* @param jwtBearer the content of the Authorization header
* @return the jwt or null if not found
*/
private static @Nullable String getJwtFromBearer(String jwtBearer) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
*/
private static @Nullable String getJwtFromBearer(String jwtBearer) {
*/
@Nullable
private static String getJwtFromBearer(String jwtBearer) {

if (!StringUtils.hasText(jwtBearer) || !jwtBearer.startsWith("Bearer ")) {
return null;
}

return jwtBearer.substring(7).trim();
iyannsch marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Checks if the jwt is valid
*
* @param tokenProvider the Artemis token provider used to generate and validate jwt's
* @param jwtToken the jwt
* @return true if the jwt is valid, false if missing or invalid
*/
private static boolean isJwtValid(TokenProvider tokenProvider, String jwtToken) {
return StringUtils.hasText(jwtToken) && tokenProvider.validateTokenForAuthority(jwtToken);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,26 @@ public long getTokenValidity(boolean rememberMe) {
* @return JWT Token
*/
public String createToken(Authentication authentication, boolean rememberMe) {
return createToken(authentication, getTokenValidity(rememberMe));
}

/**
* Create JWT Token a fully populated <code>Authentication</code> object.
*
* @param authentication Authentication Object
* @param duration the Token lifetime
* @param tools tools this token is used for
* @return JWT Token
*/
public String createToken(Authentication authentication, long duration, String... tools) {
String authorities = authentication.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.joining(","));

String toolClaims = String.join(",", tools);

long now = (new Date()).getTime();
Date validity = new Date(now + getTokenValidity(rememberMe));
return Jwts.builder().subject(authentication.getName()).claim(AUTHORITIES_KEY, authorities).signWith(key, Jwts.SIG.HS512).expiration(validity).compact();
Date validity = new Date(now + duration);
return Jwts.builder().subject(authentication.getName()).claim(AUTHORITIES_KEY, authorities).claim("tools", toolClaims).signWith(key, Jwts.SIG.HS512).expiration(validity)
.compact();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE;

import java.time.Duration;
import java.util.Date;
iyannsch marked this conversation as resolved.
Show resolved Hide resolved
import java.util.Map;
import java.util.Optional;

import jakarta.servlet.ServletException;
Expand All @@ -27,14 +30,18 @@
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import de.tum.cit.aet.artemis.core.dto.vm.LoginVM;
import de.tum.cit.aet.artemis.core.exception.AccessForbiddenException;
import de.tum.cit.aet.artemis.core.security.SecurityUtils;
import de.tum.cit.aet.artemis.core.security.UserNotActivatedException;
import de.tum.cit.aet.artemis.core.security.annotations.EnforceAtLeastStudent;
import de.tum.cit.aet.artemis.core.security.annotations.EnforceNothing;
import de.tum.cit.aet.artemis.core.security.jwt.JWTCookieService;
import de.tum.cit.aet.artemis.core.security.jwt.JWTFilter;
import de.tum.cit.aet.artemis.core.security.jwt.TokenProvider;
import de.tum.cit.aet.artemis.core.service.connectors.SAML2Service;

/**
Expand All @@ -49,12 +56,15 @@ public class PublicUserJwtResource {

private final JWTCookieService jwtCookieService;

private final TokenProvider tokenProvider;

private final AuthenticationManager authenticationManager;

private final Optional<SAML2Service> saml2Service;

public PublicUserJwtResource(JWTCookieService jwtCookieService, AuthenticationManager authenticationManager, Optional<SAML2Service> saml2Service) {
public PublicUserJwtResource(JWTCookieService jwtCookieService, TokenProvider tokenProvider, AuthenticationManager authenticationManager, Optional<SAML2Service> saml2Service) {
this.jwtCookieService = jwtCookieService;
this.tokenProvider = tokenProvider;
this.authenticationManager = authenticationManager;
this.saml2Service = saml2Service;
}
Expand All @@ -69,7 +79,7 @@ public PublicUserJwtResource(JWTCookieService jwtCookieService, AuthenticationMa
*/
@PostMapping("authenticate")
@EnforceNothing
public ResponseEntity<Void> authorize(@Valid @RequestBody LoginVM loginVM, @RequestHeader("User-Agent") String userAgent, HttpServletResponse response) {
public ResponseEntity<Map<String, String>> authorize(@Valid @RequestBody LoginVM loginVM, @RequestHeader("User-Agent") String userAgent, HttpServletResponse response) {

var username = loginVM.getUsername();
var password = loginVM.getPassword();
Expand All @@ -86,14 +96,44 @@ public ResponseEntity<Void> authorize(@Valid @RequestBody LoginVM loginVM, @Requ
ResponseCookie responseCookie = jwtCookieService.buildLoginCookie(rememberMe);
response.addHeader(HttpHeaders.SET_COOKIE, responseCookie.toString());

return ResponseEntity.ok().build();
return ResponseEntity.ok(Map.of("access_token", responseCookie.getValue()));
iyannsch marked this conversation as resolved.
Show resolved Hide resolved
}
catch (BadCredentialsException ex) {
log.warn("Wrong credentials during login for user {}", loginVM.getUsername());
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
}

/**
* Sends a theia token back as cookie and bearer token
*
* @param request HTTP request
* @param response HTTP response
* @return the ResponseEntity with status 200 (ok), 401 (unauthorized)
*/
iyannsch marked this conversation as resolved.
Show resolved Hide resolved
@PostMapping("theia-token")
@EnforceAtLeastStudent
public ResponseEntity<String> getTheiaToken(@RequestParam(name = "as-cookie", defaultValue = "false") boolean asCookie, HttpServletRequest request,
HttpServletResponse response) {
iyannsch marked this conversation as resolved.
Show resolved Hide resolved
// remaining time in milliseconds
var jwtToken = JWTFilter.extractValidJwt(request, tokenProvider);
if (jwtToken == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}

// get validity of the token
long tokenRemainingTime = tokenProvider.getExpirationDate(jwtToken).getTime() - new Date().getTime();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
long tokenRemainingTime = tokenProvider.getExpirationDate(jwtToken).getTime() - new Date().getTime();
long tokenRemainingTime = tokenProvider.getExpirationDate(jwtToken).getTime() - ZonedDateTime.now().getTime();

We use zoned date time almost everywhere else in Artemis, should we also do it here? I'm not sure

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the TokenProvider.java also uses Date() so we should be consistent with this class as this generates the token


// 1 day validity
long maxDuration = Duration.ofDays(1).toMillis();
ResponseCookie responseCookie = jwtCookieService.buildTheiaCookie(Math.min(tokenRemainingTime, maxDuration));

if (asCookie) {
response.addHeader(HttpHeaders.SET_COOKIE, responseCookie.toString());
}
return ResponseEntity.ok(responseCookie.getValue());
iyannsch marked this conversation as resolved.
Show resolved Hide resolved
}
iyannsch marked this conversation as resolved.
Show resolved Hide resolved

/**
* Authorizes a User logged in with SAML2
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
@Entity
@Table(name = "programming_exercise_build_config")
@JsonInclude(JsonInclude.Include.NON_EMPTY)
@JsonIgnoreProperties(value = { "programmingExercise" })
iyannsch marked this conversation as resolved.
Show resolved Hide resolved
public class ProgrammingExerciseBuildConfig extends DomainObject {

private static final Logger log = LoggerFactory.getLogger(ProgrammingExerciseBuildConfig.class);
Expand Down Expand Up @@ -64,7 +65,6 @@ public class ProgrammingExerciseBuildConfig extends DomainObject {
private String dockerFlags;

@OneToOne(mappedBy = "buildConfig")
@JsonIgnoreProperties("buildConfig")
private ProgrammingExercise programmingExercise;

@Column(name = "testwise_coverage_enabled")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,14 @@ default void generateBuildPlanAccessSecretIfNotExists(ProgrammingExerciseBuildCo
default void loadAndSetBuildConfig(ProgrammingExercise programmingExercise) {
programmingExercise.setBuildConfig(getProgrammingExerciseBuildConfigElseThrow(programmingExercise));
}

/**
* Find a build config by its programming exercise's id and throw an Exception if it cannot be found
*
* @param programmingExerciseId of the programming exercise.
* @return The programming exercise related to the given id
*/
default ProgrammingExerciseBuildConfig findByExerciseIdElseThrow(long programmingExerciseId) {
return getValueElseThrow(findByProgrammingExerciseId(programmingExerciseId));
}
iyannsch marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
import de.tum.cit.aet.artemis.core.security.annotations.EnforceAtLeastEditor;
import de.tum.cit.aet.artemis.core.security.annotations.EnforceAtLeastInstructor;
import de.tum.cit.aet.artemis.core.security.annotations.EnforceAtLeastTutor;
import de.tum.cit.aet.artemis.core.security.annotations.enforceRoleInExercise.EnforceAtLeastStudentInExercise;
import de.tum.cit.aet.artemis.core.security.annotations.enforceRoleInExercise.EnforceAtLeastTutorInExercise;
import de.tum.cit.aet.artemis.core.service.AuthorizationCheckService;
import de.tum.cit.aet.artemis.core.service.CourseService;
Expand All @@ -71,13 +72,15 @@
import de.tum.cit.aet.artemis.plagiarism.service.PlagiarismDetectionConfigHelper;
import de.tum.cit.aet.artemis.programming.domain.AuxiliaryRepository;
import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise;
import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseBuildConfig;
import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseTestCase;
import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage;
import de.tum.cit.aet.artemis.programming.dto.BuildLogStatisticsDTO;
import de.tum.cit.aet.artemis.programming.dto.CheckoutDirectoriesDTO;
import de.tum.cit.aet.artemis.programming.dto.ProgrammingExerciseResetOptionsDTO;
import de.tum.cit.aet.artemis.programming.dto.ProgrammingExerciseTestCaseStateDTO;
import de.tum.cit.aet.artemis.programming.repository.BuildLogStatisticsEntryRepository;
import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseBuildConfigRepository;
import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseRepository;
import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseTestCaseRepository;
import de.tum.cit.aet.artemis.programming.repository.SolutionProgrammingExerciseParticipationRepository;
Expand Down Expand Up @@ -114,6 +117,8 @@ public class ProgrammingExerciseResource {

private final ProgrammingExerciseTestCaseRepository programmingExerciseTestCaseRepository;

private final ProgrammingExerciseBuildConfigRepository programmingExerciseBuildConfigRepository;

private final UserRepository userRepository;

private final CourseService courseService;
Expand Down Expand Up @@ -157,9 +162,9 @@ public class ProgrammingExerciseResource {
private final Environment environment;

public ProgrammingExerciseResource(ProgrammingExerciseRepository programmingExerciseRepository, ProgrammingExerciseTestCaseRepository programmingExerciseTestCaseRepository,
UserRepository userRepository, AuthorizationCheckService authCheckService, CourseService courseService,
Optional<ContinuousIntegrationService> continuousIntegrationService, Optional<VersionControlService> versionControlService, ExerciseService exerciseService,
ExerciseDeletionService exerciseDeletionService, ProgrammingExerciseService programmingExerciseService,
ProgrammingExerciseBuildConfigRepository programmingExerciseBuildConfigRepository, UserRepository userRepository, AuthorizationCheckService authCheckService,
CourseService courseService, Optional<ContinuousIntegrationService> continuousIntegrationService, Optional<VersionControlService> versionControlService,
ExerciseService exerciseService, ExerciseDeletionService exerciseDeletionService, ProgrammingExerciseService programmingExerciseService,
ProgrammingExerciseRepositoryService programmingExerciseRepositoryService, ProgrammingExerciseTaskService programmingExerciseTaskService,
StudentParticipationRepository studentParticipationRepository, StaticCodeAnalysisService staticCodeAnalysisService,
GradingCriterionRepository gradingCriterionRepository, CourseRepository courseRepository, GitService gitService, AuxiliaryRepositoryService auxiliaryRepositoryService,
Expand All @@ -170,6 +175,7 @@ public ProgrammingExerciseResource(ProgrammingExerciseRepository programmingExer
this.programmingExerciseTaskService = programmingExerciseTaskService;
this.programmingExerciseRepository = programmingExerciseRepository;
this.programmingExerciseTestCaseRepository = programmingExerciseTestCaseRepository;
this.programmingExerciseBuildConfigRepository = programmingExerciseBuildConfigRepository;
this.userRepository = userRepository;
this.courseService = courseService;
this.authCheckService = authCheckService;
Expand Down Expand Up @@ -494,6 +500,21 @@ public ResponseEntity<ProgrammingExercise> getProgrammingExercise(@PathVariable
return ResponseEntity.ok().body(programmingExercise);
}

/**
* GET /programming-exercises/:exerciseId/build-config : get the build config of "exerciseId" programmingExercise.
*
* @param exerciseId the id of the programmingExercise to retrieve the config for
* @return the ResponseEntity with status 200 (OK) and with body the programmingExerciseBuildConfig, or with status 404 (Not Found)
*/
@GetMapping("programming-exercises/{exerciseId}/build-config")
@EnforceAtLeastStudentInExercise
public ResponseEntity<ProgrammingExerciseBuildConfig> getBuildConfig(@PathVariable long exerciseId) {
iyannsch marked this conversation as resolved.
Show resolved Hide resolved
log.debug("REST request to get build config of ProgrammingExercise : {}", exerciseId);
var buildConfig = programmingExerciseBuildConfigRepository.findByExerciseIdElseThrow(exerciseId);

return ResponseEntity.ok().body(buildConfig);
}

/**
* GET /programming-exercises/:exerciseId/with-participations/ : get the "exerciseId" programmingExercise.
*
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/config/application-theia.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ theia:
# Upper level key is the language category (must match the language key in the programming-exercise configuration)
java:
# Lower level key can be multiple flavors of the image, e.g. version, tag, or additional dependencies
Java-17: "my-registry/my-image:my-tag"
java17: "my-registry/my-image:my-tag"
# Add more flavors here (e.g. Java-11, Java-8, etc.)
# Add more languages here (e.g. c, python, etc.)
c:
Expand Down
8 changes: 8 additions & 0 deletions src/main/webapp/app/core/auth/account.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -385,4 +385,12 @@ export class AccountService implements IAccountService {
const params = new HttpParams().set('participationId', participationId);
return this.http.put<string>('api/account/participation-vcs-access-token', null, { observe: 'response', params, responseType: 'text' as 'json' });
}

/**
* Trades the current cookie for a new bearer token which is also able to authenticate the user.
* The Cookie stays valid, a new bearer token is generated on every call.
*/
rekeyCookieToBearerToken() {
return this.http.post<string>('api/public/theia-token', null, { responseType: 'text' as 'json' });
}
iyannsch marked this conversation as resolved.
Show resolved Hide resolved
}
Loading
Loading