Skip to content

Commit

Permalink
add ssh and token authentication
Browse files Browse the repository at this point in the history
  • Loading branch information
SimonEntholzer committed Dec 16, 2024
1 parent 9e712a2 commit 8296602
Show file tree
Hide file tree
Showing 24 changed files with 851 additions and 229 deletions.
17 changes: 16 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,22 @@ dependencies {
implementation "org.springframework.security:spring-security-messaging:${springSecurityVersion}"
implementation "commons-io:commons-io:2.16.1"
implementation "com.thedeanda:lorem:2.2"
implementation "org.eclipse.jgit:org.eclipse.jgit:6.9.0.202403050737-r"
implementation "org.eclipse.jgit:org.eclipse.jgit:7.1.0.202411261347-r"
// https://search.maven.org/artifact/org.eclipse.jgit/org.eclipse.jgit
implementation "org.eclipse.jgit:org.eclipse.jgit.ssh.apache:7.1.0.202411261347-r"
// Note: jgit.htt.server is not compatible with jakarta yet and neither is there a timeline. Hence, we had to add the source files to our repository.
// Once the compatibility is given, we can switch back to the maven dependency.
implementation "org.eclipse.jgit:org.eclipse.jgit.http.server:7.1.0.202411261347-r"

// apache ssh enabled the ssh git operations in LocalVC together with JGit
implementation "org.apache.sshd:sshd-core:2.14.0"
implementation "org.apache.sshd:sshd-git:2.14.0"
implementation "org.apache.sshd:sshd-osgi:2.14.0"
implementation "org.apache.sshd:sshd-sftp:2.14.0"

implementation "org.bouncycastle:bcpkix-jdk18on:1.79"
implementation "org.bouncycastle:bcprov-jdk18on:1.79"

implementation "io.reactivex.rxjava3:rxjava:3.1.8"
implementation 'io.netty:netty-resolver-dns-native-macos:4.1.100.Final:osx-aarch_64'
implementation 'com.opencsv:opencsv:5.9'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package de.tum.cit.ase.artemisModel;

public enum ArtemisAuthMechanism {
ONLINE_IDE,
PASSWORD,
PARTICIPATION_TOKEN,
SSH,
USER_TOKEN,
}
43 changes: 43 additions & 0 deletions src/main/java/de/tum/cit/ase/domain/ArtemisUser.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,21 @@
import com.opencsv.bean.CsvBindByName;
import de.tum.cit.ase.util.ArtemisServer;
import jakarta.persistence.*;
import java.io.ByteArrayOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.security.*;
import java.security.interfaces.RSAPublicKey;
import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.Base64;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemWriter;
import org.springframework.data.util.Pair;

@Entity
@Table(name = "artemis_user")
Expand Down Expand Up @@ -35,6 +49,14 @@ public class ArtemisUser {
@JsonIgnore
private ZonedDateTime tokenExpirationDate;

@Column(name = "public_ssh_key")
@JsonIgnore
private String publicKey;

@Column(name = "private_ssh_key")
@JsonIgnore
private String privateKey;

public Long getId() {
return id;
}
Expand Down Expand Up @@ -90,4 +112,25 @@ public ZonedDateTime getTokenExpirationDate() {
public void setTokenExpirationDate(ZonedDateTime tokenExpirationDate) {
this.tokenExpirationDate = tokenExpirationDate;
}

public String getPrivateKey() {
return privateKey;
}

public void setPrivateKey(String privateKey) {
this.privateKey = privateKey;
}

public String getPublicKey() {
return publicKey;
}

public void setPublicKey(String publicKey) {
this.publicKey = publicKey;
}

public void setKeyPair(Pair<String, String> keyPair) {
this.publicKey = keyPair.getFirst();
this.privateKey = keyPair.getSecond();
}
}
8 changes: 8 additions & 0 deletions src/main/java/de/tum/cit/ase/domain/RequestType.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,12 @@ public enum RequestType {
REPOSITORY_INFO,
REPOSITORY_FILES,
MISC,
SETUP_SSH_KEYS,
FETCH_PARTICIPATION_VCS_ACCESS_TOKEN,
CLONE_SSH,
CLONE_TOKEN,
CLONE_PASSWORD,
PUSH_SSH,
PUSH_TOKEN,
PUSH_PASSWORD,
}
50 changes: 50 additions & 0 deletions src/main/java/de/tum/cit/ase/domain/Simulation.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,24 @@ public class Simulation {
@Column(name = "user_range")
private String userRange;

@Deprecated
@JsonIgnore
@Enumerated(EnumType.STRING)
@Column(name = "ide_type", nullable = false)
private IDEType ideType;

@Column(name = "onlineide_percentage", nullable = false)
private double onlineIdePercentage;

@Column(name = "password_percentage", nullable = false)
private double passwordPercentage;

@Column(name = "token_percentage", nullable = false)
private double tokenPercentage;

@Column(name = "ssh_percentage", nullable = false)
private double sshPercentage;

@Column(name = "number_of_commits_and_pushes_from")
private int numberOfCommitsAndPushesFrom;

Expand Down Expand Up @@ -212,6 +226,42 @@ public boolean instructorCredentialsProvided() {
return instructorUsername != null && instructorPassword != null;
}

public double getOnlineIdePercentage() {
return onlineIdePercentage;
}

public void setOnlineIdePercentage(double onlineIdePercentage) {
this.onlineIdePercentage = onlineIdePercentage;
}

public double getPasswordPercentage() {
return passwordPercentage;
}

public void setPasswordPercentage(double passwordPercentage) {
this.passwordPercentage = passwordPercentage;
}

public double getTokenPercentage() {
return tokenPercentage;
}

public void setTokenPercentage(double tokenPercentage) {
this.tokenPercentage = tokenPercentage;
}

public double getSshPercentage() {
return sshPercentage;
}

public void setSshPercentage(double sshPercentage) {
this.sshPercentage = sshPercentage;
}

public boolean participationPercentagesSumUpToHundredPercent() {
return (this.onlineIdePercentage + this.passwordPercentage + this.tokenPercentage + this.sshPercentage) == 100.0;
}

public enum Mode {
/**
* We create a temporary course and exam, prepare the exam and delete everything afterwards.
Expand Down
6 changes: 5 additions & 1 deletion src/main/java/de/tum/cit/ase/service/CiStatusService.java
Original file line number Diff line number Diff line change
Expand Up @@ -134,18 +134,22 @@ public CompletableFuture<Void> subscribeToCiStatusViaResults(SimulationRun simul
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
log.debug("Updating CI status for simulation run {}", simulationRun.getId());
log.info("Updating CI status for simulation run {}", simulationRun.getId());

submissions = new ArrayList<>();
for (var participation : participations) {
submissions.addAll(admin.getSubmissions(participation.getId()));
}
numberOfQueuedJobs = submissions.size() - getNumberOfResults(submissions);
log.info("Currently queued buildjobs: {}", numberOfQueuedJobs);

status.setQueuedJobs(numberOfQueuedJobs);
status.setTimeInMinutes(status.getTimeInMinutes() + 1);
status.setAvgJobsPerMinute((double) (status.getTotalJobs() - status.getQueuedJobs()) / status.getTimeInMinutes());
status = ciStatusRepository.save(status);
websocketService.sendRunCiUpdate(simulationRun.getId(), status);
} while (numberOfQueuedJobs > 0);

status.setFinished(true);
status = ciStatusRepository.save(status);
websocketService.sendRunCiUpdate(simulationRun.getId(), status);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,15 @@
import de.tum.cit.ase.service.dto.ArtemisUserPatternDTO;
import de.tum.cit.ase.util.ArtemisServer;
import de.tum.cit.ase.util.NumberRangeParser;
import de.tum.cit.ase.util.SshUtils;
import de.tum.cit.ase.web.rest.errors.BadRequestAlertException;
import java.io.*;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.util.Pair;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

Expand Down Expand Up @@ -61,14 +65,28 @@ public List<ArtemisUser> createArtemisUsersByPattern(ArtemisServer server, Artem
if (admin == null) {
throw new BadRequestAlertException("No admin user found for server", "artemisUser", "missingAdmin");
}
simulatedArtemisAdmin =
SimulatedArtemisUser.createArtemisAdminFromCredentials(
artemisConfiguration.getUrl(server),
admin.getUsername(),
admin.getPassword()
);
simulatedArtemisAdmin = SimulatedArtemisUser.createArtemisAdminFromCredentials(
artemisConfiguration.getUrl(server),
admin.getUsername(),
admin.getPassword()
);
simulatedArtemisAdmin.login();
}
log.info("Generate SSH keys... this might take some time");
AtomicInteger sshKeyCounter = new AtomicInteger(0);
int totalKeys = pattern.getTo() - pattern.getFrom();
Pair<String, String>[] pregeneratedSSHkeys = new Pair[totalKeys + 1];

IntStream.range(pattern.getFrom(), pattern.getTo() + 1)
.parallel()
.forEach(i -> {
if (sshKeyCounter.get() % 100 == 0) {
log.info("{{}} of {{}} keys created...", sshKeyCounter.get(), totalKeys);
}
pregeneratedSSHkeys[i - pattern.getFrom()] = SshUtils.generateSshKeyPair();
sshKeyCounter.getAndIncrement();
});
log.info("Done generating {{}} SSH keys", totalKeys);

List<ArtemisUser> createdUsers = new ArrayList<>();
for (int i = pattern.getFrom(); i < pattern.getTo(); i++) {
Expand All @@ -79,6 +97,8 @@ public List<ArtemisUser> createArtemisUsersByPattern(ArtemisServer server, Artem
var password = pattern.getPasswordPattern().replace("{i}", String.valueOf(i));
artemisUser.setUsername(username);
artemisUser.setPassword(password);
artemisUser.setKeyPair(pregeneratedSSHkeys[i - pattern.getFrom()]);

try {
ArtemisUser createdUser = saveArtemisUser(artemisUser);
// Create user on Artemis if necessary
Expand Down Expand Up @@ -112,6 +132,7 @@ public ArtemisUser createArtemisUser(ArtemisServer server, ArtemisUserForCreatio
artemisUser.setServer(server);
artemisUser.setUsername(artemisUserDTO.getUsername());
artemisUser.setPassword(artemisUserDTO.getPassword());
artemisUser.setKeyPair(SshUtils.generateSshKeyPair());

if (artemisUserDTO.getServerWideId() != null) {
artemisUser.setServerWideId(artemisUserDTO.getServerWideId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,19 @@ public Course createCourse() {
.block();
}

public void cancelAllQueuedBuildJobs() {
if (!authenticated) {
throw new IllegalStateException("User " + username + " is not logged in or does not have the necessary access rights.");
}

webClient
.delete()
.uri(uriBuilder -> uriBuilder.pathSegment("api", "admin", "cancel-all-queued-jobs").build())
.retrieve()
.toBodilessEntity()
.block();
}

/**
* Create an exam for benchmarking.
* @param course the course for which to create the exam
Expand Down
Loading

0 comments on commit 8296602

Please sign in to comment.