Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Integrated code lifecycle: Add access log for repositories #9184

Merged
merged 81 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
7c99e02
add VcsAccessLog table
SimonEntholzer Aug 4, 2024
3323544
Merge branch 'develop' into feature/repository-access-logging
SimonEntholzer Aug 5, 2024
7030993
added name and email to log entry, improved database entry efficiency
SimonEntholzer Aug 7, 2024
6d9ba87
added commit_hash to table, similar to bitbucket git push log
SimonEntholzer Aug 7, 2024
eb55959
merge and resolve conflicts
SimonEntholzer Aug 14, 2024
6c3c048
fixed remaining conflicts
SimonEntholzer Aug 15, 2024
af3cd22
Merge branch 'develop' into feature/repository-access-logging
SimonEntholzer Aug 17, 2024
b0e1d72
Merge branch 'develop' into feature/repository-access-logging
SimonEntholzer Aug 19, 2024
ea9e1e8
add commithash to log
SimonEntholzer Aug 20, 2024
fdb6746
Merge branch 'develop' into feature/repository-access-logging
SimonEntholzer Aug 20, 2024
d9b334a
Merge branch 'develop' into feature/repository-access-logging
SimonEntholzer Aug 21, 2024
0bdc05d
Merge branch 'develop' into feature/repository-access-logging
SimonEntholzer Aug 22, 2024
2a836fd
Merge branch 'develop' into feature/repository-access-logging
SimonEntholzer Aug 24, 2024
0e39d58
code editor submit logging
SimonEntholzer Aug 25, 2024
fd2c65a
added UI
SimonEntholzer Aug 26, 2024
6b91dd2
added UI
SimonEntholzer Aug 26, 2024
316dbaa
remove onDestroy
SimonEntholzer Aug 26, 2024
3f14a59
Merge branch 'develop' into feature/repository-access-logging
SimonEntholzer Aug 26, 2024
5bd2d84
remove empty line
SimonEntholzer Aug 26, 2024
488e041
fix java docs
SimonEntholzer Aug 26, 2024
54238ae
fix java docs and improve client code
SimonEntholzer Aug 26, 2024
326376e
add java integration tests
SimonEntholzer Aug 27, 2024
6725e97
add java integration tests
SimonEntholzer Aug 27, 2024
ebf8872
fix java docs
SimonEntholzer Aug 27, 2024
6a22ed1
Merge branch 'develop' into feature/repository-access-logging
SimonEntholzer Aug 27, 2024
bdfb881
only show to instructors in repository view (course-management)
SimonEntholzer Aug 27, 2024
a41647f
Merge branch 'develop' into feature/repository-access-logging
SimonEntholzer Aug 27, 2024
65a6d4a
fix typo
SimonEntholzer Aug 27, 2024
bfce78a
increase size of name
SimonEntholzer Aug 27, 2024
ca04dc1
cleanup entries after 120 days
SimonEntholzer Aug 27, 2024
9564657
fix params
SimonEntholzer Aug 28, 2024
f727129
fix java docs in Repository
SimonEntholzer Aug 28, 2024
0c01195
fix authorization
SimonEntholzer Aug 28, 2024
5905ce2
unsubscribe subscription
SimonEntholzer Aug 28, 2024
3343cf2
remove comments
SimonEntholzer Aug 28, 2024
237539a
Merge branch 'develop' into feature/repository-access-logging
SimonEntholzer Aug 28, 2024
35902d9
Update src/main/resources/config/application-prod.yml
SimonEntholzer Aug 30, 2024
22a0ec5
Update src/main/webapp/i18n/en/repository.json
SimonEntholzer Aug 30, 2024
053e208
Update src/main/webapp/i18n/de/repository.json
SimonEntholzer Aug 30, 2024
7720081
Update src/main/webapp/i18n/de/repository.json
SimonEntholzer Aug 30, 2024
a161f68
Merge branch 'develop' into feature/repository-access-logging
SimonEntholzer Aug 30, 2024
56a5320
added improved java docs and added more logging
SimonEntholzer Aug 30, 2024
b0fae59
Merge branch 'develop' into feature/repository-access-logging
SimonEntholzer Sep 1, 2024
94011e5
Merge remote-tracking branch 'refs/remotes/origin/develop' into featu…
SimonEntholzer Sep 3, 2024
c8f9165
improve client test
SimonEntholzer Sep 3, 2024
030b03d
only show button to instructors (and admins)
SimonEntholzer Sep 3, 2024
cc13acd
remove console log
SimonEntholzer Sep 3, 2024
6c2e34b
add docs and return value annotation
SimonEntholzer Sep 3, 2024
d3ca8e7
only enable features when using LocalVc
SimonEntholzer Sep 3, 2024
292293a
moved tests
SimonEntholzer Sep 3, 2024
8fd49bd
moved tests
SimonEntholzer Sep 3, 2024
e79ae43
improved test
SimonEntholzer Sep 3, 2024
01b5c6e
Merge branch 'develop' into feature/repository-access-logging
SimonEntholzer Sep 5, 2024
a8f7339
Merge branch 'develop' into feature/repository-access-logging
SimonEntholzer Sep 5, 2024
a617992
implemented feedback
SimonEntholzer Sep 6, 2024
fd49c1e
removed redundant check
SimonEntholzer Sep 6, 2024
ffd25f2
mock profileService in test
SimonEntholzer Sep 6, 2024
bf83d62
improved java docs
SimonEntholzer Sep 6, 2024
00599e5
Merge branch 'develop' into feature/repository-access-logging
SimonEntholzer Sep 6, 2024
df93727
improve Authorization
SimonEntholzer Sep 6, 2024
b776d11
fix client test
SimonEntholzer Sep 6, 2024
24a6c26
adapt query to only return one element
SimonEntholzer Sep 6, 2024
fa2bf89
Merge branch 'develop' into feature/repository-access-logging
SimonEntholzer Sep 7, 2024
07728c4
Merge branch 'develop' into feature/repository-access-logging
SimonEntholzer Sep 8, 2024
3396c28
removed unnecessary ArtemisSharedModule import
SimonEntholzer Sep 9, 2024
b05008f
refactored for new client guidelines
SimonEntholzer Sep 9, 2024
0b2b6c0
change client to new client guidelines
SimonEntholzer Sep 9, 2024
f36edeb
Merge branch 'develop' into feature/repository-access-logging
SimonEntholzer Sep 9, 2024
b69971f
Merge branch 'refs/heads/develop' into feature/repository-access-logging
SimonEntholzer Sep 10, 2024
63d9566
moved test setup to beginning
SimonEntholzer Sep 10, 2024
160e6f7
add translate directive
SimonEntholzer Sep 10, 2024
1e1d1a3
Merge branch 'develop' into feature/repository-access-logging
SimonEntholzer Sep 12, 2024
2a2f3b3
Merge branch 'refs/heads/develop' into feature/repository-access-logging
SimonEntholzer Sep 12, 2024
5028068
resolved conflicts
SimonEntholzer Sep 12, 2024
bd093a0
Merge branch 'develop' into feature/repository-access-logging
SimonEntholzer Sep 13, 2024
204ca60
Merge branch 'develop' into feature/repository-access-logging
SimonEntholzer Sep 13, 2024
24a4f73
Merge branch 'develop' into feature/repository-access-logging
SimonEntholzer Sep 14, 2024
2b1c91b
Merge branch 'develop' into feature/repository-access-logging
SimonEntholzer Sep 15, 2024
c24608a
Merge branch 'develop' into feature/repository-access-logging
SimonEntholzer Sep 17, 2024
a39771e
Merge branch 'develop' into feature/repository-access-logging
SimonEntholzer Sep 18, 2024
2998161
Merge branch 'develop' into feature/repository-access-logging
SimonEntholzer Sep 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package de.tum.in.www1.artemis.domain.vcstokens;

public enum AuthenticationMechanism {
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
/**
* The user used password to authenticate to the LocalVC
*/
PASSWORD,
/**
* The user used the participation+user token to authenticate to the LocalVC
*/
PARTICIPATION_VCS_ACCESS_TOKEN,
/**
* The user used the user token to authenticate to the LocalVC
*/
USER_VCS_ACCESS_TOKEN,
/**
* The user used SSH user token to authenticate to the LocalVC
*/
SSH
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package de.tum.in.www1.artemis.domain.vcstokens;

import java.time.ZonedDateTime;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;

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

import com.fasterxml.jackson.annotation.JsonInclude;

import de.tum.in.www1.artemis.domain.DomainObject;
import de.tum.in.www1.artemis.domain.User;
import de.tum.in.www1.artemis.domain.participation.Participation;
import de.tum.in.www1.artemis.web.rest.repository.RepositoryActionType;

/**
* A Vcs access log entry.
*/
@Entity
@Table(name = "vcs_access_log")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class VcsAccessLog extends DomainObject {

@ManyToOne
private User user;

@ManyToOne
private Participation participation;

@Column(name = "name")
private String name;

@Column(name = "email")
private String email;

@Column(name = "repository_action_type", nullable = false)
@Enumerated(EnumType.ORDINAL)
private RepositoryActionType repositoryActionType;

@Column(name = "authentication_mechanism", nullable = false)
@Enumerated(EnumType.ORDINAL)
private AuthenticationMechanism authenticationMechanism;
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved

@Column(name = "commit_hash")
private String commitHash;

@Column(name = "ip_address")
private String ipAddress;

@Column(name = "timestamp")
private ZonedDateTime timestamp;

public VcsAccessLog(User user, Participation participation, String name, String email, RepositoryActionType repositoryActionType,
AuthenticationMechanism authenticationMechanism, String commitHash, String ipAddress) {
this.user = user;
this.participation = participation;
this.name = name;
this.email = email;
this.repositoryActionType = repositoryActionType;
this.authenticationMechanism = authenticationMechanism;
this.commitHash = commitHash;
this.ipAddress = ipAddress;
this.timestamp = ZonedDateTime.now();
}

public VcsAccessLog() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package de.tum.in.www1.artemis.repository;

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

import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Repository;

import de.tum.in.www1.artemis.domain.vcstokens.VcsAccessLog;
import de.tum.in.www1.artemis.repository.base.ArtemisJpaRepository;

/**
* Spring Data JPA repository for the User entity.<br>
* <br>
* <p>
* <b>Note</b>: Please keep in mind that the User entities are soft-deleted when adding new queries to this repository.
* If you don't need deleted user entities, add `WHERE user.isDeleted = FALSE` to your query.
* </p>
*/
@Profile(PROFILE_CORE)
@Repository
public interface VcsAccessLogRepository extends ArtemisJpaRepository<VcsAccessLog, Long> {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package de.tum.in.www1.artemis.service;

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

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

import de.tum.in.www1.artemis.domain.User;
import de.tum.in.www1.artemis.domain.participation.Participation;
import de.tum.in.www1.artemis.domain.participation.ProgrammingExerciseParticipation;
import de.tum.in.www1.artemis.domain.vcstokens.AuthenticationMechanism;
import de.tum.in.www1.artemis.domain.vcstokens.VcsAccessLog;
import de.tum.in.www1.artemis.repository.VcsAccessLogRepository;
import de.tum.in.www1.artemis.web.rest.repository.RepositoryActionType;

@Profile(PROFILE_LOCALVC)
@Service
public class VcsAccessLogService {

private static final Logger log = LoggerFactory.getLogger(VcsAccessLogService.class);

private final VcsAccessLogRepository vcsAccessLogRepository;

VcsAccessLogService(VcsAccessLogRepository vcsAccessLogRepository) {
this.vcsAccessLogRepository = vcsAccessLogRepository;
}

/**
* Creates a vcs access log entry and stores it to the database
*
* @param user The user accessing the repository
* @param participation The participation which owns the repository
* @param actionType The action type: READ or WRITE
* @param authenticationMechanism The used authentication mechanism: password, token or SSH
*/
public void storeAccessLog(User user, ProgrammingExerciseParticipation participation, RepositoryActionType actionType, AuthenticationMechanism authenticationMechanism,
String ipAddress) {
log.debug("Storing access operation for user {}", user);

VcsAccessLog accessLogEntry = new VcsAccessLog(user, (Participation) participation, user.getName(), user.getEmail(), actionType, authenticationMechanism, null, ipAddress);
vcsAccessLogRepository.save(accessLogEntry);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import de.tum.in.www1.artemis.domain.participation.ProgrammingExerciseParticipation;
import de.tum.in.www1.artemis.domain.participation.ProgrammingExerciseStudentParticipation;
import de.tum.in.www1.artemis.domain.participation.SolutionProgrammingExerciseParticipation;
import de.tum.in.www1.artemis.domain.vcstokens.AuthenticationMechanism;
import de.tum.in.www1.artemis.exception.ContinuousIntegrationException;
import de.tum.in.www1.artemis.exception.VersionControlException;
import de.tum.in.www1.artemis.exception.localvc.LocalVCAuthException;
Expand All @@ -55,6 +56,7 @@
import de.tum.in.www1.artemis.repository.UserRepository;
import de.tum.in.www1.artemis.security.SecurityUtils;
import de.tum.in.www1.artemis.service.AuthorizationCheckService;
import de.tum.in.www1.artemis.service.VcsAccessLogService;
import de.tum.in.www1.artemis.service.connectors.ci.ContinuousIntegrationTriggerService;
import de.tum.in.www1.artemis.service.programming.AuxiliaryRepositoryService;
import de.tum.in.www1.artemis.service.programming.ProgrammingExerciseParticipationService;
Expand Down Expand Up @@ -100,6 +102,8 @@ public class LocalVCServletService {

private final ProgrammingTriggerService programmingTriggerService;

private final VcsAccessLogService vcsAccessLogService;

private static URL localVCBaseUrl;

private final ParticipationVCSAccessTokenRepository participationVCSAccessTokenRepository;
Expand Down Expand Up @@ -132,7 +136,7 @@ public LocalVCServletService(AuthenticationManager authenticationManager, UserRe
ProgrammingExerciseParticipationService programmingExerciseParticipationService, AuxiliaryRepositoryService auxiliaryRepositoryService,
ContinuousIntegrationTriggerService ciTriggerService, ProgrammingSubmissionService programmingSubmissionService,
ProgrammingMessagingService programmingMessagingService, ProgrammingTriggerService programmingTriggerService,
ParticipationVCSAccessTokenRepository participationVCSAccessTokenRepository) {
ParticipationVCSAccessTokenRepository participationVCSAccessTokenRepository, VcsAccessLogService vcsAccessLogService) {
this.authenticationManager = authenticationManager;
this.userRepository = userRepository;
this.programmingExerciseRepository = programmingExerciseRepository;
Expand All @@ -145,6 +149,8 @@ public LocalVCServletService(AuthenticationManager authenticationManager, UserRe
this.programmingMessagingService = programmingMessagingService;
this.programmingTriggerService = programmingTriggerService;
this.participationVCSAccessTokenRepository = participationVCSAccessTokenRepository;
this.vcsAccessLogService = vcsAccessLogService;

}
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved

/**
Expand Down Expand Up @@ -238,13 +244,37 @@ public void authenticateAndAuthorizeGitRequest(HttpServletRequest request, Repos
throw new LocalVCForbiddenException();
}

authorizeUser(repositoryTypeOrUserName, user, exercise, repositoryAction, localVCRepositoryUri.isPracticeRepository());
var authenticationMechanism = resolveAuthenticationMechanism(authorizationHeader, user);
var ipAddress = request.getRemoteAddr();
authorizeUser(repositoryTypeOrUserName, user, exercise, repositoryAction, authenticationMechanism, ipAddress, localVCRepositoryUri.isPracticeRepository());

request.setAttribute("user", user);

log.debug("Authorizing user {} for repository {} took {}", user.getLogin(), localVCRepositoryUri, TimeLogUtil.formatDurationFrom(timeNanoStart));
}

/**
* Resolves the user's authentication mechanism for the repository
*
* @param authorizationHeader the request's authorizationHeader, containing the token or password
* @param user the user
* @return the authentication type
* @throws LocalVCAuthException if extracting the token or password from the authorizationHeader fails
*/
private AuthenticationMechanism resolveAuthenticationMechanism(String authorizationHeader, User user) throws LocalVCAuthException {
UsernameAndPassword usernameAndPassword = extractUsernameAndPassword(authorizationHeader);

String password = usernameAndPassword.password();
if (!password.startsWith("vcpat")) {
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
return AuthenticationMechanism.PASSWORD;
}
if (password.equals(user.getVcsAccessToken())) {
return AuthenticationMechanism.USER_VCS_ACCESS_TOKEN;
}
return AuthenticationMechanism.PARTICIPATION_VCS_ACCESS_TOKEN;

}
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved

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

UsernameAndPassword usernameAndPassword = extractUsernameAndPassword(authorizationHeader);
Expand Down Expand Up @@ -380,8 +410,8 @@ private UsernameAndPassword extractUsernameAndPassword(String authorizationHeade
* @param isPracticeRepository Whether the repository is a practice repository.
* @throws LocalVCForbiddenException If the user is not allowed to access the repository.
*/
public void authorizeUser(String repositoryTypeOrUserName, User user, ProgrammingExercise exercise, RepositoryActionType repositoryActionType, boolean isPracticeRepository)
throws LocalVCForbiddenException {
public void authorizeUser(String repositoryTypeOrUserName, User user, ProgrammingExercise exercise, RepositoryActionType repositoryActionType,
AuthenticationMechanism authenticationMechanism, String ipAddress, boolean isPracticeRepository) throws LocalVCForbiddenException {

if (repositoryTypeOrUserName.equals(RepositoryType.TESTS.toString()) || auxiliaryRepositoryService.isAuxiliaryRepositoryOfExercise(repositoryTypeOrUserName, exercise)) {
// Test and auxiliary repositories are only accessible by instructors and higher.
Expand All @@ -405,6 +435,7 @@ public void authorizeUser(String repositoryTypeOrUserName, User user, Programmin

try {
repositoryAccessService.checkAccessRepositoryElseThrow(participation, user, exercise, repositoryActionType);
vcsAccessLogService.storeAccessLog(user, participation, repositoryActionType, authenticationMechanism, ipAddress);
}
catch (AccessForbiddenException e) {
throw new LocalVCForbiddenException(e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import de.tum.in.www1.artemis.config.icl.ssh.SshConstants;
import de.tum.in.www1.artemis.domain.ProgrammingExercise;
import de.tum.in.www1.artemis.domain.vcstokens.AuthenticationMechanism;
import de.tum.in.www1.artemis.exception.localvc.LocalVCForbiddenException;
import de.tum.in.www1.artemis.exception.localvc.LocalVCInternalException;
import de.tum.in.www1.artemis.repository.ProgrammingExerciseRepository;
Expand Down Expand Up @@ -78,7 +79,8 @@ public Path resolveRootDirectory(String command, String[] args, ServerSession se
else {
final var user = session.getAttribute(SshConstants.USER_KEY);
try {
localVCServletService.authorizeUser(repositoryTypeOrUserName, user, exercise, repositoryAction, localVCRepositoryUri.isPracticeRepository());
localVCServletService.authorizeUser(repositoryTypeOrUserName, user, exercise, repositoryAction, AuthenticationMechanism.SSH, session.getClientAddress().toString(),
localVCRepositoryUri.isPracticeRepository());
}
catch (LocalVCForbiddenException e) {
log.error("User {} does not have access to the repository {}", user.getLogin(), repositoryPath);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
<changeSet id="20240804144500" author="entholzer">
<!--
Change Set for creating a new table for storing the LocalVC access logs
-->
<createTable tableName="vcs_access_log">
<column autoIncrement="true" name="id" type="bigint">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="user_id" type="bigint">
<constraints nullable="false"/>
</column>
<column name="participation_id" type="bigint">
<constraints nullable="false"/>
</column>
<column name="name" type="varchar(50)">
<constraints nullable="false"/>
</column>
<column name="email" type="varchar(100)">
<constraints nullable="false"/>
</column>
<column name="repository_action_type" type="tinyint">
<constraints nullable="false"/>
</column>
<column name="authentication_mechanism" type="tinyint">
<constraints nullable="false"/>
</column>
<column name="commit_hash" type="varchar(40)">
<constraints nullable="true"/>
</column>
<column name="ip_address" type="varchar(45)">
<constraints nullable="true"/>
</column>
<column defaultValueComputed="CURRENT_TIMESTAMP" name="timestamp" type="timestamp">
<constraints nullable="false"/>
</column>
</createTable>
<createIndex indexName="idx_vcs_access_log_user_participation" tableName="vcs_access_log">
<column name="user_id"/>
<column name="participation_id"/>
</createIndex>
</changeSet>
</databaseChangeLog>
2 changes: 2 additions & 0 deletions src/main/resources/config/liquibase/master.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
<include file="classpath:config/liquibase/changelog/20240626200000_changelog.xml" relativeToChangelogFile="false"/>
<include file="classpath:config/liquibase/changelog/20240708144500_changelog.xml" relativeToChangelogFile="false"/>
<include file="classpath:config/liquibase/changelog/20240802091201_changelog.xml" relativeToChangelogFile="false"/>
<include file="classpath:config/liquibase/changelog/20240804144500_changelog.xml" relativeToChangelogFile="false"/>

<!-- NOTE: please use the format "YYYYMMDDhhmmss_changelog.xml", i.e. year month day hour minutes seconds and not something else! -->
<!-- we should also stay in a chronological order! -->
<!-- you can use the command 'date '+%Y%m%d%H%M%S'' to get the current date and time in the correct format -->
Expand Down
Loading