Skip to content

Commit

Permalink
Merge pull request #393 from mp-access/dev
Browse files Browse the repository at this point in the history
Release v0.12.0
  • Loading branch information
a-a-hofmann authored Nov 13, 2019
2 parents 56d7260 + f3d11e9 commit 3432374
Show file tree
Hide file tree
Showing 49 changed files with 696 additions and 328 deletions.
25 changes: 14 additions & 11 deletions src/main/java/ch/uzh/ifi/access/coderunner/CodeRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
import com.spotify.docker.client.DockerClient;
import com.spotify.docker.client.exceptions.DockerCertificateException;
import com.spotify.docker.client.exceptions.DockerException;
import com.spotify.docker.client.messages.*;
import com.spotify.docker.client.messages.ContainerConfig;
import com.spotify.docker.client.messages.ContainerCreation;
import com.spotify.docker.client.messages.ContainerState;
import com.spotify.docker.client.messages.Info;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
Expand All @@ -15,7 +18,6 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.StringJoiner;
import java.util.concurrent.*;
import java.util.stream.Stream;
Expand All @@ -34,11 +36,16 @@ public class CodeRunner {

public CodeRunner() throws DockerCertificateException {
docker = DefaultDockerClient.fromEnv().build();
logDebugInfo();
pullImageIfNotPresent();
logDockerInfo();
pullImage();
}

private void logDebugInfo() {
public void check() {
logDockerInfo();
pullImage();
}

public void logDockerInfo() {
try {
Info info = docker.info();
logger.debug("Connected to docker daemon @ " + docker.getHost());
Expand All @@ -48,13 +55,9 @@ private void logDebugInfo() {
}
}

private void pullImageIfNotPresent() {
private void pullImage() {
try {
List<Image> images = docker.listImages(DockerClient.ListImagesParam.byName(PythonImageConfig.PYTHON_DOCKER_IMAGE));
if (images.isEmpty()) {
logger.debug(String.format("No suitable python image found (searched for %s), pulling new image from registry", PythonImageConfig.PYTHON_DOCKER_IMAGE));
docker.pull(PythonImageConfig.PYTHON_DOCKER_IMAGE);
}
docker.pull(PythonImageConfig.PYTHON_DOCKER_IMAGE);
} catch (DockerException | InterruptedException e) {
logger.warn("Failed to pull python docker image", e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
@NoArgsConstructor
public class PythonImageConfig {

public static final String PYTHON_DOCKER_IMAGE = "python:3.7-alpine";
public static final String PYTHON_DOCKER_IMAGE = "hoal/access-python:3.7";

public static final String STUDENT_CODE_FOLDER = "/usr/src/";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public Authentication authentication(String courseId, String admin) {
Authentication auth = new UsernamePasswordAuthenticationToken("testUser", "---", List.of(new SimpleGrantedAuthority("USER")));
boolean isAdmin = Boolean.parseBoolean(admin);
boolean isStudent = !isAdmin;
GrantedCourseAccess access = new GrantedCourseAccess(Optional.ofNullable(courseId).orElse(""), isStudent, isAdmin);
GrantedCourseAccess access = new GrantedCourseAccess(Optional.ofNullable(courseId).orElse(""), isStudent, false, isAdmin);
return new CourseAuthentication(request, auth, Set.of(access), "") {
@Override
public boolean hasAccess(String courseId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,9 @@ GrantedCourseAccess parseCourseAccess(String courseElement) {
}
String courseKey = group.get(0);
boolean isStudent = group.get(1).toLowerCase().contains("students");
boolean isAuthor = group.get(1).toLowerCase().contains("authors");
boolean isAssistant = group.get(1).toLowerCase().contains("assistants");
boolean isAdmin = group.get(1).toLowerCase().contains("admins");

return new GrantedCourseAccess(courseKey, isStudent, isAuthor);
return new GrantedCourseAccess(courseKey, isStudent, isAssistant, isAdmin);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package ch.uzh.ifi.access.config;

import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
Expand Down Expand Up @@ -33,7 +32,7 @@ public class SecurityProperties {

private String keycloakApiPassword = "admin";

private String realm = "dev";
private String realm = "access";

private String frontendClientId= "access-frontend";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ private void filterAssignmentsByStartDate(Collection<CourseMetadataDTO> courses,
.getAssignments()
.removeIf(assignment -> {
// A course admin of a course has unrestricted access to said course
if (authentication.hasAdminAccess(course.getId())) {
if (authentication.hasPrivilegedAccess(course.getId())) {
return false;
}
return !assignment.isPublished();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import ch.uzh.ifi.access.course.model.security.GrantedCourseAccess;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;

Expand Down Expand Up @@ -34,11 +35,33 @@ public boolean hasAccess(String courseId) {
return courseAccesses.stream().anyMatch(access -> access.evaluateAccess(courseId));
}

public boolean hasPrivilegedAccess(String courseId) {
return courseAccesses.stream().anyMatch(access -> access.evaluateAssistantAccess(courseId));
}

public boolean hasAdminAccess(String courseId) {
return courseAccesses.stream().anyMatch(access -> access.evaluateAdminAccess(courseId));
}

public String getUserId() {
return userId;
}

/**
* Creates a new authentication object for the given user and sets it to the security context.
* If the user is not allowed to impersonate a user, does nothing and returns null.
*
* @param userId the user to impersonate
* @param courseId the course for which the calling user has admin access
* @return impersonated authentication object if admin, null otherwise.
*/
public CourseAuthentication impersonateUser(String userId, String courseId) {
if (hasAdminAccess(courseId)) {
CourseAuthentication impersonatedAuth = new CourseAuthentication(getOAuth2Request(), this, courseAccesses, userId);
SecurityContextHolder.getContext().setAuthentication(impersonatedAuth);
return impersonatedAuth;
}

return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,6 @@ public boolean hasAdminAccessToCourse(CourseAuthentication authentication, Cours
public boolean hasAccessToExercise(CourseAuthentication authentication, Exercise exercise) {
Assignment assignment = exercise.getAssignment();
String courseId = exercise.getCourseId();
return authentication.hasAccess(courseId) && (assignment.isPublished() || authentication.hasAdminAccess(courseId));
return authentication.hasAccess(courseId) && (assignment.isPublished() || authentication.hasPrivilegedAccess(courseId));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,6 @@ public ResponseEntity<Resource> searchForFile(
}

private boolean hasAccessToExerciseSolutions(Exercise exercise, CourseAuthentication authentication) {
return exercise.isPastDueDate() || authentication.hasAdminAccess(exercise.getCourseId());
return exercise.isPastDueDate() || authentication.hasPrivilegedAccess(exercise.getCourseId());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public class CourseMetadataDTO {
private String title;
private String description;
private String owner;
private String semester;
private String gitHash;
private ZonedDateTime startDate;
private ZonedDateTime endDate;
Expand All @@ -29,6 +30,7 @@ public CourseMetadataDTO(Course course) {
this.title = course.getTitle();
this.description = course.getDescription();
this.owner = course.getOwner();
this.semester = course.getSemester();
this.gitHash = course.getGitHash();
this.startDate = course.getStartDate();
this.endDate = course.getEndDate();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public class ExerciseMetadataDTO {
private ExerciseType type;
private String language;
private Boolean isGraded;
private int maxScore;
private double maxScore;

public ExerciseMetadataDTO(Exercise exercise) {
this.id = exercise.getId();
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/ch/uzh/ifi/access/course/model/Assignment.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ public Optional<Exercise> findExerciseById(String id) {
return exercises.stream().filter(e -> e.getId().equals(id)).findFirst();
}

public int getMaxScore() {
return exercises.stream().mapToInt(e -> e.getMaxScore()).sum();
public double getMaxScore() {
return exercises.stream().mapToDouble(ExerciseConfig::getMaxScore).sum();
}


Expand Down
6 changes: 4 additions & 2 deletions src/main/java/ch/uzh/ifi/access/course/model/Course.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ public Course(String name) {
}

@Builder
public Course(String title, String description, String owner, ZonedDateTime startDate, ZonedDateTime endDate, List<String> assistants, List<String> students, String id, String gitHash, String gitURL, String directory, List<Assignment> assignments) {
super(title, description, owner, startDate, endDate, assistants, students);
public Course(String title, String description, String owner, String semester, ZonedDateTime startDate, ZonedDateTime endDate, List<String> admins, List<String> assistants, List<String> students, String id, String gitHash, String gitURL, String directory, List<Assignment> assignments) {
super(title, description, owner, semester, startDate, endDate, admins, assistants, students);
this.id = id;
this.gitHash = gitHash;
this.gitURL = gitURL;
Expand All @@ -49,8 +49,10 @@ public void set(CourseConfig other) {
this.title = other.getTitle();
this.description = other.getDescription();
this.owner = other.getOwner();
this.semester = other.getSemester();
this.startDate = other.getStartDate();
this.endDate = other.getEndDate();
this.admins = other.getAdmins();
this.assistants = other.getAssistants();
this.students = other.getStudents();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,22 @@
@Data
@AllArgsConstructor
public class CourseConfig implements Serializable {

protected String title;
protected String description;
protected String owner;
protected String semester;
protected ZonedDateTime startDate;
protected ZonedDateTime endDate;

protected List<String> admins = List.of();
protected List<String> assistants = List.of();
protected List<String> students = List.of();

public CourseConfig() {
this.description = "";
this.owner = "";
this.semester = "";
}
}

}
6 changes: 4 additions & 2 deletions src/main/java/ch/uzh/ifi/access/course/model/Exercise.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public class Exercise extends ExerciseConfig implements Indexed<Exercise>, HasBr
@ToString.Exclude
private String question;


@JsonIgnore
@ToString.Exclude
private List<VirtualFile> private_files;
Expand All @@ -51,8 +52,8 @@ public Exercise(String name) {
}

@Builder
private Exercise(ExerciseType type, String language, Boolean isGraded, int maxScore, int maxSubmits, String gradingSetup, List<String> options, List<String> solutions, List<String> hints, String id, int index, String gitHash, Assignment assignment, String question, List<VirtualFile> private_files, List<VirtualFile> solution_files, List<VirtualFile> resource_files, List<VirtualFile> public_files, CodeExecutionLimits executionLimits, String title, String longTitle) {
super(title, longTitle, type, language, isGraded, maxScore, maxSubmits, gradingSetup, options, solutions, hints, executionLimits);
private Exercise(ExerciseType type, String language, Boolean isGraded, int maxScore, int maxSubmits, String gradingSetup, List<String> options, List<String> solutions, List<String> hints, String id, int index, String gitHash, Assignment assignment, String question, List<VirtualFile> private_files, List<VirtualFile> solution_files, List<VirtualFile> resource_files, List<VirtualFile> public_files, CodeExecutionLimits executionLimits, String title, String longTitle, Rounding rounding) {
super(title, longTitle, type, language, isGraded, maxScore, maxSubmits, gradingSetup, rounding, options, solutions, hints, executionLimits);
this.id = id;
this.index = index;
this.gitHash = gitHash;
Expand Down Expand Up @@ -82,6 +83,7 @@ public void set(ExerciseConfig other) {
this.solutions = other.getSolutions();
this.hints = other.getHints();
this.executionLimits = other.getExecutionLimits();
this.rounding = other.getRounding();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,23 @@ public class ExerciseConfig implements Serializable {
protected ExerciseType type;
protected String language;
protected Boolean isGraded;
protected int maxScore;
protected double maxScore;
protected int maxSubmits;
protected String gradingSetup;
protected Rounding rounding;

protected List<String> options;
protected List<String> solutions;
protected List<String> hints;

protected CodeExecutionLimits executionLimits;


public ExerciseConfig() {
this.isGraded = true;
this.maxSubmits = 1;
this.maxScore = 1;
this.executionLimits = CodeExecutionLimits.DEFAULTS;
this.rounding = Rounding.DEFAULT;
}

}
58 changes: 58 additions & 0 deletions src/main/java/ch/uzh/ifi/access/course/model/Rounding.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package ch.uzh.ifi.access.course.model;

import lombok.Data;

import java.io.Serializable;
import java.util.function.BiFunction;

@Data
public class Rounding implements Serializable {

public static final Rounding DEFAULT = new Rounding(Strategy.ROUND, 4);

private Strategy strategy;
private int steps;

public Rounding() {
}

public Rounding(Strategy strategy, int steps) {
this.strategy = strategy;
this.steps = steps;
}

public double round(double value) {
return strategy.round(value, steps);
}

public enum Strategy {

CEILING(Strategy::ceil),
FLOOR(Strategy::floor),
ROUND(Strategy::roundUp);

private BiFunction<Double, Integer, Double> algorithm;

Strategy(BiFunction<Double, Integer, Double> algorithm) {
this.algorithm = algorithm;
}

double round(double unroundedValue, int steps) {
return algorithm.apply(unroundedValue, steps);
}

static double ceil(Double unroundedValue, Integer steps) {
return Math.ceil( unroundedValue*steps)/steps;
}

static double floor(Double unroundedValue, Integer steps) {
return Math.floor(unroundedValue * steps)/steps;
}

static double roundUp(double unroundedValue, Integer steps) {
return (double) Math.round(unroundedValue * steps) / steps;
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public VirtualFile() {
}

public VirtualFile(String fullPath, String virtualPath) {
this.id = new Utils().getID();
this.id = new Utils().getID(fullPath);
this.file = new File(fullPath);
this.path = virtualPath;

Expand Down
Loading

0 comments on commit 3432374

Please sign in to comment.