Skip to content

Commit

Permalink
cleanup 5
Browse files Browse the repository at this point in the history
  • Loading branch information
SimonEntholzer committed Nov 19, 2024
1 parent 7812d47 commit ed27a1b
Show file tree
Hide file tree
Showing 11 changed files with 90 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,6 @@ public interface TemplateProgrammingExerciseParticipationRepository
""")
Optional<TemplateProgrammingExerciseParticipation> findByBuildPlanIdWithResults(@Param("buildPlanId") String buildPlanId);

@EntityGraph(type = LOAD, attributePaths = { "results", "submissions" })
Optional<TemplateProgrammingExerciseParticipation> findWithEagerResultsAndSubmissionsByProgrammingExerciseId(long exerciseId);

default TemplateProgrammingExerciseParticipation findWithEagerResultsAndSubmissionsByProgrammingExerciseIdElseThrow(long exerciseId) {
return getValueElseThrow(findWithEagerResultsAndSubmissionsByProgrammingExerciseId(exerciseId));
}

@EntityGraph(type = LOAD, attributePaths = { "submissions" })
Optional<TemplateProgrammingExerciseParticipation> findWithSubmissionsByRepositoryUri(String repositoryUri);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,11 @@ public interface VcsAccessLogRepository extends ArtemisJpaRepository<VcsAccessLo
* @return an Optional containing the newest {@link VcsAccessLog} of the participation, or empty if none exists.
*/
@Query("""
SELECT log
FROM VcsAccessLog log
LEFT JOIN TREAT (log.participation AS ProgrammingExerciseStudentParticipation) participation
SELECT vcsAccessLog
FROM VcsAccessLog vcsAccessLog
LEFT JOIN TREAT (vcsAccessLog.participation AS ProgrammingExerciseStudentParticipation) participation
WHERE participation.repositoryUri = :repositoryUri
ORDER BY log.id DESC
ORDER BY vcsAccessLog.id DESC
LIMIT 1
""")
Optional<VcsAccessLog> findNewestByRepositoryUri(@Param("repositoryUri") String repositoryUri);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ public ProgrammingExerciseParticipation fetchParticipationWithSubmissionsByRepos

}

public ProgrammingExerciseParticipation retrieveSolutionPar(Exercise exercise) {
public ProgrammingExerciseParticipation retrieveSolutionParticipation(Exercise exercise) {
return solutionParticipationRepository.findByProgrammingExerciseIdElseThrow(exercise.getId());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ public void init() {
this.setReceivePackFactory((request, repository) -> {
ReceivePack receivePack = new ReceivePack(repository);
// Add a hook that prevents illegal actions on push (delete branch, rename branch, force push).
// user is always null here
receivePack.setPreReceiveHook(new LocalVCPrePushHook(localVCServletService, (User) request.getSession().getAttribute("user")));
// the user inside the request is always null here
receivePack.setPreReceiveHook(new LocalVCPrePushHook(localVCServletService, (User) request.getAttribute("user")));
// Add a hook that triggers the creation of a new submission after the push went through successfully.
receivePack.setPostReceiveHook(new LocalVCPostPushHook(localVCServletService));
return receivePack;
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ public LocalVCFetchPreUploadHook(LocalVCServletService localVCServletService, Ht
public void onBeginNegotiateRound(UploadPack uploadPack, Collection<? extends ObjectId> collection, int clientOffered) {
String authorizationHeader = request.getHeader(LocalVCServletService.AUTHORIZATION_HEADER);
localVCServletService.updateAndStoreVCSAccessLogForCloneAndPullHTTPS(request, authorizationHeader, clientOffered);

}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public void onPreReceive(ReceivePack receivePack, Collection<ReceiveCommand> com

String defaultBranchName;
try {
defaultBranchName = LocalVCServletService.getDefaultBranchOfRepository(repository);
defaultBranchName = LocalVCService.getDefaultBranchOfRepository(repository.getDirectory().toPath().toString());
}
catch (LocalVCInternalException e) {
command.setResult(ReceiveCommand.Result.REJECTED_OTHER_REASON, "An error occurred while checking the branch.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
import org.springframework.http.HttpStatus;
import org.springframework.scheduling.annotation.Async;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
Expand Down Expand Up @@ -221,7 +220,7 @@ public void authenticateAndAuthorizeGitRequest(HttpServletRequest request, Repos

String authorizationHeader = request.getHeader(LocalVCServletService.AUTHORIZATION_HEADER);

// The first request does not contain an authorizationHeader
// The first request does not contain an authorizationHeader, the client expects this response
if (authorizationHeader == null) {
throw new LocalVCAuthException("No authorization header provided");
}
Expand Down Expand Up @@ -305,6 +304,18 @@ private AuthenticationMechanism resolveHTTPSAuthenticationMechanism(String autho
return AuthenticationMechanism.PARTICIPATION_VCS_ACCESS_TOKEN;
}

/**
* Authenticates a user based on the provided authorization header for a specific programming exercise/repository.
* Authentication is tried with: 1) user VCS access token, 2) user participation VCS access token 3) password
*
* @param authorizationHeader the authorization header containing authentication credentials
* @param exercise the programming exercise the user is attempting to access
* @param localVCRepositoryUri the URI of the local version control repository the user is attempting to access
*
* @return the authenticated {@link User} if authentication is successful
* @throws LocalVCAuthException if an error occurs during authentication with the local version control system
* @throws AuthenticationException if the authentication credentials are invalid or authentication fails
*/
private User authenticateUser(String authorizationHeader, ProgrammingExercise exercise, LocalVCRepositoryUri localVCRepositoryUri)
throws LocalVCAuthException, AuthenticationException {

Expand All @@ -318,7 +329,7 @@ private User authenticateUser(String authorizationHeader, ProgrammingExercise ex
SecurityUtils.checkUsernameAndPasswordValidity(username, passwordOrToken);
}
catch (AccessForbiddenException | AuthenticationException e) {
if (!StringUtils.isEmpty(passwordOrToken)) {
if (StringUtils.isNotEmpty(passwordOrToken)) {
log.warn("Failed login attempt for user {} with password {} due to issue: {}", username, passwordOrToken, e.getMessage());
}
throw new LocalVCAuthException(e.getMessage());
Expand All @@ -331,7 +342,7 @@ private User authenticateUser(String authorizationHeader, ProgrammingExercise ex
}

// check user participation VCS access token
if (tryAuthenticationWithVCSAccessToken(user, passwordOrToken, exercise, localVCRepositoryUri)) {
if (tryAuthenticationWithParticipationVCSAccessToken(user, passwordOrToken, exercise, localVCRepositoryUri)) {
return user;
}

Expand All @@ -343,7 +354,16 @@ private User authenticateUser(String authorizationHeader, ProgrammingExercise ex
return user;
}

private boolean tryAuthenticationWithVCSAccessToken(User user, String providedToken, ProgrammingExercise exercise, LocalVCRepositoryUri localVCRepositoryUri)
/**
* Attempts to authenticate a user with the provided participation VCS access token
*
* @param user the user attempting authentication
* @param providedToken the participation VCS access token provided by the user
* @param exercise the programming exercise containing the repository the user tries to access
* @param localVCRepositoryUri the URI of the local version control repository the user tries to access
* @return {@code true} if the authentication is successful, {@code false} otherwise
*/
private boolean tryAuthenticationWithParticipationVCSAccessToken(User user, String providedToken, ProgrammingExercise exercise, LocalVCRepositoryUri localVCRepositoryUri)
throws LocalVCAuthException {

// Note: we first check if the user has used a vcs access token instead of a password
Expand All @@ -362,9 +382,9 @@ private boolean tryAuthenticationWithVCSAccessToken(User user, String providedTo
studentParticipation = participations.stream().filter(participation -> participation.getRepositoryUri().equals(localVCRepositoryUri.toString())).findAny();
}
if (studentParticipation.isPresent()) {
var token = participationVCSAccessTokenRepository.findByUserIdAndParticipationId(user.getId(), studentParticipation.get().getId());
if (token.isPresent() && Objects.equals(token.get().getVcsAccessToken(), providedToken)) {
user.setVcsAccessToken(token.get().getVcsAccessToken());
var storedToken = participationVCSAccessTokenRepository.findByUserIdAndParticipationId(user.getId(), studentParticipation.get().getId());
if (storedToken.isPresent() && Objects.equals(storedToken.get().getVcsAccessToken(), providedToken)) {
user.setVcsAccessToken(storedToken.get().getVcsAccessToken());
return true;
}
}
Expand Down Expand Up @@ -413,6 +433,13 @@ private ProgrammingExercise getProgrammingExerciseOrThrow(String projectKey) {
}
}

/**
* Extracts the username and password from a Basic Authorization header.
*
* @param authorizationHeader the authorization header containing Basic credentials
* @return a {@link UsernameAndPassword} object with the extracted username and password
* @throws LocalVCAuthException if the header is missing, invalid, or improperly formatted
*/
private UsernameAndPassword extractUsernameAndPassword(String authorizationHeader) throws LocalVCAuthException {
if (authorizationHeader == null) {
throw new LocalVCAuthException("No authorization header provided");
Expand Down Expand Up @@ -462,7 +489,7 @@ public Optional<ProgrammingExerciseParticipation> authorizeUser(String repositor
}

private ProgrammingExerciseParticipation tryToLoadParticipation(boolean usingSSH, String repositoryTypeOrUserName, LocalVCRepositoryUri localVCRepositoryUri,
ProgrammingExercise exercise) throws LocalVCForbiddenException {
ProgrammingExercise exercise) throws LocalVCInternalException {
ProgrammingExerciseParticipation participation;
try {
if (usingSSH) {
Expand Down Expand Up @@ -626,15 +653,16 @@ public void processNewPush(String commitHash, Repository repository, Optional<Pr
;

try {
participation = cachedParticipation.orElseGet(() -> fetchParticipationFromLocalVCRepositoryUri(localVCRepositoryUri, exercise));
participation = cachedParticipation.orElseGet(() -> programmingExerciseParticipationService
.fetchParticipationWithSubmissionsByRepository(localVCRepositoryUri.getRepositoryTypeOrUserName(), localVCRepositoryUri.toString(), exercise));
}
catch (EntityNotFoundException e) {
repositoryType = getRepositoryType(repositoryTypeOrUserName, exercise);
if (repositoryType.equals(RepositoryType.AUXILIARY) || repositoryType.equals(RepositoryType.TESTS)) {
participation = retrieveSolutionParticipation(exercise);
}
else {
throw e;
throw new VersionControlException("Could not find participation for repository", e);
}
}

Expand Down Expand Up @@ -682,7 +710,7 @@ public void processNewPush(String commitHash, Repository repository, Optional<Pr
}

private ProgrammingExerciseParticipation retrieveSolutionParticipation(ProgrammingExercise exercise) {
return programmingExerciseParticipationService.retrieveSolutionPar(exercise);
return programmingExerciseParticipationService.retrieveSolutionParticipation(exercise);
}

private ProgrammingExercise getProgrammingExercise(String projectKey) {
Expand Down Expand Up @@ -843,28 +871,6 @@ private Commit extractCommitInfo(String commitHash, Repository repository) throw
return new Commit(commitHash, author.getName(), revCommit.getFullMessage(), author.getEmailAddress(), branch);
}

/**
* Retrieves the participation for a programming exercise based on the repository URI.
*
* @param localVCRepositoryUri the {@link LocalVCRepositoryUri} containing details about the repository.
* @return the {@link ProgrammingExerciseParticipation} corresponding to the repository URI.
*/
private ProgrammingExerciseParticipation fetchParticipationFromLocalVCRepositoryUri(LocalVCRepositoryUri localVCRepositoryUri, ProgrammingExercise exercise) {
return programmingExerciseParticipationService.fetchParticipationWithSubmissionsByRepository(localVCRepositoryUri.getRepositoryTypeOrUserName(),
localVCRepositoryUri.toString(), exercise);
}

/**
* Determine the default branch of the given repository.
*
* @param repository the repository for which the default branch should be determined.
* @return the name of the default branch.
*/
public static String getDefaultBranchOfRepository(Repository repository) {
Path repositoryFolderPath = repository.getDirectory().toPath();
return LocalVCService.getDefaultBranchOfRepository(repositoryFolderPath.toString());
}

/**
* Updates the VCS (Version Control System) access log for clone and pull actions using HTTPS.
* <p>
Expand Down Expand Up @@ -907,7 +913,6 @@ public void updateAndStoreVCSAccessLogForCloneAndPullHTTPS(HttpServletRequest re
* @param clientOffered the number of objects offered by the client in the operation, used to determine
* if the action is a clone (if 0) or a pull (if greater than 0).
*/
@Async
public void updateAndStoreVCSAccessLogForCloneAndPullSSH(ServerSession session, int clientOffered) {
try {
if (session.getAttribute(SshConstants.USER_KEY).getName().equals(BUILD_USER_NAME)) {
Expand Down Expand Up @@ -937,7 +942,8 @@ public void createVCSAccessLogForFailedAuthenticationAttempt(HttpServletRequest
User user = userRepository.findOneByLogin(usernameAndPassword.username()).orElseThrow(LocalVCAuthException::new);
AuthenticationMechanism mechanism = usernameAndPassword.password().startsWith("vcpat-") ? AuthenticationMechanism.VCS_ACCESS_TOKEN : AuthenticationMechanism.PASSWORD;
LocalVCRepositoryUri localVCRepositoryUri = parseRepositoryUri(servletRequest);
var participation = fetchParticipationFromLocalVCRepositoryUri(localVCRepositoryUri, null);
var participation = programmingExerciseParticipationService.fetchParticipationWithSubmissionsByRepository(localVCRepositoryUri.getRepositoryTypeOrUserName(),
localVCRepositoryUri.toString(), null);
var ipAddress = servletRequest.getRemoteAddr();
vcsAccessLogService.ifPresent(service -> service.storeAccessLog(user, participation, RepositoryActionType.CLONE_FAIL, mechanism, "", ipAddress));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ public void updateCommitHash(ProgrammingExerciseParticipation participation, Str
}
}

/**
* Updates the commit hash of the newest log entry
*
* @param localVCRepositoryUri The localVCRepositoryUri of the participation to which vcsAccessLog belongs to
* @param repositoryActionType The repository action type to which the vcsAccessLog should get updated to
*/
@Async
public void updateRepositoryActionType(LocalVCRepositoryUri localVCRepositoryUri, RepositoryActionType repositoryActionType) {
var repositoryURL = localVCRepositoryUri.toString().replace("/git-upload-pack", "").replace("/git-receive-pack", "");
Expand All @@ -82,6 +88,11 @@ public void updateRepositoryActionType(LocalVCRepositoryUri localVCRepositoryUri
}
}

/**
* Saves an vcsAccessLog
*
* @param vcsAccessLog The vcsAccessLog to save
*/
@Async
public void saveVcsAccesslog(VcsAccessLog vcsAccessLog) {
vcsAccessLogRepository.save(vcsAccessLog);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package de.tum.cit.aet.artemis.programming.test_repository;

import static org.springframework.data.jpa.repository.EntityGraph.EntityGraphType.LOAD;

import java.util.Optional;

import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.stereotype.Repository;

import de.tum.cit.aet.artemis.programming.domain.TemplateProgrammingExerciseParticipation;
import de.tum.cit.aet.artemis.programming.repository.TemplateProgrammingExerciseParticipationRepository;

@Repository
@Primary
public interface TemplateProgrammingExerciseParticipationTestRepository extends TemplateProgrammingExerciseParticipationRepository {

@EntityGraph(type = LOAD, attributePaths = { "results", "submissions" })
Optional<TemplateProgrammingExerciseParticipation> findWithEagerResultsAndSubmissionsByProgrammingExerciseId(long exerciseId);

default TemplateProgrammingExerciseParticipation findWithEagerResultsAndSubmissionsByProgrammingExerciseIdElseThrow(long exerciseId) {
return getValueElseThrow(findWithEagerResultsAndSubmissionsByProgrammingExerciseId(exerciseId));
}
}
Loading

0 comments on commit ed27a1b

Please sign in to comment.