SEBSERV-376 finished up

This commit is contained in:
anhefti 2023-02-07 15:39:26 +01:00
parent 504c2a0843
commit e85f2acfd2
20 changed files with 245 additions and 143 deletions

View file

@ -27,8 +27,8 @@ public class AllowedSEBVersion {
public final int major; public final int major;
public final int minor; public final int minor;
public final int patch; public final int patch;
public final boolean alianceEdition; public boolean alianceEdition;
public final boolean minimal; public boolean minimal;
public final boolean isValidFormat; public final boolean isValidFormat;
public AllowedSEBVersion(final String wholeVersionString) { public AllowedSEBVersion(final String wholeVersionString) {
@ -61,29 +61,43 @@ public class AllowedSEBVersion {
valid = false; valid = false;
} }
this.minor = num; this.minor = num;
try { if (split.length >= 3) {
num = Integer.valueOf(split[3]); try {
} catch (final Exception e) { num = Integer.valueOf(split[3]);
valid = false; } catch (final Exception e) {
num = 0;
if (split[3].equals(ALIANCE_EDITION_IDENTIFIER)) {
this.alianceEdition = true;
} else if (split[3].equals(MINIMAL_IDENTIFIER)) {
this.minimal = true;
} else {
valid = false;
}
}
} else {
num = 0;
} }
this.patch = num; this.patch = num;
if (split.length > 4 && ALIANCE_EDITION_IDENTIFIER.equalsIgnoreCase(split[4])) { if (valid && split.length > 4) {
this.alianceEdition = true; if (!this.alianceEdition && split[4].equals(ALIANCE_EDITION_IDENTIFIER)) {
if (split.length > 5 && MINIMAL_IDENTIFIER.equalsIgnoreCase(split[5])) { this.alianceEdition = true;
} else if (!this.minimal && split[4].equals(MINIMAL_IDENTIFIER)) {
this.minimal = true; this.minimal = true;
} else { } else {
this.minimal = false; valid = false;
}
} else {
this.alianceEdition = false;
if (split.length > 4 && MINIMAL_IDENTIFIER.equalsIgnoreCase(split[4])) {
this.minimal = true;
} else {
this.minimal = false;
} }
} }
if (valid && split.length > 5) {
if (!this.alianceEdition && split[5].equals(ALIANCE_EDITION_IDENTIFIER)) {
this.alianceEdition = true;
} else if (!this.minimal && split[5].equals(MINIMAL_IDENTIFIER)) {
this.minimal = true;
} else {
valid = false;
}
}
this.isValidFormat = valid; this.isValidFormat = valid;
} }
@ -91,9 +105,12 @@ public class AllowedSEBVersion {
if (Objects.equals(this.osTypeString, clientVersion.osTypeString)) { if (Objects.equals(this.osTypeString, clientVersion.osTypeString)) {
if (this.minimal) { if (this.minimal) {
// check greater or equals minimum version // check greater or equals minimum version
return this.major <= clientVersion.major || return this.major < clientVersion.major
this.minor <= clientVersion.minor || || (this.major == clientVersion.major
this.patch <= clientVersion.patch; && this.minor < clientVersion.minor)
|| (this.major == clientVersion.major
&& this.minor == clientVersion.minor
&& this.patch <= clientVersion.patch);
} else { } else {
// check exact match // check exact match
return this.major == clientVersion.major && return this.major == clientVersion.major &&

View file

@ -222,7 +222,7 @@ public final class ClientConnection implements GrantEntity {
.append(Constants.LIST_SEPARATOR) .append(Constants.LIST_SEPARATOR)
.append(getOSInfo(seb_os_name)) .append(getOSInfo(seb_os_name))
.append(Constants.LIST_SEPARATOR) .append(Constants.LIST_SEPARATOR)
.append((clientAddress != null) ? "IP: " + clientAddress : Constants.EMPTY_NOTE) .append((clientAddress != null) ? " IP:" + clientAddress : Constants.EMPTY_NOTE)
.toString(); .toString();
this.securityCheckGranted = securityCheckGranted; this.securityCheckGranted = securityCheckGranted;
@ -442,13 +442,13 @@ public final class ClientConnection implements GrantEntity {
} }
private String getSEBInfo(final String seb_version) { private String getSEBInfo(final String seb_version) {
return (seb_version != null) ? "SEBV: " + seb_version : Constants.EMPTY_NOTE; return (seb_version != null) ? "SEB:" + seb_version : Constants.EMPTY_NOTE;
} }
private String getOSInfo(final String seb_os_name) { private String getOSInfo(final String seb_os_name) {
if (seb_os_name != null) { if (seb_os_name != null) {
final String[] split = StringUtils.split(seb_os_name, Constants.LIST_SEPARATOR); final String[] split = StringUtils.split(seb_os_name, Constants.LIST_SEPARATOR);
return "OSV: " + split[0]; return " OS:" + split[0];
} }
return Constants.EMPTY_NOTE; return Constants.EMPTY_NOTE;
} }

View file

@ -24,12 +24,13 @@ public class ClientMonitoringData implements ClientMonitoringDataView {
public final Long id; public final Long id;
public final ConnectionStatus status; public final ConnectionStatus status;
public final Map<Long, String> indicatorVals; public final Map<Long, String> indicatorVals;
public final Integer notificationFlag;
public final int notificationFlag;
public final boolean missingPing; public final boolean missingPing;
public final boolean grantChecked; public final boolean grantChecked;
public final boolean grantDenied; public final boolean grantDenied;
public final boolean pendingNotification; public final boolean pendingNotification;
public final boolean sebVersionDenied;
@JsonCreator @JsonCreator
public ClientMonitoringData( public ClientMonitoringData(
@ -41,11 +42,12 @@ public class ClientMonitoringData implements ClientMonitoringDataView {
this.id = id; this.id = id;
this.status = status; this.status = status;
this.indicatorVals = indicatorVals; this.indicatorVals = indicatorVals;
this.notificationFlag = notificationFlag; this.notificationFlag = notificationFlag != null ? notificationFlag : -1;
this.missingPing = notificationFlag != null && (notificationFlag & FLAG_MISSING_PING) > 0; this.missingPing = notificationFlag != null && (notificationFlag & FLAG_MISSING_PING) > 0;
this.grantChecked = notificationFlag == null || (notificationFlag & FLAG_GRANT_NOT_CHECKED) == 0; this.grantChecked = notificationFlag == null || (notificationFlag & FLAG_GRANT_NOT_CHECKED) == 0;
this.grantDenied = notificationFlag != null && (notificationFlag & FLAG_GRANT_DENIED) > 0; this.grantDenied = notificationFlag != null && (notificationFlag & FLAG_GRANT_DENIED) > 0;
this.pendingNotification = notificationFlag != null && (notificationFlag & FLAG_PENDING_NOTIFICATION) > 0; this.pendingNotification = notificationFlag != null && (notificationFlag & FLAG_PENDING_NOTIFICATION) > 0;
this.sebVersionDenied = notificationFlag != null && (notificationFlag & FLAG_INVALID_SEB_VERSION) > 0;
} }
@Override @Override

View file

@ -100,6 +100,7 @@ public class ResourceService {
private static final String MISSING_CLIENT_PING_NAME_KEY = "MISSING_PING"; private static final String MISSING_CLIENT_PING_NAME_KEY = "MISSING_PING";
private static final String DENIED_CLIENT_SEC_GRANT_NAME_KEY = "GRANT_DENIED"; private static final String DENIED_CLIENT_SEC_GRANT_NAME_KEY = "GRANT_DENIED";
private static final String MISSING_CLIENT_SEC_GRANT_NAME_KEY = "MISSING_GRANT"; private static final String MISSING_CLIENT_SEC_GRANT_NAME_KEY = "MISSING_GRANT";
private static final String DENIED_CLIENT_SEB_VERSION_NAME_KEY = "SEB_VERSION_DENIED";
public static final Comparator<Tuple<String>> RESOURCE_COMPARATOR = Comparator.comparing(t -> t._2); public static final Comparator<Tuple<String>> RESOURCE_COMPARATOR = Comparator.comparing(t -> t._2);
public static final Comparator<Tuple3<String>> RESOURCE_COMPARATOR_TUPLE_3 = Comparator.comparing(t -> t._2); public static final Comparator<Tuple3<String>> RESOURCE_COMPARATOR_TUPLE_3 = Comparator.comparing(t -> t._2);
@ -638,6 +639,7 @@ public class ResourceService {
final String grantMissingText = this.i18nSupport.getText( final String grantMissingText = this.i18nSupport.getText(
SEB_CONNECTION_STATUS_KEY_PREFIX + MISSING_CLIENT_SEC_GRANT_NAME_KEY, SEB_CONNECTION_STATUS_KEY_PREFIX + MISSING_CLIENT_SEC_GRANT_NAME_KEY,
MISSING_CLIENT_SEC_GRANT_NAME_KEY); MISSING_CLIENT_SEC_GRANT_NAME_KEY);
final EnumMap<ConnectionStatus, String> localizedNames = new EnumMap<>(ConnectionStatus.class); final EnumMap<ConnectionStatus, String> localizedNames = new EnumMap<>(ConnectionStatus.class);
Arrays.asList(ConnectionStatus.values()).stream().forEach(state -> localizedNames.put(state, this.i18nSupport Arrays.asList(ConnectionStatus.values()).stream().forEach(state -> localizedNames.put(state, this.i18nSupport
.getText(SEB_CONNECTION_STATUS_KEY_PREFIX + state.name(), state.name()))); .getText(SEB_CONNECTION_STATUS_KEY_PREFIX + state.name(), state.name())));

View file

@ -80,7 +80,8 @@ public class TextFieldListBuilder extends AbstractTableFieldBuilder {
innerGrid, innerGrid,
new LocTextKey(attributeNameKey), new LocTextKey(attributeNameKey),
3, 3,
this.widgetFactory); this.widgetFactory,
!viewContext.isReadonly());
WidgetFactory.setTestId(textListInput, attributeNameKey); WidgetFactory.setTestId(textListInput, attributeNameKey);
textListInput.setLayoutData(gridData); textListInput.setLayoutData(gridData);

View file

@ -179,6 +179,17 @@ public class ClientConnectionDetails implements MonitoringEntry {
return this.grantDenied; return this.grantDenied;
} }
@Override
public int incidentFlag() {
return -1;
}
@Override
public boolean sebVersionDenied() {
return this.connectionData.clientConnection.clientVersionGranted != null &&
!this.connectionData.clientConnection.clientVersionGranted;
}
@Override @Override
public boolean showNoGrantCheckApplied() { public boolean showNoGrantCheckApplied() {
return this.checkSecurityGrant; return this.checkSecurityGrant;

View file

@ -94,6 +94,7 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
private final PageService pageService; private final PageService pageService;
private final Exam exam; private final Exam exam;
private final boolean checkSecurityGrant; private final boolean checkSecurityGrant;
private final boolean checkSEBVersion;
private final boolean distributedSetup; private final boolean distributedSetup;
private final Map<Long, IndicatorData> indicatorMapping; private final Map<Long, IndicatorData> indicatorMapping;
@ -131,6 +132,7 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
this.exam = exam; this.exam = exam;
this.checkSecurityGrant = BooleanUtils.toBoolean( this.checkSecurityGrant = BooleanUtils.toBoolean(
exam.additionalAttributes.get(Exam.ADDITIONAL_ATTR_SIGNATURE_KEY_CHECK_ENABLED)); exam.additionalAttributes.get(Exam.ADDITIONAL_ATTR_SIGNATURE_KEY_CHECK_ENABLED));
this.checkSEBVersion = exam.additionalAttributes.containsKey(Exam.ADDITIONAL_ATTR_ALLOWED_SEB_VERSIONS);
this.distributedSetup = distributedSetup; this.distributedSetup = distributedSetup;
final WidgetFactory widgetFactory = pageService.getWidgetFactory(); final WidgetFactory widgetFactory = pageService.getWidgetFactory();
@ -488,6 +490,16 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
return this.monitoringData.grantDenied; return this.monitoringData.grantDenied;
} }
@Override
public boolean sebVersionDenied() {
return this.monitoringData.sebVersionDenied;
}
@Override
public int incidentFlag() {
return this.monitoringData.notificationFlag;
}
@Override @Override
public boolean showNoGrantCheckApplied() { public boolean showNoGrantCheckApplied() {
return ClientConnectionTable.this.checkSecurityGrant; return ClientConnectionTable.this.checkSecurityGrant;
@ -506,20 +518,22 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
} }
private void updateData(final TableItem tableItem) { private void updateData(final TableItem tableItem) {
tableItem.setText(0, getConnectionIdentifier()); int row = 0;
tableItem.setText(row++, getConnectionIdentifier());
if (ClientConnectionTable.this.hasClientGroups) { if (ClientConnectionTable.this.hasClientGroups) {
tableItem.setText(1, getGroupInfo()); tableItem.setText(row++, getGroupInfo());
tableItem.setText(2, getConnectionInfo());
tableItem.setText(
3,
ClientConnectionTable.this.localizedClientConnectionStatusNameFunction.apply(this));
} else {
tableItem.setText(1, getConnectionInfo());
tableItem.setText(
2,
ClientConnectionTable.this.localizedClientConnectionStatusNameFunction.apply(this));
} }
tableItem.setText(row++, getConnectionInfo());
if (ClientConnectionTable.this.checkSEBVersion) {
if (sebVersionDenied()) {
tableItem.setBackground(row - 1, ClientConnectionTable.this.colorData.color2);
} else {
tableItem.setBackground(row - 1, ClientConnectionTable.this.colorData.color1);
}
}
tableItem.setText(
row++,
ClientConnectionTable.this.localizedClientConnectionStatusNameFunction.apply(this));
if (this.monitoringData != null) { if (this.monitoringData != null) {
updateConnectionStatusColor(tableItem); updateConnectionStatusColor(tableItem);
updateNotifications(tableItem); updateNotifications(tableItem);
@ -546,9 +560,11 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
if (BooleanUtils.isTrue(this.monitoringData.pendingNotification)) { if (BooleanUtils.isTrue(this.monitoringData.pendingNotification)) {
tableItem.setImage(0, tableItem.setImage(0,
WidgetFactory.ImageIcon.NOTIFICATION.getImage(ClientConnectionTable.this.table.getDisplay())); WidgetFactory.ImageIcon.NOTIFICATION.getImage(ClientConnectionTable.this.table.getDisplay()));
tableItem.setBackground(0, ClientConnectionTable.this.colorData.color2);
} else { } else {
if (tableItem.getImage(0) != null) { if (tableItem.getImage(0) != null) {
tableItem.setImage(0, null); tableItem.setImage(0, null);
tableItem.setBackground(0, ClientConnectionTable.this.colorData.color1);
} }
} }
} }
@ -745,6 +761,7 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
private ClientConnectionTable getOuterType() { private ClientConnectionTable getOuterType() {
return ClientConnectionTable.this; return ClientConnectionTable.this;
} }
} }
private void fetchStaticClientConnectionData() { private void fetchStaticClientConnectionData() {

View file

@ -14,7 +14,7 @@ import org.eclipse.swt.widgets.Display;
import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus; import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData; import ch.ethz.seb.sebserver.gbl.model.session.ClientMonitoringDataView;
import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.gbl.util.Utils;
public class ColorData { public class ColorData {
@ -42,10 +42,21 @@ public class ColorData {
} }
switch (status) { switch (status) {
case ACTIVE: case CONNECTION_REQUESTED:
return (entry.grantChecked() && entry.grantDenied()) case AUTHENTICATED:
? this.color3 : (entry.hasMissingPing()) case ACTIVE: {
? this.color2 : this.color1; final int incidentFlag = entry.incidentFlag();
if (incidentFlag > 0) {
if ((incidentFlag & ClientMonitoringDataView.FLAG_GRANT_DENIED) > 0) {
return this.color3;
}
if ((incidentFlag & (ClientMonitoringDataView.FLAG_GRANT_NOT_CHECKED
| ClientMonitoringDataView.FLAG_MISSING_PING)) > 0) {
return this.color2;
}
}
return this.color1;
}
default: default:
return this.defaultColor; return this.defaultColor;
} }
@ -55,41 +66,25 @@ public class ColorData {
return Utils.darkColorContrast(statusColor.getRGB()) ? this.darkColor : this.lightColor; return Utils.darkColorContrast(statusColor.getRGB()) ? this.darkColor : this.lightColor;
} }
int statusWeight(final ClientConnectionData connectionData) {
if (connectionData == null) {
return 100;
}
switch (connectionData.clientConnection.status) {
case CONNECTION_REQUESTED:
case AUTHENTICATED:
return 1;
case ACTIVE:
return (connectionData.clientConnection.securityCheckGranted)
? -1 : (connectionData.missingPing)
? 0 : 2;
case CLOSED:
return 3;
default:
return 10;
}
}
int statusWeight(final MonitoringEntry entry) { int statusWeight(final MonitoringEntry entry) {
if (entry == null) { if (entry == null) {
return 100; return 100;
} }
final Boolean grantDenied = entry.grantDenied();
switch (entry.getStatus()) { switch (entry.getStatus()) {
case CONNECTION_REQUESTED: case CONNECTION_REQUESTED:
case AUTHENTICATED: case AUTHENTICATED: {
if (entry.incidentFlag() > 0) {
return -1;
}
return 1; return 1;
case ACTIVE: }
return (grantDenied == null) case ACTIVE: {
? -1 : (grantDenied) if (entry.incidentFlag() > 0) {
? -2 : (entry.hasMissingPing()) return -1;
? 0 : 2; }
return 2;
}
case CLOSED: case CLOSED:
return 4; return 4;
default: default:

View file

@ -14,12 +14,16 @@ public interface MonitoringEntry {
ConnectionStatus getStatus(); ConnectionStatus getStatus();
int incidentFlag();
boolean hasMissingPing(); boolean hasMissingPing();
boolean grantChecked(); boolean grantChecked();
boolean grantDenied(); boolean grantDenied();
boolean sebVersionDenied();
boolean showNoGrantCheckApplied(); boolean showNoGrantCheckApplied();
} }

View file

@ -12,7 +12,6 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.swt.SWT; import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.layout.GridLayout;
@ -26,7 +25,6 @@ import org.eclipse.swt.widgets.Text;
import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.ImageIcon; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.ImageIcon;
public class TextListInput extends Composite { public class TextListInput extends Composite {
@ -44,16 +42,20 @@ public class TextListInput extends Composite {
private Listener valueChangeEventListener = null; private Listener valueChangeEventListener = null;
private boolean isEditable = true;
public TextListInput( public TextListInput(
final Composite parent, final Composite parent,
final LocTextKey nameKey, final LocTextKey nameKey,
final int initialSize, final int initialSize,
final WidgetFactory widgetFactory) { final WidgetFactory widgetFactory,
final boolean editable) {
super(parent, SWT.NONE); super(parent, SWT.NONE);
this.nameKey = nameKey; this.nameKey = nameKey;
this.initialSize = initialSize; this.initialSize = initialSize;
this.widgetFactory = widgetFactory; this.widgetFactory = widgetFactory;
this.isEditable = editable;
// main grid layout // main grid layout
GridLayout gridLayout = new GridLayout(1, false); GridLayout gridLayout = new GridLayout(1, false);
@ -93,9 +95,6 @@ public class TextListInput extends Composite {
this.addAction.setLayoutData(gridData); this.addAction.setLayoutData(gridData);
this.content = header; this.content = header;
for (int i = 0; i < initialSize; i++) {
addRow(null);
}
} }
public void addListener(final Listener valueChangeEventListener) { public void addListener(final Listener valueChangeEventListener) {
@ -123,7 +122,6 @@ public class TextListInput extends Composite {
} }
public void setValue(final String value) { public void setValue(final String value) {
System.out.println("************ value: " + value);
if (StringUtils.isBlank(value)) { if (StringUtils.isBlank(value)) {
// clear rows // clear rows
new ArrayList<>(this.list).stream().forEach(row -> row.deleteRow()); new ArrayList<>(this.list).stream().forEach(row -> row.deleteRow());
@ -148,8 +146,9 @@ public class TextListInput extends Composite {
} }
public void setEditable(final boolean b) { public void setEditable(final boolean b) {
this.isEditable = b;
this.addAction.setEnabled(b); this.addAction.setEnabled(b);
this.list.stream().forEach(row -> row.setEditable(b)); this.setValue(getValue());
} }
private final class Row { private final class Row {
@ -162,17 +161,23 @@ public class TextListInput extends Composite {
TextListInput.this.content, TextListInput.this.content,
TextListInput.this.nameKey); TextListInput.this.nameKey);
GridData gridData = new GridData(SWT.FILL, SWT.CENTER, true, true); GridData gridData = new GridData(SWT.FILL, SWT.CENTER, true, true);
this.textInput.setLayoutData(gridData); this.textInput.setEditable(TextListInput.this.isEditable);
this.addListener(); if (TextListInput.this.isEditable) {
this.textInput.setLayoutData(gridData);
this.addListener();
this.deleteButton = TextListInput.this.widgetFactory.imageButton(
ImageIcon.REMOVE_BOX,
TextListInput.this.content,
new LocTextKey(TextListInput.this.nameKey.name + ".removeAction"),
deleteEvent -> deleteRow());
gridData = new GridData(SWT.RIGHT, SWT.CENTER, false, true);
gridData.widthHint = ACTION_COLUMN_WIDTH;
this.deleteButton.setLayoutData(gridData);
this.deleteButton.setEnabled(TextListInput.this.isEditable);
} else {
this.deleteButton = null;
}
this.deleteButton = TextListInput.this.widgetFactory.imageButton(
ImageIcon.REMOVE_BOX,
TextListInput.this.content,
new LocTextKey(TextListInput.this.nameKey.name + ".removeAction"),
deleteEvent -> deleteRow());
gridData = new GridData(SWT.RIGHT, SWT.CENTER, false, true);
gridData.widthHint = ACTION_COLUMN_WIDTH;
this.deleteButton.setLayoutData(gridData);
} }
private void addListener() { private void addListener() {
@ -185,20 +190,24 @@ public class TextListInput extends Composite {
public void deleteRow() { public void deleteRow() {
TextListInput.this.list.remove(this); TextListInput.this.list.remove(this);
this.textInput.dispose(); this.textInput.dispose();
this.deleteButton.dispose(); if (this.deleteButton != null) {
this.deleteButton.dispose();
}
TextListInput.this.content.getParent().getParent().layout(true, true); TextListInput.this.content.getParent().getParent().layout(true, true);
if (TextListInput.this.valueChangeEventListener != null) { if (TextListInput.this.valueChangeEventListener != null) {
TextListInput.this.valueChangeEventListener.handleEvent(null); TextListInput.this.valueChangeEventListener.handleEvent(null);
} }
} }
public void setEditable(final boolean e) { // public void setEditable(final boolean e) {
this.textInput.setEditable(e); // this.textInput.setEditable(e);
this.textInput.setData( // this.textInput.setData(
RWT.CUSTOM_VARIANT, // RWT.CUSTOM_VARIANT,
e ? null : CustomVariant.CONFIG_INPUT_READONLY.key); // e ? null : CustomVariant.CONFIG_INPUT_READONLY.key);
this.deleteButton.setEnabled(e); // if (this.deleteButton != null) {
} // this.deleteButton.setEnabled(e);
// }
// }
} }
} }

View file

@ -104,6 +104,11 @@ public interface ExamAdminService {
return isProctoringEnabled(exam.id); return isProctoringEnabled(exam.id);
} }
/** Updates needed additional attributes from assigned exam configuration for the exam
*
* @param examId The exam identifier */
void updateAdditionalExamConfigAttributes(final Long examId);
/** This indicates if proctoring is set and enabled for a certain exam. /** This indicates if proctoring is set and enabled for a certain exam.
* *
* @param examId the exam identifier * @param examId the exam identifier

View file

@ -12,6 +12,7 @@ public interface ExamConfigurationValueService {
public static final String CONFIG_ATTR_NAME_QUIT_LINK = "quitURL"; public static final String CONFIG_ATTR_NAME_QUIT_LINK = "quitURL";
public static final String CONFIG_ATTR_NAME_QUIT_SECRET = "hashedQuitPassword"; public static final String CONFIG_ATTR_NAME_QUIT_SECRET = "hashedQuitPassword";
public static final String CONFIG_ATTR_NAME_ALLOWED_SEB_VERSION = "sebAllowedVersions";
/** Get the actual SEB settings attribute value for the exam configuration mapped as default configuration /** Get the actual SEB settings attribute value for the exam configuration mapped as default configuration
* to the given exam * to the given exam
@ -19,7 +20,21 @@ public interface ExamConfigurationValueService {
* @param examId The exam identifier * @param examId The exam identifier
* @param configAttributeName The name of the SEB settings attribute * @param configAttributeName The name of the SEB settings attribute
* @return The current value of the above SEB settings attribute and given exam. */ * @return The current value of the above SEB settings attribute and given exam. */
String getMappedDefaultConfigAttributeValue(Long examId, String configAttributeName); default String getMappedDefaultConfigAttributeValue(final Long examId, final String configAttributeName) {
return getMappedDefaultConfigAttributeValue(examId, configAttributeName, null);
}
/** Get the actual SEB settings attribute value for the exam configuration mapped as default configuration
* to the given exam
*
* @param examId The exam identifier
* @param configAttributeName The name of the SEB settings attribute
* @param The default value that is given back if there is no value from configuration
* @return The current value of the above SEB settings attribute and given exam. */
String getMappedDefaultConfigAttributeValue(
Long examId,
String configAttributeName,
String defaultValue);
/** Get the quitPassword SEB Setting from the Exam Configuration that is applied to the given exam. /** Get the quitPassword SEB Setting from the Exam Configuration that is applied to the given exam.
* *

View file

@ -46,6 +46,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationNodeDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamConfigurationMapDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamConfigurationMapDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamAdminService; import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamAdminService;
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamConfigurationValueService;
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ProctoringAdminService; import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ProctoringAdminService;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate;
@ -67,6 +68,7 @@ public class ExamAdminServiceImpl implements ExamAdminService {
private final LmsAPIService lmsAPIService; private final LmsAPIService lmsAPIService;
private final boolean appSignatureKeyEnabled; private final boolean appSignatureKeyEnabled;
private final int defaultNumericalTrustThreshold; private final int defaultNumericalTrustThreshold;
private final ExamConfigurationValueService examConfigurationValueService;
protected ExamAdminServiceImpl( protected ExamAdminServiceImpl(
final ExamDAO examDAO, final ExamDAO examDAO,
@ -75,6 +77,7 @@ public class ExamAdminServiceImpl implements ExamAdminService {
final ConfigurationNodeDAO configurationNodeDAO, final ConfigurationNodeDAO configurationNodeDAO,
final ExamConfigurationMapDAO examConfigurationMapDAO, final ExamConfigurationMapDAO examConfigurationMapDAO,
final LmsAPIService lmsAPIService, final LmsAPIService lmsAPIService,
final ExamConfigurationValueService examConfigurationValueService,
final @Value("${sebserver.webservice.api.admin.exam.app.signature.key.enabled:false}") boolean appSignatureKeyEnabled, final @Value("${sebserver.webservice.api.admin.exam.app.signature.key.enabled:false}") boolean appSignatureKeyEnabled,
final @Value("${sebserver.webservice.api.admin.exam.app.signature.key.numerical.threshold:2}") int defaultNumericalTrustThreshold) { final @Value("${sebserver.webservice.api.admin.exam.app.signature.key.numerical.threshold:2}") int defaultNumericalTrustThreshold) {
@ -84,6 +87,7 @@ public class ExamAdminServiceImpl implements ExamAdminService {
this.configurationNodeDAO = configurationNodeDAO; this.configurationNodeDAO = configurationNodeDAO;
this.examConfigurationMapDAO = examConfigurationMapDAO; this.examConfigurationMapDAO = examConfigurationMapDAO;
this.lmsAPIService = lmsAPIService; this.lmsAPIService = lmsAPIService;
this.examConfigurationValueService = examConfigurationValueService;
this.appSignatureKeyEnabled = appSignatureKeyEnabled; this.appSignatureKeyEnabled = appSignatureKeyEnabled;
this.defaultNumericalTrustThreshold = defaultNumericalTrustThreshold; this.defaultNumericalTrustThreshold = defaultNumericalTrustThreshold;
} }
@ -198,6 +202,30 @@ public class ExamAdminServiceImpl implements ExamAdminService {
.onError(error -> log.error("Failed to check SEB restriction: ", error)); .onError(error -> log.error("Failed to check SEB restriction: ", error));
} }
@Override
public void updateAdditionalExamConfigAttributes(final Long examId) {
try {
final String allowedSEBVersion = this.examConfigurationValueService
.getAllowedSEBVersion(examId);
if (StringUtils.isNotBlank(allowedSEBVersion)) {
this.additionalAttributesDAO.saveAdditionalAttribute(
EntityType.EXAM,
examId, Exam.ADDITIONAL_ATTR_ALLOWED_SEB_VERSIONS,
allowedSEBVersion)
.getOrThrow();
} else {
this.additionalAttributesDAO.delete(
EntityType.EXAM,
examId, Exam.ADDITIONAL_ATTR_ALLOWED_SEB_VERSIONS);
}
} catch (final Exception e) {
log.error("Unexpected error while trying to save additional Exam Configuration settings for exam: {}",
examId, e);
}
}
@Override @Override
public Result<ProctoringServiceSettings> getProctoringServiceSettings(final Long examId) { public Result<ProctoringServiceSettings> getProctoringServiceSettings(final Long examId) {
return this.proctoringAdminService.getProctoringSettings(new EntityKey(examId, EntityType.EXAM)); return this.proctoringAdminService.getProctoringSettings(new EntityKey(examId, EntityType.EXAM));

View file

@ -50,7 +50,11 @@ public class ExamConfigurationValueServiceImpl implements ExamConfigurationValue
} }
@Override @Override
public String getMappedDefaultConfigAttributeValue(final Long examId, final String configAttributeName) { public String getMappedDefaultConfigAttributeValue(
final Long examId,
final String configAttributeName,
final String defaultValue) {
try { try {
final Long configId = this.examConfigurationMapDAO final Long configId = this.examConfigurationMapDAO
@ -62,7 +66,7 @@ public class ExamConfigurationValueServiceImpl implements ExamConfigurationValue
.getOr(null); .getOr(null);
if (configId == null) { if (configId == null) {
return null; return defaultValue;
} }
final Long attrId = this.configurationAttributeDAO final Long attrId = this.configurationAttributeDAO
@ -73,11 +77,18 @@ public class ExamConfigurationValueServiceImpl implements ExamConfigurationValue
return this.configurationValueDAO return this.configurationValueDAO
.getConfigAttributeValue(configId, attrId) .getConfigAttributeValue(configId, attrId)
.getOrThrow(); .onError(error -> log.warn(
"Failed to get exam config attribute: {} {} error: {}",
examId,
configAttributeName,
error.getMessage()))
.getOr(defaultValue);
} catch (final Exception e) { } catch (final Exception e) {
log.error("Unexpected error while trying to extract SEB settings attribute value:", e); if (defaultValue == null) {
return null; log.error("Unexpected error while trying to extract SEB settings attribute value:", e);
}
return defaultValue;
} }
} }
@ -130,10 +141,10 @@ public class ExamConfigurationValueServiceImpl implements ExamConfigurationValue
return getMappedDefaultConfigAttributeValue( return getMappedDefaultConfigAttributeValue(
examId, examId,
CONFIG_ATTR_NAME_QUIT_LINK); CONFIG_ATTR_NAME_ALLOWED_SEB_VERSION,
StringUtils.EMPTY);
} catch (final Exception e) { } catch (final Exception e) {
log.error("Failed to get SEB restriction with quit link: ", e);
return null; return null;
} }
} }

View file

@ -153,7 +153,8 @@ public class ClientConnectionDataInternal extends ClientConnectionData {
| (isMissingPing() ? ClientMonitoringDataView.FLAG_MISSING_PING : 0) | (isMissingPing() ? ClientMonitoringDataView.FLAG_MISSING_PING : 0)
| (isPendingNotification() ? ClientMonitoringDataView.FLAG_PENDING_NOTIFICATION : 0) | (isPendingNotification() ? ClientMonitoringDataView.FLAG_PENDING_NOTIFICATION : 0)
| (!isGrantChecked() ? ClientMonitoringDataView.FLAG_GRANT_NOT_CHECKED : 0) | (!isGrantChecked() ? ClientMonitoringDataView.FLAG_GRANT_NOT_CHECKED : 0)
| (isGrantDenied() ? ClientMonitoringDataView.FLAG_GRANT_DENIED : 0); | (isGrantDenied() ? ClientMonitoringDataView.FLAG_GRANT_DENIED : 0)
| (isSEBVersionDenied() ? ClientMonitoringDataView.FLAG_INVALID_SEB_VERSION : 0);
return (flag > 0) ? flag : null; return (flag > 0) ? flag : null;
} }

View file

@ -29,6 +29,7 @@ import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamConfigurationMapDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamConfigurationMapDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamAdminService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamConfigUpdateService; import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamConfigUpdateService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService; import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService;
@ -44,19 +45,22 @@ public class ExamConfigUpdateServiceImpl implements ExamConfigUpdateService {
private final ExamConfigurationMapDAO examConfigurationMapDAO; private final ExamConfigurationMapDAO examConfigurationMapDAO;
private final ExamSessionService examSessionService; private final ExamSessionService examSessionService;
private final ExamUpdateHandler examUpdateHandler; private final ExamUpdateHandler examUpdateHandler;
private final ExamAdminService examAdminService;
protected ExamConfigUpdateServiceImpl( protected ExamConfigUpdateServiceImpl(
final ExamDAO examDAO, final ExamDAO examDAO,
final ConfigurationDAO configurationDAO, final ConfigurationDAO configurationDAO,
final ExamConfigurationMapDAO examConfigurationMapDAO, final ExamConfigurationMapDAO examConfigurationMapDAO,
final ExamSessionService examSessionService, final ExamSessionService examSessionService,
final ExamUpdateHandler examUpdateHandler) { final ExamUpdateHandler examUpdateHandler,
final ExamAdminService examAdminService) {
this.examDAO = examDAO; this.examDAO = examDAO;
this.configurationDAO = configurationDAO; this.configurationDAO = configurationDAO;
this.examConfigurationMapDAO = examConfigurationMapDAO; this.examConfigurationMapDAO = examConfigurationMapDAO;
this.examSessionService = examSessionService; this.examSessionService = examSessionService;
this.examUpdateHandler = examUpdateHandler; this.examUpdateHandler = examUpdateHandler;
this.examAdminService = examAdminService;
} }
// processing: // processing:
@ -133,6 +137,7 @@ public class ExamConfigUpdateServiceImpl implements ExamConfigUpdateService {
.flatMap(e -> this.examDAO.setSEBRestriction(e.id, true)) .flatMap(e -> this.examDAO.setSEBRestriction(e.id, true))
.onError(t -> log.error("Failed to update SEB Client restriction for Exam: {}", exam, t)); .onError(t -> log.error("Failed to update SEB Client restriction for Exam: {}", exam, t));
} }
this.examAdminService.updateAdditionalExamConfigAttributes(exam.id);
} }
// evict each Exam from cache and release the update-lock on DB // evict each Exam from cache and release the update-lock on DB

View file

@ -96,8 +96,8 @@ public class SEBClientVersionServiceImpl implements SEBClientVersionService {
final String[] versionNumberSplit = StringUtils.split(versioNumber, Constants.DOT); final String[] versionNumberSplit = StringUtils.split(versioNumber, Constants.DOT);
final int major = extractVersionNumber(versionNumberSplit[0]); final int major = extractVersionNumber(versionNumberSplit[0]);
final int minor = extractVersionNumber(versionNumberSplit[1]); final int minor = (versionNumberSplit.length > 1) ? extractVersionNumber(versionNumberSplit[1]) : 0;
final int patch = extractVersionNumber(versionNumberSplit[2]); final int patch = (versionNumberSplit.length > 2) ? extractVersionNumber(versionNumberSplit[2]) : 0;
final ClientVersion version = new ClientVersion(osType, major, minor, patch); final ClientVersion version = new ClientVersion(osType, major, minor, patch);
return allowedSEBVersions return allowedSEBVersions
@ -106,7 +106,9 @@ public class SEBClientVersionServiceImpl implements SEBClientVersionService {
.findFirst() .findFirst()
.isPresent(); .isPresent();
} catch (final Exception e) { } catch (final Exception e) {
log.warn("Unexpected error while trying to parse SEB version number in: {} {}", clientOSName, log.warn(
"Invalid SEB version number in: {} {}",
clientOSName,
clientVersion); clientVersion);
return false; return false;
} }

View file

@ -64,13 +64,11 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.Authorization
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.UserService; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.UserService;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.impl.SEBServerUser; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.impl.SEBServerUser;
import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionService; import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionService;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.AdditionalAttributesDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamAdminService; import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamAdminService;
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamConfigurationValueService;
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamTemplateService; import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamTemplateService;
import ch.ethz.seb.sebserver.webservice.servicelayer.institution.SecurityKeyService; import ch.ethz.seb.sebserver.webservice.servicelayer.institution.SecurityKeyService;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService;
@ -95,8 +93,6 @@ public class ExamAdministrationController extends EntityController<Exam, Exam> {
private final SEBRestrictionService sebRestrictionService; private final SEBRestrictionService sebRestrictionService;
private final SecurityKeyService securityKeyService; private final SecurityKeyService securityKeyService;
private final ExamProctoringRoomService examProctoringRoomService; private final ExamProctoringRoomService examProctoringRoomService;
private final ExamConfigurationValueService examConfigurationValueService;
private final AdditionalAttributesDAO additionalAttributesDAO;
public ExamAdministrationController( public ExamAdministrationController(
final AuthorizationService authorization, final AuthorizationService authorization,
@ -112,9 +108,7 @@ public class ExamAdministrationController extends EntityController<Exam, Exam> {
final ExamSessionService examSessionService, final ExamSessionService examSessionService,
final SEBRestrictionService sebRestrictionService, final SEBRestrictionService sebRestrictionService,
final SecurityKeyService securityKeyService, final SecurityKeyService securityKeyService,
final ExamProctoringRoomService examProctoringRoomService, final ExamProctoringRoomService examProctoringRoomService) {
final ExamConfigurationValueService examConfigurationValueService,
final AdditionalAttributesDAO additionalAttributesDAO) {
super(authorization, super(authorization,
bulkActionService, bulkActionService,
@ -132,8 +126,6 @@ public class ExamAdministrationController extends EntityController<Exam, Exam> {
this.sebRestrictionService = sebRestrictionService; this.sebRestrictionService = sebRestrictionService;
this.securityKeyService = securityKeyService; this.securityKeyService = securityKeyService;
this.examProctoringRoomService = examProctoringRoomService; this.examProctoringRoomService = examProctoringRoomService;
this.examConfigurationValueService = examConfigurationValueService;
this.additionalAttributesDAO = additionalAttributesDAO;
} }
@Override @Override
@ -607,7 +599,7 @@ public class ExamAdministrationController extends EntityController<Exam, Exam> {
@Override @Override
protected Result<Exam> notifySaved(final Exam entity) { protected Result<Exam> notifySaved(final Exam entity) {
return Result.tryCatch(() -> { return Result.tryCatch(() -> {
this.saveAdditionalExamConfigAttributes(entity); this.examAdminService.updateAdditionalExamConfigAttributes(entity.id);
this.examSessionService.flushCache(entity); this.examSessionService.flushCache(entity);
return entity; return entity;
}); });
@ -711,29 +703,6 @@ public class ExamAdministrationController extends EntityController<Exam, Exam> {
}); });
} }
private void saveAdditionalExamConfigAttributes(final Exam entity) {
try {
final String allowedSEBVersion = this.examConfigurationValueService
.getAllowedSEBVersion(entity.id);
if (StringUtils.isNotBlank(allowedSEBVersion)) {
this.additionalAttributesDAO.saveAdditionalAttribute(
EntityType.EXAM,
entity.id, Exam.ADDITIONAL_ATTR_ALLOWED_SEB_VERSIONS,
allowedSEBVersion)
.getOrThrow();
} else {
this.additionalAttributesDAO.delete(
EntityType.EXAM,
entity.id, Exam.ADDITIONAL_ATTR_ALLOWED_SEB_VERSIONS);
}
} catch (final Exception e) {
log.error("Unexpected error while trying to save additional Exam Configuration settings for exam: {}",
entity, e);
}
}
static Function<Collection<Exam>, List<Exam>> pageSort(final String sort) { static Function<Collection<Exam>, List<Exam>> pageSort(final String sort) {
final String sortBy = PageSortOrder.decode(sort); final String sortBy = PageSortOrder.decode(sort);

View file

@ -47,6 +47,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationNodeDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.EntityDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.EntityDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamAdminService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamConfigUpdateService; import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamConfigUpdateService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService; import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService;
import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService; import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService;
@ -60,6 +61,7 @@ public class ExamConfigurationMappingController extends EntityController<ExamCon
private final ConfigurationNodeDAO configurationNodeDAO; private final ConfigurationNodeDAO configurationNodeDAO;
private final ExamConfigUpdateService examConfigUpdateService; private final ExamConfigUpdateService examConfigUpdateService;
private final ExamSessionService examSessionService; private final ExamSessionService examSessionService;
private final ExamAdminService examAdminService;
protected ExamConfigurationMappingController( protected ExamConfigurationMappingController(
final AuthorizationService authorization, final AuthorizationService authorization,
@ -71,7 +73,8 @@ public class ExamConfigurationMappingController extends EntityController<ExamCon
final ExamDAO examDao, final ExamDAO examDao,
final ConfigurationNodeDAO configurationNodeDAO, final ConfigurationNodeDAO configurationNodeDAO,
final ExamConfigUpdateService examConfigUpdateService, final ExamConfigUpdateService examConfigUpdateService,
final ExamSessionService examSessionService) { final ExamSessionService examSessionService,
final ExamAdminService examAdminService) {
super( super(
authorization, authorization,
@ -85,6 +88,7 @@ public class ExamConfigurationMappingController extends EntityController<ExamCon
this.configurationNodeDAO = configurationNodeDAO; this.configurationNodeDAO = configurationNodeDAO;
this.examConfigUpdateService = examConfigUpdateService; this.examConfigUpdateService = examConfigUpdateService;
this.examSessionService = examSessionService; this.examSessionService = examSessionService;
this.examAdminService = examAdminService;
} }
@Override @Override
@ -181,6 +185,7 @@ public class ExamConfigurationMappingController extends EntityController<ExamCon
@Override @Override
protected Result<ExamConfigurationMap> notifyCreated(final ExamConfigurationMap entity) { protected Result<ExamConfigurationMap> notifyCreated(final ExamConfigurationMap entity) {
this.examAdminService.updateAdditionalExamConfigAttributes(entity.examId);
// update the attached configurations state to "In Use" // update the attached configurations state to "In Use"
return this.configurationNodeDAO.save(new ConfigurationNode( return this.configurationNodeDAO.save(new ConfigurationNode(
entity.configurationNodeId, entity.configurationNodeId,
@ -200,6 +205,8 @@ public class ExamConfigurationMappingController extends EntityController<ExamCon
@Override @Override
protected Result<Pair<ExamConfigurationMap, EntityProcessingReport>> notifyDeleted( protected Result<Pair<ExamConfigurationMap, EntityProcessingReport>> notifyDeleted(
final Pair<ExamConfigurationMap, EntityProcessingReport> pair) { final Pair<ExamConfigurationMap, EntityProcessingReport> pair) {
this.examAdminService.updateAdditionalExamConfigAttributes(pair.a.examId);
// update the attached configurations state to "Ready" // update the attached configurations state to "Ready"
return this.configurationNodeDAO.save(new ConfigurationNode( return this.configurationNodeDAO.save(new ConfigurationNode(
pair.a.configurationNodeId, pair.a.configurationNodeId,

View file

@ -2218,6 +2218,7 @@ sebserver.monitoring.exam.connection.status.DISABLED=Canceled
sebserver.monitoring.exam.connection.status.MISSING_PING=Missing sebserver.monitoring.exam.connection.status.MISSING_PING=Missing
sebserver.monitoring.exam.connection.status.MISSING_GRANT=&nbsp;&nbsp;(No ASK Grant) sebserver.monitoring.exam.connection.status.MISSING_GRANT=&nbsp;&nbsp;(No ASK Grant)
sebserver.monitoring.exam.connection.status.GRANT_DENIED=ASK Grant Denied sebserver.monitoring.exam.connection.status.GRANT_DENIED=ASK Grant Denied
sebserver.monitoring.exam.connection.status.SEB_VERSION_DENIED=SEB Version Invalid
sebserver.monitoring.lock.title=Lock SEB Clients sebserver.monitoring.lock.title=Lock SEB Clients
sebserver.monitoring.lock.form.info.title=Info sebserver.monitoring.lock.form.info.title=Info