diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/SEBClientConfig.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/SEBClientConfig.java index 82013905..af73c7ed 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/SEBClientConfig.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/SEBClientConfig.java @@ -31,6 +31,12 @@ import ch.ethz.seb.sebserver.gbl.model.GrantEntity; public final class SEBClientConfig implements GrantEntity, Activatable { public static final String ATTR_CONFIG_PURPOSE = "sebConfigPurpose"; + public static final String ATTR_PING_INTERVAL = "sebServerPingTime"; + public static final String ATTR_VDI_TYPE = "vdiSetup"; + public static final String ATTR_VDI_EXECUTABLE = "vdiExecutable"; + public static final String ATTR_VDI_PATH = "vdiPath"; + public static final String ATTR_VDI_ARGUMENTS = "vdiArguments"; + public static final String ATTR_FALLBACK = "sebServerFallback "; public static final String ATTR_FALLBACK_START_URL = "startURL"; public static final String ATTR_FALLBACK_TIMEOUT = "sebServerFallbackTimeout"; @@ -49,6 +55,40 @@ public final class SEBClientConfig implements GrantEntity, Activatable { CONFIGURE_CLIENT } + public enum VDIType { + NO, + VM_WARE( + "VMware View", + "vmware-view.exe", + "VMware\\VMware Horizon View Client", + "--LoginAsCurrentUser true\n--serverurl view.example.com\n--desktopLayout fullscreen\n--desktopProtocol PCOIP\n--desktopName \"let-vdi-1-exam"); + + public final String title; + public final String defaultExecutable; + public final String defaultPath; + public final String defaultArguments; + + private VDIType() { + this.title = "NONE"; + this.defaultExecutable = null; + this.defaultPath = null; + this.defaultArguments = null; + } + + private VDIType( + final String title, + final String defaultExecutable, + final String defaultPath, + final String defaultArguments) { + + this.title = title; + this.defaultExecutable = defaultExecutable; + this.defaultPath = defaultPath; + this.defaultArguments = defaultArguments; + } + + } + @JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_ID) public final Long id; @@ -65,6 +105,21 @@ public final class SEBClientConfig implements GrantEntity, Activatable { @JsonProperty(ATTR_CONFIG_PURPOSE) public final ConfigPurpose configPurpose; + @JsonProperty(ATTR_PING_INTERVAL) + public final Long sebServerPingTime; + + @JsonProperty(ATTR_VDI_TYPE) + public final VDIType vdiType; + + @JsonProperty(ATTR_VDI_EXECUTABLE) + public final String vdiExecutable; + + @JsonProperty(ATTR_VDI_PATH) + public final String vdiPath; + + @JsonProperty(ATTR_VDI_ARGUMENTS) + public final String vdiArguments; + @JsonProperty(ATTR_FALLBACK) public final Boolean fallback; @@ -111,6 +166,13 @@ public final class SEBClientConfig implements GrantEntity, Activatable { @JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_INSTITUTION_ID) final Long institutionId, @JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_NAME) final String name, @JsonProperty(ATTR_CONFIG_PURPOSE) final ConfigPurpose configPurpose, + + @JsonProperty(ATTR_PING_INTERVAL) final Long sebServerPingTime, + @JsonProperty(ATTR_VDI_TYPE) final VDIType vdiType, + @JsonProperty(ATTR_VDI_EXECUTABLE) final String vdiExecutable, + @JsonProperty(ATTR_VDI_PATH) final String vdiPath, + @JsonProperty(ATTR_VDI_ARGUMENTS) final String vdiArguments, + @JsonProperty(ATTR_FALLBACK) final Boolean fallback, @JsonProperty(ATTR_FALLBACK_START_URL) final String fallbackStartURL, @JsonProperty(ATTR_FALLBACK_TIMEOUT) final Long fallbackTimeout, @@ -129,6 +191,13 @@ public final class SEBClientConfig implements GrantEntity, Activatable { this.institutionId = institutionId; this.name = name; this.configPurpose = configPurpose; + + this.sebServerPingTime = sebServerPingTime; + this.vdiType = vdiType; + this.vdiExecutable = vdiExecutable != null ? vdiExecutable : vdiType.defaultExecutable; + this.vdiPath = vdiPath != null ? vdiPath : vdiType.defaultPath; + this.vdiArguments = vdiArguments != null ? vdiArguments : vdiType.defaultArguments; + this.fallback = fallback; this.fallbackStartURL = fallbackStartURL; this.fallbackTimeout = fallbackTimeout; @@ -149,6 +218,23 @@ public final class SEBClientConfig implements GrantEntity, Activatable { this.institutionId = institutionId; this.name = postParams.getString(Domain.SEB_CLIENT_CONFIGURATION.ATTR_NAME); this.configPurpose = postParams.getEnum(ATTR_CONFIG_PURPOSE, ConfigPurpose.class); + + this.sebServerPingTime = postParams.getLong(ATTR_PING_INTERVAL) != null + ? postParams.getLong(ATTR_PING_INTERVAL) + : 1000; + this.vdiType = postParams.getEnum(ATTR_VDI_TYPE, VDIType.class) != null + ? postParams.getEnum(ATTR_VDI_TYPE, VDIType.class) + : VDIType.NO; + this.vdiExecutable = postParams.getString(ATTR_VDI_EXECUTABLE) != null + ? postParams.getString(ATTR_VDI_EXECUTABLE) + : this.vdiType.defaultExecutable; + this.vdiPath = postParams.getString(ATTR_VDI_PATH) != null + ? postParams.getString(ATTR_VDI_PATH) + : this.vdiType.defaultPath; + this.vdiArguments = postParams.getString(ATTR_VDI_ARGUMENTS) != null + ? postParams.getString(ATTR_VDI_ARGUMENTS) + : this.vdiType.defaultArguments; + this.fallback = postParams.getBoolean(ATTR_FALLBACK); this.fallbackStartURL = postParams.getString(ATTR_FALLBACK_START_URL); this.fallbackTimeout = postParams.getLong(ATTR_FALLBACK_TIMEOUT); @@ -269,26 +355,77 @@ public final class SEBClientConfig implements GrantEntity, Activatable { return this.active; } +// @Override +// public String toString() { +// final StringBuilder sb = new StringBuilder("SEBClientConfig{"); +// sb.append("id=").append(this.id); +// sb.append(", institutionId=").append(this.institutionId); +// sb.append(", name='").append(this.name).append('\''); +// sb.append(", configPurpose=").append(this.configPurpose); +// sb.append(", fallback=").append(this.fallback); +// sb.append(", fallbackStartURL='").append(this.fallbackStartURL).append('\''); +// sb.append(", fallbackTimeout=").append(this.fallbackTimeout); +// sb.append(", fallbackAttempts=").append(this.fallbackAttempts); +// sb.append(", fallbackAttemptInterval=").append(this.fallbackAttemptInterval); +// sb.append(", fallbackPassword=").append(this.fallbackPassword); +// sb.append(", fallbackPasswordConfirm=").append(this.fallbackPasswordConfirm); +// sb.append(", date=").append(this.date); +// sb.append(", encryptSecret=").append(this.encryptSecret); +// sb.append(", encryptSecretConfirm=").append(this.encryptSecretConfirm); +// sb.append(", active=").append(this.active); +// sb.append('}'); +// return sb.toString(); +// } + @Override public String toString() { - final StringBuilder sb = new StringBuilder("SEBClientConfig{"); - sb.append("id=").append(this.id); - sb.append(", institutionId=").append(this.institutionId); - sb.append(", name='").append(this.name).append('\''); - sb.append(", configPurpose=").append(this.configPurpose); - sb.append(", fallback=").append(this.fallback); - sb.append(", fallbackStartURL='").append(this.fallbackStartURL).append('\''); - sb.append(", fallbackTimeout=").append(this.fallbackTimeout); - sb.append(", fallbackAttempts=").append(this.fallbackAttempts); - sb.append(", fallbackAttemptInterval=").append(this.fallbackAttemptInterval); - sb.append(", fallbackPassword=").append(this.fallbackPassword); - sb.append(", fallbackPasswordConfirm=").append(this.fallbackPasswordConfirm); - sb.append(", date=").append(this.date); - sb.append(", encryptSecret=").append(this.encryptSecret); - sb.append(", encryptSecretConfirm=").append(this.encryptSecretConfirm); - sb.append(", active=").append(this.active); - sb.append('}'); - return sb.toString(); + final StringBuilder builder = new StringBuilder(); + builder.append("SEBClientConfig [id="); + builder.append(this.id); + builder.append(", institutionId="); + builder.append(this.institutionId); + builder.append(", name="); + builder.append(this.name); + builder.append(", configPurpose="); + builder.append(this.configPurpose); + builder.append(", sebServerPingTime="); + builder.append(this.sebServerPingTime); + builder.append(", vdiType="); + builder.append(this.vdiType); + builder.append(", vdiExecutable="); + builder.append(this.vdiExecutable); + builder.append(", vdiPath="); + builder.append(this.vdiPath); + builder.append(", vdiArguments="); + builder.append(this.vdiArguments); + builder.append(", fallback="); + builder.append(this.fallback); + builder.append(", fallbackStartURL="); + builder.append(this.fallbackStartURL); + builder.append(", fallbackTimeout="); + builder.append(this.fallbackTimeout); + builder.append(", fallbackAttempts="); + builder.append(this.fallbackAttempts); + builder.append(", fallbackAttemptInterval="); + builder.append(this.fallbackAttemptInterval); + builder.append(", fallbackPassword="); + builder.append(this.fallbackPassword); + builder.append(", fallbackPasswordConfirm="); + builder.append(this.fallbackPasswordConfirm); + builder.append(", quitPassword="); + builder.append(this.quitPassword); + builder.append(", quitPasswordConfirm="); + builder.append(this.quitPasswordConfirm); + builder.append(", date="); + builder.append(this.date); + builder.append(", encryptSecret="); + builder.append(this.encryptSecret); + builder.append(", encryptSecretConfirm="); + builder.append(this.encryptSecretConfirm); + builder.append(", active="); + builder.append(this.active); + builder.append("]"); + return builder.toString(); } @Override @@ -298,6 +435,11 @@ public final class SEBClientConfig implements GrantEntity, Activatable { this.institutionId, this.name, this.configPurpose, + this.sebServerPingTime, + this.vdiType, + this.vdiExecutable, + this.vdiPath, + this.vdiArguments, this.fallback, this.fallbackStartURL, this.fallbackTimeout, @@ -319,6 +461,11 @@ public final class SEBClientConfig implements GrantEntity, Activatable { institutionId, null, ConfigPurpose.CONFIGURE_CLIENT, + 1000L, + VDIType.NO, + null, + null, + null, false, null, null, diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/SEBClientConfigForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/SEBClientConfigForm.java index 036236d2..8a1a4efe 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/SEBClientConfigForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/SEBClientConfigForm.java @@ -29,6 +29,7 @@ import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig.VDIType; import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; @@ -50,6 +51,7 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig. import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.SaveClientConfig; import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser.EntityGrantCheck; +import ch.ethz.seb.sebserver.gui.widget.Selection; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; @Lazy @@ -66,9 +68,20 @@ public class SEBClientConfigForm implements TemplateComposer { private static final LocTextKey FORM_DATE_TEXT_KEY = new LocTextKey("sebserver.clientconfig.form.date"); - private static final LocTextKey CLIENT_PURPOSE_TEXT_KEY = new LocTextKey("sebserver.clientconfig.form.sebConfigPurpose"); + + private static final LocTextKey PING_TEXT_KEY = + new LocTextKey("sebserver.clientconfig.form.pinginterval"); + private static final LocTextKey VDI_TYPE_TEXT_KEY = + new LocTextKey("sebserver.clientconfig.form.vditype"); + private static final LocTextKey VDI_EXEC_TEXT_KEY = + new LocTextKey("sebserver.clientconfig.form.vdi.executable"); + private static final LocTextKey VDI_PATH_TEXT_KEY = + new LocTextKey("sebserver.clientconfig.form.vdi.path"); + private static final LocTextKey VDI_ARGS_TEXT_KEY = + new LocTextKey("sebserver.clientconfig.form.vdi.args"); + private static final LocTextKey FALLBACK_TEXT_KEY = new LocTextKey("sebserver.clientconfig.form.fallback"); private static final LocTextKey FALLBACK_URL_TEXT_KEY = @@ -93,6 +106,10 @@ public class SEBClientConfigForm implements TemplateComposer { private static final LocTextKey FORM_CONFIRM_ENCRYPT_SECRET_TEXT_KEY = new LocTextKey("sebserver.clientconfig.form.encryptSecret.confirm"); + private static final Set VDI_ATTRIBUTES = new HashSet<>(Arrays.asList( + SEBClientConfig.ATTR_VDI_EXECUTABLE, + SEBClientConfig.ATTR_VDI_PATH, + SEBClientConfig.ATTR_VDI_ARGUMENTS)); private static final Set FALLBACK_ATTRIBUTES = new HashSet<>(Arrays.asList( SEBClientConfig.ATTR_FALLBACK_START_URL, SEBClientConfig.ATTR_FALLBACK_ATTEMPT_INTERVAL, @@ -103,6 +120,7 @@ public class SEBClientConfigForm implements TemplateComposer { SEBClientConfig.ATTR_QUIT_PASSWORD, SEBClientConfig.ATTR_QUIT_PASSWORD_CONFIRM)); + private static final String DEFAULT_PING_INTERVAL = String.valueOf(1000); private static final String FALLBACK_DEFAULT_TIME = String.valueOf(30 * Constants.SECOND_IN_MILLIS); private static final String FALLBACK_DEFAULT_ATTEMPTS = String.valueOf(5); private static final String FALLBACK_DEFAULT_ATTEMPT_INTERVAL = String.valueOf(2 * Constants.SECOND_IN_MILLIS); @@ -211,6 +229,51 @@ public class SEBClientConfigForm implements TemplateComposer { FORM_CONFIRM_ENCRYPT_SECRET_TEXT_KEY, clientConfig.getEncryptSecret())) + .withDefaultSpanInput(1) + .addField(FormBuilder.text( + SEBClientConfig.ATTR_PING_INTERVAL, + PING_TEXT_KEY, + clientConfig.sebServerPingTime != null + ? String.valueOf(clientConfig.sebServerPingTime) + : DEFAULT_PING_INTERVAL) + .asNumber(this::checkNaturalNumber) + .mandatory(!isReadonly)) + .withDefaultSpanEmptyCell(4) + .withDefaultSpanInput(2) + .addField(FormBuilder.singleSelection( + SEBClientConfig.ATTR_VDI_TYPE, + VDI_TYPE_TEXT_KEY, + clientConfig.vdiType != null + ? clientConfig.vdiType.name() + : SEBClientConfig.VDIType.NO.name(), + () -> this.pageService.getResourceService().vdiTypeResources()) + .mandatory(!isReadonly)) + .withDefaultSpanEmptyCell(3) + .withDefaultSpanInput(3) + .addField(FormBuilder.text( + SEBClientConfig.ATTR_VDI_EXECUTABLE, + VDI_EXEC_TEXT_KEY, + clientConfig.vdiExecutable) + .mandatory(!isReadonly)) + .withDefaultSpanEmptyCell(2) + + .withDefaultSpanInput(4) + .addField(FormBuilder.text( + SEBClientConfig.ATTR_VDI_PATH, + VDI_PATH_TEXT_KEY, + clientConfig.vdiPath) + .mandatory(!isReadonly)) + .withDefaultSpanEmptyCell(1) + + .withDefaultSpanInput(4) + .addField(FormBuilder.text( + SEBClientConfig.ATTR_VDI_ARGUMENTS, + VDI_ARGS_TEXT_KEY, + clientConfig.vdiArguments) + .asArea() + .mandatory(!isReadonly)) + .withDefaultSpanEmptyCell(1) + .addField(FormBuilder.checkbox( SEBClientConfig.ATTR_FALLBACK, FALLBACK_TEXT_KEY, @@ -304,6 +367,10 @@ public class SEBClientConfigForm implements TemplateComposer { FALLBACK_ATTRIBUTES::contains, ffa -> ffa.setVisible(BooleanUtils.isTrue(clientConfig.fallback))); + formHandle.process( + VDI_ATTRIBUTES::contains, + ffa -> ffa.setVisible(BooleanUtils.isTrue(clientConfig.vdiType != VDIType.NO))); + if (!isReadonly) { formHandle.getForm().getFieldInput(SEBClientConfig.ATTR_FALLBACK) .addListener(SWT.Selection, event -> formHandle.process( @@ -316,6 +383,18 @@ public class SEBClientConfigForm implements TemplateComposer { ffa.setStringValue(StringUtils.EMPTY); } })); + formHandle.getForm().getFieldInput(SEBClientConfig.ATTR_VDI_TYPE) + .addListener(SWT.Selection, event -> formHandle.process( + VDI_ATTRIBUTES::contains, + ffa -> { + final boolean show = + !VDIType.NO.name().equals(((Selection) event.widget).getSelectionValue()); + ffa.setVisible(show); + if (!show && ffa.hasError()) { + ffa.resetError(); + ffa.setStringValue(StringUtils.EMPTY); + } + })); } final UrlLauncher urlLauncher = RWT.getClient().getService(UrlLauncher.class); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/form/Form.java b/src/main/java/ch/ethz/seb/sebserver/gui/form/Form.java index 5f34c0dd..349bea2c 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/form/Form.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/form/Form.java @@ -18,8 +18,6 @@ import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Predicate; -import ch.ethz.seb.sebserver.gbl.util.Cryptor; -import ch.ethz.seb.sebserver.gui.widget.PasswordInput; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; import org.eclipse.rap.rwt.RWT; @@ -38,11 +36,13 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.api.JSONMapper; import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.Threshold; +import ch.ethz.seb.sebserver.gbl.util.Cryptor; import ch.ethz.seb.sebserver.gbl.util.Tuple; import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.FormBinding; import ch.ethz.seb.sebserver.gui.widget.FileUploadSelection; import ch.ethz.seb.sebserver.gui.widget.ImageUploadSelection; +import ch.ethz.seb.sebserver.gui.widget.PasswordInput; import ch.ethz.seb.sebserver.gui.widget.Selection; import ch.ethz.seb.sebserver.gui.widget.Selection.Type; import ch.ethz.seb.sebserver.gui.widget.ThresholdList; @@ -166,7 +166,8 @@ public final class Form implements FormBinding { return this; } - Form putField(final String name, final Control label, final FileUploadSelection fileUpload, final Label errorLabel) { + Form putField(final String name, final Control label, final FileUploadSelection fileUpload, + final Label errorLabel) { final FormFieldAccessor createAccessor = createAccessor(label, fileUpload, errorLabel); fileUpload.setErrorHandler(createAccessor::setError); this.formFields.add(name, createAccessor); @@ -319,7 +320,7 @@ public final class Form implements FormBinding { @Override public String getStringValue() {return pwdInput.getValue() != null ? pwdInput.getValue().toString() : null;} @Override public void setStringValue(final String value) { if (StringUtils.isNotBlank(value)) { - pwdInput.setValue(cryptor.decrypt(value)); + pwdInput.setValue(Form.this.cryptor.decrypt(value)); } else { pwdInput.setValue(value); } @@ -508,6 +509,13 @@ public final class Form implements FormBinding { this.input.setVisible(visible); } + public void setEnabled(final boolean enable) { + if (this.label != null) { + this.label.setEnabled(enable); + } + this.input.setEnabled(enable); + } + public void putJsonValue(final String key, final ObjectNode objectRoot) { this.jsonValueAdapter.accept(new Tuple<>(key, getStringValue()), objectRoot); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/form/FormBuilder.java b/src/main/java/ch/ethz/seb/sebserver/gui/form/FormBuilder.java index 56d0f661..3eb45487 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/form/FormBuilder.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/form/FormBuilder.java @@ -195,6 +195,8 @@ public class FormBuilder { final GridData gridData = new GridData(SWT.LEFT, SWT.TOP, false, false, hspan, vspan); gridData.minimumWidth = 0; gridData.widthHint = 0; + gridData.heightHint = 0; + gridData.minimumHeight = 0; empty.setLayoutData(gridData); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/ResourceService.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/ResourceService.java index 5dd83925..77830eb2 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/ResourceService.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/ResourceService.java @@ -46,6 +46,7 @@ import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode.ConfigurationStatus; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode.ConfigurationType; import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig.VDIType; import ch.ethz.seb.sebserver.gbl.model.sebconfig.TemplateAttribute; import ch.ethz.seb.sebserver.gbl.model.sebconfig.View; import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus; @@ -113,6 +114,7 @@ public class ResourceService { public static final String SEB_RESTRICTION_PERMISSIONS_PREFIX = "sebserver.exam.form.sebrestriction.permissions."; public static final String SEB_CLIENT_CONFIG_PURPOSE_PREFIX = "sebserver.clientconfig.config.purpose."; public static final String EXAM_PROCTORING_TYPE_PREFIX = "sebserver.exam.proctoring.type.servertype."; + public static final String VDI_TYPE_PREFIX = "sebserver.clientconfig.form.vditype."; public static final EnumSet ATTRIBUTE_TYPES_NOT_DISPLAYED = EnumSet.of( AttributeType.LABEL, @@ -416,6 +418,18 @@ public class ResourceService { .collect(Collectors.toList()); } + public List> vdiTypeResources() { + return Arrays.stream(VDIType.values()) + .map(type -> new Tuple3<>( + type.name(), + this.i18nSupport.getText(VDI_TYPE_PREFIX + type.name()), + Utils.formatLineBreaks(this.i18nSupport.getText( + VDI_TYPE_PREFIX + type.name() + Constants.TOOLTIP_TEXT_KEY_SUFFIX, + StringUtils.EMPTY)))) + .sorted(RESOURCE_COMPARATOR) + .collect(Collectors.toList()); + } + public List> examConfigStatusResources() { return examConfigStatusResources(false); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/SEBClientConfigDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/SEBClientConfigDAOImpl.java index 8b9eacde..59c5f71c 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/SEBClientConfigDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/SEBClientConfigDAOImpl.java @@ -39,6 +39,7 @@ import ch.ethz.seb.sebserver.gbl.model.EntityDependency; import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig; import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig.ConfigPurpose; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig.VDIType; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Utils; @@ -384,6 +385,23 @@ public class SEBClientConfigDAOImpl implements SEBClientConfigDAO { ? ConfigPurpose .valueOf(additionalAttributes.get(SEBClientConfig.ATTR_CONFIG_PURPOSE).getValue()) : ConfigPurpose.START_EXAM, + additionalAttributes.containsKey(SEBClientConfig.ATTR_PING_INTERVAL) + ? Long + .valueOf(additionalAttributes.get(SEBClientConfig.ATTR_PING_INTERVAL).getValue()) + : 1000L, + additionalAttributes.containsKey(SEBClientConfig.ATTR_VDI_TYPE) + ? VDIType + .valueOf(additionalAttributes.get(SEBClientConfig.ATTR_VDI_TYPE).getValue()) + : VDIType.NO, + additionalAttributes.containsKey(SEBClientConfig.ATTR_VDI_EXECUTABLE) + ? additionalAttributes.get(SEBClientConfig.ATTR_VDI_EXECUTABLE).getValue() + : null, + additionalAttributes.containsKey(SEBClientConfig.ATTR_VDI_PATH) + ? additionalAttributes.get(SEBClientConfig.ATTR_VDI_PATH).getValue() + : null, + additionalAttributes.containsKey(SEBClientConfig.ATTR_VDI_ARGUMENTS) + ? additionalAttributes.get(SEBClientConfig.ATTR_VDI_ARGUMENTS).getValue() + : null, additionalAttributes.containsKey(SEBClientConfig.ATTR_FALLBACK) && BooleanUtils.toBoolean(additionalAttributes.get(SEBClientConfig.ATTR_FALLBACK).getValue()), additionalAttributes.containsKey(SEBClientConfig.ATTR_FALLBACK_START_URL) @@ -453,6 +471,42 @@ public class SEBClientConfigDAOImpl implements SEBClientConfigDAO { ? sebClientConfig.configPurpose.name() : ConfigPurpose.CONFIGURE_CLIENT.name()); + this.additionalAttributesDAO.saveAdditionalAttribute( + EntityType.SEB_CLIENT_CONFIGURATION, + configId, + SEBClientConfig.ATTR_PING_INTERVAL, + sebClientConfig.sebServerPingTime.toString()); + + this.additionalAttributesDAO.saveAdditionalAttribute( + EntityType.SEB_CLIENT_CONFIGURATION, + configId, + SEBClientConfig.ATTR_VDI_TYPE, + sebClientConfig.vdiType.name()); + + if (sebClientConfig.vdiExecutable != null) { + this.additionalAttributesDAO.saveAdditionalAttribute( + EntityType.SEB_CLIENT_CONFIGURATION, + configId, + SEBClientConfig.ATTR_VDI_EXECUTABLE, + sebClientConfig.vdiExecutable); + } + + if (sebClientConfig.vdiExecutable != null) { + this.additionalAttributesDAO.saveAdditionalAttribute( + EntityType.SEB_CLIENT_CONFIGURATION, + configId, + SEBClientConfig.ATTR_VDI_PATH, + sebClientConfig.vdiPath); + } + + if (sebClientConfig.vdiExecutable != null) { + this.additionalAttributesDAO.saveAdditionalAttribute( + EntityType.SEB_CLIENT_CONFIGURATION, + configId, + SEBClientConfig.ATTR_VDI_ARGUMENTS, + sebClientConfig.vdiArguments); + } + this.additionalAttributesDAO.saveAdditionalAttribute( EntityType.SEB_CLIENT_CONFIGURATION, configId, diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/ClientConfigService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/ClientConfigService.java index 1f50138a..3ebd907d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/ClientConfigService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/ClientConfigService.java @@ -33,20 +33,14 @@ public interface ClientConfigService { * @return true if there is any SEBClientConfiguration for a specified institution. False otherwise */ boolean hasSEBClientConfigurationForInstitution(Long institutionId); - /** Use this to auto-generate a SEBClientConfiguration for a specified institution. - * clientName and clientSecret are randomly generated. - * - * @param institutionId the institution identifier - * @return the created SEBClientConfiguration */ - Result autoCreateSEBClientConfigurationForInstitution(Long institutionId); - /** Use this to export a specified SEBClientConfiguration within a given OutputStream. * The SEB Client Configuration is exported in the defined SEB Configuration format * as described here: https://www.safeexambrowser.org/developer/seb-file-format.html * * @param out OutputStream to write the export to - * @param modelId the model identifier of the SEBClientConfiguration to export - * @param examId The exam identifier. May be null, if not the exported client config will contain the exam information*/ + * @param modelId the model identifier of the SEBClientConfiguration to export + * @param examId The exam identifier. May be null, if not the exported client config will contain the exam + * information */ void exportSEBClientConfiguration( OutputStream out, final String modelId, diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/ClientConfigServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/ClientConfigServiceImpl.java index a06e63a5..b24e3b6a 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/ClientConfigServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/ClientConfigServiceImpl.java @@ -18,7 +18,6 @@ import java.security.NoSuchAlgorithmException; import java.util.Base64; import java.util.Collection; import java.util.Collections; -import java.util.UUID; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.BooleanUtils; @@ -47,14 +46,12 @@ import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.api.API; import ch.ethz.seb.sebserver.gbl.client.ClientCredentialService; import ch.ethz.seb.sebserver.gbl.client.ClientCredentials; -import ch.ethz.seb.sebserver.gbl.model.institution.Institution; import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig; import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig.ConfigPurpose; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.webservice.WebserviceInfo; -import ch.ethz.seb.sebserver.webservice.servicelayer.dao.InstitutionDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.SEBClientConfigDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ClientConfigService; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SEBConfigEncryptionService; @@ -103,7 +100,6 @@ public class ClientConfigServiceImpl implements ClientConfigService { " %s%n" + " %s%n"; - private final InstitutionDAO institutionDAO; private final SEBClientConfigDAO sebClientConfigDAO; private final ClientCredentialService clientCredentialService; private final SEBConfigEncryptionService sebConfigEncryptionService; @@ -112,7 +108,6 @@ public class ClientConfigServiceImpl implements ClientConfigService { private final WebserviceInfo webserviceInfo; protected ClientConfigServiceImpl( - final InstitutionDAO institutionDAO, final SEBClientConfigDAO sebClientConfigDAO, final ClientCredentialService clientCredentialService, final SEBConfigEncryptionService sebConfigEncryptionService, @@ -120,7 +115,6 @@ public class ClientConfigServiceImpl implements ClientConfigService { @Qualifier(WebSecurityConfig.CLIENT_PASSWORD_ENCODER_BEAN_NAME) final PasswordEncoder clientPasswordEncoder, final WebserviceInfo webserviceInfo) { - this.institutionDAO = institutionDAO; this.sebClientConfigDAO = sebClientConfigDAO; this.clientCredentialService = clientCredentialService; this.sebConfigEncryptionService = sebConfigEncryptionService; @@ -135,35 +129,6 @@ public class ClientConfigServiceImpl implements ClientConfigService { return all != null && !all.hasError() && !all.getOrThrow().isEmpty(); } - @Override - public Result autoCreateSEBClientConfigurationForInstitution(final Long institutionId) { - return Result.tryCatch(() -> { - final Institution institution = this.institutionDAO - .byPK(institutionId) - .getOrThrow(); - - return new SEBClientConfig( - null, - institutionId, - institution.name + "_" + UUID.randomUUID(), - null, - false, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - true); - }) - .flatMap(this.sebClientConfigDAO::createNew); - } - @Override public Result getClientConfigDetails(final String clientName) { return this.getEncodedClientConfigSecret(clientName) diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index bd88c16d..bada0fa6 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -679,6 +679,18 @@ sebserver.clientconfig.form.title=Connection Configuration sebserver.clientconfig.form.title.subtitle= sebserver.clientconfig.form.name=Name sebserver.clientconfig.form.name.tooltip=The name of the connection configuration.
Any name that not already is in use for another connection configuration + +sebserver.clientconfig.form.pinginterval=Ping Interval +sebserver.clientconfig.form.pinginterval.tooltip=Defines an interval time in milliseconds for a SEB client to send a ping to the SEB Server +sebserver.clientconfig.form.vditype=VDI Setup +sebserver.clientconfig.form.vditype.tooltip=Select a VDI Type to apply this connection configuration to SEB client select a VDI environment on startup +sebserver.clientconfig.form.vditype.NO=No VDI +sebserver.clientconfig.form.vditype.VM_WARE=VDI with VMWare +sebserver.clientconfig.form.vdi.executable=Executable +sebserver.clientconfig.form.vdi.path=Path to Executable +sebserver.clientconfig.form.vdi.args=Arguments +sebserver.clientconfig.form.vdi.args.tooltip=A list of arguments to use with the executable to startup the virtual client software + sebserver.clientconfig.form.fallback=With Fallback sebserver.clientconfig.form.fallback.tooltip=Indicates whether this connection configuration has a fallback definition or not sebserver.clientconfig.form.fallback-url=Fallback Start URL diff --git a/src/test/java/ch/ethz/seb/sebserver/gbl/model/ModelObjectJSONGenerator.java b/src/test/java/ch/ethz/seb/sebserver/gbl/model/ModelObjectJSONGenerator.java index 5c6618bd..02cea90b 100644 --- a/src/test/java/ch/ethz/seb/sebserver/gbl/model/ModelObjectJSONGenerator.java +++ b/src/test/java/ch/ethz/seb/sebserver/gbl/model/ModelObjectJSONGenerator.java @@ -50,6 +50,7 @@ import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue; import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation; import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig; import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig.ConfigPurpose; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig.VDIType; import ch.ethz.seb.sebserver.gbl.model.sebconfig.TitleOrientation; import ch.ethz.seb.sebserver.gbl.model.sebconfig.View; import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection; @@ -114,6 +115,8 @@ public class ModelObjectJSONGenerator { domainObject = new SEBClientConfig( 1L, 1L, "name", ConfigPurpose.CONFIGURE_CLIENT, + 1000L, + VDIType.NO, null, null, null, true, "fallbackStartURL", 20000L, (short) 3, (short) 1000, "fallbackPassword", "fallbackPasswordConfirm", "quitPassword", "quitPasswordConfirm", DateTime.now(), "encryptSecret", "encryptSecretConfirm", true); diff --git a/src/test/java/ch/ethz/seb/sebserver/gui/integration/ClientConfigTest.java b/src/test/java/ch/ethz/seb/sebserver/gui/integration/ClientConfigTest.java index d6cbeb9c..e42fc3db 100644 --- a/src/test/java/ch/ethz/seb/sebserver/gui/integration/ClientConfigTest.java +++ b/src/test/java/ch/ethz/seb/sebserver/gui/integration/ClientConfigTest.java @@ -17,6 +17,7 @@ import ch.ethz.seb.sebserver.gbl.api.API; import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport; import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig.VDIType; import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestServiceImpl; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.ActivateClientConfig; @@ -109,6 +110,8 @@ public class ClientConfigTest extends GuiIntegrationTest { config.institutionId, "new client config", SEBClientConfig.ConfigPurpose.START_EXAM, + 1000L, + VDIType.NO, null, null, null, null, null, null, @@ -136,6 +139,8 @@ public class ClientConfigTest extends GuiIntegrationTest { config.institutionId, "new client config", SEBClientConfig.ConfigPurpose.START_EXAM, + 1000L, + VDIType.NO, null, null, null, null, null, null,