From f47a0b26b579e57890da0613d3c0a30af5ae2fff Mon Sep 17 00:00:00 2001 From: entholzer Date: Fri, 4 Oct 2024 23:50:53 +0200 Subject: [PATCH 01/16] improved access log handling --- .../programming/domain/VcsAccessLog.java | 4 + .../repository/VcsAccessLogRepository.java | 15 +- .../localvc/ArtemisGitServletService.java | 9 ++ .../service/localvc/LocalVCFetchFilter.java | 6 + .../localvc/LocalVCFetchPreUploadHook.java | 37 +++++ .../localvc/LocalVCFetchPreUploadHookSSH.java | 39 +++++ .../service/localvc/LocalVCPushFilter.java | 1 + .../localvc/LocalVCServletService.java | 137 +++++++++++++++++- .../SshGitLocationResolverService.java | 3 +- .../service/localvc/VcsAccessLogService.java | 23 ++- .../service/localvc/ssh/SshGitCommand.java | 2 + .../web/repository/RepositoryActionType.java | 2 +- 12 files changed, 256 insertions(+), 22 deletions(-) create mode 100644 src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchPreUploadHook.java create mode 100644 src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchPreUploadHookSSH.java diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/domain/VcsAccessLog.java b/src/main/java/de/tum/cit/aet/artemis/programming/domain/VcsAccessLog.java index 560ca52a31c1..9d5155d63f14 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/domain/VcsAccessLog.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/domain/VcsAccessLog.java @@ -77,6 +77,10 @@ public void setCommitHash(String commitHash) { this.commitHash = commitHash; } + public void setRepositoryActionType(RepositoryActionType repositoryActionType) { + this.repositoryActionType = repositoryActionType; + } + public User getUser() { return user; } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/repository/VcsAccessLogRepository.java b/src/main/java/de/tum/cit/aet/artemis/programming/repository/VcsAccessLogRepository.java index af342179e111..628019f34eaa 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/repository/VcsAccessLogRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/repository/VcsAccessLogRepository.java @@ -33,14 +33,14 @@ public interface VcsAccessLogRepository extends ArtemisJpaRepository findNewestByParticipationIdWhereCommitHashIsNull(@Param("participationId") long participationId); + FROM VcsAccessLog vcsAccessLog + WHERE vcsAccessLog.participation.id = :participationId + ORDER BY vcsAccessLog.timestamp DESC + LIMIT 1 + """) + Optional findNewestByParticipationId(@Param("participationId") long participationId); /** * Retrieves a list of {@link VcsAccessLog} entities associated with the specified participation ID. @@ -62,7 +62,6 @@ public interface VcsAccessLogRepository extends ArtemisJpaRepository { + UploadPack uploadPack = new UploadPack(repository); + + // Add the custom pre-upload hook + uploadPack.setPreUploadHook(new LocalVCFetchPreUploadHook(localVCServletService, request)); + return uploadPack; + }); } } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchFilter.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchFilter.java index 504355f1cdf2..ea3b18a0e59b 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchFilter.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchFilter.java @@ -10,6 +10,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.security.core.AuthenticationException; import org.springframework.web.filter.OncePerRequestFilter; import de.tum.cit.aet.artemis.core.exception.localvc.LocalVCAuthException; @@ -44,6 +45,11 @@ public void doFilterInternal(HttpServletRequest servletRequest, HttpServletRespo servletResponse.setStatus(localVCServletService.getHttpStatusForException(e, servletRequest.getRequestURI())); return; } + catch (AuthenticationException e) { + // intercept failed authentication to log it in the VCS access log + localVCServletService.logFailedAttempt(servletRequest); + throw e; + } filterChain.doFilter(servletRequest, servletResponse); } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchPreUploadHook.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchPreUploadHook.java new file mode 100644 index 000000000000..6988864d0e22 --- /dev/null +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchPreUploadHook.java @@ -0,0 +1,37 @@ +package de.tum.cit.aet.artemis.programming.service.localvc; + +import java.util.Collection; + +import jakarta.servlet.http.HttpServletRequest; + +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.transport.PreUploadHook; +import org.eclipse.jgit.transport.UploadPack; + +public class LocalVCFetchPreUploadHook implements PreUploadHook { + + private final LocalVCServletService localVCServletService; + + private final HttpServletRequest request; + + public LocalVCFetchPreUploadHook(LocalVCServletService localVCServletService, HttpServletRequest request) { + this.localVCServletService = localVCServletService; + this.request = request; + } + + @Override + public void onBeginNegotiateRound(UploadPack uploadPack, Collection collection, int cntOffered) { + localVCServletService.addVCSAccessLogForCloneAndPull(request, cntOffered); + + } + + @Override + public void onEndNegotiateRound(UploadPack uploadPack, Collection collection, int i, int i1, boolean b) { + + } + + @Override + public void onSendPack(UploadPack uploadPack, Collection collection, Collection collection1) { + + } +} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchPreUploadHookSSH.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchPreUploadHookSSH.java new file mode 100644 index 000000000000..cca73a741067 --- /dev/null +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchPreUploadHookSSH.java @@ -0,0 +1,39 @@ +package de.tum.cit.aet.artemis.programming.service.localvc; + +import java.nio.file.Path; +import java.util.Collection; + +import org.apache.sshd.server.session.ServerSession; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.transport.PreUploadHook; +import org.eclipse.jgit.transport.UploadPack; + +public class LocalVCFetchPreUploadHookSSH implements PreUploadHook { + + private final LocalVCServletService localVCServletService; + + private final ServerSession serverSession; + + private final Path rootDir; + + public LocalVCFetchPreUploadHookSSH(LocalVCServletService localVCServletService, ServerSession serverSession, Path rootDir) { + this.localVCServletService = localVCServletService; + this.serverSession = serverSession; + this.rootDir = rootDir; + } + + @Override + public void onBeginNegotiateRound(UploadPack uploadPack, Collection collection, int cntOffered) { + localVCServletService.addVCSAccessLogForCloneAndPulloverSSH(serverSession, rootDir, cntOffered); + } + + @Override + public void onEndNegotiateRound(UploadPack uploadPack, Collection collection, int i, int i1, boolean b) { + + } + + @Override + public void onSendPack(UploadPack uploadPack, Collection collection, Collection collection1) { + + } +} diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCPushFilter.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCPushFilter.java index 3d178b998cdd..5ac5967f227a 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCPushFilter.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCPushFilter.java @@ -47,6 +47,7 @@ public void doFilterInternal(HttpServletRequest servletRequest, HttpServletRespo servletResponse.setStatus(localVCServletService.getHttpStatusForException(e, servletRequest.getRequestURI())); return; } + this.localVCServletService.addVCSAccessLogForPush(servletRequest); filterChain.doFilter(servletRequest, servletResponse); diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java index 470b7f815322..423c91368995 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java @@ -5,6 +5,7 @@ import static de.tum.cit.aet.artemis.programming.service.localvc.LocalVCPersonalAccessTokenManagementService.VCS_ACCESS_TOKEN_LENGTH; import java.io.IOException; +import java.net.InetSocketAddress; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; @@ -20,6 +21,7 @@ import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; +import org.apache.sshd.server.session.ServerSession; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.errors.RepositoryNotFoundException; @@ -32,6 +34,7 @@ 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; @@ -66,6 +69,7 @@ import de.tum.cit.aet.artemis.programming.service.ProgrammingTriggerService; import de.tum.cit.aet.artemis.programming.service.RepositoryAccessService; import de.tum.cit.aet.artemis.programming.service.ci.ContinuousIntegrationTriggerService; +import de.tum.cit.aet.artemis.programming.service.localvc.ssh.SshConstants; import de.tum.cit.aet.artemis.programming.web.repository.RepositoryActionType; /** @@ -207,7 +211,8 @@ public Repository resolveRepository(String repositoryPath) throws RepositoryNotF * @throws LocalVCForbiddenException If the user is not allowed to access the repository, e.g. because offline IDE usage is not allowed or the due date has passed. * @throws LocalVCInternalException If an internal error occurs, e.g. because the LocalVCRepositoryUri could not be created. */ - public void authenticateAndAuthorizeGitRequest(HttpServletRequest request, RepositoryActionType repositoryAction) throws LocalVCAuthException, LocalVCForbiddenException { + public void authenticateAndAuthorizeGitRequest(HttpServletRequest request, RepositoryActionType repositoryAction) + throws LocalVCAuthException, LocalVCForbiddenException, AuthenticationException { long timeNanoStart = System.nanoTime(); @@ -274,7 +279,8 @@ private AuthenticationMechanism resolveAuthenticationMechanism(String authorizat return AuthenticationMechanism.PARTICIPATION_VCS_ACCESS_TOKEN; } - private User authenticateUser(String authorizationHeader, ProgrammingExercise exercise, LocalVCRepositoryUri localVCRepositoryUri) throws LocalVCAuthException { + private User authenticateUser(String authorizationHeader, ProgrammingExercise exercise, LocalVCRepositoryUri localVCRepositoryUri) + throws LocalVCAuthException, AuthenticationException { UsernameAndPassword usernameAndPassword = extractUsernameAndPassword(authorizationHeader); @@ -445,15 +451,15 @@ public void authorizeUser(String repositoryTypeOrUserName, User user, Programmin // TODO: retrieving the git commit hash should be done ASYNC together with storing the log in the database to avoid long waiting times during permission check String commitHash = null; try { - if (repositoryActionType == RepositoryActionType.READ) { - String relativeRepositoryPath = localVCRepositoryUri.getRelativeRepositoryPath().toString(); - try (Repository repository = resolveRepository(relativeRepositoryPath)) { - commitHash = getLatestCommitHash(repository); - } + String relativeRepositoryPath = localVCRepositoryUri.getRelativeRepositoryPath().toString(); + try (Repository repository = resolveRepository(relativeRepositoryPath)) { + commitHash = getLatestCommitHash(repository); } + // Write a access log entry to the database + RepositoryActionType finalRepositoryActionType = repositoryActionType == RepositoryActionType.READ ? RepositoryActionType.PULL : RepositoryActionType.PUSH; String finalCommitHash = commitHash; - vcsAccessLogService.ifPresent(service -> service.storeAccessLog(user, participation, repositoryActionType, authenticationMechanism, finalCommitHash, ipAddress)); + vcsAccessLogService.ifPresent(service -> service.storeAccessLog(user, participation, finalRepositoryActionType, authenticationMechanism, finalCommitHash, ipAddress)); } // NOTE: we intentionally catch all issues here to avoid that the user is blocked from accessing the repository catch (Exception e) { @@ -704,6 +710,26 @@ private Commit extractCommitInfo(String commitHash, Repository repository) throw return new Commit(commitHash, author.getName(), revCommit.getFullMessage(), author.getEmailAddress(), branch); } + private User getUserFromRequest(HttpServletRequest request) throws LocalVCAuthException { + String authorizationHeader = request.getHeader(LocalVCServletService.AUTHORIZATION_HEADER); + + UsernameAndPassword usernameAndPassword = extractUsernameAndPassword(authorizationHeader); + return userRepository.findOneByLogin(usernameAndPassword.username()).orElseThrow(LocalVCAuthException::new); + } + + private ProgrammingExerciseParticipation getExerciseParticipationFromLocalVCRepositoryUri(LocalVCRepositoryUri localVCRepositoryUri) { + String projectKey = localVCRepositoryUri.getProjectKey(); + String repositoryTypeOrUserName = localVCRepositoryUri.getRepositoryTypeOrUserName(); + ProgrammingExercise exercise = getProgrammingExerciseOrThrow(projectKey); + + return programmingExerciseParticipationService.getParticipationForRepository(exercise, repositoryTypeOrUserName, localVCRepositoryUri.isPracticeRepository(), false); + } + + private ProgrammingExerciseParticipation getExerciseParticipationFromRequest(HttpServletRequest request) { + LocalVCRepositoryUri localVCRepositoryUri = parseRepositoryUri(request); + return getExerciseParticipationFromLocalVCRepositoryUri(localVCRepositoryUri); + } + /** * Determine the default branch of the given repository. * @@ -715,6 +741,101 @@ public static String getDefaultBranchOfRepository(Repository repository) { return LocalVCService.getDefaultBranchOfRepository(repositoryFolderPath.toString()); } + public void logFailedAttempt(HttpServletRequest servletRequest) { + try { + User user = getUserFromRequest(servletRequest); + var participation = getExerciseParticipationFromRequest(servletRequest); + vcsAccessLogService.ifPresent(service -> service.storeAccessLog(user, participation, RepositoryActionType.CLONE_FAIL, AuthenticationMechanism.PASSWORD, "", "")); + } + catch (LocalVCAuthException ignored) { + } + } + + public void addVCSAccessLogForCloneAndPull(HttpServletRequest request, int cntOffered) { + try { + String authorizationHeader = request.getHeader("Authorization"); + UsernameAndPassword usernameAndPassword = extractUsernameAndPassword(authorizationHeader); + String userName = usernameAndPassword.username(); + if (userName.equals("buildjob_user")) { + return; + } + RepositoryActionType repositoryActionType = getRepositoryActionReadType(cntOffered); + var participation = getExerciseParticipationFromRequest(request); + + vcsAccessLogService.ifPresent(service -> service.updateRepositoryActionType(participation, repositoryActionType)); + + log.info("username {} int {}", usernameAndPassword.username(), cntOffered); + } + catch (Exception ignored) { + } + } + + public void addVCSAccessLogForCloneAndPulloverSSH(ServerSession session, Path rootDir, int cntOffered) { + try { + if (session.getAttribute(SshConstants.USER_KEY).getName().equals("buildjob_user")) { + return; + } + RepositoryActionType repositoryActionType = getRepositoryActionReadType(cntOffered); + var parti = getExerciseParticipationFromLocalVCRepositoryUri(getLocalVCRepositoryUri(rootDir)); + vcsAccessLogService.ifPresent(service -> service.updateRepositoryActionType(parti, repositoryActionType)); + } + catch (Exception ignored) { + } + } + + @Async + public void addVCSAccessLogForPush(HttpServletRequest request) { + if (!request.getMethod().equals("POST")) { + return; + } + try { + String authorizationHeader = request.getHeader("Authorization"); + UsernameAndPassword usernameAndPassword = extractUsernameAndPassword(authorizationHeader); + String userName = usernameAndPassword.username(); + + if (userName.equals("buildjob_user")) { + return; + } + RepositoryActionType repositoryActionType = RepositoryActionType.PUSH; + + var participation = getExerciseParticipationFromRequest(request); + + vcsAccessLogService.ifPresent(service -> service.updateRepositoryActionType(participation, repositoryActionType)); + + } + catch (Exception ignored) { + } + } + + @Async + public void addVcsAccessLogForSSH(User user, LocalVCRepositoryUri localVCRepositoryUri, Repository repo, RepositoryActionType repositoryAction, ServerSession session) { + try { + String commitHash = null; + try { + commitHash = getLatestCommitHash(repo); + } + catch (Exception e) { + log.warn("failed attempt to add VCS access log for user " + user.getName() + ": " + e.getMessage()); + } + var participation = getExerciseParticipationFromLocalVCRepositoryUri(localVCRepositoryUri); + var ipAddress = ((InetSocketAddress) session.getRemoteAddress()).getAddress().getHostAddress(); + RepositoryActionType finalActionType = repositoryAction == RepositoryActionType.READ ? RepositoryActionType.PULL : RepositoryActionType.PUSH; + String finalCommitHash = commitHash; + vcsAccessLogService.ifPresent(service -> service.storeAccessLog(user, participation, finalActionType, AuthenticationMechanism.SSH, finalCommitHash, ipAddress)); + } + catch (Exception ignored) { + } + } + + public RepositoryActionType getRepositoryActionReadType(int cntOffered) { + if (cntOffered == 0) { + return RepositoryActionType.CLONE; + } + else { + return RepositoryActionType.PULL; + } + } + record UsernameAndPassword(String username, String password) { } } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/SshGitLocationResolverService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/SshGitLocationResolverService.java index a61712685ef7..5f30b9926939 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/SshGitLocationResolverService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/SshGitLocationResolverService.java @@ -70,12 +70,12 @@ public Path resolveRootDirectory(String command, String[] args, ServerSession se // git-upload-pack means fetch (read operation), git-receive-pack means push (write operation) final var repositoryAction = gitCommand.equals("git-upload-pack") ? RepositoryActionType.READ : gitCommand.equals("git-receive-pack") ? RepositoryActionType.WRITE : null; + final var user = session.getAttribute(SshConstants.USER_KEY); if (session.getAttribute(SshConstants.IS_BUILD_AGENT_KEY) && repositoryAction == RepositoryActionType.READ) { // We already checked for build agent authenticity } else { - final var user = session.getAttribute(SshConstants.USER_KEY); try { localVCServletService.authorizeUser(repositoryTypeOrUserName, user, exercise, repositoryAction, AuthenticationMechanism.SSH, session.getClientAddress().toString(), localVCRepositoryUri); @@ -89,6 +89,7 @@ public Path resolveRootDirectory(String command, String[] args, ServerSession se // we cannot trust unvalidated user input final var localRepositoryPath = localVCRepositoryUri.getRelativeRepositoryPath().toString(); try (Repository repo = localVCServletService.resolveRepository(localRepositoryPath)) { + localVCServletService.addVcsAccessLogForSSH(user, localVCRepositoryUri, repo, repositoryAction, session); return repo.getDirectory().toPath(); } } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/VcsAccessLogService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/VcsAccessLogService.java index 57330b4d51e0..1b9d1da2f657 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/VcsAccessLogService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/VcsAccessLogService.java @@ -7,6 +7,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Profile; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import de.tum.cit.aet.artemis.core.domain.User; @@ -44,7 +45,7 @@ public class VcsAccessLogService { * @param commitHash The latest commit hash * @param ipAddress The ip address of the user accessing the repository */ - // TODO: this should be ASYNC to avoid long waiting times during permission check + @Async public void storeAccessLog(User user, ProgrammingExerciseParticipation participation, RepositoryActionType actionType, AuthenticationMechanism authenticationMechanism, String commitHash, String ipAddress) { log.debug("Storing access operation for user {}", user); @@ -55,19 +56,33 @@ public void storeAccessLog(User user, ProgrammingExerciseParticipation participa } /** - * Updates the commit hash after a successful push + * Updates the commit hash of the newest log entry * * @param participation The participation to which the repository belongs to * @param commitHash The newest commit hash which should get set for the access log entry */ - // TODO: this should be ASYNC to avoid long waiting times during permission check + @Async public void updateCommitHash(ProgrammingExerciseParticipation participation, String commitHash) { - vcsAccessLogRepository.findNewestByParticipationIdWhereCommitHashIsNull(participation.getId()).ifPresent(entry -> { + vcsAccessLogRepository.findNewestByParticipationId(participation.getId()).ifPresent(entry -> { entry.setCommitHash(commitHash); vcsAccessLogRepository.save(entry); }); } + /** + * Updates the repository action type of the newest log entry + * + * @param participation The participation to which the repository belongs to + * @param repositoryActionType The repositoryActionType which should get set for the newest access log entry + */ + @Async + public void updateRepositoryActionType(ProgrammingExerciseParticipation participation, RepositoryActionType repositoryActionType) { + vcsAccessLogRepository.findNewestByParticipationId(participation.getId()).ifPresent(entry -> { + entry.setRepositoryActionType(repositoryActionType); + vcsAccessLogRepository.save(entry); + }); + } + /** * Stores the log for a push from the code editor. * diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/ssh/SshGitCommand.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/ssh/SshGitCommand.java index b0307ec38bb4..7ec968646217 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/ssh/SshGitCommand.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/ssh/SshGitCommand.java @@ -21,6 +21,7 @@ import org.eclipse.jgit.util.FS; import de.tum.cit.aet.artemis.core.domain.User; +import de.tum.cit.aet.artemis.programming.service.localvc.LocalVCFetchPreUploadHookSSH; import de.tum.cit.aet.artemis.programming.service.localvc.LocalVCPostPushHook; import de.tum.cit.aet.artemis.programming.service.localvc.LocalVCPrePushHook; import de.tum.cit.aet.artemis.programming.service.localvc.LocalVCServletService; @@ -84,6 +85,7 @@ public void run() { if (GenericUtils.isNotBlank(protocol)) { uploadPack.setExtraParameters(Collections.singleton(protocol)); } + uploadPack.setPreUploadHook(new LocalVCFetchPreUploadHookSSH(localVCServletService, getServerSession(), rootDir)); uploadPack.upload(getInputStream(), getOutputStream(), getErrorStream()); } else if (RemoteConfig.DEFAULT_RECEIVE_PACK.equals(subCommand)) { diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/web/repository/RepositoryActionType.java b/src/main/java/de/tum/cit/aet/artemis/programming/web/repository/RepositoryActionType.java index f7f62ebb4989..76c3a88daec6 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/web/repository/RepositoryActionType.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/web/repository/RepositoryActionType.java @@ -4,5 +4,5 @@ * Determines if a repository action only reads (e.g. get a file from the repo) or updates (e.g. create a new file in the repo). */ public enum RepositoryActionType { - READ, WRITE, RESET + READ, WRITE, RESET, CLONE, PULL, PUSH, CLONE_FAIL, PULL_FAIL, PUSH_FAIL, DEBUG_1, DEBUG_2 } From 573062f5010a437b399b62742e13bb262939f9a4 Mon Sep 17 00:00:00 2001 From: entholzer Date: Tue, 8 Oct 2024 16:55:52 +0200 Subject: [PATCH 02/16] removed redundant method --- .../localvc/LocalVCServletService.java | 21 ------------------- .../SshGitLocationResolverService.java | 1 - 2 files changed, 22 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java index 423c91368995..a5b07201d660 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java @@ -5,7 +5,6 @@ import static de.tum.cit.aet.artemis.programming.service.localvc.LocalVCPersonalAccessTokenManagementService.VCS_ACCESS_TOKEN_LENGTH; import java.io.IOException; -import java.net.InetSocketAddress; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; @@ -807,26 +806,6 @@ public void addVCSAccessLogForPush(HttpServletRequest request) { } } - @Async - public void addVcsAccessLogForSSH(User user, LocalVCRepositoryUri localVCRepositoryUri, Repository repo, RepositoryActionType repositoryAction, ServerSession session) { - try { - String commitHash = null; - try { - commitHash = getLatestCommitHash(repo); - } - catch (Exception e) { - log.warn("failed attempt to add VCS access log for user " + user.getName() + ": " + e.getMessage()); - } - var participation = getExerciseParticipationFromLocalVCRepositoryUri(localVCRepositoryUri); - var ipAddress = ((InetSocketAddress) session.getRemoteAddress()).getAddress().getHostAddress(); - RepositoryActionType finalActionType = repositoryAction == RepositoryActionType.READ ? RepositoryActionType.PULL : RepositoryActionType.PUSH; - String finalCommitHash = commitHash; - vcsAccessLogService.ifPresent(service -> service.storeAccessLog(user, participation, finalActionType, AuthenticationMechanism.SSH, finalCommitHash, ipAddress)); - } - catch (Exception ignored) { - } - } - public RepositoryActionType getRepositoryActionReadType(int cntOffered) { if (cntOffered == 0) { return RepositoryActionType.CLONE; diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/SshGitLocationResolverService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/SshGitLocationResolverService.java index 5f30b9926939..d45bfda1c179 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/SshGitLocationResolverService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/SshGitLocationResolverService.java @@ -89,7 +89,6 @@ public Path resolveRootDirectory(String command, String[] args, ServerSession se // we cannot trust unvalidated user input final var localRepositoryPath = localVCRepositoryUri.getRelativeRepositoryPath().toString(); try (Repository repo = localVCServletService.resolveRepository(localRepositoryPath)) { - localVCServletService.addVcsAccessLogForSSH(user, localVCRepositoryUri, repo, repositoryAction, session); return repo.getDirectory().toPath(); } } From 055e4708e3c3876827dae1e1c8263abd9d3bb042 Mon Sep 17 00:00:00 2001 From: entholzer Date: Tue, 8 Oct 2024 17:03:58 +0200 Subject: [PATCH 03/16] improved naming --- .../service/localvc/LocalVCFetchFilter.java | 2 +- .../localvc/LocalVCFetchPreUploadHook.java | 2 +- .../localvc/LocalVCFetchPreUploadHookSSH.java | 2 +- .../service/localvc/LocalVCPushFilter.java | 2 +- .../localvc/LocalVCServletService.java | 56 ++++++++----------- 5 files changed, 28 insertions(+), 36 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchFilter.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchFilter.java index ea3b18a0e59b..0a3467289521 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchFilter.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchFilter.java @@ -47,7 +47,7 @@ public void doFilterInternal(HttpServletRequest servletRequest, HttpServletRespo } catch (AuthenticationException e) { // intercept failed authentication to log it in the VCS access log - localVCServletService.logFailedAttempt(servletRequest); + localVCServletService.addVCSAccessLogFailedAttempt(servletRequest); throw e; } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchPreUploadHook.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchPreUploadHook.java index 6988864d0e22..35d214c8ce66 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchPreUploadHook.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchPreUploadHook.java @@ -21,7 +21,7 @@ public LocalVCFetchPreUploadHook(LocalVCServletService localVCServletService, Ht @Override public void onBeginNegotiateRound(UploadPack uploadPack, Collection collection, int cntOffered) { - localVCServletService.addVCSAccessLogForCloneAndPull(request, cntOffered); + localVCServletService.updateVCSAccessLogForCloneAndPullHTTPS(request, cntOffered); } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchPreUploadHookSSH.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchPreUploadHookSSH.java index cca73a741067..82f6e1d0ff4b 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchPreUploadHookSSH.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchPreUploadHookSSH.java @@ -24,7 +24,7 @@ public LocalVCFetchPreUploadHookSSH(LocalVCServletService localVCServletService, @Override public void onBeginNegotiateRound(UploadPack uploadPack, Collection collection, int cntOffered) { - localVCServletService.addVCSAccessLogForCloneAndPulloverSSH(serverSession, rootDir, cntOffered); + localVCServletService.updateVCSAccessLogForCloneAndPullSSH(serverSession, rootDir, cntOffered); } @Override diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCPushFilter.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCPushFilter.java index 5ac5967f227a..914205081e60 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCPushFilter.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCPushFilter.java @@ -47,7 +47,7 @@ public void doFilterInternal(HttpServletRequest servletRequest, HttpServletRespo servletResponse.setStatus(localVCServletService.getHttpStatusForException(e, servletRequest.getRequestURI())); return; } - this.localVCServletService.addVCSAccessLogForPush(servletRequest); + this.localVCServletService.updateVCSAccessLogForPushHTTPS(servletRequest); filterChain.doFilter(servletRequest, servletResponse); diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java index a5b07201d660..8197c5e6c896 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java @@ -740,17 +740,7 @@ public static String getDefaultBranchOfRepository(Repository repository) { return LocalVCService.getDefaultBranchOfRepository(repositoryFolderPath.toString()); } - public void logFailedAttempt(HttpServletRequest servletRequest) { - try { - User user = getUserFromRequest(servletRequest); - var participation = getExerciseParticipationFromRequest(servletRequest); - vcsAccessLogService.ifPresent(service -> service.storeAccessLog(user, participation, RepositoryActionType.CLONE_FAIL, AuthenticationMechanism.PASSWORD, "", "")); - } - catch (LocalVCAuthException ignored) { - } - } - - public void addVCSAccessLogForCloneAndPull(HttpServletRequest request, int cntOffered) { + public void updateVCSAccessLogForCloneAndPullHTTPS(HttpServletRequest request, int cntOffered) { try { String authorizationHeader = request.getHeader("Authorization"); UsernameAndPassword usernameAndPassword = extractUsernameAndPassword(authorizationHeader); @@ -769,21 +759,8 @@ public void addVCSAccessLogForCloneAndPull(HttpServletRequest request, int cntOf } } - public void addVCSAccessLogForCloneAndPulloverSSH(ServerSession session, Path rootDir, int cntOffered) { - try { - if (session.getAttribute(SshConstants.USER_KEY).getName().equals("buildjob_user")) { - return; - } - RepositoryActionType repositoryActionType = getRepositoryActionReadType(cntOffered); - var parti = getExerciseParticipationFromLocalVCRepositoryUri(getLocalVCRepositoryUri(rootDir)); - vcsAccessLogService.ifPresent(service -> service.updateRepositoryActionType(parti, repositoryActionType)); - } - catch (Exception ignored) { - } - } - @Async - public void addVCSAccessLogForPush(HttpServletRequest request) { + public void updateVCSAccessLogForPushHTTPS(HttpServletRequest request) { if (!request.getMethod().equals("POST")) { return; } @@ -791,30 +768,45 @@ public void addVCSAccessLogForPush(HttpServletRequest request) { String authorizationHeader = request.getHeader("Authorization"); UsernameAndPassword usernameAndPassword = extractUsernameAndPassword(authorizationHeader); String userName = usernameAndPassword.username(); - if (userName.equals("buildjob_user")) { return; } RepositoryActionType repositoryActionType = RepositoryActionType.PUSH; - var participation = getExerciseParticipationFromRequest(request); vcsAccessLogService.ifPresent(service -> service.updateRepositoryActionType(participation, repositoryActionType)); + } + catch (Exception ignored) { + } + } + public void updateVCSAccessLogForCloneAndPullSSH(ServerSession session, Path rootDir, int cntOffered) { + try { + if (session.getAttribute(SshConstants.USER_KEY).getName().equals("buildjob_user")) { + return; + } + RepositoryActionType repositoryActionType = getRepositoryActionReadType(cntOffered); + var parti = getExerciseParticipationFromLocalVCRepositoryUri(getLocalVCRepositoryUri(rootDir)); + vcsAccessLogService.ifPresent(service -> service.updateRepositoryActionType(parti, repositoryActionType)); } catch (Exception ignored) { } } - public RepositoryActionType getRepositoryActionReadType(int cntOffered) { - if (cntOffered == 0) { - return RepositoryActionType.CLONE; + public void addVCSAccessLogFailedAttempt(HttpServletRequest servletRequest) { + try { + User user = getUserFromRequest(servletRequest); + var participation = getExerciseParticipationFromRequest(servletRequest); + vcsAccessLogService.ifPresent(service -> service.storeAccessLog(user, participation, RepositoryActionType.CLONE_FAIL, AuthenticationMechanism.PASSWORD, "", "")); } - else { - return RepositoryActionType.PULL; + catch (LocalVCAuthException ignored) { } } + public RepositoryActionType getRepositoryActionReadType(int cntOffered) { + return cntOffered == 0 ? RepositoryActionType.CLONE : RepositoryActionType.PULL; + } + record UsernameAndPassword(String username, String password) { } } From 657f8dcbef03ad299711398a18c5e0e5106059ee Mon Sep 17 00:00:00 2001 From: entholzer Date: Tue, 8 Oct 2024 17:21:43 +0200 Subject: [PATCH 04/16] add docs --- .../localvc/LocalVCServletService.java | 73 ++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java index 8197c5e6c896..6f2d7d00d879 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java @@ -448,8 +448,8 @@ public void authorizeUser(String repositoryTypeOrUserName, User user, Programmin throw new LocalVCForbiddenException(e); } // TODO: retrieving the git commit hash should be done ASYNC together with storing the log in the database to avoid long waiting times during permission check - String commitHash = null; try { + String commitHash; String relativeRepositoryPath = localVCRepositoryUri.getRelativeRepositoryPath().toString(); try (Repository repository = resolveRepository(relativeRepositoryPath)) { commitHash = getLatestCommitHash(repository); @@ -709,6 +709,13 @@ private Commit extractCommitInfo(String commitHash, Repository repository) throw return new Commit(commitHash, author.getName(), revCommit.getFullMessage(), author.getEmailAddress(), branch); } + /** + * Retrieves the user from the HTTP request's authorization header. + * + * @param request the {@link HttpServletRequest} containing the authorization header. + * @return the {@link User} associated with the login found in the authorization header. + * @throws LocalVCAuthException if the user cannot be found or authorization fails. + */ private User getUserFromRequest(HttpServletRequest request) throws LocalVCAuthException { String authorizationHeader = request.getHeader(LocalVCServletService.AUTHORIZATION_HEADER); @@ -716,6 +723,12 @@ private User getUserFromRequest(HttpServletRequest request) throws LocalVCAuthEx return userRepository.findOneByLogin(usernameAndPassword.username()).orElseThrow(LocalVCAuthException::new); } + /** + * 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 getExerciseParticipationFromLocalVCRepositoryUri(LocalVCRepositoryUri localVCRepositoryUri) { String projectKey = localVCRepositoryUri.getProjectKey(); String repositoryTypeOrUserName = localVCRepositoryUri.getRepositoryTypeOrUserName(); @@ -724,6 +737,12 @@ private ProgrammingExerciseParticipation getExerciseParticipationFromLocalVCRepo return programmingExerciseParticipationService.getParticipationForRepository(exercise, repositoryTypeOrUserName, localVCRepositoryUri.isPracticeRepository(), false); } + /** + * Retrieves the participation for a programming exercise based on the HTTP request. + * + * @param request the {@link HttpServletRequest} containing the repository URI. + * @return the {@link ProgrammingExerciseParticipation} corresponding to the repository details in the request. + */ private ProgrammingExerciseParticipation getExerciseParticipationFromRequest(HttpServletRequest request) { LocalVCRepositoryUri localVCRepositoryUri = parseRepositoryUri(request); return getExerciseParticipationFromLocalVCRepositoryUri(localVCRepositoryUri); @@ -740,6 +759,18 @@ public static String getDefaultBranchOfRepository(Repository repository) { return LocalVCService.getDefaultBranchOfRepository(repositoryFolderPath.toString()); } + /** + * Updates the VCS (Version Control System) access log for clone and pull actions using HTTPS. + *

+ * This method logs the access information based on the incoming HTTP request. It checks if the action + * is performed by a build job user and, if not, records the user's repository action (clone or pull). + * The action type is determined based on the number of offers (`cntOffered`). + * + * @param request the {@link HttpServletRequest} containing the HTTP request data, including headers. + * @param cntOffered 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 updateVCSAccessLogForCloneAndPullHTTPS(HttpServletRequest request, int cntOffered) { try { String authorizationHeader = request.getHeader("Authorization"); @@ -759,6 +790,16 @@ public void updateVCSAccessLogForCloneAndPullHTTPS(HttpServletRequest request, i } } + /** + * Updates the VCS access log for a push action using HTTPS. + *

+ * This method logs the access information if the HTTP request is a POST request and the action + * is not performed by a build job user. The repository action type is set as a push action. + * + * This method is asynchronous. + * + * @param request the {@link HttpServletRequest} containing the HTTP request data, including headers. + */ @Async public void updateVCSAccessLogForPushHTTPS(HttpServletRequest request) { if (!request.getMethod().equals("POST")) { @@ -780,6 +821,19 @@ public void updateVCSAccessLogForPushHTTPS(HttpServletRequest request) { } } + /** + * Updates the VCS access log for clone and pull actions performed over SSH. + *

+ * This method logs access information based on the SSH session and the root directory of the repository. + * It determines the repository action (clone or pull) based on the number of offers (`cntOffered`) and + * fetches participation details from the local VC repository URI. + * + * @param session the {@link ServerSession} representing the SSH session. + * @param rootDir the {@link Path} to the root directory of the repository. + * @param cntOffered 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 updateVCSAccessLogForCloneAndPullSSH(ServerSession session, Path rootDir, int cntOffered) { try { if (session.getAttribute(SshConstants.USER_KEY).getName().equals("buildjob_user")) { @@ -793,6 +847,14 @@ public void updateVCSAccessLogForCloneAndPullSSH(ServerSession session, Path roo } } + /** + * Adds a failed VCS access attempt to the log. + *

+ * This method logs a failed clone attempt, associating it with the user and participation retrieved + * from the incoming HTTP request. It assumes that the failed attempt used password authentication. + * + * @param servletRequest the {@link HttpServletRequest} containing the HTTP request data. + */ public void addVCSAccessLogFailedAttempt(HttpServletRequest servletRequest) { try { User user = getUserFromRequest(servletRequest); @@ -803,6 +865,15 @@ public void addVCSAccessLogFailedAttempt(HttpServletRequest servletRequest) { } } + /** + * Determines the repository action type for read operations (clone or pull). + *

+ * This method returns a {@link RepositoryActionType} based on the number of objects offered. + * If no objects are offered (0), it is considered a clone; otherwise, it is a pull action. + * + * @param cntOffered the number of objects offered to the client in the operation. + * @return the {@link RepositoryActionType} based on the number of objects offered (clone if 0, pull if greater than 0). + */ public RepositoryActionType getRepositoryActionReadType(int cntOffered) { return cntOffered == 0 ? RepositoryActionType.CLONE : RepositoryActionType.PULL; } From 1dfedd1545505f3b8b723fdc647ed59c1794948c Mon Sep 17 00:00:00 2001 From: entholzer Date: Wed, 9 Oct 2024 22:50:43 +0200 Subject: [PATCH 05/16] added suggested changes and fixed Arch test --- .../service/localvc/LocalVCFetchFilter.java | 2 +- .../localvc/LocalVCFetchPreUploadHook.java | 7 +-- .../localvc/LocalVCFetchPreUploadHookSSH.java | 6 +-- .../localvc/LocalVCServletService.java | 44 ++++++++++--------- .../service/localvc/VcsAccessLogService.java | 9 ++-- 5 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchFilter.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchFilter.java index 0a3467289521..e789bf4c5e78 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchFilter.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchFilter.java @@ -47,7 +47,7 @@ public void doFilterInternal(HttpServletRequest servletRequest, HttpServletRespo } catch (AuthenticationException e) { // intercept failed authentication to log it in the VCS access log - localVCServletService.addVCSAccessLogFailedAttempt(servletRequest); + localVCServletService.createVCSAccessLogForFailedAuthenticationAttempt(servletRequest); throw e; } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchPreUploadHook.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchPreUploadHook.java index 35d214c8ce66..97e7523991d7 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchPreUploadHook.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchPreUploadHook.java @@ -20,18 +20,15 @@ public LocalVCFetchPreUploadHook(LocalVCServletService localVCServletService, Ht } @Override - public void onBeginNegotiateRound(UploadPack uploadPack, Collection collection, int cntOffered) { - localVCServletService.updateVCSAccessLogForCloneAndPullHTTPS(request, cntOffered); - + public void onBeginNegotiateRound(UploadPack uploadPack, Collection collection, int clientOffered) { + localVCServletService.updateVCSAccessLogForCloneAndPullHTTPS(request, clientOffered); } @Override public void onEndNegotiateRound(UploadPack uploadPack, Collection collection, int i, int i1, boolean b) { - } @Override public void onSendPack(UploadPack uploadPack, Collection collection, Collection collection1) { - } } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchPreUploadHookSSH.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchPreUploadHookSSH.java index 82f6e1d0ff4b..09f79348c180 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchPreUploadHookSSH.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchPreUploadHookSSH.java @@ -23,17 +23,15 @@ public LocalVCFetchPreUploadHookSSH(LocalVCServletService localVCServletService, } @Override - public void onBeginNegotiateRound(UploadPack uploadPack, Collection collection, int cntOffered) { - localVCServletService.updateVCSAccessLogForCloneAndPullSSH(serverSession, rootDir, cntOffered); + public void onBeginNegotiateRound(UploadPack uploadPack, Collection collection, int clientOffered) { + localVCServletService.updateVCSAccessLogForCloneAndPullSSH(serverSession, rootDir, clientOffered); } @Override public void onEndNegotiateRound(UploadPack uploadPack, Collection collection, int i, int i1, boolean b) { - } @Override public void onSendPack(UploadPack uploadPack, Collection collection, Collection collection1) { - } } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java index 6f2d7d00d879..7cb6e54faa5a 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java @@ -130,6 +130,8 @@ public void setLocalVCBaseUrl(URL localVCBaseUrl) { */ public static final String AUTHORIZATION_HEADER = "Authorization"; + public static final String BUILD_USER_NAME = "buildjob_user"; + // Cache the retrieved repositories for quicker access. // The resolveRepository method is called multiple times per request. // Key: repositoryPath --> Value: Repository @@ -764,27 +766,27 @@ public static String getDefaultBranchOfRepository(Repository repository) { *

* This method logs the access information based on the incoming HTTP request. It checks if the action * is performed by a build job user and, if not, records the user's repository action (clone or pull). - * The action type is determined based on the number of offers (`cntOffered`). + * The action type is determined based on the number of offers (`clientOffered`). * - * @param request the {@link HttpServletRequest} containing the HTTP request data, including headers. - * @param cntOffered 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). + * @param request the {@link HttpServletRequest} containing the HTTP request data, including headers. + * @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 updateVCSAccessLogForCloneAndPullHTTPS(HttpServletRequest request, int cntOffered) { + public void updateVCSAccessLogForCloneAndPullHTTPS(HttpServletRequest request, int clientOffered) { try { String authorizationHeader = request.getHeader("Authorization"); UsernameAndPassword usernameAndPassword = extractUsernameAndPassword(authorizationHeader); String userName = usernameAndPassword.username(); - if (userName.equals("buildjob_user")) { + if (userName.equals(BUILD_USER_NAME)) { return; } - RepositoryActionType repositoryActionType = getRepositoryActionReadType(cntOffered); + RepositoryActionType repositoryActionType = getRepositoryActionReadType(clientOffered); var participation = getExerciseParticipationFromRequest(request); vcsAccessLogService.ifPresent(service -> service.updateRepositoryActionType(participation, repositoryActionType)); - log.info("username {} int {}", usernameAndPassword.username(), cntOffered); + log.info("username {} int {}", usernameAndPassword.username(), clientOffered); } catch (Exception ignored) { } @@ -809,7 +811,7 @@ public void updateVCSAccessLogForPushHTTPS(HttpServletRequest request) { String authorizationHeader = request.getHeader("Authorization"); UsernameAndPassword usernameAndPassword = extractUsernameAndPassword(authorizationHeader); String userName = usernameAndPassword.username(); - if (userName.equals("buildjob_user")) { + if (userName.equals(BUILD_USER_NAME)) { return; } RepositoryActionType repositoryActionType = RepositoryActionType.PUSH; @@ -825,21 +827,21 @@ public void updateVCSAccessLogForPushHTTPS(HttpServletRequest request) { * Updates the VCS access log for clone and pull actions performed over SSH. *

* This method logs access information based on the SSH session and the root directory of the repository. - * It determines the repository action (clone or pull) based on the number of offers (`cntOffered`) and + * It determines the repository action (clone or pull) based on the number of offers (`clientOffered`) and * fetches participation details from the local VC repository URI. * - * @param session the {@link ServerSession} representing the SSH session. - * @param rootDir the {@link Path} to the root directory of the repository. - * @param cntOffered 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). + * @param session the {@link ServerSession} representing the SSH session. + * @param rootDir the {@link Path} to the root directory of the repository. + * @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 updateVCSAccessLogForCloneAndPullSSH(ServerSession session, Path rootDir, int cntOffered) { + public void updateVCSAccessLogForCloneAndPullSSH(ServerSession session, Path rootDir, int clientOffered) { try { - if (session.getAttribute(SshConstants.USER_KEY).getName().equals("buildjob_user")) { + if (session.getAttribute(SshConstants.USER_KEY).getName().equals(BUILD_USER_NAME)) { return; } - RepositoryActionType repositoryActionType = getRepositoryActionReadType(cntOffered); + RepositoryActionType repositoryActionType = getRepositoryActionReadType(clientOffered); var parti = getExerciseParticipationFromLocalVCRepositoryUri(getLocalVCRepositoryUri(rootDir)); vcsAccessLogService.ifPresent(service -> service.updateRepositoryActionType(parti, repositoryActionType)); } @@ -855,7 +857,7 @@ public void updateVCSAccessLogForCloneAndPullSSH(ServerSession session, Path roo * * @param servletRequest the {@link HttpServletRequest} containing the HTTP request data. */ - public void addVCSAccessLogFailedAttempt(HttpServletRequest servletRequest) { + public void createVCSAccessLogForFailedAuthenticationAttempt(HttpServletRequest servletRequest) { try { User user = getUserFromRequest(servletRequest); var participation = getExerciseParticipationFromRequest(servletRequest); @@ -871,11 +873,11 @@ public void addVCSAccessLogFailedAttempt(HttpServletRequest servletRequest) { * This method returns a {@link RepositoryActionType} based on the number of objects offered. * If no objects are offered (0), it is considered a clone; otherwise, it is a pull action. * - * @param cntOffered the number of objects offered to the client in the operation. + * @param clientOffered the number of objects offered to the client in the operation. * @return the {@link RepositoryActionType} based on the number of objects offered (clone if 0, pull if greater than 0). */ - public RepositoryActionType getRepositoryActionReadType(int cntOffered) { - return cntOffered == 0 ? RepositoryActionType.CLONE : RepositoryActionType.PULL; + public RepositoryActionType getRepositoryActionReadType(int clientOffered) { + return clientOffered == 0 ? RepositoryActionType.CLONE : RepositoryActionType.PULL; } record UsernameAndPassword(String username, String password) { diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/VcsAccessLogService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/VcsAccessLogService.java index 1b9d1da2f657..af1972ce2e1c 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/VcsAccessLogService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/VcsAccessLogService.java @@ -70,12 +70,11 @@ public void updateCommitHash(ProgrammingExerciseParticipation participation, Str } /** - * Updates the repository action type of the newest log entry + * Updates the repository action type of the newest log entry. This method is not Async, as it should already be called from an @Async context * * @param participation The participation to which the repository belongs to * @param repositoryActionType The repositoryActionType which should get set for the newest access log entry */ - @Async public void updateRepositoryActionType(ProgrammingExerciseParticipation participation, RepositoryActionType repositoryActionType) { vcsAccessLogRepository.findNewestByParticipationId(participation.getId()).ifPresent(entry -> { entry.setRepositoryActionType(repositoryActionType); @@ -96,7 +95,11 @@ public void storeCodeEditorAccessLog(Repository repo, User user, Long participat String lastCommitHash = git.log().setMaxCount(1).call().iterator().next().getName(); var participation = participationRepository.findById(participationId); if (participation.isPresent() && participation.get() instanceof ProgrammingExerciseParticipation programmingParticipation) { - storeAccessLog(user, programmingParticipation, RepositoryActionType.WRITE, AuthenticationMechanism.CODE_EDITOR, lastCommitHash, null); + log.debug("Storing access operation for user {}", user); + + VcsAccessLog accessLogEntry = new VcsAccessLog(user, (Participation) programmingParticipation, user.getName(), user.getEmail(), RepositoryActionType.WRITE, + AuthenticationMechanism.CODE_EDITOR, lastCommitHash, null); + vcsAccessLogRepository.save(accessLogEntry); } } } From 2be62834de1348cd52a7f197bcb72026feb33dd0 Mon Sep 17 00:00:00 2001 From: entholzer Date: Thu, 10 Oct 2024 13:28:39 +0200 Subject: [PATCH 06/16] reduced database calls --- ...xerciseStudentParticipationRepository.java | 6 ++++++ ...ammingExerciseParticipationRepository.java | 6 ++++++ ...ammingExerciseParticipationRepository.java | 6 ++++++ ...ogrammingExerciseParticipationService.java | 21 +++++++++++++++++++ .../localvc/LocalVCServletService.java | 15 +++++++------ 5 files changed, 46 insertions(+), 8 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseStudentParticipationRepository.java b/src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseStudentParticipationRepository.java index 3739ed8dff71..9ba656a1ec00 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseStudentParticipationRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseStudentParticipationRepository.java @@ -82,6 +82,12 @@ default ProgrammingExerciseStudentParticipation findByExerciseIdAndStudentLoginO return getValueElseThrow(findByExerciseIdAndStudentLogin(exerciseId, username)); } + Optional findByRepositoryUri(@Param("repositoryUri") String repositoryUri); + + default ProgrammingExerciseStudentParticipation findByRepositoryUriElseThrow(String repositoryUri) { + return getValueElseThrow(findByRepositoryUri(repositoryUri)); + } + @EntityGraph(type = LOAD, attributePaths = { "submissions" }) Optional findWithSubmissionsByExerciseIdAndStudentLogin(long exerciseId, String username); diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/repository/SolutionProgrammingExerciseParticipationRepository.java b/src/main/java/de/tum/cit/aet/artemis/programming/repository/SolutionProgrammingExerciseParticipationRepository.java index 2949576a13cb..852c9f920c63 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/repository/SolutionProgrammingExerciseParticipationRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/repository/SolutionProgrammingExerciseParticipationRepository.java @@ -43,6 +43,12 @@ public interface SolutionProgrammingExerciseParticipationRepository """) Optional findByBuildPlanIdWithResults(@Param("buildPlanId") String buildPlanId); + Optional findByRepositoryUri(@Param("repositoryUri") String repositoryUri); + + default SolutionProgrammingExerciseParticipation findByRepositoryUriElseThrow(String repositoryUri) { + return getValueElseThrow(findByRepositoryUri(repositoryUri)); + } + @EntityGraph(type = LOAD, attributePaths = { "results", "submissions", "submissions.results" }) Optional findWithEagerResultsAndSubmissionsByProgrammingExerciseId(long exerciseId); diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/repository/TemplateProgrammingExerciseParticipationRepository.java b/src/main/java/de/tum/cit/aet/artemis/programming/repository/TemplateProgrammingExerciseParticipationRepository.java index bc609dcd06fa..f709131869e0 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/repository/TemplateProgrammingExerciseParticipationRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/repository/TemplateProgrammingExerciseParticipationRepository.java @@ -48,6 +48,12 @@ default TemplateProgrammingExerciseParticipation findWithEagerResultsAndSubmissi return getValueElseThrow(findWithEagerResultsAndSubmissionsByProgrammingExerciseId(exerciseId)); } + Optional findByRepositoryUri(@Param("repositoryUri") String repositoryUri); + + default TemplateProgrammingExerciseParticipation findByRepositoryUriElseThrow(String repositoryUri) { + return getValueElseThrow(findByRepositoryUri(repositoryUri)); + } + @EntityGraph(type = LOAD, attributePaths = { "results", "results.feedbacks", "results.feedbacks.testCase", "submissions" }) Optional findWithEagerResultsAndFeedbacksAndTestCasesAndSubmissionsByProgrammingExerciseId(long exerciseId); diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseParticipationService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseParticipationService.java index 4731295ed3c3..fe6aea286cd2 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseParticipationService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseParticipationService.java @@ -501,6 +501,27 @@ public ProgrammingExerciseParticipation getParticipationForRepository(Programmin return findStudentParticipationByExerciseAndStudentLoginAndTestRunOrThrow(exercise, repositoryTypeOrUserName, isPracticeRepository, withSubmissions); } + /** + * Get the participation for a given repository url and a repository type or user name. This method is used by the local VC system to get the + * participation for logging operations on the repository. + * + * @param repositoryTypeOrUserName the name of the user or the type of the repository + * @param repositoryURI the participation's repository URL + * @return the participation belonging to the provided repositoryURI and repository type or username + */ + public ProgrammingExerciseParticipation getParticipationForRepository(String repositoryTypeOrUserName, String repositoryURI) { + if (repositoryTypeOrUserName.equals(RepositoryType.SOLUTION.toString()) || repositoryTypeOrUserName.equals(RepositoryType.TESTS.toString())) { + return solutionParticipationRepository.findByRepositoryUriElseThrow(repositoryURI); + } + if (repositoryTypeOrUserName.equals(RepositoryType.TEMPLATE.toString())) { + return templateParticipationRepository.findByRepositoryUriElseThrow(repositoryURI); + } + if (repositoryTypeOrUserName.equals(RepositoryType.AUXILIARY.toString())) { + throw new EntityNotFoundException("Auxiliary repositories do not have participations."); + } + return studentParticipationRepository.findByRepositoryUriElseThrow(repositoryURI); + } + /** * Get the commits information for the given participation. * diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java index 7cb6e54faa5a..45d51c8b85ae 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java @@ -435,8 +435,7 @@ public void authorizeUser(String repositoryTypeOrUserName, User user, Programmin ProgrammingExerciseParticipation participation; try { - participation = programmingExerciseParticipationService.getParticipationForRepository(exercise, repositoryTypeOrUserName, localVCRepositoryUri.isPracticeRepository(), - false); + participation = programmingExerciseParticipationService.getParticipationForRepository(repositoryTypeOrUserName, localVCRepositoryUri.toString()); } catch (EntityNotFoundException e) { throw new LocalVCInternalException( @@ -732,11 +731,11 @@ private User getUserFromRequest(HttpServletRequest request) throws LocalVCAuthEx * @return the {@link ProgrammingExerciseParticipation} corresponding to the repository URI. */ private ProgrammingExerciseParticipation getExerciseParticipationFromLocalVCRepositoryUri(LocalVCRepositoryUri localVCRepositoryUri) { - String projectKey = localVCRepositoryUri.getProjectKey(); + // String projectKey = localVCRepositoryUri.getURI(); String repositoryTypeOrUserName = localVCRepositoryUri.getRepositoryTypeOrUserName(); - ProgrammingExercise exercise = getProgrammingExerciseOrThrow(projectKey); - - return programmingExerciseParticipationService.getParticipationForRepository(exercise, repositoryTypeOrUserName, localVCRepositoryUri.isPracticeRepository(), false); + // ProgrammingExercise exercise = getProgrammingExerciseOrThrow(projectKey); + return programmingExerciseParticipationService.getParticipationForRepository(repositoryTypeOrUserName, localVCRepositoryUri.toString()); + // return programmingExerciseParticipationService.getParticipationForRepository(exercise, repositoryTypeOrUserName, localVCRepositoryUri.isPracticeRepository(), false); } /** @@ -842,8 +841,8 @@ public void updateVCSAccessLogForCloneAndPullSSH(ServerSession session, Path roo return; } RepositoryActionType repositoryActionType = getRepositoryActionReadType(clientOffered); - var parti = getExerciseParticipationFromLocalVCRepositoryUri(getLocalVCRepositoryUri(rootDir)); - vcsAccessLogService.ifPresent(service -> service.updateRepositoryActionType(parti, repositoryActionType)); + var participation = getExerciseParticipationFromLocalVCRepositoryUri(getLocalVCRepositoryUri(rootDir)); + vcsAccessLogService.ifPresent(service -> service.updateRepositoryActionType(participation, repositoryActionType)); } catch (Exception ignored) { } From c76c05bdd2182a57652a0d9bce6d60bc96e239b6 Mon Sep 17 00:00:00 2001 From: entholzer Date: Thu, 10 Oct 2024 13:41:35 +0200 Subject: [PATCH 07/16] renamed method --- ...ogrammingExerciseParticipationService.java | 4 ++-- .../localvc/LocalVCServletService.java | 20 ++++++++----------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseParticipationService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseParticipationService.java index fe6aea286cd2..aa12b04d8459 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseParticipationService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseParticipationService.java @@ -452,7 +452,7 @@ public void resetRepository(VcsRepositoryUri targetURL, VcsRepositoryUri sourceU * @return the participation. * @throws EntityNotFoundException if the participation could not be found. */ - public ProgrammingExerciseParticipation getParticipationForRepository(ProgrammingExercise exercise, String repositoryTypeOrUserName, boolean isPracticeRepository, + public ProgrammingExerciseParticipation retrieveParticipationForRepository(ProgrammingExercise exercise, String repositoryTypeOrUserName, boolean isPracticeRepository, boolean withSubmissions) { boolean isAuxiliaryRepository = auxiliaryRepositoryService.isAuxiliaryRepositoryOfExercise(repositoryTypeOrUserName, exercise); @@ -509,7 +509,7 @@ public ProgrammingExerciseParticipation getParticipationForRepository(Programmin * @param repositoryURI the participation's repository URL * @return the participation belonging to the provided repositoryURI and repository type or username */ - public ProgrammingExerciseParticipation getParticipationForRepository(String repositoryTypeOrUserName, String repositoryURI) { + public ProgrammingExerciseParticipation retrieveParticipationForRepository(String repositoryTypeOrUserName, String repositoryURI) { if (repositoryTypeOrUserName.equals(RepositoryType.SOLUTION.toString()) || repositoryTypeOrUserName.equals(RepositoryType.TESTS.toString())) { return solutionParticipationRepository.findByRepositoryUriElseThrow(repositoryURI); } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java index 45d51c8b85ae..28d926db6ab6 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java @@ -435,7 +435,7 @@ public void authorizeUser(String repositoryTypeOrUserName, User user, Programmin ProgrammingExerciseParticipation participation; try { - participation = programmingExerciseParticipationService.getParticipationForRepository(repositoryTypeOrUserName, localVCRepositoryUri.toString()); + participation = programmingExerciseParticipationService.retrieveParticipationForRepository(repositoryTypeOrUserName, localVCRepositoryUri.toString()); } catch (EntityNotFoundException e) { throw new LocalVCInternalException( @@ -558,8 +558,8 @@ private ProgrammingExerciseParticipation getProgrammingExerciseParticipation(Loc ProgrammingExercise exercise) { ProgrammingExerciseParticipation participation; try { - participation = programmingExerciseParticipationService.getParticipationForRepository(exercise, repositoryTypeOrUserName, localVCRepositoryUri.isPracticeRepository(), - true); + participation = programmingExerciseParticipationService.retrieveParticipationForRepository(exercise, repositoryTypeOrUserName, + localVCRepositoryUri.isPracticeRepository(), true); } catch (EntityNotFoundException e) { throw new VersionControlException("Could not find participation for repository " + repositoryTypeOrUserName + " of exercise " + exercise, e); @@ -719,7 +719,6 @@ private Commit extractCommitInfo(String commitHash, Repository repository) throw */ private User getUserFromRequest(HttpServletRequest request) throws LocalVCAuthException { String authorizationHeader = request.getHeader(LocalVCServletService.AUTHORIZATION_HEADER); - UsernameAndPassword usernameAndPassword = extractUsernameAndPassword(authorizationHeader); return userRepository.findOneByLogin(usernameAndPassword.username()).orElseThrow(LocalVCAuthException::new); } @@ -730,12 +729,9 @@ private User getUserFromRequest(HttpServletRequest request) throws LocalVCAuthEx * @param localVCRepositoryUri the {@link LocalVCRepositoryUri} containing details about the repository. * @return the {@link ProgrammingExerciseParticipation} corresponding to the repository URI. */ - private ProgrammingExerciseParticipation getExerciseParticipationFromLocalVCRepositoryUri(LocalVCRepositoryUri localVCRepositoryUri) { - // String projectKey = localVCRepositoryUri.getURI(); + private ProgrammingExerciseParticipation retrieveParticipationFromLocalVCRepositoryUri(LocalVCRepositoryUri localVCRepositoryUri) { String repositoryTypeOrUserName = localVCRepositoryUri.getRepositoryTypeOrUserName(); - // ProgrammingExercise exercise = getProgrammingExerciseOrThrow(projectKey); - return programmingExerciseParticipationService.getParticipationForRepository(repositoryTypeOrUserName, localVCRepositoryUri.toString()); - // return programmingExerciseParticipationService.getParticipationForRepository(exercise, repositoryTypeOrUserName, localVCRepositoryUri.isPracticeRepository(), false); + return programmingExerciseParticipationService.retrieveParticipationForRepository(repositoryTypeOrUserName, localVCRepositoryUri.toString()); } /** @@ -746,7 +742,7 @@ private ProgrammingExerciseParticipation getExerciseParticipationFromLocalVCRepo */ private ProgrammingExerciseParticipation getExerciseParticipationFromRequest(HttpServletRequest request) { LocalVCRepositoryUri localVCRepositoryUri = parseRepositoryUri(request); - return getExerciseParticipationFromLocalVCRepositoryUri(localVCRepositoryUri); + return retrieveParticipationFromLocalVCRepositoryUri(localVCRepositoryUri); } /** @@ -774,7 +770,7 @@ public static String getDefaultBranchOfRepository(Repository repository) { @Async public void updateVCSAccessLogForCloneAndPullHTTPS(HttpServletRequest request, int clientOffered) { try { - String authorizationHeader = request.getHeader("Authorization"); + String authorizationHeader = request.getHeader(LocalVCServletService.AUTHORIZATION_HEADER); UsernameAndPassword usernameAndPassword = extractUsernameAndPassword(authorizationHeader); String userName = usernameAndPassword.username(); if (userName.equals(BUILD_USER_NAME)) { @@ -841,7 +837,7 @@ public void updateVCSAccessLogForCloneAndPullSSH(ServerSession session, Path roo return; } RepositoryActionType repositoryActionType = getRepositoryActionReadType(clientOffered); - var participation = getExerciseParticipationFromLocalVCRepositoryUri(getLocalVCRepositoryUri(rootDir)); + var participation = retrieveParticipationFromLocalVCRepositoryUri(getLocalVCRepositoryUri(rootDir)); vcsAccessLogService.ifPresent(service -> service.updateRepositoryActionType(participation, repositoryActionType)); } catch (Exception ignored) { From 28e3f67fabb9bbbdaae32f47f7c38214133ba2f9 Mon Sep 17 00:00:00 2001 From: entholzer Date: Thu, 10 Oct 2024 14:28:11 +0200 Subject: [PATCH 08/16] remove debug action types --- .../programming/web/repository/RepositoryActionType.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/web/repository/RepositoryActionType.java b/src/main/java/de/tum/cit/aet/artemis/programming/web/repository/RepositoryActionType.java index 76c3a88daec6..8d803cc66a53 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/web/repository/RepositoryActionType.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/web/repository/RepositoryActionType.java @@ -4,5 +4,5 @@ * Determines if a repository action only reads (e.g. get a file from the repo) or updates (e.g. create a new file in the repo). */ public enum RepositoryActionType { - READ, WRITE, RESET, CLONE, PULL, PUSH, CLONE_FAIL, PULL_FAIL, PUSH_FAIL, DEBUG_1, DEBUG_2 + READ, WRITE, RESET, CLONE, PULL, PUSH, CLONE_FAIL, PULL_FAIL, PUSH_FAIL } From 93153bfb4986b99ecd381f0aee409d75cc17d89b Mon Sep 17 00:00:00 2001 From: entholzer Date: Thu, 10 Oct 2024 22:02:47 +0200 Subject: [PATCH 09/16] made initial access log storing async --- .../localvc/LocalVCServletService.java | 47 ++++++++++++------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java index 28d926db6ab6..29bc17711418 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java @@ -16,6 +16,7 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.concurrent.CompletableFuture; import jakarta.servlet.http.HttpServletRequest; @@ -448,7 +449,29 @@ public void authorizeUser(String repositoryTypeOrUserName, User user, Programmin catch (AccessForbiddenException e) { throw new LocalVCForbiddenException(e); } - // TODO: retrieving the git commit hash should be done ASYNC together with storing the log in the database to avoid long waiting times during permission check + + // Asynchronously store an VCS access log entry + CompletableFuture.runAsync(() -> storeAccessLogAsync(user, participation, repositoryActionType, authenticationMechanism, ipAddress, localVCRepositoryUri)) + .exceptionally(ex -> { + log.warn("Failed to asynchronously obtain commit hash or store access log for repository {}. Error: {}", + localVCRepositoryUri.getRelativeRepositoryPath().toString(), ex.getMessage()); + return null; + }); + } + + /** + * Asynchronously retrieves the latest commit hash from the specified repository and logs the access to the repository. + * This method runs without blocking the user during repository access checks. + * + * @param user the user accessing the repository + * @param participation the participation associated with the repository + * @param repositoryActionType the action performed on the repository (READ or WRITE) + * @param authenticationMechanism the mechanism used for authentication (e.g., token, basic auth) + * @param ipAddress the IP address of the user accessing the repository + * @param localVCRepositoryUri the URI of the localVC repository + */ + private void storeAccessLogAsync(User user, ProgrammingExerciseParticipation participation, RepositoryActionType repositoryActionType, + AuthenticationMechanism authenticationMechanism, String ipAddress, LocalVCRepositoryUri localVCRepositoryUri) { try { String commitHash; String relativeRepositoryPath = localVCRepositoryUri.getRelativeRepositoryPath().toString(); @@ -456,12 +479,10 @@ public void authorizeUser(String repositoryTypeOrUserName, User user, Programmin commitHash = getLatestCommitHash(repository); } - // Write a access log entry to the database RepositoryActionType finalRepositoryActionType = repositoryActionType == RepositoryActionType.READ ? RepositoryActionType.PULL : RepositoryActionType.PUSH; - String finalCommitHash = commitHash; - vcsAccessLogService.ifPresent(service -> service.storeAccessLog(user, participation, finalRepositoryActionType, authenticationMechanism, finalCommitHash, ipAddress)); + vcsAccessLogService.ifPresent(service -> service.storeAccessLog(user, participation, finalRepositoryActionType, authenticationMechanism, commitHash, ipAddress)); + } - // NOTE: we intentionally catch all issues here to avoid that the user is blocked from accessing the repository catch (Exception e) { log.warn("Failed to obtain commit hash or store access log for repository {}. Error: {}", localVCRepositoryUri.getRelativeRepositoryPath().toString(), e.getMessage()); } @@ -532,16 +553,9 @@ public void processNewPush(String commitHash, Repository repository) { // Process push to any repository other than the test repository. processNewPushToRepository(participation, commit); - try { - // For push the correct commitHash is only available here, therefore the preliminary null value is overwritten - String finalCommitHash = commitHash; - vcsAccessLogService.ifPresent(service -> service.updateCommitHash(participation, finalCommitHash)); - } - // NOTE: we intentionally catch all issues here to avoid that the user is blocked from accessing the repository - catch (Exception e) { - log.warn("Failed to obtain commit hash or store access log for repository {}. Error: {}", localVCRepositoryUri.getRelativeRepositoryPath().toString(), - e.getMessage()); - } + // For push the correct commitHash is only available here, therefore the preliminary null value is overwritten + String finalCommitHash = commitHash; + vcsAccessLogService.ifPresent(service -> service.updateCommitHash(participation, finalCommitHash)); } catch (GitAPIException | IOException e) { // This catch clause does not catch exceptions that happen during runBuildJob() as that method is called asynchronously. @@ -731,7 +745,8 @@ private User getUserFromRequest(HttpServletRequest request) throws LocalVCAuthEx */ private ProgrammingExerciseParticipation retrieveParticipationFromLocalVCRepositoryUri(LocalVCRepositoryUri localVCRepositoryUri) { String repositoryTypeOrUserName = localVCRepositoryUri.getRepositoryTypeOrUserName(); - return programmingExerciseParticipationService.retrieveParticipationForRepository(repositoryTypeOrUserName, localVCRepositoryUri.toString()); + var repositoryURL = localVCRepositoryUri.toString().replace("/git-upload-pack", "").replace("/git-receive-pack", ""); + return programmingExerciseParticipationService.retrieveParticipationForRepository(repositoryTypeOrUserName, repositoryURL); } /** From 4377396ad64448b0752c30b481cccf179ab7333e Mon Sep 17 00:00:00 2001 From: entholzer Date: Mon, 14 Oct 2024 17:00:07 +0200 Subject: [PATCH 10/16] improved shown Authentication Mechanism when clone failed --- .../domain/AuthenticationMechanism.java | 4 ++++ .../localvc/LocalVCServletService.java | 21 ++++++------------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/domain/AuthenticationMechanism.java b/src/main/java/de/tum/cit/aet/artemis/programming/domain/AuthenticationMechanism.java index 239ef3674d44..c837a00a7ece 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/domain/AuthenticationMechanism.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/domain/AuthenticationMechanism.java @@ -21,4 +21,8 @@ public enum AuthenticationMechanism { * The user used the artemis client code editor to authenticate to the LocalVC */ CODE_EDITOR, + /** + * The user used some token to try to authenticate to the LocalVC, either a user token, or a participation token + */ + VCS_ACCESS_TOKEN, } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java index 29bc17711418..53e6985f7e2a 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java @@ -724,19 +724,6 @@ private Commit extractCommitInfo(String commitHash, Repository repository) throw return new Commit(commitHash, author.getName(), revCommit.getFullMessage(), author.getEmailAddress(), branch); } - /** - * Retrieves the user from the HTTP request's authorization header. - * - * @param request the {@link HttpServletRequest} containing the authorization header. - * @return the {@link User} associated with the login found in the authorization header. - * @throws LocalVCAuthException if the user cannot be found or authorization fails. - */ - private User getUserFromRequest(HttpServletRequest request) throws LocalVCAuthException { - String authorizationHeader = request.getHeader(LocalVCServletService.AUTHORIZATION_HEADER); - UsernameAndPassword usernameAndPassword = extractUsernameAndPassword(authorizationHeader); - return userRepository.findOneByLogin(usernameAndPassword.username()).orElseThrow(LocalVCAuthException::new); - } - /** * Retrieves the participation for a programming exercise based on the repository URI. * @@ -869,9 +856,13 @@ public void updateVCSAccessLogForCloneAndPullSSH(ServerSession session, Path roo */ public void createVCSAccessLogForFailedAuthenticationAttempt(HttpServletRequest servletRequest) { try { - User user = getUserFromRequest(servletRequest); + String authorizationHeader = servletRequest.getHeader(LocalVCServletService.AUTHORIZATION_HEADER); + UsernameAndPassword usernameAndPassword = extractUsernameAndPassword(authorizationHeader); + User user = userRepository.findOneByLogin(usernameAndPassword.username()).orElseThrow(LocalVCAuthException::new); + AuthenticationMechanism mechanism = usernameAndPassword.password().startsWith("vcpat-") ? AuthenticationMechanism.VCS_ACCESS_TOKEN : AuthenticationMechanism.PASSWORD; var participation = getExerciseParticipationFromRequest(servletRequest); - vcsAccessLogService.ifPresent(service -> service.storeAccessLog(user, participation, RepositoryActionType.CLONE_FAIL, AuthenticationMechanism.PASSWORD, "", "")); + var ipAddress = servletRequest.getRemoteAddr(); + vcsAccessLogService.ifPresent(service -> service.storeAccessLog(user, participation, RepositoryActionType.CLONE_FAIL, mechanism, "", ipAddress)); } catch (LocalVCAuthException ignored) { } From dd4969842a6d54254fe659ff4a233aa29a79dc8f Mon Sep 17 00:00:00 2001 From: entholzer Date: Mon, 14 Oct 2024 17:34:32 +0200 Subject: [PATCH 11/16] remove toString --- .../programming/service/localvc/LocalVCServletService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java index 53e6985f7e2a..d0ef6f310dec 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java @@ -453,8 +453,8 @@ public void authorizeUser(String repositoryTypeOrUserName, User user, Programmin // Asynchronously store an VCS access log entry CompletableFuture.runAsync(() -> storeAccessLogAsync(user, participation, repositoryActionType, authenticationMechanism, ipAddress, localVCRepositoryUri)) .exceptionally(ex -> { - log.warn("Failed to asynchronously obtain commit hash or store access log for repository {}. Error: {}", - localVCRepositoryUri.getRelativeRepositoryPath().toString(), ex.getMessage()); + log.warn("Failed to asynchronously obtain commit hash or store access log for repository {}. Error: {}", localVCRepositoryUri.getRelativeRepositoryPath(), + ex.getMessage()); return null; }); } From f85fd210f9b27fe205b68cf8ae6936358f5a64c0 Mon Sep 17 00:00:00 2001 From: entholzer Date: Mon, 14 Oct 2024 17:53:07 +0200 Subject: [PATCH 12/16] added code rabbit suggestions --- .../artemis/programming/domain/AuthenticationMechanism.java | 2 +- .../programming/service/localvc/LocalVCServletService.java | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/domain/AuthenticationMechanism.java b/src/main/java/de/tum/cit/aet/artemis/programming/domain/AuthenticationMechanism.java index c837a00a7ece..4f00e1bb117f 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/domain/AuthenticationMechanism.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/domain/AuthenticationMechanism.java @@ -22,7 +22,7 @@ public enum AuthenticationMechanism { */ CODE_EDITOR, /** - * The user used some token to try to authenticate to the LocalVC, either a user token, or a participation token + * The user attempted to authenticate to the LocalVC using either a user token or a participation token */ VCS_ACCESS_TOKEN, } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java index d0ef6f310dec..f4bcf1ea2e89 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java @@ -782,8 +782,6 @@ public void updateVCSAccessLogForCloneAndPullHTTPS(HttpServletRequest request, i var participation = getExerciseParticipationFromRequest(request); vcsAccessLogService.ifPresent(service -> service.updateRepositoryActionType(participation, repositoryActionType)); - - log.info("username {} int {}", usernameAndPassword.username(), clientOffered); } catch (Exception ignored) { } @@ -805,7 +803,7 @@ public void updateVCSAccessLogForPushHTTPS(HttpServletRequest request) { return; } try { - String authorizationHeader = request.getHeader("Authorization"); + String authorizationHeader = request.getHeader(LocalVCServletService.AUTHORIZATION_HEADER); UsernameAndPassword usernameAndPassword = extractUsernameAndPassword(authorizationHeader); String userName = usernameAndPassword.username(); if (userName.equals(BUILD_USER_NAME)) { @@ -877,7 +875,7 @@ public void createVCSAccessLogForFailedAuthenticationAttempt(HttpServletRequest * @param clientOffered the number of objects offered to the client in the operation. * @return the {@link RepositoryActionType} based on the number of objects offered (clone if 0, pull if greater than 0). */ - public RepositoryActionType getRepositoryActionReadType(int clientOffered) { + private RepositoryActionType getRepositoryActionReadType(int clientOffered) { return clientOffered == 0 ? RepositoryActionType.CLONE : RepositoryActionType.PULL; } From 82d1f3000dbc063c65dd050c4c6517ad413a4e34 Mon Sep 17 00:00:00 2001 From: entholzer Date: Tue, 15 Oct 2024 12:25:44 +0200 Subject: [PATCH 13/16] added some more documentation with reference to git documentation --- .../service/localvc/ArtemisGitServletService.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/ArtemisGitServletService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/ArtemisGitServletService.java index 60681ec1a01d..0bfbbbe700e0 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/ArtemisGitServletService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/ArtemisGitServletService.java @@ -32,6 +32,13 @@ public ArtemisGitServletService(LocalVCServletService localVCServletService) { /** * Initialize the ArtemisGitServlet by setting the repository resolver and adding filters for fetch and push requests. + * Sets the pre/post receive/upload hooks. + *

+ * For general information on the different hooks and git packs see the git documentation: + *

+ * https://git-scm.com/docs/git-receive-pack + *

+ * https://git-scm.com/docs/git-upload-pack */ @PostConstruct @Override @@ -60,7 +67,7 @@ public void init() { this.setUploadPackFactory((request, repository) -> { UploadPack uploadPack = new UploadPack(repository); - // Add the custom pre-upload hook + // Add the custom pre-upload hook, to distinguish between clone and pull operations uploadPack.setPreUploadHook(new LocalVCFetchPreUploadHook(localVCServletService, request)); return uploadPack; }); From 6819d5f7d743431273b7f133b7f0207c6737e5fd Mon Sep 17 00:00:00 2001 From: entholzer Date: Sat, 19 Oct 2024 13:55:44 +0200 Subject: [PATCH 14/16] imporve aux repo tool tip --- .../programming-exercise-information.component.scss | 4 ++++ src/main/webapp/i18n/de/programmingExercise.json | 2 +- src/main/webapp/i18n/en/programmingExercise.json | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-information.component.scss b/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-information.component.scss index 76bf7863f5c5..47462abdee4e 100644 --- a/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-information.component.scss +++ b/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-information.component.scss @@ -1,3 +1,7 @@ #creation-config-selector { position: relative; } + +::ng-deep .tooltip-inner { + max-width: 600px; +} diff --git a/src/main/webapp/i18n/de/programmingExercise.json b/src/main/webapp/i18n/de/programmingExercise.json index 387ffb3f548a..4081f50b033a 100644 --- a/src/main/webapp/i18n/de/programmingExercise.json +++ b/src/main/webapp/i18n/de/programmingExercise.json @@ -519,7 +519,7 @@ "auxiliaryRepository": { "error": "Es gibt ein Problem mit den Hilfs-Repositories!", "addAuxiliaryRepository": "Hilfs-Repository anlegen", - "usageDescription": "Du kannst Hilfsrepositorys verwenden, um zusätzlichen Code bereitzustellen, den Studierende nicht sehen oder ändern können. Der zusätzliche Code wird während des Builds der Abgabe am angegebenen Checkout-Verzeichnis eingefügt.", + "usageDescription": "Du kannst Hilfsrepositorien verwenden, um zusätzlichen Code bereitzustellen, den die Studierenden nicht sehen oder ändern können. Der zusätzliche Code wird im angegebenen Checkout-Verzeichnis eingefügt, bevor der Build erstellt wird. Er wird durch eine mv Operation eingefügt. Das bedeutet, dass es einen Unterschied beim Einfügen gibt, je nachdem, ob der Ordner des Checkout-Verzeichnis bereits existiert oder nicht. Wenn der Ordner existiert, wird der Inhalt des Repositorys im Checkout-Verzeichnis in einen neuen Ordner mit dem Namen des Repositorys eingefügt. Existiert der Ordner nicht, wird ein neuer Ordner im Checkout-Verzeichnis erstellt und der Inhalt des Hilfsrepositorys direkt dort eingefügt. Um die Dateien der Studierenden zu überschreiben, musst du das Build-Skript anpassen.", "repositoryName": "Name des Repositorys", "checkoutDirectory": "Checkout-Verzeichnis", "description": "Beschreibung", diff --git a/src/main/webapp/i18n/en/programmingExercise.json b/src/main/webapp/i18n/en/programmingExercise.json index f4163cf7a4d8..51c6831affcb 100644 --- a/src/main/webapp/i18n/en/programmingExercise.json +++ b/src/main/webapp/i18n/en/programmingExercise.json @@ -519,7 +519,7 @@ "auxiliaryRepository": { "error": "There is a problem with the auxiliary repository.", "addAuxiliaryRepository": "Add Auxiliary Repository", - "usageDescription": "You can use auxiliary repositories to provide additional code that students cannot see or modify. The additional code is inserted at the specified Checkout Directory during the build of the submission.", + "usageDescription": "You can use auxiliary repositories to provide additional code that students cannot see or modify. The additional code is inserted at the specified Checkout Directory before the build of the submission. It is inserted via a mv operation. This means, there is a difference on how it is inserted, depending whether the checkout dir already exists or not. If it exists, the repository's content is added at the checkout dir in a new folder with the repository's name. If it does not exist, the checkout dir's folder is created and the auxiliary repositories content is inserted directly there. To overwrite student's files, you need to adapt the build script.", "repositoryName": "Repository Name", "checkoutDirectory": "Checkout Directory", "description": "Description", From 429baab9bbda87328c7b07a71bf733c448101dc3 Mon Sep 17 00:00:00 2001 From: entholzer Date: Sat, 19 Oct 2024 17:45:58 +0200 Subject: [PATCH 15/16] Revert "imporve aux repo tool tip" This reverts commit 6819d5f7d743431273b7f133b7f0207c6737e5fd. --- .../programming-exercise-information.component.scss | 4 ---- src/main/webapp/i18n/de/programmingExercise.json | 2 +- src/main/webapp/i18n/en/programmingExercise.json | 2 +- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-information.component.scss b/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-information.component.scss index 47462abdee4e..76bf7863f5c5 100644 --- a/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-information.component.scss +++ b/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-information.component.scss @@ -1,7 +1,3 @@ #creation-config-selector { position: relative; } - -::ng-deep .tooltip-inner { - max-width: 600px; -} diff --git a/src/main/webapp/i18n/de/programmingExercise.json b/src/main/webapp/i18n/de/programmingExercise.json index 4081f50b033a..387ffb3f548a 100644 --- a/src/main/webapp/i18n/de/programmingExercise.json +++ b/src/main/webapp/i18n/de/programmingExercise.json @@ -519,7 +519,7 @@ "auxiliaryRepository": { "error": "Es gibt ein Problem mit den Hilfs-Repositories!", "addAuxiliaryRepository": "Hilfs-Repository anlegen", - "usageDescription": "Du kannst Hilfsrepositorien verwenden, um zusätzlichen Code bereitzustellen, den die Studierenden nicht sehen oder ändern können. Der zusätzliche Code wird im angegebenen Checkout-Verzeichnis eingefügt, bevor der Build erstellt wird. Er wird durch eine mv Operation eingefügt. Das bedeutet, dass es einen Unterschied beim Einfügen gibt, je nachdem, ob der Ordner des Checkout-Verzeichnis bereits existiert oder nicht. Wenn der Ordner existiert, wird der Inhalt des Repositorys im Checkout-Verzeichnis in einen neuen Ordner mit dem Namen des Repositorys eingefügt. Existiert der Ordner nicht, wird ein neuer Ordner im Checkout-Verzeichnis erstellt und der Inhalt des Hilfsrepositorys direkt dort eingefügt. Um die Dateien der Studierenden zu überschreiben, musst du das Build-Skript anpassen.", + "usageDescription": "Du kannst Hilfsrepositorys verwenden, um zusätzlichen Code bereitzustellen, den Studierende nicht sehen oder ändern können. Der zusätzliche Code wird während des Builds der Abgabe am angegebenen Checkout-Verzeichnis eingefügt.", "repositoryName": "Name des Repositorys", "checkoutDirectory": "Checkout-Verzeichnis", "description": "Beschreibung", diff --git a/src/main/webapp/i18n/en/programmingExercise.json b/src/main/webapp/i18n/en/programmingExercise.json index 51c6831affcb..f4163cf7a4d8 100644 --- a/src/main/webapp/i18n/en/programmingExercise.json +++ b/src/main/webapp/i18n/en/programmingExercise.json @@ -519,7 +519,7 @@ "auxiliaryRepository": { "error": "There is a problem with the auxiliary repository.", "addAuxiliaryRepository": "Add Auxiliary Repository", - "usageDescription": "You can use auxiliary repositories to provide additional code that students cannot see or modify. The additional code is inserted at the specified Checkout Directory before the build of the submission. It is inserted via a mv operation. This means, there is a difference on how it is inserted, depending whether the checkout dir already exists or not. If it exists, the repository's content is added at the checkout dir in a new folder with the repository's name. If it does not exist, the checkout dir's folder is created and the auxiliary repositories content is inserted directly there. To overwrite student's files, you need to adapt the build script.", + "usageDescription": "You can use auxiliary repositories to provide additional code that students cannot see or modify. The additional code is inserted at the specified Checkout Directory during the build of the submission.", "repositoryName": "Repository Name", "checkoutDirectory": "Checkout Directory", "description": "Description", From cc3f916d3ef8a79b9da31172727135dd1a8f3c43 Mon Sep 17 00:00:00 2001 From: entholzer Date: Sat, 19 Oct 2024 17:46:13 +0200 Subject: [PATCH 16/16] revert and add --- ...rogrammingExerciseStudentParticipationRepository.java | 2 +- ...lutionProgrammingExerciseParticipationRepository.java | 2 +- ...mplateProgrammingExerciseParticipationRepository.java | 2 +- .../participation/util/ParticipationUtilService.java | 9 ++++++--- .../programming/icl/LocalVCLocalCIIntegrationTest.java | 8 ++++++++ .../programming/util/ProgrammingExerciseFactory.java | 7 ++++++- .../programming/util/ProgrammingExerciseUtilService.java | 7 +++++-- 7 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseStudentParticipationRepository.java b/src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseStudentParticipationRepository.java index 9ba656a1ec00..cc1f57c533fa 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseStudentParticipationRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseStudentParticipationRepository.java @@ -82,7 +82,7 @@ default ProgrammingExerciseStudentParticipation findByExerciseIdAndStudentLoginO return getValueElseThrow(findByExerciseIdAndStudentLogin(exerciseId, username)); } - Optional findByRepositoryUri(@Param("repositoryUri") String repositoryUri); + Optional findByRepositoryUri(String repositoryUri); default ProgrammingExerciseStudentParticipation findByRepositoryUriElseThrow(String repositoryUri) { return getValueElseThrow(findByRepositoryUri(repositoryUri)); diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/repository/SolutionProgrammingExerciseParticipationRepository.java b/src/main/java/de/tum/cit/aet/artemis/programming/repository/SolutionProgrammingExerciseParticipationRepository.java index 852c9f920c63..4297b39f9ea9 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/repository/SolutionProgrammingExerciseParticipationRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/repository/SolutionProgrammingExerciseParticipationRepository.java @@ -43,7 +43,7 @@ public interface SolutionProgrammingExerciseParticipationRepository """) Optional findByBuildPlanIdWithResults(@Param("buildPlanId") String buildPlanId); - Optional findByRepositoryUri(@Param("repositoryUri") String repositoryUri); + Optional findByRepositoryUri(String repositoryUri); default SolutionProgrammingExerciseParticipation findByRepositoryUriElseThrow(String repositoryUri) { return getValueElseThrow(findByRepositoryUri(repositoryUri)); diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/repository/TemplateProgrammingExerciseParticipationRepository.java b/src/main/java/de/tum/cit/aet/artemis/programming/repository/TemplateProgrammingExerciseParticipationRepository.java index f709131869e0..67795b58ae54 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/repository/TemplateProgrammingExerciseParticipationRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/repository/TemplateProgrammingExerciseParticipationRepository.java @@ -48,7 +48,7 @@ default TemplateProgrammingExerciseParticipation findWithEagerResultsAndSubmissi return getValueElseThrow(findWithEagerResultsAndSubmissionsByProgrammingExerciseId(exerciseId)); } - Optional findByRepositoryUri(@Param("repositoryUri") String repositoryUri); + Optional findByRepositoryUri(String repositoryUri); default TemplateProgrammingExerciseParticipation findByRepositoryUriElseThrow(String repositoryUri) { return getValueElseThrow(findByRepositoryUri(repositoryUri)); diff --git a/src/test/java/de/tum/cit/aet/artemis/exercise/participation/util/ParticipationUtilService.java b/src/test/java/de/tum/cit/aet/artemis/exercise/participation/util/ParticipationUtilService.java index ff3ab5f260e6..1d0f5953f3e9 100644 --- a/src/test/java/de/tum/cit/aet/artemis/exercise/participation/util/ParticipationUtilService.java +++ b/src/test/java/de/tum/cit/aet/artemis/exercise/participation/util/ParticipationUtilService.java @@ -132,6 +132,9 @@ public class ParticipationUtilService { @Value("${artemis.version-control.default-branch:main}") protected String defaultBranch; + @Value("${artemis.version-control.url}") + protected String artemisVersionControlUrl; + /** * Creates and saves a Result for a ProgrammingExerciseStudentParticipation associated with the given ProgrammingExercise and login. If no corresponding * ProgrammingExerciseStudentParticipation exists, it will be created and saved as well. @@ -153,7 +156,7 @@ public Result addProgrammingParticipationWithResultForExercise(ProgrammingExerci participation.setBuildPlanId(buildPlanId); participation.setProgrammingExercise(exercise); participation.setInitializationState(InitializationState.INITIALIZED); - participation.setRepositoryUri(String.format("http://some.test.url/%s/%s.git", exercise.getProjectKey(), repoName)); + participation.setRepositoryUri(String.format("%s/git/%s/%s.git", artemisVersionControlUrl, exercise.getProjectKey(), repoName)); programmingExerciseStudentParticipationRepo.save(participation); storedParticipation = programmingExerciseStudentParticipationRepo.findByExerciseIdAndStudentLogin(exercise.getId(), login); assertThat(storedParticipation).isPresent(); @@ -315,7 +318,7 @@ public ProgrammingExerciseStudentParticipation addStudentParticipationForProgram ProgrammingExerciseStudentParticipation participation = ParticipationFactory.generateIndividualProgrammingExerciseStudentParticipation(exercise, userUtilService.getUserByLogin(login)); final var repoName = (exercise.getProjectKey() + "-" + login).toLowerCase(); - participation.setRepositoryUri(String.format("http://some.test.url/scm/%s/%s.git", exercise.getProjectKey(), repoName)); + participation.setRepositoryUri(String.format("%s/git/%s/%s.git", artemisVersionControlUrl, exercise.getProjectKey(), repoName)); participation = programmingExerciseStudentParticipationRepo.save(participation); participationVCSAccessTokenService.createParticipationVCSAccessToken(userUtilService.getUserByLogin(login), participation); return (ProgrammingExerciseStudentParticipation) studentParticipationRepo.findWithEagerLegalSubmissionsAndResultsAssessorsById(participation.getId()).orElseThrow(); @@ -337,7 +340,7 @@ public ProgrammingExerciseStudentParticipation addTeamParticipationForProgrammin } ProgrammingExerciseStudentParticipation participation = ParticipationFactory.generateTeamProgrammingExerciseStudentParticipation(exercise, team); final var repoName = (exercise.getProjectKey() + "-" + team.getShortName()).toLowerCase(); - participation.setRepositoryUri(String.format("http://some.test.url/scm/%s/%s.git", exercise.getProjectKey(), repoName)); + participation.setRepositoryUri(String.format("%s/git/%s/%s.git", artemisVersionControlUrl, exercise.getProjectKey(), repoName)); participation = programmingExerciseStudentParticipationRepo.save(participation); return (ProgrammingExerciseStudentParticipation) studentParticipationRepo.findWithEagerLegalSubmissionsAndResultsAssessorsById(participation.getId()).orElseThrow(); diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalVCLocalCIIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalVCLocalCIIntegrationTest.java index 6b8eaf1b4f9b..17ca800422e9 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalVCLocalCIIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/icl/LocalVCLocalCIIntegrationTest.java @@ -111,6 +111,9 @@ class LocalVCLocalCIIntegrationTest extends AbstractLocalCILocalVCIntegrationTes @Value("${artemis.user-management.internal-admin.password}") private String localVCPassword; + @Value("${artemis.version-control.url}") + protected String artemisVersionControlUrl; + // ---- Repository handles ---- private LocalRepository templateRepository; @@ -911,6 +914,9 @@ void testFetchPush_teachingAssistantPracticeRepository() throws Exception { // Create practice participation. ProgrammingExerciseStudentParticipation practiceParticipation = participationUtilService.addStudentParticipationForProgrammingExercise(programmingExercise, tutor1Login); practiceParticipation.setPracticeMode(true); + practiceParticipation.setRepositoryUri(localVCLocalCITestService.constructLocalVCUrl("", "", projectKey1, practiceRepositorySlug)); + + // practiceParticipation.setRepositoryUri(String.format("%s/git/%s/%s.git", artemisVersionControlUrl, programmingExercise.getProjectKey(), practiceRepositorySlug)); programmingExerciseStudentParticipationRepository.save(practiceParticipation); // Students should not be able to access, teaching assistants should be able to fetch and push and editors and higher should be able to fetch and push. @@ -950,6 +956,8 @@ void testFetchPush_instructorPracticeRepository() throws Exception { ProgrammingExerciseStudentParticipation practiceParticipation = participationUtilService.addStudentParticipationForProgrammingExercise(programmingExercise, instructor1Login); practiceParticipation.setPracticeMode(true); + practiceParticipation.setRepositoryUri(localVCLocalCITestService.constructLocalVCUrl("", "", projectKey1, practiceRepositorySlug)); + programmingExerciseStudentParticipationRepository.save(practiceParticipation); // Students should not be able to access, teaching assistants should be able to fetch, and editors and higher should be able to fetch and push. diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseFactory.java b/src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseFactory.java index e01c45a01b9e..e4dca9d8be57 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseFactory.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseFactory.java @@ -9,6 +9,8 @@ import java.util.List; import java.util.Set; +import org.springframework.beans.factory.annotation.Value; + import de.tum.cit.aet.artemis.assessment.domain.AssessmentType; import de.tum.cit.aet.artemis.assessment.domain.CategoryState; import de.tum.cit.aet.artemis.assessment.domain.Feedback; @@ -43,6 +45,9 @@ public class ProgrammingExerciseFactory { public static final String DEFAULT_BRANCH = "main"; + @Value("${artemis.version-control.url}") + protected static String artemisVersionControlUrl; + /** * Generates a programming exercise with the given release and due date. This exercise is added to the provided course. * @@ -137,7 +142,7 @@ else if (programmingLanguage == ProgrammingLanguage.SWIFT) { } programmingExercise.setPackageName(programmingLanguage == ProgrammingLanguage.SWIFT ? "swiftTest" : "de.test"); final var repoName = programmingExercise.generateRepositoryName(RepositoryType.TESTS); - String testRepoUri = String.format("http://some.test.url/scm/%s/%s.git", programmingExercise.getProjectKey(), repoName); + String testRepoUri = String.format("%s/git/%s/%s.git", artemisVersionControlUrl, programmingExercise.getProjectKey(), repoName); programmingExercise.setTestRepositoryUri(testRepoUri); programmingExercise.getBuildConfig().setBranch(DEFAULT_BRANCH); } diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseUtilService.java b/src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseUtilService.java index 984ca2f5ae26..e1571792335a 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseUtilService.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseUtilService.java @@ -93,6 +93,9 @@ public class ProgrammingExerciseUtilService { @Value("${artemis.version-control.default-branch:main}") protected String defaultBranch; + @Value("${artemis.version-control.url}") + protected String artemisVersionControlUrl; + @Autowired private TemplateProgrammingExerciseParticipationRepository templateProgrammingExerciseParticipationRepo; @@ -197,7 +200,7 @@ public ProgrammingExercise addTemplateParticipationForProgrammingExercise(Progra TemplateProgrammingExerciseParticipation participation = new TemplateProgrammingExerciseParticipation(); participation.setProgrammingExercise(exercise); participation.setBuildPlanId(exercise.generateBuildPlanId(BuildPlanType.TEMPLATE)); - participation.setRepositoryUri(String.format("http://some.test.url/scm/%s/%s.git", exercise.getProjectKey(), repoName)); + participation.setRepositoryUri(String.format("%s/git/%s/%s.git", artemisVersionControlUrl, exercise.getProjectKey(), repoName)); participation.setInitializationState(InitializationState.INITIALIZED); templateProgrammingExerciseParticipationRepo.save(participation); exercise.setTemplateParticipation(participation); @@ -215,7 +218,7 @@ public ProgrammingExercise addSolutionParticipationForProgrammingExercise(Progra SolutionProgrammingExerciseParticipation participation = new SolutionProgrammingExerciseParticipation(); participation.setProgrammingExercise(exercise); participation.setBuildPlanId(exercise.generateBuildPlanId(BuildPlanType.SOLUTION)); - participation.setRepositoryUri(String.format("http://some.test.url/scm/%s/%s.git", exercise.getProjectKey(), repoName)); + participation.setRepositoryUri(String.format("%s/git/%s/%s.git", artemisVersionControlUrl, exercise.getProjectKey(), repoName)); participation.setInitializationState(InitializationState.INITIALIZED); solutionProgrammingExerciseParticipationRepo.save(participation); exercise.setSolutionParticipation(participation);