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

Integrated code lifecycle: Add access tokens for authentication to LocalVC repositories #8929

Merged
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
126 commits
Select commit Hold shift + click to select a range
2f3afe9
migration
icelord42 Jun 23, 2024
90dfd6d
stash commit
icelord42 Jun 26, 2024
0094cba
remove unneccessary migration
icelord42 Jun 28, 2024
c86a593
added participation access token table
icelord42 Jun 30, 2024
463016f
Merge branch 'develop' into feature/programming-exercises/access-toke…
SimonEntholzer Jul 1, 2024
472e1c5
removed adding token to user
icelord42 Jul 1, 2024
17b5e09
Merge branch 'develop' into feature/programming-exercises/access-toke…
SimonEntholzer Jul 1, 2024
5b85662
Merge branch 'develop' into feature/programming-exercises/access-toke…
SimonEntholzer Jul 3, 2024
7b71766
fixed injection of vcs token
icelord42 Jul 5, 2024
0b5636c
Merge branch 'develop' into feature/programming-exercises/access-toke…
SimonEntholzer Jul 5, 2024
64097b3
removed logs
icelord42 Jul 5, 2024
5708cbd
Merge remote-tracking branch 'refs/remotes/origin/feature/programming…
icelord42 Jul 5, 2024
ade6e01
removed logs
icelord42 Jul 5, 2024
08a91dc
Update src/main/java/de/tum/in/www1/artemis/service/connectors/localv…
SimonEntholzer Jul 5, 2024
9cf72a5
Update src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab…
SimonEntholzer Jul 5, 2024
a776bdf
Update src/main/java/de/tum/in/www1/artemis/service/connectors/localv…
SimonEntholzer Jul 5, 2024
a687a74
removed debugging log
icelord42 Jul 5, 2024
d4cf294
Merge remote-tracking branch 'refs/remotes/origin/feature/programming…
icelord42 Jul 5, 2024
90324f5
fixed master.xml
icelord42 Jul 5, 2024
8bc779d
removed unused resource
icelord42 Jul 5, 2024
a1c0e04
removed unused resource
icelord42 Jul 5, 2024
5948f5d
Merge branch 'develop' into feature/programming-exercises/access-toke…
SimonEntholzer Jul 8, 2024
a8277cd
Merge branch 'develop' into feature/programming-exercises/access-toke…
SimonEntholzer Jul 10, 2024
537f510
fixed build error
icelord42 Jul 10, 2024
118a92c
renamed yml arguments for clarity
icelord42 Jul 11, 2024
e0e4192
fixed bugs and renamed variable to better reflect its use
icelord42 Jul 12, 2024
13a526a
removed unused dependencies
icelord42 Jul 12, 2024
c7b58db
re-alinged naming of useVersionControlAccessToken variable (removed g…
icelord42 Jul 12, 2024
3432b07
Merge branch 'develop' into feature/programming-exercises/access-toke…
SimonEntholzer Jul 12, 2024
75b81b3
removed unused variables
icelord42 Jul 12, 2024
36ec48f
Merge branch 'refs/heads/develop' into feature/programming-exercises/…
icelord42 Jul 15, 2024
6430446
Merge branch 'develop' into feature/programming-exercises/access-toke…
SimonEntholzer Jul 16, 2024
e783902
removed unused variables and code
icelord42 Jul 16, 2024
58b9124
fixed merge
icelord42 Jul 16, 2024
659934e
Fix style, add javadoc
icelord42 Jul 16, 2024
ab3f050
added more integration tests
icelord42 Jul 16, 2024
f084550
Merge branch 'develop' into feature/programming-exercises/access-toke…
SimonEntholzer Jul 16, 2024
4999f00
fix architecture violation
icelord42 Jul 16, 2024
91f05b7
adjust use of access token
icelord42 Jul 16, 2024
bd687e6
fixed grammar
icelord42 Jul 16, 2024
1be21cc
improve secure token generation
icelord42 Jul 16, 2024
b984289
fix profileInfo refactor
icelord42 Jul 16, 2024
c9b6f5c
adjusted contributor
icelord42 Jul 16, 2024
9563955
Merge branch 'develop' into feature/programming-exercises/access-toke…
SimonEntholzer Jul 16, 2024
8b50156
removed debugging console log
icelord42 Jul 16, 2024
ebf73e0
Merge remote-tracking branch 'refs/remotes/origin/feature/programming…
icelord42 Jul 16, 2024
38c0c31
fix client test
icelord42 Jul 16, 2024
def35a2
removed console logs
icelord42 Jul 17, 2024
396b961
added suggestions
icelord42 Jul 17, 2024
c92e9bd
always delete token
icelord42 Jul 17, 2024
5747a7e
simpler cast
icelord42 Jul 17, 2024
4ea207b
Merge branch 'develop' into feature/programming-exercises/access-toke…
SimonEntholzer Jul 17, 2024
bf476f6
Merge remote-tracking branch 'refs/remotes/origin/feature/programming…
icelord42 Jul 17, 2024
13d9db2
Merge branch 'develop' into feature/programming-exercises/access-toke…
SimonEntholzer Jul 17, 2024
227efee
enabled tokens
icelord42 Jul 17, 2024
1614378
fixed user being undefined because of race condition
icelord42 Jul 17, 2024
c72ca76
improved maintainability
icelord42 Jul 17, 2024
fbfd519
re-enable use of yml variable
icelord42 Jul 17, 2024
cddfaa2
add migration to generate tokens for existing programming-exercise st…
icelord42 Jul 17, 2024
d34fdff
fix achitecture requirement
icelord42 Jul 17, 2024
3c8e06f
Improve test coverage
icelord42 Jul 18, 2024
abacc45
simplify creation of missing tokens
icelord42 Jul 18, 2024
8595825
removed migration for simplicity
icelord42 Jul 18, 2024
676b674
fix test
icelord42 Jul 18, 2024
393f32e
improved error handling
icelord42 Jul 18, 2024
20e51f7
set use-version-control-access-token to true
icelord42 Jul 18, 2024
bcfb404
fix reafactor
icelord42 Jul 18, 2024
9c05cce
test improvement
icelord42 Jul 18, 2024
c4585b0
added change request changes
icelord42 Jul 18, 2024
868c74c
added on-demand token creation
icelord42 Jul 18, 2024
1cfc021
improved coverage and added on-demand loading
icelord42 Jul 19, 2024
f1dbeec
added repository improvement
icelord42 Jul 19, 2024
a2ef0fb
improved repository calls
icelord42 Jul 19, 2024
c03a7de
minor refactor
icelord42 Jul 19, 2024
c81d559
minor refactor and added check if tokens shuold actually be used
icelord42 Jul 19, 2024
803c330
add javadoc
icelord42 Jul 19, 2024
f60f410
fix practice mode participation access
icelord42 Jul 20, 2024
17dd18f
Merge branch 'develop' into feature/programming-exercises/access-toke…
SimonEntholzer Jul 20, 2024
153811e
Improved migration, added indexes for faster lookup, and delete token…
icelord42 Jul 20, 2024
e7b7011
Merge remote-tracking branch 'refs/remotes/origin/feature/programming…
icelord42 Jul 20, 2024
a101d1d
Merge branch 'develop' into feature/programming-exercises/access-toke…
SimonEntholzer Jul 20, 2024
e36a313
Merge branch 'develop' into feature/programming-exercises/access-toke…
SimonEntholzer Jul 21, 2024
6b8d049
Merge branch 'develop' into feature/programming-exercises/access-toke…
SimonEntholzer Jul 22, 2024
bf0dd6a
more client tests
icelord42 Jul 22, 2024
ea03de2
Merge branch 'develop' into feature/programming-exercises/access-toke…
SimonEntholzer Jul 26, 2024
2db3eb9
Merge branch 'develop' into feature/programming-exercises/access-toke…
SimonEntholzer Jul 27, 2024
02c1967
added unique constraint to migration
icelord42 Jul 27, 2024
322601c
refactored LocalVCPersonalAccessTokenManagementService
icelord42 Jul 27, 2024
204f79a
refactored e2e test
icelord42 Jul 27, 2024
207e51f
refactored test and team exercise participation
icelord42 Jul 27, 2024
23e82c2
clean up
icelord42 Jul 27, 2024
9284293
Merge branch 'develop' into feature/programming-exercises/access-toke…
SimonEntholzer Jul 27, 2024
5cbfea9
fix
icelord42 Jul 27, 2024
f780676
undo rename
icelord42 Jul 27, 2024
69cd6bf
removed unique constraint
icelord42 Jul 27, 2024
8bf1007
re-added changelog
icelord42 Jul 27, 2024
9493012
fix migration
icelord42 Jul 28, 2024
e96f85a
fix migration 2
SimonEntholzer Jul 28, 2024
b47a70f
Merge branch 'develop' into feature/programming-exercises/access-toke…
SimonEntholzer Jul 28, 2024
def042b
completely remove migration for now
SimonEntholzer Jul 28, 2024
a900dd9
re-add migration
SimonEntholzer Jul 28, 2024
b78af67
remove migration for testing
SimonEntholzer Jul 29, 2024
93acbac
fix fetching tokens for multiple participations
SimonEntholzer Jul 29, 2024
30571d3
reload tokens when new participations arrive in ngOnChanges
SimonEntholzer Jul 29, 2024
3a6002b
fix showing link with token for instructors with token
SimonEntholzer Aug 2, 2024
61fed08
Merge branch 'develop' into feature/programming-exercises/access-toke…
SimonEntholzer Aug 2, 2024
bcdf122
Merge remote-tracking branch 'refs/remotes/origin/feature/programming…
SimonEntholzer Aug 2, 2024
fd6b847
re-added migration
SimonEntholzer Aug 2, 2024
4edc17a
Merge branch 'develop' into feature/programming-exercises/access-toke…
SimonEntholzer Aug 3, 2024
9152a37
Merge branch 'develop' into feature/programming-exercises/access-toke…
SimonEntholzer Aug 3, 2024
5d72185
Merge branch 'develop' into feature/programming-exercises/access-toke…
SimonEntholzer Aug 4, 2024
56f32a8
Merge branch 'develop' into feature/programming-exercises/access-toke…
SimonEntholzer Aug 5, 2024
cc4784f
Merge branch 'develop' into feature/programming-exercises/access-toke…
SimonEntholzer Aug 6, 2024
8cb81b3
Merge branch 'develop' into feature/programming-exercises/access-toke…
SimonEntholzer Aug 7, 2024
7745a85
Merge branch 'develop' into feature/programming-exercises/access-toke…
krusche Aug 11, 2024
4c4f595
Merge branch 'develop' into feature/programming-exercises/access-toke…
Aug 11, 2024
577f135
fix an issue in a server test
Aug 11, 2024
94a3492
avoid too many unnecessary exceptions in logs
Aug 11, 2024
6ffb604
Merge branch 'refs/heads/develop' into feature/programming-exercises/…
SimonEntholzer Aug 12, 2024
746fd90
add option to not use token in code button dropdown
SimonEntholzer Aug 12, 2024
62a7f67
adjusted tests
SimonEntholzer Aug 12, 2024
b9e8b6d
fix import
SimonEntholzer Aug 12, 2024
1e6f019
fix highlighting
SimonEntholzer Aug 12, 2024
29705aa
fixed programming exam summary not handing down participation to the …
SimonEntholzer Aug 12, 2024
b91b915
don't show token URL if there is no token (or no participation)
SimonEntholzer Aug 12, 2024
7485fdd
added possibility to disable showing the URL without a token
SimonEntholzer Aug 12, 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
JohannesStoehr marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package de.tum.in.www1.artemis.domain.participation;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.persistence.UniqueConstraint;

import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;

import com.fasterxml.jackson.annotation.JsonInclude;

import de.tum.in.www1.artemis.domain.DomainObject;
import de.tum.in.www1.artemis.domain.User;

/**
* A Participation.
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
*/
@Entity
@Table(name = "participation_vcs_access_token", uniqueConstraints = { @UniqueConstraint(columnNames = { "user_id", "participation_id" }) })
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)

@JsonInclude(JsonInclude.Include.NON_EMPTY)
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
public class ParticipationVCSAccessToken extends DomainObject {

@ManyToOne
private User user;

@ManyToOne
private Participation participation;

@Column(name = "vcs_access_token", length = 50)
private String vcsAccessToken;
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved

public void setUser(User user) {
this.user = user;
}
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved

public User getUser() {
return user;
}
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved

public void setParticipation(Participation participation) {
this.participation = participation;
}
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved

public Participation getParticipation() {
return participation;
}

public String getVcsAccessToken() {
return vcsAccessToken;
}

public void setVcsAccessToken(String vcsAccessToken) {
this.vcsAccessToken = vcsAccessToken;
}
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package de.tum.in.www1.artemis.repository;

import static de.tum.in.www1.artemis.config.Constants.PROFILE_CORE;

import java.util.List;
import java.util.Optional;

import org.springframework.context.annotation.Profile;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import de.tum.in.www1.artemis.domain.participation.ParticipationVCSAccessToken;
import de.tum.in.www1.artemis.repository.base.ArtemisJpaRepository;

@Profile(PROFILE_CORE)
@Repository
public interface ParticipationVCSAccessTokenRepository extends ArtemisJpaRepository<ParticipationVCSAccessToken, Long> {

@Query("""
SELECT DISTINCT p
FROM ParticipationVCSAccessToken p
LEFT JOIN FETCH p.participation
LEFT JOIN FETCH p.user
WHERE p.id = :participationVCSAccessTokenId
""")
Optional<ParticipationVCSAccessToken> findByIdWithParticipationAndUser(@Param("participationVCSAccessTokenId") long participationVCSAccessTokenId);

@Query("""
SELECT DISTINCT p
FROM ParticipationVCSAccessToken p
LEFT JOIN FETCH p.participation
Left JOIN FETCH p.user
WHERE p.user.id = :userId
""")
List<ParticipationVCSAccessToken> findByUserIdWithEagerParticipation(@Param("userId") Long userId);

@Query("""
SELECT p
FROM ParticipationVCSAccessToken p
WHERE p.user.id = :userId AND p.participation.id = :participationId
""")
Optional<ParticipationVCSAccessToken> findByUserIdAndParticipationId(@Param("userId") Long userId, @Param("participationId") Long participationId);

@Query("""
SELECT DISTINCT p
FROM ParticipationVCSAccessToken p
WHERE p.user.id = :userId
""")
List<ParticipationVCSAccessToken> findByUserId(@Param("userId") Long userId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import de.tum.in.www1.artemis.domain.enumeration.InitializationState;
import de.tum.in.www1.artemis.domain.enumeration.SubmissionType;
import de.tum.in.www1.artemis.domain.participation.Participant;
import de.tum.in.www1.artemis.domain.participation.ParticipationVCSAccessToken;
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
import de.tum.in.www1.artemis.domain.participation.ProgrammingExerciseStudentParticipation;
import de.tum.in.www1.artemis.domain.participation.StudentParticipation;
import de.tum.in.www1.artemis.domain.quiz.QuizExercise;
Expand All @@ -39,17 +40,20 @@
import de.tum.in.www1.artemis.repository.BuildLogStatisticsEntryRepository;
import de.tum.in.www1.artemis.repository.ParticipantScoreRepository;
import de.tum.in.www1.artemis.repository.ParticipationRepository;
import de.tum.in.www1.artemis.repository.ParticipationVCSAccessTokenRepository;
import de.tum.in.www1.artemis.repository.ProgrammingExerciseRepository;
import de.tum.in.www1.artemis.repository.ProgrammingExerciseStudentParticipationRepository;
import de.tum.in.www1.artemis.repository.StudentParticipationRepository;
import de.tum.in.www1.artemis.repository.StudentScoreRepository;
import de.tum.in.www1.artemis.repository.SubmissionRepository;
import de.tum.in.www1.artemis.repository.TeamRepository;
import de.tum.in.www1.artemis.repository.TeamScoreRepository;
import de.tum.in.www1.artemis.repository.UserRepository;
import de.tum.in.www1.artemis.repository.hestia.CoverageReportRepository;
import de.tum.in.www1.artemis.service.connectors.GitService;
import de.tum.in.www1.artemis.service.connectors.ci.ContinuousIntegrationService;
import de.tum.in.www1.artemis.service.connectors.localci.SharedQueueManagementService;
import de.tum.in.www1.artemis.service.connectors.localvc.LocalVCPersonalAccessTokenManagementService;
import de.tum.in.www1.artemis.service.connectors.vcs.VersionControlService;
import de.tum.in.www1.artemis.web.rest.errors.EntityNotFoundException;

Expand Down Expand Up @@ -100,13 +104,18 @@ public class ParticipationService {

private final ProfileService profileService;

private final UserRepository userRepository;

private final ParticipationVCSAccessTokenRepository participationVCSAccessTokenRepository;

public ParticipationService(GitService gitService, Optional<ContinuousIntegrationService> continuousIntegrationService, Optional<VersionControlService> versionControlService,
BuildLogEntryService buildLogEntryService, ParticipationRepository participationRepository, StudentParticipationRepository studentParticipationRepository,
ProgrammingExerciseStudentParticipationRepository programmingExerciseStudentParticipationRepository, ProgrammingExerciseRepository programmingExerciseRepository,
SubmissionRepository submissionRepository, TeamRepository teamRepository, UriService uriService, ResultService resultService,
CoverageReportRepository coverageReportRepository, BuildLogStatisticsEntryRepository buildLogStatisticsEntryRepository,
ParticipantScoreRepository participantScoreRepository, StudentScoreRepository studentScoreRepository, TeamScoreRepository teamScoreRepository,
Optional<SharedQueueManagementService> localCISharedBuildJobQueueService, ProfileService profileService) {
Optional<SharedQueueManagementService> localCISharedBuildJobQueueService, ProfileService profileService, UserRepository userRepository,
ParticipationVCSAccessTokenRepository participationVCSAccessTokenRepository) {
this.gitService = gitService;
this.continuousIntegrationService = continuousIntegrationService;
this.versionControlService = versionControlService;
Expand All @@ -126,6 +135,8 @@ public ParticipationService(GitService gitService, Optional<ContinuousIntegratio
this.teamScoreRepository = teamScoreRepository;
this.localCISharedBuildJobQueueService = localCISharedBuildJobQueueService;
this.profileService = profileService;
this.userRepository = userRepository;
this.participationVCSAccessTokenRepository = participationVCSAccessTokenRepository;
}

/**
Expand Down Expand Up @@ -222,14 +233,24 @@ private StudentParticipation createNewParticipationWithInitializationDate(Exerci
else {
participation = new StudentParticipation();
}
User user = userRepository.getUser();
participation.setInitializationState(InitializationState.UNINITIALIZED);
participation.setExercise(exercise);
participation.setParticipant(participant);

// StartedDate is used to link a Participation to a test exam exercise
if (initializationDate != null) {
participation.setInitializationDate(initializationDate);
}
return studentParticipationRepository.saveAndFlush(participation);
participation = studentParticipationRepository.saveAndFlush(participation);

ParticipationVCSAccessToken participationVCSAccessToken = new ParticipationVCSAccessToken();
participationVCSAccessToken.setUser(user);
participationVCSAccessToken.setParticipation(participation);
participationVCSAccessToken.setVcsAccessToken(LocalVCPersonalAccessTokenManagementService.generateSecureVCSAccessToken());
participationVCSAccessTokenRepository.save(participationVCSAccessToken);

return participation;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ public class GitLabPersonalAccessTokenManagementService extends VcsTokenManageme
/**
* The config parameter for enabling VCS access tokens.
*/
@Value("${artemis.version-control.version-control-access-token:#{false}}")
private Boolean versionControlAccessToken;
@Value("${artemis.version-control.version-control-access-token:#{true}}")
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
b-fein marked this conversation as resolved.
Show resolved Hide resolved
b-fein marked this conversation as resolved.
Show resolved Hide resolved
private Boolean useVersionControlAccessToken;
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved

public GitLabPersonalAccessTokenManagementService(UserRepository userRepository, GitLabApi gitlabApi, @Qualifier("gitlabRestTemplate") RestTemplate restTemplate) {
this.userRepository = userRepository;
Expand All @@ -72,7 +72,7 @@ public GitLabPersonalAccessTokenManagementService(UserRepository userRepository,
*/
@Override
public void createAccessToken(User user, Duration lifetime) {
if (versionControlAccessToken) {
if (useVersionControlAccessToken) {
if (user.getVcsAccessToken() != null) {
throw new IllegalArgumentException("User already has an access token");
}
Expand Down Expand Up @@ -115,7 +115,7 @@ private void savePersonalAccessTokenOfUser(ImpersonationToken token, User user)
*/
@Override
public void renewAccessToken(User user, Duration newLifetime) {
if (versionControlAccessToken) {
if (useVersionControlAccessToken) {
if (user.getVcsAccessToken() == null) {
throw new IllegalArgumentException("User has no VCS access token to be renewed");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public void contribute(Info.Builder builder) {
// TODO: only activate this when access tokens are available and make sure this does not lead to issues
// TODO: If activated, reflect this in LocalVCInfoContributorTest
// with the account.service.ts and its check if the access token is required
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
builder.withDetail(Constants.INFO_VERSION_CONTROL_ACCESS_TOKEN_DETAIL, false);
builder.withDetail(Constants.INFO_VERSION_CONTROL_ACCESS_TOKEN_DETAIL, true);
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved

// Store ssh url template
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package de.tum.in.www1.artemis.service.connectors.localvc;

import static de.tum.in.www1.artemis.config.Constants.PROFILE_LOCALVC;

import java.security.SecureRandom;
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;

import de.tum.in.www1.artemis.web.rest.UserResource;

@Service
@Profile(PROFILE_LOCALVC)
public class LocalVCPersonalAccessTokenManagementService {

private static final Logger log = LoggerFactory.getLogger(UserResource.class);
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
b-fein marked this conversation as resolved.
Show resolved Hide resolved
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved

@Value("${artemis.version-control.version-control-access-token:#{false}}")
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
private boolean versionControlAccessToken;

@Value("${artemis.version-control.vc-access-token-max-lifetime-in-days:365}")
private int vcMaxLifetimeInDays;
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved

private static final String TOKEN_PREFIX = "vcpat-";

private static final int RANDOM_STRING_LENGTH = 40;

private static final String CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

public static String generateSecureVCSAccessToken() {
SecureRandom secureRandom = new SecureRandom();
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
StringBuilder randomString = new StringBuilder(RANDOM_STRING_LENGTH);

for (int i = 0; i < RANDOM_STRING_LENGTH; i++) {
int randomIndex = secureRandom.nextInt(CHARACTERS.length());
randomString.append(CHARACTERS.charAt(randomIndex));
}
return TOKEN_PREFIX + randomString.toString();
}
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

import jakarta.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
Expand Down Expand Up @@ -44,6 +43,7 @@
import de.tum.in.www1.artemis.exception.localvc.LocalVCAuthException;
import de.tum.in.www1.artemis.exception.localvc.LocalVCForbiddenException;
import de.tum.in.www1.artemis.exception.localvc.LocalVCInternalException;
import de.tum.in.www1.artemis.repository.ParticipationVCSAccessTokenRepository;
import de.tum.in.www1.artemis.repository.ProgrammingExerciseRepository;
import de.tum.in.www1.artemis.repository.UserRepository;
import de.tum.in.www1.artemis.security.SecurityUtils;
Expand Down Expand Up @@ -95,6 +95,8 @@ public class LocalVCServletService {

private static URL localVCBaseUrl;

private final ParticipationVCSAccessTokenRepository participationVCSAccessTokenRepository;

@Value("${artemis.version-control.url}")
public void setLocalVCBaseUrl(URL localVCBaseUrl) {
LocalVCServletService.localVCBaseUrl = localVCBaseUrl;
Expand Down Expand Up @@ -122,7 +124,8 @@ public LocalVCServletService(AuthenticationManager authenticationManager, UserRe
RepositoryAccessService repositoryAccessService, AuthorizationCheckService authorizationCheckService,
ProgrammingExerciseParticipationService programmingExerciseParticipationService, AuxiliaryRepositoryService auxiliaryRepositoryService,
ContinuousIntegrationTriggerService ciTriggerService, ProgrammingSubmissionService programmingSubmissionService,
ProgrammingMessagingService programmingMessagingService, ProgrammingTriggerService programmingTriggerService) {
ProgrammingMessagingService programmingMessagingService, ProgrammingTriggerService programmingTriggerService,
ParticipationVCSAccessTokenRepository participationVCSAccessTokenRepository) {
this.authenticationManager = authenticationManager;
this.userRepository = userRepository;
this.programmingExerciseRepository = programmingExerciseRepository;
Expand All @@ -134,6 +137,7 @@ public LocalVCServletService(AuthenticationManager authenticationManager, UserRe
this.programmingSubmissionService = programmingSubmissionService;
this.programmingMessagingService = programmingMessagingService;
this.programmingTriggerService = programmingTriggerService;
this.participationVCSAccessTokenRepository = participationVCSAccessTokenRepository;
}

/**
Expand Down Expand Up @@ -205,8 +209,6 @@ public void authenticateAndAuthorizeGitRequest(HttpServletRequest request, Repos
}
}

User user = authenticateUser(authorizationHeader);

// Optimization.
// For each git command (i.e. 'git fetch' or 'git push'), the git client sends three requests.
// The URLs of the first two requests end on '[repository URI]/info/refs'. The third one ends on '[repository URI]/git-receive-pack' (for push) and '[repository
Expand All @@ -223,6 +225,8 @@ public void authenticateAndAuthorizeGitRequest(HttpServletRequest request, Repos

ProgrammingExercise exercise = getProgrammingExerciseOrThrow(projectKey);

User user = authenticateUser(authorizationHeader, exercise);

// Check that offline IDE usage is allowed.
if (Boolean.FALSE.equals(exercise.isAllowOfflineIde()) && authorizationCheckService.isOnlyStudentInCourse(exercise.getCourseViaExerciseGroupOrCourseMember(), user)) {
throw new LocalVCForbiddenException();
Expand All @@ -235,7 +239,7 @@ public void authenticateAndAuthorizeGitRequest(HttpServletRequest request, Repos
log.debug("Authorizing user {} for repository {} took {}", user.getLogin(), localVCRepositoryUri, TimeLogUtil.formatDurationFrom(timeNanoStart));
}

private User authenticateUser(String authorizationHeader) throws LocalVCAuthException {
private User authenticateUser(String authorizationHeader, ProgrammingExercise exercise) throws LocalVCAuthException {

UsernameAndPassword usernameAndPassword = extractUsernameAndPassword(authorizationHeader);

Expand All @@ -249,9 +253,20 @@ private User authenticateUser(String authorizationHeader) throws LocalVCAuthExce

// Note: we first check if the user has used a vcs access token instead of a password

if (user.isPresent() && !StringUtils.isEmpty(user.get().getVcsAccessToken()) && Objects.equals(user.get().getVcsAccessToken(), password)) {
if (user.isPresent()) { // !StringUtils.isEmpty(user.get().getVcsAccessToken()) && Objects.equals(user.get().getVcsAccessToken(), password)) {
// user is authenticated by using the correct access token
return user.get();
// var studentParticipation = programmingExerciseParticipationService.findStudentParticipationByExerciseAndStudentId(exercise, user.get().getLogin());
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
var participationVCSAccessToken = participationVCSAccessTokenRepository.findByUserId(user.get().getId()).getFirst();

// todo dont get first but the correct one for the participation
// todo entholzer - get token corresponding to only the participation
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
if (Objects.equals(participationVCSAccessToken.getVcsAccessToken(), password)) {
User suser = user.get();
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
suser.setVcsAccessToken(participationVCSAccessToken.getVcsAccessToken());
return suser;
}
// suser.setVcsAccessToken();
throw new LocalVCAuthException(new AccessForbiddenException());
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
}

// if the user does not have an access token or has used a password, we try to authenticate the user with it
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,12 @@ public class VcsTokenRenewalService {
/**
* The config parameter for enabling VCS access tokens.
*/
private final boolean versionControlAccessToken;
private final boolean useVersionControlAccessToken;
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved

// note: we inject the configuration value here to easily test with different ones
public VcsTokenRenewalService(@Value("${artemis.version-control.version-control-access-token:#{false}}") boolean versionControlAccessToken,
Optional<VcsTokenManagementService> vcsTokenManagementService, UserRepository userRepository) {
this.versionControlAccessToken = versionControlAccessToken;
this.useVersionControlAccessToken = versionControlAccessToken;
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
this.vcsTokenManagementService = vcsTokenManagementService;
this.userRepository = userRepository;
}
Expand All @@ -59,7 +59,7 @@ public VcsTokenRenewalService(@Value("${artemis.version-control.version-control-
*/
@Scheduled(cron = "0 0 4 * * SUN") // Every sunday at 4 am
public void renewAllVcsAccessTokens() {
if (versionControlAccessToken && vcsTokenManagementService.isPresent()) {
if (useVersionControlAccessToken && vcsTokenManagementService.isPresent()) {
log.info("Started scheduled access token renewal");
int renewedAccessTokenCount = renewExpiringAccessTokens();
int createdAccessTokenCount = createMissingAccessTokens();
Expand Down
Loading
Loading