Skip to content

Commit

Permalink
CSCEXAM-1237 Permission for creating BYOD exams
Browse files Browse the repository at this point in the history
  • Loading branch information
Matti Lupari committed Jan 19, 2024
1 parent bd4ecbf commit ca87144
Show file tree
Hide file tree
Showing 15 changed files with 64 additions and 18 deletions.
7 changes: 7 additions & 0 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 @@ -494,6 +495,12 @@ public Result createExamDraft(Http.Request request) {
return badRequest("Unsupported execution type");
}
User user = request.attrs().get(Attrs.AUTHENTICATED_USER);
if (
implementation != Exam.Implementation.AQUARIUM.toString() &&
!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
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
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
# Require special permission for being able to create BYOD examinations
exam.byod.permission = false
# Maximum number of concurrent BYOD examination participants
exam.byod.maxConcurrentParticipants = 100000
# SEB configuration
Expand Down
1 change: 1 addition & 0 deletions conf/evolutions/default/132.sql
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# --- !Ups
<<<<<<< HEAD
ALTER TABLE examination_event_configuration ADD encrypted_quit_password BYTEA NULL;
ALTER TABLE examination_event_configuration ADD quit_password_salt VARCHAR(36) NULL;
# --- !Downs
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;
13 changes: 8 additions & 5 deletions ui/src/app/administrative/users/users.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
}
</div>
</span>
<!-- ng-show="permissions.length > 0" -->

<span class="dropdown" ngbDropdown>
<button
ngbDropdownToggle
Expand Down Expand Up @@ -126,13 +126,13 @@
<td>{{ user.lastLogin | date: 'dd.MM.yyyy HH:mm:ss' }}</td>
<td>
@if (hasRole(user, 'ADMIN')) {
<i class="bi-gear-fill"></i>
<i class="bi-gear-fill pe-1"></i>
}
@if (hasRole(user, 'TEACHER')) {
<i class="bi-person-fill"></i>
<i class="bi-person pe-1"></i>
}
@if (hasRole(user, 'STUDENT')) {
<i class="bi-person"></i>
<i class="bi-mortarboard pe-1"></i>
}
<div class="float-end">
<span class="no-caret pointer" ngbDropdown>
Expand Down Expand Up @@ -213,7 +213,10 @@
</td>
<td>
@if (hasPermission(user, 'CAN_INSPECT_LANGUAGE')) {
<i class="bi-pencil"></i>
<i class="bi-alphabet pe-1"></i>
}
@if (hasPermission(user, 'CAN_CREATE_BYOD_EXAM')) {
<i class="bi-house-gear pe-1"></i>
}
<div class="float-end">
<span class="no-caret" ngbDropdown>
Expand Down
13 changes: 10 additions & 3 deletions ui/src/app/administrative/users/users.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ export class UsersComponent implements OnInit, OnDestroy {
ngUnsubscribe = new Subject();
roles: RoleOption[] = [
{ type: 'ADMIN', name: 'i18n_admin', icon: 'bi-gear' },
{ type: 'TEACHER', name: 'i18n_teacher', icon: 'bi-person-fill' },
{ type: 'STUDENT', name: 'i18n_student', icon: 'bi-person' },
{ type: 'TEACHER', name: 'i18n_teacher', icon: 'bi-person' },
{ type: 'STUDENT', name: 'i18n_student', icon: 'bi-mortarboard' },
];
permissions: PermissionOption[] = [];
loader = { loading: false };
Expand Down Expand Up @@ -99,7 +99,14 @@ export class UsersComponent implements OnInit, OnDestroy {
return {
...p,
name: 'i18n_can_inspect_language',
icon: 'bi-pencil',
icon: 'bi-alphabet',
};
}
if (p.type === PermissionType.CAN_CREATE_BYOD_EXAM) {
return {
...p,
name: 'i18n_can_create_byod_exam',
icon: 'bi-house-gear',
};
}

Expand Down
1 change: 1 addition & 0 deletions ui/src/app/administrative/users/users.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { Role, User } from '../../session/session.service';

export enum PermissionType {
CAN_INSPECT_LANGUAGE = 'CAN_INSPECT_LANGUAGE',
CAN_CREATE_BYOD_EXAM = 'CAN_CREATE_BYOD_EXAM',
}

export interface Permission {
Expand Down
16 changes: 12 additions & 4 deletions ui/src/app/exam/editor/creation/new-exam.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@
popoverTitle="{{ 'i18n_instructions' | translate }}"
ngbPopover="{{ 'i18n_new_exam_type_description' | translate }}"
>
<img src="/assets/images/icon_tooltip.svg" alt="" />
<img
src="/assets/images/icon_tooltip.svg"
alt=""
onerror="this.onerror=null;this.src='/assets/images/icon_tooltip.png'"
/>
</sup>
</label>
<select
Expand Down Expand Up @@ -57,7 +61,11 @@
popoverTitle="{{ 'i18n_instructions' | translate }}"
ngbPopover="{{ 'i18n_examination_type_description' | translate }}"
>
<img src="/assets/images/icon_tooltip.svg" alt="" />
<img
src="/assets/images/icon_tooltip.svg"
alt=""
onerror="this.onerror=null;this.src='/assets/images/icon_tooltip.png'"
/>
</sup>
</label>
<select
Expand All @@ -69,12 +77,12 @@
>
<option value="AQUARIUM">{{ 'i18n_examination_type_aquarium' | translate }}</option>
@if (sebExaminationSupported) {
<option value="CLIENT_AUTH">
<option [disabled]="!canCreateByodExams" value="CLIENT_AUTH">
{{ 'i18n_examination_type_seb' | translate }}
</option>
}
@if (homeExaminationSupported) {
<option value="WHATEVER">
<option [disabled]="!canCreateByodExams" value="WHATEVER">
{{ 'i18n_examination_type_home_exam' | translate }}
</option>
}
Expand Down
4 changes: 4 additions & 0 deletions ui/src/app/exam/editor/creation/new-exam.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { NgbPopover } from '@ng-bootstrap/ng-bootstrap';
import { TranslateModule } from '@ngx-translate/core';
import { SessionService } from 'src/app/session/session.service';
import { HistoryBackComponent } from '../../../shared/history/history-back.component';
import type { ExamExecutionType, Implementation } from '../../exam.model';
import { ExamService } from '../../exam.service';
Expand All @@ -35,13 +36,16 @@ export class NewExamComponent implements OnInit {
examinationType: Implementation = 'AQUARIUM';
homeExaminationSupported = false;
sebExaminationSupported = false;
canCreateByodExams = false;

constructor(
private http: HttpClient,
private Exam: ExamService,
private Session: SessionService,
) {}

ngOnInit() {
this.canCreateByodExams = this.Session.getUser().canCreateByodExam;
this.Exam.listExecutionTypes$().subscribe((types) => {
this.executionTypes = types;
this.http
Expand Down
2 changes: 1 addition & 1 deletion ui/src/app/session/role/role-picker-dialog.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import type { User } from '../session.service';
title="{{ role.displayName || '' | translate }}"
(click)="activeModal.close(role)"
>
{{ role.displayName || '' | translate }} <i [ngClass]="role.icon || ''"></i>
{{ role.displayName || '' | translate }} <i class="ps-1" [ngClass]="role.icon"></i>
</button>
}
</div>
Expand Down
7 changes: 5 additions & 2 deletions ui/src/app/session/session.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export interface User {
isLanguageInspector: boolean;
employeeNumber: string | null;
lastLogin: string | null;
canCreateByodExam: boolean;
}

interface Env {
Expand Down Expand Up @@ -245,6 +246,7 @@ export class SessionService implements OnDestroy {
user.isTeacher = role.name === 'TEACHER';
user.isStudent = role.name === 'STUDENT';
user.isLanguageInspector = user.isTeacher && this.hasPermission(user, 'CAN_INSPECT_LANGUAGE');
user.canCreateByodExam = !user.isStudent && this.hasPermission(user, 'CAN_CREATE_BYOD_EXAM');
return user;
}),
);
Expand All @@ -259,11 +261,11 @@ export class SessionService implements OnDestroy {
break;
case 'TEACHER':
role.displayName = 'i18n_teacher';
role.icon = 'bi-person-fill';
role.icon = 'bi-person';
break;
case 'STUDENT':
role.displayName = 'i18n_student';
role.icon = 'bi-person';
role.icon = 'bi-mortarboard';
break;
}
});
Expand All @@ -278,6 +280,7 @@ export class SessionService implements OnDestroy {
isAdmin: loginRole != null && loginRole === 'ADMIN',
isStudent: loginRole != null && loginRole === 'STUDENT',
isLanguageInspector: isTeacher && this.hasPermission(user, 'CAN_INSPECT_LANGUAGE'),
canCreateByodExam: loginRole !== 'STUDENT' && this.hasPermission(user, 'CAN_CREATE_BYOD_EXAM'),
})),
);
}
Expand Down
3 changes: 2 additions & 1 deletion ui/src/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1159,5 +1159,6 @@
"i18n_button_preview": "Preview question",
"i18n_no_preview_available": "No preview available",
"i18n_used_in_exams": "Used in exams",
"i18n_quit_password": "SEB-poistumissalasana EN"
"i18n_quit_password": "SEB-poistumissalasana EN",
"i18n_can_create_byod_exam": "Voi luoda omakonetentin EN"
}
3 changes: 2 additions & 1 deletion ui/src/assets/i18n/fi.json
Original file line number Diff line number Diff line change
Expand Up @@ -1159,5 +1159,6 @@
"i18n_button_preview": "Esikatsele kysymys",
"i18n_no_preview_available": "Kysymyksen esikatselu ei ole saatavilla",
"i18n_used_in_exams": "Käytössä tenteissä",
"i18n_quit_password": "SEB-poistumissalasana"
"i18n_quit_password": "SEB-poistumissalasana",
"i18n_can_create_byod_exam": "Voi luoda omakonetentin"
}
3 changes: 2 additions & 1 deletion ui/src/assets/i18n/sv.json
Original file line number Diff line number Diff line change
Expand Up @@ -1159,5 +1159,6 @@
"i18n_button_preview": "Preview question SV",
"i18n_no_preview_available": "No preview available SV",
"i18n_used_in_exams": "Käytössä tenteissä SV",
"i18n_quit_password": "SEB-poistumissalasana SV"
"i18n_quit_password": "SEB-poistumissalasana SV",
"i18n_can_create_byod_exam": "Voi luoda omakonetentin SV"
}

0 comments on commit ca87144

Please sign in to comment.