Skip to content

Commit

Permalink
Merge branch 'dev' into CSCEXAM-1254
Browse files Browse the repository at this point in the history
  • Loading branch information
VirmasaloA authored Feb 5, 2024
2 parents e981145 + eac262e commit 94ff800
Show file tree
Hide file tree
Showing 26 changed files with 1,508 additions and 2,487 deletions.
20 changes: 17 additions & 3 deletions app/controllers/ExamController.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import models.ExamType;
import models.GradeScale;
import models.Language;
import models.Permission;
import models.Role;
import models.Software;
import models.User;
Expand Down Expand Up @@ -249,16 +250,23 @@ public Result getExam(Long id, Http.Request request) {
if (exam == null) {
return notFound("i18n_error_exam_not_found");
}
// decipher the settings passwords if any
// decipher the passwords if any
if (exam.getImplementation() == Exam.Implementation.CLIENT_AUTH) {
exam
.getExaminationEventConfigurations()
.forEach(eec -> {
String plainTextPwd = byodConfigHandler.getPlaintextPassword(
String plainTextSettingsPwd = byodConfigHandler.getPlaintextPassword(
eec.getEncryptedSettingsPassword(),
eec.getSettingsPasswordSalt()
);
eec.setSettingsPassword(plainTextPwd);
eec.setSettingsPassword(plainTextSettingsPwd);
if (eec.getEncryptedQuitPassword() != null) {
String plainTextQuitPwd = byodConfigHandler.getPlaintextPassword(
eec.getEncryptedQuitPassword(),
eec.getQuitPasswordSalt()
);
eec.setQuitPassword(plainTextQuitPwd);
}
});
}
User user = request.attrs().get(Attrs.AUTHENTICATED_USER);
Expand Down Expand Up @@ -494,6 +502,12 @@ public Result createExamDraft(Http.Request request) {
return badRequest("Unsupported execution type");
}
User user = request.attrs().get(Attrs.AUTHENTICATED_USER);
if (
Exam.Implementation.valueOf(implementation) != Exam.Implementation.AQUARIUM &&
!user.hasPermission(Permission.Type.CAN_CREATE_BYOD_EXAM)
) {
return forbidden("No permission to create home examinations");
}
Exam exam = new Exam();
exam.generateHash();
exam.setState(Exam.State.DRAFT);
Expand Down
19 changes: 18 additions & 1 deletion app/controllers/ExaminationEventController.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import javax.inject.Inject;
import models.Exam;
import models.ExaminationDate;
Expand Down Expand Up @@ -209,7 +210,7 @@ public Result updateExaminationEvent(Long eid, Long eecid, Http.Request request)
return ok(eec);
}
if (!hasEnrolments) {
encryptQuitPassword(eec, settingsPassword);
encryptQuitPassword(eec, quitPassword);
encryptSettingsPassword(eec, settingsPassword, quitPassword);
eec.save();
// Pass back the plaintext passwords, so they can be shown to user
Expand Down Expand Up @@ -321,4 +322,20 @@ public Result listExaminationEvents(Optional<String> start, Optional<String> end
Set<ExaminationEventConfiguration> exams = query.where().eq("exam.state", Exam.State.PUBLISHED).findSet();
return ok(exams, pp);
}

@Restrict({ @Group("TEACHER"), @Group("ADMIN") })
public Result listOverlappingExaminationEvents(String start, Integer duration) {
PathProperties pp = PathProperties.parse("(*, examinationEventConfiguration(exam(id, duration)))");
DateTime startDate = DateTime.parse(start, ISODateTimeFormat.dateTimeParser());
DateTime endDate = startDate.plusMinutes(duration);
Set<ExaminationEvent> events = DB
.find(ExaminationEvent.class)
.where()
.le("start", endDate)
.findSet()
.stream()
.filter(ee -> !getEventEnding(ee).isBefore(startDate))
.collect(Collectors.toSet());
return ok(events, pp);
}
}
10 changes: 10 additions & 0 deletions app/controllers/SessionController.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import models.ExamEnrolment;
import models.Language;
import models.Organisation;
import models.Permission;
import models.Reservation;
import models.Role;
import models.User;
Expand Down Expand Up @@ -329,6 +330,15 @@ private void updateUser(User user, Http.Request request) throws AddressException
user.setUserIdentifier(
parse(request.header("schacPersonalUniqueCode").orElse("")).map(this::parseUserIdentifier).orElse(null)
);
// Grant BYOD permission automatically for teachers if configuration so mandates
if (user.hasRole(Role.Name.TEACHER) && configReader.isByodExamCreationPermissionGrantedForNewUsers()) {
Permission permission = DB
.find(Permission.class)
.where()
.eq("type", Permission.Type.CAN_CREATE_BYOD_EXAM)
.findOne();
user.getPermissions().add(permission);
}
user.setEmail(
parse(request.header("mail").orElse(""))
.flatMap(this::validateEmail)
Expand Down
7 changes: 7 additions & 0 deletions app/controllers/SettingsController.java
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,13 @@ public Result getCourseCodePrefix() {
return ok(Json.toJson(node));
}

@Restrict({ @Group("ADMIN"), @Group("TEACHER") })
public Result getByodMaxParticipants() {
ObjectNode node = Json.newObject();
node.put("max", configReader.getMaxByodExaminationParticipantCount());
return ok(Json.toJson(node));
}

private URL parseExternalUrl(String reservationRef) throws MalformedURLException {
return URI
.create(configReader.getIopHost() + String.format("/api/enrolments/%s/instructions", reservationRef))
Expand Down
2 changes: 2 additions & 0 deletions app/models/Permission.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ public class Permission extends GeneratedIdentityModel implements be.objectify.d
public enum Type {
@EnumValue("1")
CAN_INSPECT_LANGUAGE,
@EnumValue("2")
CAN_CREATE_BYOD_EXAM,
}

private Type type;
Expand Down
1 change: 1 addition & 0 deletions app/util/config/ConfigReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public interface ConfigReader {
String getSettingsPasswordEncryptionKey();
String getHomeOrganisationRef();
Integer getMaxByodExaminationParticipantCount();
boolean isByodExamCreationPermissionGrantedForNewUsers();
String getCourseCodePrefix();
String getIopHost();
boolean isApiKeyUsed();
Expand Down
5 changes: 5 additions & 0 deletions app/util/config/ConfigReaderImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,11 @@ public Integer getMaxByodExaminationParticipantCount() {
return config.getInt("exam.byod.maxConcurrentParticipants");
}

@Override
public boolean isByodExamCreationPermissionGrantedForNewUsers() {
return config.getBoolean("exam.byod.permission.allowed");
}

@Override
public String getCourseCodePrefix() {
return config.getString("exam.course.code.prefix");
Expand Down
2 changes: 2 additions & 0 deletions conf/application.conf
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,8 @@ exam.user.studentIds.multiple.organisations = "org1.org,org2.org,org3.org"
exam.byod.seb.active = false
# Enable / disable support for unsupervised home examination
exam.byod.home.active = false
# Automatically grant permission to create BYOD examinations for new users with teacher role
exam.byod.permission.allowed = true
# Maximum number of concurrent BYOD examination participants
exam.byod.maxConcurrentParticipants = 100000
# SEB configuration
Expand Down
5 changes: 5 additions & 0 deletions conf/evolutions/default/133.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# --- !Ups
INSERT INTO permission (id, object_version, type, description) VALUES (2, 1, 2, 'can create BYOD exams')

# --- !Downs
DELETE FROM permission where id = 2;
2 changes: 2 additions & 0 deletions conf/routes
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ POST /app/exam/:eid/examinationevents controll
PUT /app/exam/:eid/examinationevents/:eecid controllers.ExaminationEventController.updateExaminationEvent(eid: Long, eecid: Long, request: Request)
DELETE /app/exam/:eid/examinationevents/:eecid controllers.ExaminationEventController.removeExaminationEvent(eid: Long, eecid: Long)
GET /app/examinationevents controllers.ExaminationEventController.listExaminationEvents(start: java.util.Optional[String], end: java.util.Optional[String])
GET /app/examinationevents/conflicting controllers.ExaminationEventController.listOverlappingExaminationEvents(start: String, duration: Int)

############### Question review interface ###############
GET /app/exam/:id/questions controllers.QuestionReviewController.getEssays(id: Long, ids: java.util.Optional[LongList], request: Request)
Expand Down Expand Up @@ -438,6 +439,7 @@ GET /app/settings/appVersion controlle
GET /app/settings/maturityInstructions controllers.SettingsController.getMaturityInstructions(lang: String, ref: java.util.Optional[String])
GET /app/settings/examinationQuitLink controllers.SettingsController.getExaminationQuitLink
GET /app/settings/coursecodeprefix controllers.SettingsController.getCourseCodePrefix
GET /app/settings/byodmaxparticipants controllers.SettingsController.getByodMaxParticipants


################# Statistics interface ##################
Expand Down
Loading

0 comments on commit 94ff800

Please sign in to comment.