fallback attributes and new password attribute handling

This commit is contained in:
anhefti 2020-02-19 17:10:21 +01:00
parent 26561288c9
commit 7238369550
52 changed files with 11407 additions and 10482 deletions

View file

@ -120,6 +120,8 @@ public final class Constants {
public static final int GZIP_ID2 = 0x8B; public static final int GZIP_ID2 = 0x8B;
public static final int GZIP_CM = 8; public static final int GZIP_CM = 8;
public static final String SHA_256 = "SHA-256";
public static final RGB WHITE_RGB = new RGB(255, 255, 255); public static final RGB WHITE_RGB = new RGB(255, 255, 255);
public static final RGB BLACK_RGB = new RGB(0, 0, 0); public static final RGB BLACK_RGB = new RGB(0, 0, 0);

View file

@ -67,6 +67,15 @@ public class POSTMapper {
return Long.parseLong(value); return Long.parseLong(value);
} }
public Short getShort(final String name) {
final String value = this.params.getFirst(name);
if (StringUtils.isBlank(value)) {
return null;
}
return Short.parseShort(value);
}
public Integer getInteger(final String name) { public Integer getInteger(final String name) {
final String value = this.params.getFirst(name); final String value = this.params.getFirst(name);
if (value == null) { if (value == null) {

View file

@ -30,11 +30,25 @@ import ch.ethz.seb.sebserver.gbl.model.GrantEntity;
public final class SebClientConfig implements GrantEntity, Activatable { public final class SebClientConfig implements GrantEntity, Activatable {
public static final String ATTR_FALLBACK_START_URL = "fallback_start_url"; public static final String ATTR_CONFIG_PURPOSE = "sebConfigPurpose";
public static final String ATTR_CONFIRM_ENCRYPT_SECRET = "confirm_encrypt_secret"; public static final String ATTR_FALLBACK = "sebServerFallback ";
public static final String ATTR_FALLBACK_START_URL = "startURL";
public static final String ATTR_FALLBACK_TIMEOUT = "sebServerFallbackTimeout";
public static final String ATTR_FALLBACK_ATTEMPTS = "sebServerFallbackAttempts";
public static final String ATTR_FALLBACK_ATTEMPT_INTERVAL = "sebServerFallbackAttemptInterval";
public static final String ATTR_FALLBACK_PASSWORD = "sebServerFallbackPasswordHash";
public static final String ATTR_FALLBACK_PASSWORD_CONFIRM = "sebServerFallbackPasswordHashConfirm";
public static final String ATTR_QUIT_PASSWORD = "hashedQuitPassword";
public static final String ATTR_QUIT_PASSWORD_CONFIRM = "hashedQuitPasswordConfirm";
public static final String ATTR_ENCRYPT_SECRET_CONFIRM = "confirm_encrypt_secret";
public static final String FILTER_ATTR_CREATION_DATE = "creation_date"; public static final String FILTER_ATTR_CREATION_DATE = "creation_date";
public enum ConfigPurpose {
START_EXAM,
CONFIGURE_CLIENT
}
@JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_ID) @JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_ID)
public final Long id; public final Long id;
@ -47,18 +61,46 @@ public final class SebClientConfig implements GrantEntity, Activatable {
@JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_NAME) @JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_NAME)
public final String name; public final String name;
@NotNull(message = "clientconfig:sebConfigPurpose:notNull")
@JsonProperty(ATTR_CONFIG_PURPOSE)
public final ConfigPurpose configPurpose;
@JsonProperty(ATTR_FALLBACK)
public final Boolean fallback;
@JsonProperty(ATTR_FALLBACK_START_URL) @JsonProperty(ATTR_FALLBACK_START_URL)
@URL(message = "clientconfig:fallback_start_url:invalidURL") @URL(message = "clientconfig:startURL:invalidURL")
public final String fallbackStartURL; public final String fallbackStartURL;
@JsonProperty(ATTR_FALLBACK_TIMEOUT)
public final Long fallbackTimeout;
@JsonProperty(ATTR_FALLBACK_ATTEMPTS)
public final Short fallbackAttempts;
@JsonProperty(ATTR_FALLBACK_ATTEMPT_INTERVAL)
public final Short fallbackAttemptInterval;
@JsonProperty(ATTR_FALLBACK_PASSWORD)
public final CharSequence fallbackPassword;
@JsonProperty(ATTR_FALLBACK_PASSWORD_CONFIRM)
public final CharSequence fallbackPasswordConfirm;
@JsonProperty(ATTR_QUIT_PASSWORD)
public final CharSequence quitPassword;
@JsonProperty(ATTR_QUIT_PASSWORD_CONFIRM)
public final CharSequence quitPasswordConfirm;
@JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_DATE) @JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_DATE)
public final DateTime date; public final DateTime date;
@JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_ENCRYPT_SECRET) @JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_ENCRYPT_SECRET)
public final CharSequence encryptSecret; public final CharSequence encryptSecret;
@JsonProperty(ATTR_CONFIRM_ENCRYPT_SECRET) @JsonProperty(ATTR_ENCRYPT_SECRET_CONFIRM)
public final CharSequence confirmEncryptSecret; public final CharSequence encryptSecretConfirm;
@JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_ACTIVE) @JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_ACTIVE)
public final Boolean active; public final Boolean active;
@ -68,19 +110,37 @@ public final class SebClientConfig implements GrantEntity, Activatable {
@JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_ID) final Long id, @JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_ID) final Long id,
@JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_INSTITUTION_ID) final Long institutionId, @JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_INSTITUTION_ID) final Long institutionId,
@JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_NAME) final String name, @JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_NAME) final String name,
@JsonProperty(ATTR_CONFIG_PURPOSE) final ConfigPurpose configPurpose,
@JsonProperty(ATTR_FALLBACK) final Boolean fallback,
@JsonProperty(ATTR_FALLBACK_START_URL) final String fallbackStartURL, @JsonProperty(ATTR_FALLBACK_START_URL) final String fallbackStartURL,
@JsonProperty(ATTR_FALLBACK_TIMEOUT) final Long fallbackTimeout,
@JsonProperty(ATTR_FALLBACK_ATTEMPTS) final Short fallbackAttempts,
@JsonProperty(ATTR_FALLBACK_ATTEMPT_INTERVAL) final Short fallbackAttemptInterval,
@JsonProperty(ATTR_FALLBACK_PASSWORD) final CharSequence fallbackPassword,
@JsonProperty(ATTR_FALLBACK_PASSWORD_CONFIRM) final CharSequence fallbackPasswordConfirm,
@JsonProperty(ATTR_QUIT_PASSWORD) final CharSequence quitPassword,
@JsonProperty(ATTR_QUIT_PASSWORD_CONFIRM) final CharSequence quitPasswordConfirm,
@JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_DATE) final DateTime date, @JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_DATE) final DateTime date,
@JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_ENCRYPT_SECRET) final CharSequence encryptSecret, @JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_ENCRYPT_SECRET) final CharSequence encryptSecret,
@JsonProperty(ATTR_CONFIRM_ENCRYPT_SECRET) final CharSequence confirmEncryptSecret, @JsonProperty(ATTR_ENCRYPT_SECRET_CONFIRM) final CharSequence encryptSecretConfirm,
@JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_ACTIVE) final Boolean active) { @JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_ACTIVE) final Boolean active) {
this.id = id; this.id = id;
this.institutionId = institutionId; this.institutionId = institutionId;
this.name = name; this.name = name;
this.configPurpose = configPurpose;
this.fallback = fallback;
this.fallbackStartURL = fallbackStartURL; this.fallbackStartURL = fallbackStartURL;
this.fallbackTimeout = fallbackTimeout;
this.fallbackAttempts = fallbackAttempts;
this.fallbackAttemptInterval = fallbackAttemptInterval;
this.fallbackPassword = fallbackPassword;
this.fallbackPasswordConfirm = fallbackPasswordConfirm;
this.quitPassword = quitPassword;
this.quitPasswordConfirm = quitPasswordConfirm;
this.date = date; this.date = date;
this.encryptSecret = encryptSecret; this.encryptSecret = encryptSecret;
this.confirmEncryptSecret = confirmEncryptSecret; this.encryptSecretConfirm = encryptSecretConfirm;
this.active = active; this.active = active;
} }
@ -88,10 +148,19 @@ public final class SebClientConfig implements GrantEntity, Activatable {
this.id = null; this.id = null;
this.institutionId = institutionId; this.institutionId = institutionId;
this.name = postParams.getString(Domain.SEB_CLIENT_CONFIGURATION.ATTR_NAME); this.name = postParams.getString(Domain.SEB_CLIENT_CONFIGURATION.ATTR_NAME);
this.configPurpose = postParams.getEnum(ATTR_CONFIG_PURPOSE, ConfigPurpose.class);
this.fallback = postParams.getBoolean(ATTR_FALLBACK);
this.fallbackStartURL = postParams.getString(ATTR_FALLBACK_START_URL); this.fallbackStartURL = postParams.getString(ATTR_FALLBACK_START_URL);
this.fallbackTimeout = postParams.getLong(ATTR_FALLBACK_TIMEOUT);
this.fallbackAttempts = postParams.getShort(ATTR_FALLBACK_ATTEMPTS);
this.fallbackAttemptInterval = postParams.getShort(ATTR_FALLBACK_ATTEMPT_INTERVAL);
this.fallbackPassword = postParams.getCharSequence(ATTR_FALLBACK_PASSWORD);
this.fallbackPasswordConfirm = postParams.getCharSequence(ATTR_FALLBACK_PASSWORD_CONFIRM);
this.quitPassword = postParams.getCharSequence(ATTR_QUIT_PASSWORD);
this.quitPasswordConfirm = postParams.getCharSequence(ATTR_QUIT_PASSWORD_CONFIRM);
this.date = postParams.getDateTime(Domain.SEB_CLIENT_CONFIGURATION.ATTR_DATE); this.date = postParams.getDateTime(Domain.SEB_CLIENT_CONFIGURATION.ATTR_DATE);
this.encryptSecret = postParams.getCharSequence(Domain.SEB_CLIENT_CONFIGURATION.ATTR_ENCRYPT_SECRET); this.encryptSecret = postParams.getCharSequence(Domain.SEB_CLIENT_CONFIGURATION.ATTR_ENCRYPT_SECRET);
this.confirmEncryptSecret = postParams.getCharSequence(ATTR_CONFIRM_ENCRYPT_SECRET); this.encryptSecretConfirm = postParams.getCharSequence(ATTR_ENCRYPT_SECRET_CONFIRM);
this.active = false; this.active = false;
} }
@ -130,18 +199,55 @@ public final class SebClientConfig implements GrantEntity, Activatable {
return this.id; return this.id;
} }
public ConfigPurpose getConfigPurpose() {
return configPurpose;
}
public Boolean getFallback() {
return fallback;
}
public Long getFallbackTimeout() {
return fallbackTimeout;
}
public Short getFallbackAttempts() {
return fallbackAttempts;
}
public Short getFallbackAttemptInterval() {
return fallbackAttemptInterval;
}
public CharSequence getFallbackPassword() {
return fallbackPassword;
}
@JsonIgnore
public CharSequence getFallbackPasswordConfirm() {
return fallbackPasswordConfirm;
}
public CharSequence getQuitPassword() {
return quitPassword;
}
@JsonIgnore
public CharSequence getQuitPasswordConfirm() {
return quitPasswordConfirm;
}
public DateTime getDate() { public DateTime getDate() {
return this.date; return this.date;
} }
@JsonIgnore
public CharSequence getEncryptSecret() { public CharSequence getEncryptSecret() {
return this.encryptSecret; return this.encryptSecret;
} }
@JsonIgnore @JsonIgnore
public CharSequence getConfirmEncryptSecret() { public CharSequence getEncryptSecretConfirm() {
return this.confirmEncryptSecret; return this.encryptSecretConfirm;
} }
@JsonIgnore @JsonIgnore
@ -149,47 +255,78 @@ public final class SebClientConfig implements GrantEntity, Activatable {
return this.encryptSecret != null && this.encryptSecret.length() > 0; return this.encryptSecret != null && this.encryptSecret.length() > 0;
} }
@JsonIgnore
public boolean hasFallbackPassword() {
return this.fallbackPassword != null && this.fallbackPassword.length() > 0;
}
@JsonIgnore
public boolean hasQuitPassword() {
return this.quitPassword != null && this.quitPassword.length() > 0;
}
public Boolean getActive() { public Boolean getActive() {
return this.active; return this.active;
} }
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("SebClientConfig{");
sb.append("id=").append(id);
sb.append(", institutionId=").append(institutionId);
sb.append(", name='").append(name).append('\'');
sb.append(", configPurpose=").append(configPurpose);
sb.append(", fallback=").append(fallback);
sb.append(", fallbackStartURL='").append(fallbackStartURL).append('\'');
sb.append(", fallbackTimeout=").append(fallbackTimeout);
sb.append(", fallbackAttempts=").append(fallbackAttempts);
sb.append(", fallbackAttemptInterval=").append(fallbackAttemptInterval);
sb.append(", fallbackPassword=").append(fallbackPassword);
sb.append(", fallbackPasswordConfirm=").append(fallbackPasswordConfirm);
sb.append(", date=").append(date);
sb.append(", encryptSecret=").append(encryptSecret);
sb.append(", encryptSecretConfirm=").append(encryptSecretConfirm);
sb.append(", active=").append(active);
sb.append('}');
return sb.toString();
}
@Override @Override
public Entity printSecureCopy() { public Entity printSecureCopy() {
return new SebClientConfig( return new SebClientConfig(
this.id, this.id,
this.institutionId, this.institutionId,
this.name, this.name,
this.configPurpose,
this.fallback,
this.fallbackStartURL, this.fallbackStartURL,
this.fallbackTimeout,
this.fallbackAttempts,
this.fallbackAttemptInterval,
Constants.EMPTY_NOTE,
Constants.EMPTY_NOTE,
Constants.EMPTY_NOTE,
Constants.EMPTY_NOTE,
this.date, this.date,
Constants.EMPTY_NOTE, Constants.EMPTY_NOTE,
Constants.EMPTY_NOTE, Constants.EMPTY_NOTE,
this.active); this.active);
} }
@Override public static SebClientConfig createNew(final Long institutionId) {
public String 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(", fallbackStartURL=");
builder.append(this.fallbackStartURL);
builder.append(", date=");
builder.append(this.date);
builder.append(", active=");
builder.append(this.active);
builder.append("]");
return builder.toString();
}
public static final SebClientConfig createNew(final Long institutionId) {
return new SebClientConfig( return new SebClientConfig(
null, null,
institutionId, institutionId,
null, null,
ConfigPurpose.START_EXAM,
false,
null,
null,
null,
null,
null,
null,
null,
null, null,
DateTime.now(DateTimeZone.UTC), DateTime.now(DateTimeZone.UTC),
null, null,

View file

@ -0,0 +1,101 @@
/*
* Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gbl.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.env.Environment;
import org.springframework.security.crypto.encrypt.Encryptors;
import org.springframework.security.crypto.keygen.KeyGenerators;
import org.springframework.stereotype.Service;
@Lazy
@Service
public class Cryptor {
private static final Logger log = LoggerFactory.getLogger(Cryptor.class);
public static final String SEBSERVER_WEBSERVICE_INTERNAL_SECRET_KEY = "sebserver.webservice.internalSecret";
private final Environment environment;
public Cryptor(final Environment environment) {
this.environment = environment;
}
public CharSequence encrypt(final CharSequence text) {
final CharSequence secret = this.environment
.getProperty(SEBSERVER_WEBSERVICE_INTERNAL_SECRET_KEY);
return encrypt(text, secret);
}
public CharSequence decrypt(final CharSequence text) {
final CharSequence secret = this.environment
.getProperty(SEBSERVER_WEBSERVICE_INTERNAL_SECRET_KEY);
return decrypt(text, secret);
}
public static CharSequence encrypt(final CharSequence text, final CharSequence secret) {
if (text == null) {
throw new IllegalArgumentException("Text has null reference");
}
if (secret == null) {
log.warn("No internal secret supplied: skip encryption");
return text;
}
try {
final CharSequence salt = KeyGenerators.string().generateKey();
final CharSequence cipher = Encryptors
.delux(secret, salt)
.encrypt(text.toString());
return new StringBuilder(cipher)
.append(salt);
} catch (final Exception e) {
log.error("Failed to encrypt text: ", e);
throw e;
}
}
public static CharSequence decrypt(final CharSequence cipher, final CharSequence secret) {
if (cipher == null) {
throw new IllegalArgumentException("Cipher has null reference");
}
if (secret == null) {
log.warn("No internal secret supplied: skip decryption");
return cipher;
}
try {
final int length = cipher.length();
final int cipherTextLength = length - 16;
final CharSequence salt = cipher.subSequence(cipherTextLength, length);
final CharSequence cipherText = cipher.subSequence(0, cipherTextLength);
return Encryptors
.delux(secret, salt)
.decrypt(cipherText.toString());
} catch (final Exception e) {
log.error("Failed to decrypt text: ", e);
throw e;
}
}
}

View file

@ -14,6 +14,8 @@ import java.net.URLEncoder;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.CharBuffer; import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
@ -28,6 +30,7 @@ import java.util.function.Predicate;
import java.util.stream.Collector; import java.util.stream.Collector;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringEscapeUtils; import org.apache.commons.text.StringEscapeUtils;
import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.graphics.RGB;
@ -478,6 +481,20 @@ public final class Utils {
return (text == null) ? null : Constants.PERCENTAGE + text + Constants.PERCENTAGE; return (text == null) ? null : Constants.PERCENTAGE + text + Constants.PERCENTAGE;
} }
public static String hash_SHA_256_Base_16(final CharSequence chars) {
if (chars == null) {
return null;
}
try {
final MessageDigest digest = MessageDigest.getInstance(Constants.SHA_256);
final byte[] encodedHash = digest.digest(toByteArray(chars));
return Hex.encodeHexString(encodedHash);
} catch (NoSuchAlgorithmException nsae) {
throw new RuntimeException("Failed to hash text: ", nsae);
}
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <T> Predicate<T> truePredicate() { public static <T> Predicate<T> truePredicate() {
return (Predicate<T>) TRUE_PREDICATE; return (Predicate<T>) TRUE_PREDICATE;

View file

@ -213,7 +213,7 @@ final class ExamToConfigBindingForm {
String.valueOf(examConfigurationMap.configurationNodeId), String.valueOf(examConfigurationMap.configurationNodeId),
resourceService::examConfigurationSelectionResources) resourceService::examConfigurationSelectionResources)
.withSelectionListener(form -> updateFormValuesFromConfigSelection(form, resourceService)) .withSelectionListener(form -> updateFormValuesFromConfigSelection(form, resourceService))
.mandatory()) .mandatory())
.addField(FormBuilder.text( .addField(FormBuilder.text(
Domain.CONFIGURATION_NODE.ATTR_DESCRIPTION, Domain.CONFIGURATION_NODE.ATTR_DESCRIPTION,
@ -228,14 +228,15 @@ final class ExamToConfigBindingForm {
resourceService.localizedExamConfigStatusName(examConfigurationMap)) resourceService.localizedExamConfigStatusName(examConfigurationMap))
.readonly(true)) .readonly(true))
.addField(FormBuilder.text( .addField(FormBuilder.password(
Domain.EXAM_CONFIGURATION_MAP.ATTR_ENCRYPT_SECRET, Domain.EXAM_CONFIGURATION_MAP.ATTR_ENCRYPT_SECRET,
FORM_ENCRYPT_SECRET_TEXT_KEY) FORM_ENCRYPT_SECRET_TEXT_KEY,
.asPasswordField()) examConfigurationMap.encryptSecret))
.addField(FormBuilder.text(
.addField(FormBuilder.password(
ExamConfigurationMap.ATTR_CONFIRM_ENCRYPT_SECRET, ExamConfigurationMap.ATTR_CONFIRM_ENCRYPT_SECRET,
FORM_CONFIRM_ENCRYPT_SECRET_TEXT_KEY) FORM_CONFIRM_ENCRYPT_SECRET_TEXT_KEY,
.asPasswordField()) examConfigurationMap.encryptSecret))
.build(); .build();

View file

@ -8,8 +8,14 @@
package ch.ethz.seb.sebserver.gui.content; package ch.ethz.seb.sebserver.gui.content;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gui.service.page.PageMessageException;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.rap.rwt.RWT; import org.eclipse.rap.rwt.RWT;
import org.eclipse.rap.rwt.client.service.UrlLauncher; import org.eclipse.rap.rwt.client.service.UrlLauncher;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Composite;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
@ -42,6 +48,10 @@ 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.service.remote.webservice.auth.CurrentUser.EntityGrantCheck;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
@Lazy @Lazy
@Component @Component
@GuiProfile @GuiProfile
@ -53,15 +63,51 @@ public class SebClientConfigForm implements TemplateComposer {
new LocTextKey("sebserver.clientconfig.form.title"); new LocTextKey("sebserver.clientconfig.form.title");
private static final LocTextKey FORM_NAME_TEXT_KEY = private static final LocTextKey FORM_NAME_TEXT_KEY =
new LocTextKey("sebserver.clientconfig.form.name"); new LocTextKey("sebserver.clientconfig.form.name");
private static final LocTextKey FORM_FALLBACK_URL_TEXT_KEY =
new LocTextKey("sebserver.clientconfig.form.fallback-url");
private static final LocTextKey FORM_DATE_TEXT_KEY = private static final LocTextKey FORM_DATE_TEXT_KEY =
new LocTextKey("sebserver.clientconfig.form.date"); new LocTextKey("sebserver.clientconfig.form.date");
private static final LocTextKey CLIENT_PURPOSE_TEXT_KEY =
new LocTextKey("sebserver.clientconfig.form.sebConfigPurpose");
private static final LocTextKey FALLBACK_TEXT_KEY =
new LocTextKey("sebserver.clientconfig.form.fallback");
private static final LocTextKey FALLBACK_URL_TEXT_KEY =
new LocTextKey("sebserver.clientconfig.form.fallback-url");
private static final LocTextKey FALLBACK_TIMEOUT_TEXT_KEY =
new LocTextKey("sebserver.clientconfig.form.sebServerFallbackTimeout");
private static final LocTextKey FALLBACK_ATTEMPTS_TEXT_KEY =
new LocTextKey("sebserver.clientconfig.form.sebServerFallbackAttempts");
private static final LocTextKey FALLBACK_ATTEMPT_INTERVAL_TEXT_KEY =
new LocTextKey("sebserver.clientconfig.form.sebServerFallbackAttemptInterval");
private static final LocTextKey FALLBACK_PASSWORD_TEXT_KEY =
new LocTextKey("sebserver.clientconfig.form.sebServerFallbackPasswordHash");
private static final LocTextKey FALLBACK_PASSWORD_CONFIRM_TEXT_KEY =
new LocTextKey("sebserver.clientconfig.form.sebServerFallbackPasswordHash");
private static final LocTextKey QUIT_PASSWORD_TEXT_KEY =
new LocTextKey("sebserver.clientconfig.form.hashedQuitPassword");
private static final LocTextKey QUIT_PASSWORD_CONFIRM_TEXT_KEY =
new LocTextKey("sebserver.clientconfig.form.hashedQuitPassword.confirm");
private static final LocTextKey FORM_ENCRYPT_SECRET_TEXT_KEY = private static final LocTextKey FORM_ENCRYPT_SECRET_TEXT_KEY =
new LocTextKey("sebserver.clientconfig.form.encryptSecret"); new LocTextKey("sebserver.clientconfig.form.encryptSecret");
private static final LocTextKey FORM_CONFIRM_ENCRYPT_SECRET_TEXT_KEY = private static final LocTextKey FORM_CONFIRM_ENCRYPT_SECRET_TEXT_KEY =
new LocTextKey("sebserver.clientconfig.form.encryptSecret.confirm"); new LocTextKey("sebserver.clientconfig.form.encryptSecret.confirm");
private static final Set<String> FALLBACK_ATTRIBUTES = new HashSet<>(Arrays.asList(
SebClientConfig.ATTR_FALLBACK_START_URL,
SebClientConfig.ATTR_FALLBACK_ATTEMPT_INTERVAL,
SebClientConfig.ATTR_FALLBACK_ATTEMPTS,
SebClientConfig.ATTR_FALLBACK_TIMEOUT,
SebClientConfig.ATTR_FALLBACK_PASSWORD,
SebClientConfig.ATTR_FALLBACK_PASSWORD_CONFIRM,
SebClientConfig.ATTR_QUIT_PASSWORD,
SebClientConfig.ATTR_QUIT_PASSWORD_CONFIRM
));
private static final String FALLBACK_DEFAULT_TIME = String.valueOf(2 * Constants.MINUTE_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);
private final PageService pageService; private final PageService pageService;
private final RestService restService; private final RestService restService;
private final CurrentUser currentUser; private final CurrentUser currentUser;
@ -131,32 +177,147 @@ public class SebClientConfigForm implements TemplateComposer {
.putStaticValue( .putStaticValue(
Domain.SEB_CLIENT_CONFIGURATION.ATTR_INSTITUTION_ID, Domain.SEB_CLIENT_CONFIGURATION.ATTR_INSTITUTION_ID,
String.valueOf(clientConfig.getInstitutionId())) String.valueOf(clientConfig.getInstitutionId()))
.addField(FormBuilder.text(
Domain.SEB_CLIENT_CONFIGURATION.ATTR_NAME,
FORM_NAME_TEXT_KEY,
clientConfig.name))
.addField(FormBuilder.text(
SebClientConfig.ATTR_FALLBACK_START_URL,
FORM_FALLBACK_URL_TEXT_KEY,
clientConfig.fallbackStartURL))
.addFieldIf(() -> !isNew, .addFieldIf(() -> !isNew,
() -> FormBuilder.text( () -> FormBuilder.text(
Domain.SEB_CLIENT_CONFIGURATION.ATTR_DATE, Domain.SEB_CLIENT_CONFIGURATION.ATTR_DATE,
FORM_DATE_TEXT_KEY, FORM_DATE_TEXT_KEY,
i18nSupport.formatDisplayDateWithTimeZone(clientConfig.date)) i18nSupport.formatDisplayDateWithTimeZone(clientConfig.date))
.readonly(true)) .readonly(true))
.addField(FormBuilder.text( .addField(FormBuilder.text(
Domain.SEB_CLIENT_CONFIGURATION.ATTR_NAME,
FORM_NAME_TEXT_KEY,
clientConfig.name)
.mandatory(!isReadonly))
.addField(FormBuilder.singleSelection(
SebClientConfig.ATTR_CONFIG_PURPOSE,
CLIENT_PURPOSE_TEXT_KEY,
clientConfig.configPurpose != null
? clientConfig.configPurpose.name()
: SebClientConfig.ConfigPurpose.START_EXAM.name(),
() -> pageService.getResourceService().sebClientConfigPurposeResources())
.mandatory(!isReadonly))
.withDefaultSpanInput(3)
.addField(FormBuilder.password(
Domain.SEB_CLIENT_CONFIGURATION.ATTR_ENCRYPT_SECRET, Domain.SEB_CLIENT_CONFIGURATION.ATTR_ENCRYPT_SECRET,
FORM_ENCRYPT_SECRET_TEXT_KEY) FORM_ENCRYPT_SECRET_TEXT_KEY,
.asPasswordField()) clientConfig.getEncryptSecret()))
.withDefaultSpanEmptyCell(3)
.addFieldIf(
() -> !isReadonly,
() -> FormBuilder.password(
SebClientConfig.ATTR_ENCRYPT_SECRET_CONFIRM,
FORM_CONFIRM_ENCRYPT_SECRET_TEXT_KEY,
clientConfig.getEncryptSecret()))
.addField(FormBuilder.checkbox(
SebClientConfig.ATTR_FALLBACK,
FALLBACK_TEXT_KEY,
clientConfig.fallback != null
? clientConfig.fallback.toString()
: Constants.FALSE_STRING)
)
.withDefaultSpanInput(5)
.addField(FormBuilder.text( .addField(FormBuilder.text(
SebClientConfig.ATTR_CONFIRM_ENCRYPT_SECRET, SebClientConfig.ATTR_FALLBACK_START_URL,
FORM_CONFIRM_ENCRYPT_SECRET_TEXT_KEY) FALLBACK_URL_TEXT_KEY,
.asPasswordField()) clientConfig.fallbackStartURL)
.mandatory(!isReadonly))
.withDefaultSpanEmptyCell(1)
.withDefaultSpanInput(2)
.addField(FormBuilder.text(
SebClientConfig.ATTR_FALLBACK_ATTEMPTS,
FALLBACK_ATTEMPTS_TEXT_KEY,
clientConfig.fallbackAttempts != null
? String.valueOf(clientConfig.fallbackAttempts)
: FALLBACK_DEFAULT_ATTEMPTS)
.asNumber(this::checkNaturalNumber)
.mandatory(!isReadonly))
.withDefaultSpanEmptyCell(0)
.withEmptyCellSeparation(false)
.withDefaultSpanLabel(1)
.addField(FormBuilder.text(
SebClientConfig.ATTR_FALLBACK_ATTEMPT_INTERVAL,
FALLBACK_ATTEMPT_INTERVAL_TEXT_KEY,
clientConfig.fallbackAttemptInterval != null
? String.valueOf(clientConfig.fallbackAttemptInterval)
: FALLBACK_DEFAULT_ATTEMPT_INTERVAL)
.asNumber(this::checkNaturalNumber)
.mandatory(!isReadonly))
.withEmptyCellSeparation(true)
.withDefaultSpanEmptyCell(1)
.withDefaultSpanLabel(2)
.addField(FormBuilder.text(
SebClientConfig.ATTR_FALLBACK_TIMEOUT,
FALLBACK_TIMEOUT_TEXT_KEY,
clientConfig.fallbackTimeout != null
? String.valueOf(clientConfig.fallbackTimeout)
: FALLBACK_DEFAULT_TIME)
.asNumber(this::checkNaturalNumber)
.mandatory(!isReadonly))
.withEmptyCellSeparation(true)
.withDefaultSpanEmptyCell(4)
.withDefaultSpanInput(2)
.withDefaultSpanLabel(2)
.addField(FormBuilder.password(
SebClientConfig.ATTR_FALLBACK_PASSWORD,
FALLBACK_PASSWORD_TEXT_KEY,
clientConfig.getFallbackPassword()))
.withEmptyCellSeparation(false)
.withDefaultSpanLabel(1)
.addField(FormBuilder.password(
SebClientConfig.ATTR_QUIT_PASSWORD,
QUIT_PASSWORD_TEXT_KEY,
clientConfig.getQuitPassword()))
.withEmptyCellSeparation(true)
.withDefaultSpanEmptyCell(1)
.withDefaultSpanInput(2)
.withDefaultSpanLabel(2)
.addFieldIf(
() -> !isReadonly,
() -> FormBuilder.password(
SebClientConfig.ATTR_FALLBACK_PASSWORD_CONFIRM,
FALLBACK_PASSWORD_CONFIRM_TEXT_KEY,
clientConfig.getFallbackPasswordConfirm()))
.withEmptyCellSeparation(false)
.withDefaultSpanLabel(1)
.addFieldIf(
() -> !isReadonly,
() -> FormBuilder.password(
SebClientConfig.ATTR_QUIT_PASSWORD_CONFIRM,
QUIT_PASSWORD_CONFIRM_TEXT_KEY,
clientConfig.getQuitPasswordConfirm()))
.buildFor((isNew) .buildFor((isNew)
? this.restService.getRestCall(NewClientConfig.class) ? this.restService.getRestCall(NewClientConfig.class)
: this.restService.getRestCall(SaveClientConfig.class)); : this.restService.getRestCall(SaveClientConfig.class));
formHandle.process(
FALLBACK_ATTRIBUTES::contains,
ffa -> ffa.setVisible(BooleanUtils.isTrue(clientConfig.fallback))
);
formHandle.getForm().getFieldInput(SebClientConfig.ATTR_FALLBACK)
.addListener(SWT.Selection, event -> {
formHandle.process(
FALLBACK_ATTRIBUTES::contains,
ffa -> ffa.setVisible(((Button) event.widget).getSelection())
);
});
final UrlLauncher urlLauncher = RWT.getClient().getService(UrlLauncher.class); final UrlLauncher urlLauncher = RWT.getClient().getService(UrlLauncher.class);
this.pageService.pageActionBuilder(formContext.clearEntityKeys()) this.pageService.pageActionBuilder(formContext.clearEntityKeys())
@ -208,4 +369,14 @@ public class SebClientConfigForm implements TemplateComposer {
.publishIf(() -> !isReadonly); .publishIf(() -> !isReadonly);
} }
private void checkNaturalNumber(String value) {
if (StringUtils.isBlank(value)) {
return;
}
long num = Long.parseLong(value);
if (num < 0) {
throw new PageMessageException("Number must be positive");
}
}
} }

View file

@ -89,7 +89,7 @@ final class SebExamConfigImportPopup {
try { try {
final Form form = formHandle.getForm(); final Form form = formHandle.getForm();
final EntityKey entityKey = formHandle.getContext().getEntityKey(); final EntityKey entityKey = formHandle.getContext().getEntityKey();
final Control fieldControl = form.getFieldControl(API.IMPORT_FILE_ATTR_NAME); final Control fieldControl = form.getFieldInput(API.IMPORT_FILE_ATTR_NAME);
final PageContext context = formHandle.getContext(); final PageContext context = formHandle.getContext();
// Ad-hoc field validation // Ad-hoc field validation
@ -270,7 +270,7 @@ final class SebExamConfigImportPopup {
void cancelUpload() { void cancelUpload() {
if (this.form != null) { if (this.form != null) {
final Control fieldControl = this.form.getFieldControl(API.IMPORT_FILE_ATTR_NAME); final Control fieldControl = this.form.getFieldInput(API.IMPORT_FILE_ATTR_NAME);
if (fieldControl != null && fieldControl instanceof FileUploadSelection) { if (fieldControl != null && fieldControl instanceof FileUploadSelection) {
((FileUploadSelection) fieldControl).close(); ((FileUploadSelection) fieldControl).close();
} }

View file

@ -13,20 +13,27 @@ import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Label;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import java.util.function.Consumer;
public class CheckboxFieldBuilder extends FieldBuilder<String> { public class CheckboxFieldBuilder extends FieldBuilder<String> {
protected CheckboxFieldBuilder(final String name, final LocTextKey label, final String value) { protected CheckboxFieldBuilder(
final String name,
final LocTextKey label,
final String value) {
super(name, label, value); super(name, label, value);
} }
@Override @Override
void build(final FormBuilder builder) { void build(final FormBuilder builder) {
final boolean readonly = builder.readonly || this.readonly; final boolean readonly = builder.readonly || this.readonly;
final Label titleLabel = createTitleLabel(builder.formParent, builder, this); final Control titleLabel = createTitleLabel(builder.formParent, builder, this);
final Composite fieldGrid = createFieldGrid(builder.formParent, this.spanInput); final Composite fieldGrid = createFieldGrid(builder.formParent, this.spanInput);
final Button checkbox = builder.widgetFactory.buttonLocalized( final Button checkbox = builder.widgetFactory.buttonLocalized(
fieldGrid, fieldGrid,

View file

@ -16,6 +16,7 @@ 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;
import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Label;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
@ -113,7 +114,7 @@ public abstract class FieldBuilder<T> {
abstract void build(FormBuilder builder); abstract void build(FormBuilder builder);
protected static Label createTitleLabel( protected static Control createTitleLabel(
final Composite parent, final Composite parent,
final FormBuilder builder, final FormBuilder builder,
final FieldBuilder<?> fieldBuilder) { final FieldBuilder<?> fieldBuilder) {
@ -173,7 +174,7 @@ public abstract class FieldBuilder<T> {
info.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false)); info.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false));
} }
return label; return infoGrid;
} }
public static Label labelLocalized( public static Label labelLocalized(
@ -186,7 +187,7 @@ public abstract class FieldBuilder<T> {
return labelLocalized(widgetFactory, parent, locTextKey, defaultText, null, hspan, SWT.CENTER); return labelLocalized(widgetFactory, parent, locTextKey, defaultText, null, hspan, SWT.CENTER);
} }
public static final Label labelLocalized( public static Label labelLocalized(
final WidgetFactory widgetFactory, final WidgetFactory widgetFactory,
final Composite parent, final Composite parent,
final LocTextKey locTextKey, final LocTextKey locTextKey,

View file

@ -13,6 +13,7 @@ import java.util.Collection;
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.widgets.Composite; import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Label;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
@ -34,7 +35,7 @@ public class FileUploadFieldBuilder extends FieldBuilder<String> {
@Override @Override
void build(final FormBuilder builder) { void build(final FormBuilder builder) {
final Label titleLabel = createTitleLabel(builder.formParent, builder, this); final Control titleLabel = createTitleLabel(builder.formParent, builder, this);
final Composite fieldGrid = createFieldGrid(builder.formParent, this.spanInput); final Composite fieldGrid = createFieldGrid(builder.formParent, this.spanInput);
final FileUploadSelection fileUpload = builder.widgetFactory.fileUploadSelection( final FileUploadSelection fileUpload = builder.widgetFactory.fileUploadSelection(
fieldGrid, fieldGrid,

View file

@ -18,6 +18,8 @@ import java.util.function.BiConsumer;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Predicate; 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.BooleanUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.eclipse.rap.rwt.RWT; import org.eclipse.rap.rwt.RWT;
@ -48,6 +50,7 @@ import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant;
public final class Form implements FormBinding { public final class Form implements FormBinding {
private final Cryptor cryptor;
private final JSONMapper jsonMapper; private final JSONMapper jsonMapper;
private final ObjectNode objectRoot; private final ObjectNode objectRoot;
@ -55,8 +58,9 @@ public final class Form implements FormBinding {
private final MultiValueMap<String, FormFieldAccessor> formFields = new LinkedMultiValueMap<>(); private final MultiValueMap<String, FormFieldAccessor> formFields = new LinkedMultiValueMap<>();
private final Map<String, Set<String>> groups = new LinkedHashMap<>(); private final Map<String, Set<String>> groups = new LinkedHashMap<>();
Form(final JSONMapper jsonMapper) { Form(final JSONMapper jsonMapper, final Cryptor cryptor) {
this.jsonMapper = jsonMapper; this.jsonMapper = jsonMapper;
this.cryptor = cryptor;
this.objectRoot = this.jsonMapper.createObjectNode(); this.objectRoot = this.jsonMapper.createObjectNode();
} }
@ -77,27 +81,23 @@ public final class Form implements FormBinding {
appendFormUrlEncodedValue(buffer, entry.getKey(), entry.getValue()); appendFormUrlEncodedValue(buffer, entry.getKey(), entry.getValue());
} }
this.formFields.entrySet() this.formFields.forEach((key, value) -> value
.stream() .stream()
.forEach(entry -> { .filter(Form::valueApplicationFilter)
entry.getValue() .forEach(ffa -> {
.stream() if (ffa.listValue) {
.filter(Form::valueApplicationFilter) appendFormUrlEncodedValue(
.forEach(ffa -> { buffer,
if (ffa.listValue) { key,
appendFormUrlEncodedValue( ffa.getStringValue());
buffer, } else {
entry.getKey(), appendFormUrlEncodedSingleValue(
ffa.getStringValue()); buffer,
} else { key,
appendFormUrlEncodedSingleValue( ffa.getStringValue(),
buffer, false);
entry.getKey(), }
ffa.getStringValue(), }));
false);
}
});
});
return buffer.toString(); return buffer.toString();
} }
@ -123,45 +123,54 @@ public final class Form implements FormBinding {
return this.formFields.containsKey(fieldName); return this.formFields.containsKey(fieldName);
} }
Form putReadonlyField(final String name, final Label label, final Text field) { Form putReadonlyField(final String name, final Control label, final Text field) {
this.formFields.add(name, createReadonlyAccessor(label, field)); this.formFields.add(name, createReadonlyAccessor(label, field));
return this; return this;
} }
Form putReadonlyField(final String name, final Label label, final Browser field) { Form putReadonlyField(final String name, final Control label, final Browser field) {
this.formFields.add(name, createReadonlyAccessor(label, field)); this.formFields.add(name, createReadonlyAccessor(label, field));
return this; return this;
} }
Form putField(final String name, final Label label, final Text field, final Label errorLabel) { Form putField(final String name, final Control label, final Text field, final Label errorLabel) {
this.formFields.add(name, createAccessor(label, field, errorLabel)); this.formFields.add(name, createAccessor(label, field, errorLabel));
return this; return this;
} }
Form putField(final String name, final Label label, final Button checkbox) { Form putField(final String name, final Control label, final PasswordInput field, final Label errorLabel) {
this.formFields.add(name, createAccessor(label, field, errorLabel));
return this;
}
Form putField(final String name, final Control label, final Button checkbox) {
this.formFields.add(name, createAccessor(label, checkbox, null)); this.formFields.add(name, createAccessor(label, checkbox, null));
return this; return this;
} }
void putField(final String name, final Label label, final Selection field, final Label errorLabel) { Form putField(final String name, final Control label, final Selection field, final Label errorLabel) {
this.formFields.add(name, createAccessor(label, field, errorLabel)); this.formFields.add(name, createAccessor(label, field, errorLabel));
return this;
} }
void putField(final String name, final Label label, final ThresholdList field, final Label errorLabel) { Form putField(final String name, final Control label, final ThresholdList field, final Label errorLabel) {
this.formFields.add(name, createAccessor(label, field, errorLabel)); this.formFields.add(name, createAccessor(label, field, errorLabel));
return this;
} }
void putField(final String name, final Label label, final ImageUploadSelection imageUpload, Form putField(final String name, final Control label, final ImageUploadSelection imageUpload,
final Label errorLabel) { final Label errorLabel) {
final FormFieldAccessor createAccessor = createAccessor(label, imageUpload, errorLabel); final FormFieldAccessor createAccessor = createAccessor(label, imageUpload, errorLabel);
imageUpload.setErrorHandler(createAccessor::setError); imageUpload.setErrorHandler(createAccessor::setError);
this.formFields.add(name, createAccessor); this.formFields.add(name, createAccessor);
return this;
} }
void putField(final String name, final Label 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); final FormFieldAccessor createAccessor = createAccessor(label, fileUpload, errorLabel);
fileUpload.setErrorHandler(createAccessor::setError); fileUpload.setErrorHandler(createAccessor::setError);
this.formFields.add(name, createAccessor); this.formFields.add(name, createAccessor);
return this;
} }
public String getFieldValue(final String attributeName) { public String getFieldValue(final String attributeName) {
@ -173,13 +182,13 @@ public final class Form implements FormBinding {
return fieldAccessor.getStringValue(); return fieldAccessor.getStringValue();
} }
public Control getFieldControl(final String attributeName) { public Control getFieldInput(final String attributeName) {
final FormFieldAccessor fieldAccessor = this.formFields.getFirst(attributeName); final FormFieldAccessor fieldAccessor = this.formFields.getFirst(attributeName);
if (fieldAccessor == null) { if (fieldAccessor == null) {
return null; return null;
} }
return fieldAccessor.control; return fieldAccessor.input;
} }
public void setFieldValue(final String attributeName, final String attributeValue) { public void setFieldValue(final String attributeName, final String attributeValue) {
@ -222,14 +231,14 @@ public final class Form implements FormBinding {
final Set<String> namesSet = this.groups.get(group); final Set<String> namesSet = this.groups.get(group);
process( process(
name -> namesSet.contains(name), namesSet::contains,
ffa -> ffa.setVisible(visible)); ffa -> ffa.setVisible(visible));
} }
public void setFieldVisible(final boolean visible, final String fieldName) { public void setFieldVisible(final boolean visible, final String fieldName) {
final List<FormFieldAccessor> list = this.formFields.get(fieldName); final List<FormFieldAccessor> list = this.formFields.get(fieldName);
if (list != null) { if (list != null) {
list.stream().forEach(ffa -> ffa.setVisible(visible)); list.forEach(ffa -> ffa.setVisible(visible));
} }
} }
@ -237,23 +246,19 @@ public final class Form implements FormBinding {
return this.formFields.entrySet() return this.formFields.entrySet()
.stream() .stream()
.flatMap(entity -> entity.getValue().stream()) .flatMap(entity -> entity.getValue().stream())
.filter(a -> a.hasError) .anyMatch(a -> a.hasError);
.findFirst()
.isPresent();
} }
public void clearErrors() { public void clearErrors() {
process( process(
Utils.truePredicate(), Utils.truePredicate(),
ffa -> ffa.resetError()); FormFieldAccessor::resetError);
} }
public void setFieldError(final String fieldName, final String errorMessage) { public void setFieldError(final String fieldName, final String errorMessage) {
final List<FormFieldAccessor> list = this.formFields.get(fieldName); final List<FormFieldAccessor> list = this.formFields.get(fieldName);
if (list != null) { if (list != null) {
list list.forEach(ffa -> ffa.setError(errorMessage));
.stream()
.forEach(ffa -> ffa.setError(errorMessage));
} }
} }
@ -291,31 +296,43 @@ public final class Form implements FormBinding {
// following are FormFieldAccessor implementations for all field types // following are FormFieldAccessor implementations for all field types
//@formatter:off //@formatter:off
private FormFieldAccessor createReadonlyAccessor(final Label label, final Text field) { private FormFieldAccessor createReadonlyAccessor(final Control label, final Text field) {
return new FormFieldAccessor(label, field, null) { return new FormFieldAccessor(label, field, null) {
@Override public String getStringValue() { return null; } @Override public String getStringValue() { return null; }
@Override public void setStringValue(final String value) { field.setText( (value == null) ? StringUtils.EMPTY : value); } @Override public void setStringValue(final String value) { field.setText( (value == null) ? StringUtils.EMPTY : value); }
}; };
} }
private FormFieldAccessor createReadonlyAccessor(final Label label, final Browser field) { private FormFieldAccessor createReadonlyAccessor(final Control label, final Browser field) {
return new FormFieldAccessor(label, field, null) { return new FormFieldAccessor(label, field, null) {
@Override public String getStringValue() { return null; } @Override public String getStringValue() { return null; }
@Override public void setStringValue(final String value) { field.setText( (value == null) ? StringUtils.EMPTY : value); } @Override public void setStringValue(final String value) { field.setText( (value == null) ? StringUtils.EMPTY : value); }
}; };
} }
private FormFieldAccessor createAccessor(final Label label, final Text text, final Label errorLabel) { private FormFieldAccessor createAccessor(final Control label, final Text text, final Label errorLabel) {
return new FormFieldAccessor(label, text, errorLabel) { return new FormFieldAccessor(label, text, errorLabel) {
@Override public String getStringValue() {return text.getText();} @Override public String getStringValue() {return text.getText();}
@Override public void setStringValue(final String value) {text.setText(value);} @Override public void setStringValue(final String value) {text.setText(value);}
}; };
} }
private FormFieldAccessor createAccessor(final Label label, final Button checkbox, final Label errorLabel) { private FormFieldAccessor createAccessor(final Control label, final PasswordInput pwdInput, final Label errorLabel) {
return new FormFieldAccessor(label, pwdInput, errorLabel) {
@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));
} else {
pwdInput.setValue(value);
}
}
};
}
private FormFieldAccessor createAccessor(final Control label, final Button checkbox, final Label errorLabel) {
return new FormFieldAccessor(label, checkbox, errorLabel) { return new FormFieldAccessor(label, checkbox, errorLabel) {
@Override public String getStringValue() {return BooleanUtils.toStringTrueFalse(checkbox.getSelection());} @Override public String getStringValue() {return BooleanUtils.toStringTrueFalse(checkbox.getSelection());}
@Override public void setStringValue(final String value) {checkbox.setSelection(BooleanUtils.toBoolean(value));} @Override public void setStringValue(final String value) {checkbox.setSelection(BooleanUtils.toBoolean(value));}
}; };
} }
private FormFieldAccessor createAccessor(final Label label, final Selection selection, final Label errorLabel) { private FormFieldAccessor createAccessor(final Control label, final Selection selection, final Label errorLabel) {
switch (selection.type()) { switch (selection.type()) {
case MULTI: case MULTI:
case MULTI_COMBO: case MULTI_COMBO:
@ -325,7 +342,7 @@ public final class Form implements FormBinding {
} }
} }
private FormFieldAccessor createAccessor( private FormFieldAccessor createAccessor(
final Label label, final Control label,
final Selection selection, final Selection selection,
final BiConsumer<Tuple<String>, ObjectNode> jsonValueAdapter, final BiConsumer<Tuple<String>, ObjectNode> jsonValueAdapter,
final Label errorLabel) { final Label errorLabel) {
@ -340,7 +357,7 @@ public final class Form implements FormBinding {
@Override public void setStringValue(final String value) { selection.select(value); } @Override public void setStringValue(final String value) { selection.select(value); }
}; };
} }
private FormFieldAccessor createAccessor(final Label label, final ThresholdList thresholdList, final Label errorLabel) { private FormFieldAccessor createAccessor(final Control label, final ThresholdList thresholdList, final Label errorLabel) {
return new FormFieldAccessor(label, thresholdList, null, true, errorLabel) { return new FormFieldAccessor(label, thresholdList, null, true, errorLabel) {
@Override public String getStringValue() { @Override public String getStringValue() {
return ThresholdListBuilder return ThresholdListBuilder
@ -358,12 +375,12 @@ public final class Form implements FormBinding {
} }
}; };
} }
private FormFieldAccessor createAccessor(final Label label, final ImageUploadSelection imageUpload, final Label errorLabel) { private FormFieldAccessor createAccessor(final Control label, final ImageUploadSelection imageUpload, final Label errorLabel) {
return new FormFieldAccessor(label, imageUpload, errorLabel) { return new FormFieldAccessor(label, imageUpload, errorLabel) {
@Override public String getStringValue() { return imageUpload.getImageBase64(); } @Override public String getStringValue() { return imageUpload.getImageBase64(); }
}; };
} }
private FormFieldAccessor createAccessor(final Label label, final FileUploadSelection fileUpload, final Label errorLabel) { private FormFieldAccessor createAccessor(final Control label, final FileUploadSelection fileUpload, final Label errorLabel) {
return new FormFieldAccessor(label, fileUpload, errorLabel) { return new FormFieldAccessor(label, fileUpload, errorLabel) {
@Override public String getStringValue() { return fileUpload.getFileName(); } @Override public String getStringValue() { return fileUpload.getFileName(); }
}; };
@ -418,7 +435,7 @@ public final class Form implements FormBinding {
} }
} }
private static final void adaptCommaSeparatedStringToJsonArray( private static void adaptCommaSeparatedStringToJsonArray(
final Tuple<String> tuple, final Tuple<String> tuple,
final ObjectNode jsonNode) { final ObjectNode jsonNode) {
@ -433,26 +450,26 @@ public final class Form implements FormBinding {
public static abstract class FormFieldAccessor { public static abstract class FormFieldAccessor {
public final Label label; public final Control label;
public final Control control; public final Control input;
private final Label errorLabel; private final Label errorLabel;
private final BiConsumer<Tuple<String>, ObjectNode> jsonValueAdapter; private final BiConsumer<Tuple<String>, ObjectNode> jsonValueAdapter;
private boolean hasError; private boolean hasError;
private final boolean listValue; private final boolean listValue;
FormFieldAccessor(final Label label, final Control control, final Label errorLabel) { FormFieldAccessor(final Control label, final Control control, final Label errorLabel) {
this(label, control, null, false, errorLabel); this(label, control, null, false, errorLabel);
} }
FormFieldAccessor( FormFieldAccessor(
final Label label, final Control label,
final Control control, final Control input,
final BiConsumer<Tuple<String>, ObjectNode> jsonValueAdapter, final BiConsumer<Tuple<String>, ObjectNode> jsonValueAdapter,
final boolean listValue, final boolean listValue,
final Label errorLabel) { final Label errorLabel) {
this.label = label; this.label = label;
this.control = control; this.input = input;
this.errorLabel = errorLabel; this.errorLabel = errorLabel;
if (jsonValueAdapter != null) { if (jsonValueAdapter != null) {
this.jsonValueAdapter = jsonValueAdapter; this.jsonValueAdapter = jsonValueAdapter;
@ -473,14 +490,14 @@ public final class Form implements FormBinding {
} }
public void setBackgroundColor(final Color color) { public void setBackgroundColor(final Color color) {
if (this.control != null) { if (this.input != null) {
this.control.setBackground(color); this.input.setBackground(color);
} }
} }
public void setTextColor(final Color color) { public void setTextColor(final Color color) {
if (this.control != null) { if (this.input != null) {
this.control.setForeground(color); this.input.setForeground(color);
} }
} }
@ -488,7 +505,7 @@ public final class Form implements FormBinding {
if (this.label != null) { if (this.label != null) {
this.label.setVisible(visible); this.label.setVisible(visible);
} }
this.control.setVisible(visible); this.input.setVisible(visible);
} }
public void putJsonValue(final String key, final ObjectNode objectRoot) { public void putJsonValue(final String key, final ObjectNode objectRoot) {
@ -506,7 +523,7 @@ public final class Form implements FormBinding {
} }
if (!this.hasError) { if (!this.hasError) {
this.control.setData(RWT.CUSTOM_VARIANT, CustomVariant.ERROR.key); this.input.setData(RWT.CUSTOM_VARIANT, CustomVariant.ERROR.key);
this.errorLabel.setText("- " + errorMessage); this.errorLabel.setText("- " + errorMessage);
this.errorLabel.setVisible(true); this.errorLabel.setVisible(true);
this.hasError = true; this.hasError = true;
@ -519,7 +536,7 @@ public final class Form implements FormBinding {
} }
if (this.hasError) { if (this.hasError) {
this.control.setData(RWT.CUSTOM_VARIANT, null); this.input.setData(RWT.CUSTOM_VARIANT, null);
this.errorLabel.setVisible(false); this.errorLabel.setVisible(false);
this.errorLabel.setText(StringUtils.EMPTY); this.errorLabel.setText(StringUtils.EMPTY);
this.hasError = false; this.hasError = false;

View file

@ -12,8 +12,10 @@ import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.function.BooleanSupplier; import java.util.function.BooleanSupplier;
import java.util.function.Consumer;
import java.util.function.Supplier; import java.util.function.Supplier;
import ch.ethz.seb.sebserver.gbl.util.Cryptor;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.eclipse.swt.SWT; import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridData;
@ -40,6 +42,7 @@ public class FormBuilder {
private static final Logger log = LoggerFactory.getLogger(FormBuilder.class); private static final Logger log = LoggerFactory.getLogger(FormBuilder.class);
final Cryptor cryptor;
final I18nSupport i18nSupport; final I18nSupport i18nSupport;
final PageService pageService; final PageService pageService;
final WidgetFactory widgetFactory; final WidgetFactory widgetFactory;
@ -56,13 +59,15 @@ public class FormBuilder {
public FormBuilder( public FormBuilder(
final PageService pageService, final PageService pageService,
final PageContext pageContext, final PageContext pageContext,
final Cryptor cryptor,
final int rows) { final int rows) {
this.cryptor = cryptor;
this.i18nSupport = pageService.getI18nSupport(); this.i18nSupport = pageService.getI18nSupport();
this.pageService = pageService; this.pageService = pageService;
this.widgetFactory = pageService.getWidgetFactory(); this.widgetFactory = pageService.getWidgetFactory();
this.pageContext = pageContext; this.pageContext = pageContext;
this.form = new Form(pageService.getJSONMapper()); this.form = new Form(pageService.getJSONMapper(), cryptor);
this.formParent = this.widgetFactory.formGrid( this.formParent = this.widgetFactory.formGrid(
pageContext.getParent(), pageContext.getParent(),
@ -214,11 +219,18 @@ public class FormBuilder {
return new TextFieldBuilder(name, label, value); return new TextFieldBuilder(name, label, value);
} }
public static TextFieldBuilder text(final String name, final LocTextKey label, public static TextFieldBuilder text(
final String name,
final LocTextKey label,
final Supplier<String> valueSupplier) { final Supplier<String> valueSupplier) {
return new TextFieldBuilder(name, label, valueSupplier.get()); return new TextFieldBuilder(name, label, valueSupplier.get());
} }
public static PasswordFieldBuilder password(final String name, final LocTextKey label, final CharSequence value) {
return new PasswordFieldBuilder(name, label, value);
}
public static SelectionFieldBuilder singleSelection( public static SelectionFieldBuilder singleSelection(
final String name, final String name,
final LocTextKey label, final LocTextKey label,

View file

@ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.gui.form;
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.widgets.Composite; import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Label;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
@ -37,7 +38,7 @@ public final class ImageUploadFieldBuilder extends FieldBuilder<String> {
@Override @Override
void build(final FormBuilder builder) { void build(final FormBuilder builder) {
final Label titleLabel = createTitleLabel(builder.formParent, builder, this); final Control titleLabel = createTitleLabel(builder.formParent, builder, this);
final Composite fieldGrid = createFieldGrid(builder.formParent, this.spanInput); final Composite fieldGrid = createFieldGrid(builder.formParent, this.spanInput);
final ImageUploadSelection imageUpload = builder.widgetFactory.imageUploadLocalized( final ImageUploadSelection imageUpload = builder.widgetFactory.imageUploadLocalized(
fieldGrid, fieldGrid,

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gui.form;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.widget.PasswordInput;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
public class PasswordFieldBuilder extends FieldBuilder<CharSequence> {
PasswordFieldBuilder(final String name, final LocTextKey label, final CharSequence value) {
super(name, label, value);
}
@Override
void build(FormBuilder builder) {
final boolean readonly = builder.readonly || this.readonly;
final Control titleLabel = createTitleLabel(builder.formParent, builder, this);
final Composite fieldGrid = createFieldGrid(builder.formParent, this.spanInput);
final PasswordInput input = new PasswordInput(fieldGrid, builder.widgetFactory);
input.setEditable(!readonly);
input.setValue((StringUtils.isNotBlank(this.value))
? builder.cryptor.decrypt(this.value)
: this.value);
final Label errorLabel = createErrorLabel(fieldGrid);
builder.form.putField(this.name, titleLabel, input, errorLabel);
}
}

View file

@ -55,7 +55,7 @@ public final class SelectionFieldBuilder extends FieldBuilder<String> {
@Override @Override
void build(final FormBuilder builder) { void build(final FormBuilder builder) {
final Label titleLabel = createTitleLabel(builder.formParent, builder, this); final Control titleLabel = createTitleLabel(builder.formParent, builder, this);
if (builder.readonly || this.readonly) { if (builder.readonly || this.readonly) {
buildReadOnly(builder, titleLabel); buildReadOnly(builder, titleLabel);
@ -64,7 +64,7 @@ public final class SelectionFieldBuilder extends FieldBuilder<String> {
} }
} }
private void buildInput(final FormBuilder builder, final Label titleLabel) { private void buildInput(final FormBuilder builder, final Control titleLabel) {
final Composite fieldGrid = createFieldGrid(builder.formParent, this.spanInput); final Composite fieldGrid = createFieldGrid(builder.formParent, this.spanInput);
final String actionKey = (this.label != null) ? this.label.name + ".action" : null; final String actionKey = (this.label != null) ? this.label.name + ".action" : null;
@ -93,7 +93,7 @@ public final class SelectionFieldBuilder extends FieldBuilder<String> {
} }
/* Build the read-only representation of the selection field */ /* Build the read-only representation of the selection field */
private void buildReadOnly(final FormBuilder builder, final Label titleLabel) { private void buildReadOnly(final FormBuilder builder, final Control titleLabel) {
if (this.type == Type.MULTI || this.type == Type.MULTI_COMBO || this.type == Type.MULTI_CHECKBOX) { if (this.type == Type.MULTI || this.type == Type.MULTI_COMBO || this.type == Type.MULTI_CHECKBOX) {
final Composite composite = new Composite(builder.formParent, SWT.NONE); final Composite composite = new Composite(builder.formParent, SWT.NONE);
final GridLayout gridLayout = new GridLayout(1, true); final GridLayout gridLayout = new GridLayout(1, true);

View file

@ -18,6 +18,7 @@ import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.Text;
@ -88,7 +89,7 @@ public final class TextFieldBuilder extends FieldBuilder<String> {
@Override @Override
void build(final FormBuilder builder) { void build(final FormBuilder builder) {
final boolean readonly = builder.readonly || this.readonly; final boolean readonly = builder.readonly || this.readonly;
final Label titleLabel = createTitleLabel(builder.formParent, builder, this); final Control titleLabel = createTitleLabel(builder.formParent, builder, this);
final Composite fieldGrid = createFieldGrid(builder.formParent, this.spanInput); final Composite fieldGrid = createFieldGrid(builder.formParent, this.spanInput);
if (readonly && this.isHTML) { if (readonly && this.isHTML) {
@ -117,7 +118,7 @@ public final class TextFieldBuilder extends FieldBuilder<String> {
gridData.minimumHeight = this.areaMinHeight; gridData.minimumHeight = this.areaMinHeight;
} else if (this.isColorBox) { } else if (this.isColorBox) {
gridData.minimumHeight = WidgetFactory.TEXT_INPUT_MIN_HEIGHT; gridData.minimumHeight = WidgetFactory.TEXT_INPUT_MIN_HEIGHT;
textInput.setData(RWT.CUSTOM_VARIANT, "colorbox"); textInput.setData(RWT.CUSTOM_VARIANT, WidgetFactory.CustomVariant.COLOR_BOX.key);
} }
textInput.setLayoutData(gridData); textInput.setLayoutData(gridData);
if (StringUtils.isNoneBlank(this.value)) { if (StringUtils.isNoneBlank(this.value)) {

View file

@ -15,6 +15,7 @@ import org.apache.commons.lang3.StringUtils;
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.widgets.Composite; import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Label;
import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.Constants;
@ -39,7 +40,7 @@ public class ThresholdListBuilder extends FieldBuilder<Collection<Threshold>> {
@Override @Override
void build(final FormBuilder builder) { void build(final FormBuilder builder) {
final Label titleLabel = createTitleLabel(builder.formParent, builder, this); final Control titleLabel = createTitleLabel(builder.formParent, builder, this);
if (builder.readonly || this.readonly) { if (builder.readonly || this.readonly) {
// No read-only view needed for this so far? // No read-only view needed for this so far?
return; return;

View file

@ -20,6 +20,7 @@ import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.SebClientConfig;
import ch.ethz.seb.sebserver.gbl.util.Tuple3; import ch.ethz.seb.sebserver.gbl.util.Tuple3;
import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.gbl.util.Utils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -107,6 +108,7 @@ public class ResourceService {
public static final String CONFIG_ATTRIBUTE_TYPE_PREFIX = "sebserver.configtemplate.attr.type."; public static final String CONFIG_ATTRIBUTE_TYPE_PREFIX = "sebserver.configtemplate.attr.type.";
public static final String SEB_RESTRICTION_WHITE_LIST_PREFIX = "sebserver.exam.form.sebrestriction.whiteListPaths."; public static final String SEB_RESTRICTION_WHITE_LIST_PREFIX = "sebserver.exam.form.sebrestriction.whiteListPaths.";
public static final String SEB_RESTRICTION_PERMISSIONS_PREFIX = "sebserver.exam.form.sebrestriction.permissions."; 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 EnumSet<AttributeType> ATTRIBUTE_TYPES_NOT_DISPLAYED = EnumSet.of( public static final EnumSet<AttributeType> ATTRIBUTE_TYPES_NOT_DISPLAYED = EnumSet.of(
AttributeType.LABEL, AttributeType.LABEL,
@ -656,4 +658,16 @@ public class ResourceService {
.call(); .call();
} }
public List<Tuple<String>> sebClientConfigPurposeResources() {
return Arrays.stream(SebClientConfig.ConfigPurpose.values())
.map(type -> new Tuple3<>(
type.name(),
this.i18nSupport.getText(SEB_CLIENT_CONFIG_PURPOSE_PREFIX + type.name()),
Utils.formatLineBreaks(this.i18nSupport.getText(
SEB_CLIENT_CONFIG_PURPOSE_PREFIX + type.name() + Constants.TOOLTIP_TEXT_KEY_SUFFIX,
StringUtils.EMPTY))))
.sorted(RESOURCE_COMPARATOR)
.collect(Collectors.toList());
}
} }

View file

@ -8,44 +8,51 @@
package ch.ethz.seb.sebserver.gui.service.examconfig.impl; package ch.ethz.seb.sebserver.gui.service.examconfig.impl;
import java.nio.charset.StandardCharsets; import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType;
import java.security.MessageDigest; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import java.security.NoSuchAlgorithmException; import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import org.apache.commons.codec.binary.Hex; import ch.ethz.seb.sebserver.gbl.util.Cryptor;
import ch.ethz.seb.sebserver.gui.form.FieldBuilder;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputField;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputFieldBuilder;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.widget.PasswordInput;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.rap.rwt.RWT; 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.widgets.Composite; import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Text;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.form.FieldBuilder;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputField;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputFieldBuilder;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant;
@Lazy @Lazy
@Component @Component
@GuiProfile @GuiProfile
public class PassworFieldBuilder implements InputFieldBuilder { public class PasswordFieldBuilder implements InputFieldBuilder {
private static final Logger log = LoggerFactory.getLogger(PassworFieldBuilder.class); private static final Logger log = LoggerFactory.getLogger(PasswordFieldBuilder.class);
private static final LocTextKey VAL_CONFIRM_PWD_TEXT_KEY = private static final LocTextKey VAL_CONFIRM_PWD_TEXT_KEY =
new LocTextKey("sebserver.examconfig.props.validation.password.confirm"); new LocTextKey("sebserver.examconfig.props.validation.password.confirm");
private final Cryptor cryptor;
private final WidgetFactory widgetFactory;
public PasswordFieldBuilder(
final WidgetFactory widgetFactory,
final Cryptor cryptor) {
this.cryptor = cryptor;
this.widgetFactory = widgetFactory;
}
@Override @Override
public boolean builderFor( public boolean builderFor(
final ConfigurationAttribute attribute, final ConfigurationAttribute attribute,
@ -69,24 +76,26 @@ public class PassworFieldBuilder implements InputFieldBuilder {
final Composite innerGrid = InputFieldBuilder final Composite innerGrid = InputFieldBuilder
.createInnerGrid(parent, attribute, orientation); .createInnerGrid(parent, attribute, orientation);
final Text passwordInput = new Text(innerGrid, SWT.LEFT | SWT.BORDER | SWT.PASSWORD); final PasswordInput passwordInput = new PasswordInput(innerGrid, widgetFactory);
final GridData passwordInputLD = new GridData(SWT.FILL, SWT.FILL, true, false); final GridData passwordInputLD = new GridData(SWT.FILL, SWT.FILL, true, true);
passwordInput.setLayoutData(passwordInputLD); passwordInput.setLayoutData(passwordInputLD);
final Text confirmInput = new Text(innerGrid, SWT.LEFT | SWT.BORDER | SWT.PASSWORD);
final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false); final PasswordInput confirmInput = new PasswordInput(innerGrid, widgetFactory);
final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
gridData.verticalIndent = 14; gridData.verticalIndent = 14;
confirmInput.setLayoutData(gridData); confirmInput.setLayoutData(gridData);
innerGrid.setData("isPlainText", false);
final PasswordInputField passwordInputField = new PasswordInputField( final PasswordInputField passwordInputField = new PasswordInputField(
attribute, attribute,
orientation, orientation,
passwordInput, passwordInput,
confirmInput, confirmInput,
FieldBuilder.createErrorLabel(innerGrid)); FieldBuilder.createErrorLabel(innerGrid),
cryptor);
if (viewContext.readonly) { if (viewContext.readonly) {
passwordInput.setEditable(false); passwordInput.setEditable(false);
passwordInput.setData(RWT.CUSTOM_VARIANT, CustomVariant.CONFIG_INPUT_READONLY.key);
passwordInputLD.heightHint = WidgetFactory.TEXT_INPUT_MIN_HEIGHT; passwordInputLD.heightHint = WidgetFactory.TEXT_INPUT_MIN_HEIGHT;
confirmInput.setEditable(false); confirmInput.setEditable(false);
confirmInput.setData(RWT.CUSTOM_VARIANT, CustomVariant.CONFIG_INPUT_READONLY.key); confirmInput.setData(RWT.CUSTOM_VARIANT, CustomVariant.CONFIG_INPUT_READONLY.key);
@ -95,8 +104,8 @@ public class PassworFieldBuilder implements InputFieldBuilder {
final Listener valueChangeEventListener = event -> { final Listener valueChangeEventListener = event -> {
passwordInputField.clearError(); passwordInputField.clearError();
final String pwd = passwordInput.getText(); final CharSequence pwd = passwordInput.getValue();
final String confirm = confirmInput.getText(); final CharSequence confirm = confirmInput.getValue();
if (passwordInputField.initValue != null && passwordInputField.initValue.equals(pwd)) { if (passwordInputField.initValue != null && passwordInputField.initValue.equals(pwd)) {
return; return;
@ -127,50 +136,44 @@ public class PassworFieldBuilder implements InputFieldBuilder {
return passwordInputField; return passwordInputField;
} }
static final class PasswordInputField extends AbstractInputField<Text> { static final class PasswordInputField extends AbstractInputField<PasswordInput> {
private final Text confirm; private final PasswordInput confirm;
private final Cryptor cryptor;
PasswordInputField( PasswordInputField(
final ConfigurationAttribute attribute, final ConfigurationAttribute attribute,
final Orientation orientation, final Orientation orientation,
final Text control, final PasswordInput control,
final Text confirm, final PasswordInput confirm,
final Label errorLabel) { final Label errorLabel,
final Cryptor cryptor) {
super(attribute, orientation, control, errorLabel); super(attribute, orientation, control, errorLabel);
this.confirm = confirm; this.confirm = confirm;
this.cryptor = cryptor;
} }
@Override @Override
protected void setValueToControl(final String value) { protected void setValueToControl(final String value) {
// TODO clarify setting some "fake" input when a password is set (like in config tool) if (StringUtils.isNotBlank(value)) {
if (value != null) { CharSequence pwd = cryptor.decrypt(value);
this.control.setText(value); this.control.setValue(pwd.toString());
this.confirm.setText(value); this.confirm.setValue(pwd.toString());
} else {
this.control.setValue(StringUtils.EMPTY);
this.confirm.setValue(StringUtils.EMPTY);
} }
} }
@Override @Override
public String getValue() { public String getValue() {
String hashedPWD; final CharSequence pwd = this.control.getValue();
try { if (StringUtils.isNotBlank(pwd)) {
hashedPWD = hashPassword(this.control.getText()); return cryptor.encrypt(pwd).toString();
} catch (final NoSuchAlgorithmException e) {
log.error("Failed to hash password: ", e);
showError("Failed to hash password");
hashedPWD = null;
} }
return hashedPWD; return StringUtils.EMPTY;
}
private String hashPassword(final String pwd) throws NoSuchAlgorithmException {
final MessageDigest digest = MessageDigest.getInstance("SHA-256");
final byte[] encodedhash = digest.digest(
pwd.getBytes(StandardCharsets.UTF_8));
return Hex.encodeHexString(encodedhash);
} }
} }

View file

@ -20,6 +20,7 @@ import java.util.function.Supplier;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;
import ch.ethz.seb.sebserver.gbl.util.Cryptor;
import org.eclipse.rap.rwt.RWT; import org.eclipse.rap.rwt.RWT;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -78,6 +79,7 @@ public class PageServiceImpl implements PageService {
private static final String ATTR_PAGE_STATE = "PAGE_STATE"; private static final String ATTR_PAGE_STATE = "PAGE_STATE";
private static final ListenerComparator LIST_COMPARATOR = new ListenerComparator(); private static final ListenerComparator LIST_COMPARATOR = new ListenerComparator();
private final Cryptor cryptor;
private final JSONMapper jsonMapper; private final JSONMapper jsonMapper;
private final WidgetFactory widgetFactory; private final WidgetFactory widgetFactory;
private final PolyglotPageService polyglotPageService; private final PolyglotPageService polyglotPageService;
@ -85,12 +87,14 @@ public class PageServiceImpl implements PageService {
private final CurrentUser currentUser; private final CurrentUser currentUser;
public PageServiceImpl( public PageServiceImpl(
final Cryptor cryptor,
final JSONMapper jsonMapper, final JSONMapper jsonMapper,
final WidgetFactory widgetFactory, final WidgetFactory widgetFactory,
final PolyglotPageService polyglotPageService, final PolyglotPageService polyglotPageService,
final ResourceService resourceService, final ResourceService resourceService,
final CurrentUser currentUser) { final CurrentUser currentUser) {
this.cryptor = cryptor;
this.jsonMapper = jsonMapper; this.jsonMapper = jsonMapper;
this.widgetFactory = widgetFactory; this.widgetFactory = widgetFactory;
this.polyglotPageService = polyglotPageService; this.polyglotPageService = polyglotPageService;
@ -337,7 +341,7 @@ public class PageServiceImpl implements PageService {
@Override @Override
public FormBuilder formBuilder(final PageContext pageContext, final int rows) { public FormBuilder formBuilder(final PageContext pageContext, final int rows) {
return new FormBuilder(this, pageContext, rows); return new FormBuilder(this, pageContext, cryptor, rows);
} }
@Override @Override

View file

@ -0,0 +1,132 @@
/*
* Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gui.widget;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.page.PageService;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
public class PasswordInput extends Composite {
public static final LocTextKey PLAIN_TEXT_VIEW_TOOLTIP_KEY =
new LocTextKey("sebserver.overall.action.showPassword.tooltip");
private final WidgetFactory widgetFactory;
private final Composite inputAnchor;
private final Label visibilityButton;
private Text passwordInput = null;
private boolean isPlainText = true;
private boolean isEditable = true;
public PasswordInput(final Composite parent, final WidgetFactory widgetFactory) {
super(parent, SWT.NONE);
this.widgetFactory = widgetFactory;
GridLayout gridLayout = new GridLayout(2, false);
gridLayout.horizontalSpacing = 0;
gridLayout.verticalSpacing = 0;
gridLayout.marginHeight = 0;
gridLayout.marginWidth = 0;
this.setLayout(gridLayout);
this.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
inputAnchor = new Composite(this, SWT.NONE);
gridLayout = new GridLayout(1, false);
gridLayout.horizontalSpacing = 0;
gridLayout.verticalSpacing = 0;
gridLayout.marginHeight = 0;
gridLayout.marginWidth = 0;
inputAnchor.setLayout(gridLayout);
inputAnchor.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
visibilityButton = widgetFactory.imageButton(
WidgetFactory.ImageIcon.VISIBILITY,
this,
PLAIN_TEXT_VIEW_TOOLTIP_KEY,
event -> changePasswordView());
GridData ld = new GridData(SWT.RIGHT, SWT.BOTTOM, false, false);
ld.heightHint = 22;
ld.horizontalIndent = 5;
visibilityButton.setLayoutData(ld);
changePasswordView();
}
private void changePasswordView() {
final String value = (this.passwordInput != null) ? this.passwordInput.getText() : null;
final boolean buildPassword = this.isPlainText;
if (this.passwordInput != null) {
PageService.clearComposite(this.inputAnchor);
}
Text passwordInput = new Text(
inputAnchor,
SWT.LEFT | SWT.BORDER | (buildPassword ? SWT.PASSWORD : SWT.NONE));
GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
passwordInput.setLayoutData(gridData);
passwordInput.setText(value != null ? value : StringUtils.EMPTY);
if (!buildPassword) {
passwordInput.setEditable(false);
} else {
passwordInput.setEditable(isEditable);
passwordInput.setData(RWT.CUSTOM_VARIANT, WidgetFactory.CustomVariant.CONFIG_INPUT_READONLY.key);
if (!isEditable) {
gridData.heightHint = 21;
}
}
if (buildPassword) {
passwordInput.addListener(SWT.FocusOut, event -> super.notifyListeners(SWT.FocusOut, event));
passwordInput.addListener(SWT.Traverse, event -> super.notifyListeners(SWT.Traverse, event));
this.visibilityButton.setImage(WidgetFactory.ImageIcon.VISIBILITY.getImage(getDisplay()));
} else {
passwordInput.setData(RWT.CUSTOM_VARIANT, WidgetFactory.CustomVariant.PLAIN_PWD.key);
this.visibilityButton.setImage(WidgetFactory.ImageIcon.VISIBILITY_OFF.getImage(getDisplay()));
}
this.passwordInput = passwordInput;
this.isPlainText = !this.isPlainText;
super.layout(true, true);
}
public void setValue(CharSequence value) {
if (passwordInput != null) {
passwordInput.setText(value != null ? value.toString() : StringUtils.EMPTY);
}
}
public CharSequence getValue() {
if (passwordInput != null) {
return passwordInput.getText();
}
return null;
}
public void setEditable(boolean editable) {
this.isEditable = editable;
this.isPlainText = !this.isPlainText;
this.changePasswordView();
}
}

View file

@ -115,7 +115,9 @@ public class WidgetFactory {
HELP("help.png"), HELP("help.png"),
LOCK("lock.png"), LOCK("lock.png"),
UNLOCK("unlock.png"), UNLOCK("unlock.png"),
RESTRICTION("restriction.png"); RESTRICTION("restriction.png"),
VISIBILITY("visibility.png"),
VISIBILITY_OFF("visibility_off.png");
public String fileName; public String fileName;
private ImageData image = null; private ImageData image = null;
@ -189,7 +191,9 @@ public class WidgetFactory {
LOGIN_BACK("login-back"), LOGIN_BACK("login-back"),
SCROLL("scroll"), SCROLL("scroll"),
LIST_NAVIGATION("list-nav") LIST_NAVIGATION("list-nav"),
PLAIN_PWD("pwdplain"),
COLOR_BOX("colorbox")
; ;
@ -403,7 +407,7 @@ public class WidgetFactory {
public Text numberInput(final Composite content, final Consumer<String> numberCheck, final boolean readonly) { public Text numberInput(final Composite content, final Consumer<String> numberCheck, final boolean readonly) {
if (readonly) { if (readonly) {
return new Text(content, SWT.RIGHT | SWT.READ_ONLY); return new Text(content, SWT.LEFT | SWT.READ_ONLY);
} }
final Text numberInput = new Text(content, SWT.RIGHT | SWT.BORDER); final Text numberInput = new Text(content, SWT.RIGHT | SWT.BORDER);

View file

@ -8,22 +8,20 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.client; package ch.ethz.seb.sebserver.webservice.servicelayer.client;
import java.io.UnsupportedEncodingException; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import java.nio.CharBuffer; import ch.ethz.seb.sebserver.gbl.util.Cryptor;
import java.security.SecureRandom; import ch.ethz.seb.sebserver.gbl.util.Result;
import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.security.crypto.encrypt.Encryptors;
import org.springframework.security.crypto.keygen.KeyGenerators;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import java.io.UnsupportedEncodingException;
import ch.ethz.seb.sebserver.gbl.util.Result; import java.nio.CharBuffer;
import java.security.SecureRandom;
@Lazy @Lazy
@Service @Service
@ -32,12 +30,15 @@ public class ClientCredentialServiceImpl implements ClientCredentialService {
private static final Logger log = LoggerFactory.getLogger(ClientCredentialServiceImpl.class); private static final Logger log = LoggerFactory.getLogger(ClientCredentialServiceImpl.class);
static final String SEBSERVER_WEBSERVICE_INTERNAL_SECRET_KEY = "sebserver.webservice.internalSecret";
private final Environment environment; private final Environment environment;
private final Cryptor cryptor;
protected ClientCredentialServiceImpl(
final Environment environment,
final Cryptor cryptor) {
protected ClientCredentialServiceImpl(final Environment environment) {
this.environment = environment; this.environment = environment;
this.cryptor = cryptor;
} }
@Override @Override
@ -63,15 +64,15 @@ public class ClientCredentialServiceImpl implements ClientCredentialService {
final CharSequence accessTokenPlaintext) { final CharSequence accessTokenPlaintext) {
final CharSequence secret = this.environment final CharSequence secret = this.environment
.getProperty(SEBSERVER_WEBSERVICE_INTERNAL_SECRET_KEY); .getProperty(Cryptor.SEBSERVER_WEBSERVICE_INTERNAL_SECRET_KEY);
return new ClientCredentials( return new ClientCredentials(
clientIdPlaintext, clientIdPlaintext,
(StringUtils.isNoneBlank(secretPlaintext)) (StringUtils.isNoneBlank(secretPlaintext))
? encrypt(secretPlaintext, secret).toString() ? Cryptor.encrypt(secretPlaintext, secret).toString()
: null, : null,
(StringUtils.isNoneBlank(accessTokenPlaintext)) (StringUtils.isNoneBlank(accessTokenPlaintext))
? encrypt(accessTokenPlaintext, secret).toString() ? Cryptor.encrypt(accessTokenPlaintext, secret).toString()
: null); : null);
} }
@ -82,8 +83,8 @@ public class ClientCredentialServiceImpl implements ClientCredentialService {
} }
final CharSequence secret = this.environment final CharSequence secret = this.environment
.getProperty(SEBSERVER_WEBSERVICE_INTERNAL_SECRET_KEY); .getProperty(Cryptor.SEBSERVER_WEBSERVICE_INTERNAL_SECRET_KEY);
return this.decrypt(credentials.secret, secret); return Cryptor.decrypt(credentials.secret, secret);
} }
@Override @Override
@ -93,100 +94,39 @@ public class ClientCredentialServiceImpl implements ClientCredentialService {
} }
final CharSequence secret = this.environment final CharSequence secret = this.environment
.getProperty(SEBSERVER_WEBSERVICE_INTERNAL_SECRET_KEY); .getProperty(Cryptor.SEBSERVER_WEBSERVICE_INTERNAL_SECRET_KEY);
return this.decrypt(credentials.accessToken, secret); return Cryptor.decrypt(credentials.accessToken, secret);
} }
@Override @Override
public CharSequence encrypt(final CharSequence text) { public CharSequence encrypt(final CharSequence text) {
return cryptor.encrypt(text);
final CharSequence secret = this.environment
.getProperty(SEBSERVER_WEBSERVICE_INTERNAL_SECRET_KEY);
return encrypt(text, secret);
} }
@Override @Override
public CharSequence decrypt(final CharSequence text) { public CharSequence decrypt(final CharSequence text) {
return cryptor.decrypt(text);
final CharSequence secret = this.environment
.getProperty(SEBSERVER_WEBSERVICE_INTERNAL_SECRET_KEY);
return decrypt(text, secret);
}
CharSequence encrypt(final CharSequence text, final CharSequence secret) {
if (text == null) {
throw new IllegalArgumentException("Text has null reference");
}
if (secret == null) {
log.warn("No internal secret supplied: skip encryption");
return text;
}
try {
final CharSequence salt = KeyGenerators.string().generateKey();
final CharSequence cipher = Encryptors
.delux(secret, salt)
.encrypt(text.toString());
return new StringBuilder(cipher)
.append(salt);
} catch (final Exception e) {
log.error("Failed to encrypt text: ", e);
throw e;
}
}
CharSequence decrypt(final CharSequence cipher, final CharSequence secret) {
if (cipher == null) {
throw new IllegalArgumentException("Cipher has null reference");
}
if (secret == null) {
log.warn("No internal secret supplied: skip decryption");
return cipher;
}
try {
final int length = cipher.length();
final int cipherTextLength = length - 16;
final CharSequence salt = cipher.subSequence(cipherTextLength, length);
final CharSequence cipherText = cipher.subSequence(0, cipherTextLength);
return Encryptors
.delux(secret, salt)
.decrypt(cipherText.toString());
} catch (final Exception e) {
log.error("Failed to decrypt text: ", e);
throw e;
}
} }
private final static char[] possibleCharacters = private final static char[] possibleCharacters =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~`!@#$%^*()-_=+[{]}?" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~`!@#$%^*()-_=+[{]}?"
.toCharArray(); .toCharArray();
public final static CharSequence generateClientId() { public static CharSequence generateClientId() {
return RandomStringUtils.random( return RandomStringUtils.random(
16, 0, possibleCharacters.length - 1, false, false, 16, 0, possibleCharacters.length - 1, false, false,
possibleCharacters, new SecureRandom()); possibleCharacters, new SecureRandom());
} }
public final static CharSequence generateClientSecret() throws UnsupportedEncodingException { public static CharSequence generateClientSecret() throws UnsupportedEncodingException {
// TODO fine a better way to generate a random char array instead of using RandomStringUtils.random which uses a String // TODO find a better way to generate a random char array instead of using RandomStringUtils.random which uses a String
return RandomStringUtils.random( return RandomStringUtils.random(
64, 0, possibleCharacters.length - 1, false, false, 64, 0, possibleCharacters.length - 1, false, false,
possibleCharacters, new SecureRandom()); possibleCharacters, new SecureRandom());
} }
public final static void clearChars(final CharSequence sequence) { public static void clearChars(final CharSequence sequence) {
if (sequence == null) { if (sequence == null) {
return; return;
} }

View file

@ -1 +0,0 @@
/ExamDAO.java

View file

@ -30,7 +30,7 @@ public interface ExamConfigurationMapDAO extends
* @param examId The Exam mapping identifier * @param examId The Exam mapping identifier
* @param configurationNodeId the ConfigurationNode mapping identifier * @param configurationNodeId the ConfigurationNode mapping identifier
* @return Result refer to the password cipher of specified mapping or to an exception if happened */ * @return Result refer to the password cipher of specified mapping or to an exception if happened */
Result<CharSequence> getConfigPasswortCipher(Long examId, Long configurationNodeId); Result<CharSequence> getConfigPasswordCipher(Long examId, Long configurationNodeId);
/** Get the ConfigurationNode identifier of the default Exam Configuration of /** Get the ConfigurationNode identifier of the default Exam Configuration of
* the Exam with specified identifier. * the Exam with specified identifier.

View file

@ -43,7 +43,7 @@ public interface SebClientConfigDAO extends
* *
* @param modelId the model * @param modelId the model
* @return encrypted configuration password */ * @return encrypted configuration password */
Result<CharSequence> getConfigPasswortCipher(String modelId); Result<CharSequence> getConfigPasswordCipher(String modelId);
/** Get the stored encrypted configuration password from a specified SEB client configuration. /** Get the stored encrypted configuration password from a specified SEB client configuration.
* The SEB client configuration password is used to encrypt a SEB Client Configuration. * The SEB client configuration password is used to encrypt a SEB Client Configuration.
@ -52,7 +52,7 @@ public interface SebClientConfigDAO extends
* *
* @param clientName the client name * @param clientName the client name
* @return encrypted configuration password */ * @return encrypted configuration password */
Result<CharSequence> getConfigPasswortCipherByClientName(String clientName); Result<CharSequence> getConfigPasswordCipherByClientName(String clientName);
@Override @Override
@CacheEvict( @CacheEvict(

View file

@ -99,16 +99,14 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO {
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public Result<Collection<ClientConnection>> allOf(final Set<Long> pks) { public Result<Collection<ClientConnection>> allOf(final Set<Long> pks) {
return Result.tryCatch(() -> { return Result.tryCatch(() -> this.clientConnectionRecordMapper.selectByExample()
return this.clientConnectionRecordMapper.selectByExample() .where(ClientConnectionRecordDynamicSqlSupport.id, isIn(new ArrayList<>(pks)))
.where(ClientConnectionRecordDynamicSqlSupport.id, isIn(new ArrayList<>(pks))) .build()
.build() .execute()
.execute() .stream()
.stream() .map(ClientConnectionDAOImpl::toDomainModel)
.map(ClientConnectionDAOImpl::toDomainModel) .flatMap(DAOLoggingSupport::logAndSkipOnError)
.flatMap(DAOLoggingSupport::logAndSkipOnError) .collect(Collectors.toList()));
.collect(Collectors.toList());
});
} }
@Override @Override

View file

@ -77,42 +77,39 @@ public class ClientEventDAOImpl implements ClientEventDAO {
final FilterMap filterMap, final FilterMap filterMap,
final Predicate<ClientEvent> predicate) { final Predicate<ClientEvent> predicate) {
return Result.tryCatch(() -> { return Result.tryCatch(() -> this.clientEventRecordMapper
.selectByExample()
return this.clientEventRecordMapper .where(
.selectByExample() ClientEventRecordDynamicSqlSupport.clientConnectionId,
.where( isEqualToWhenPresent(filterMap.getClientEventConnectionId()))
ClientEventRecordDynamicSqlSupport.clientConnectionId, .and(
isEqualToWhenPresent(filterMap.getClientEventConnectionId())) ClientEventRecordDynamicSqlSupport.type,
.and( isEqualToWhenPresent(filterMap.getClientEventTypeId()))
ClientEventRecordDynamicSqlSupport.type, .and(
isEqualToWhenPresent(filterMap.getClientEventTypeId())) ClientEventRecordDynamicSqlSupport.type,
.and( SqlBuilder.isNotEqualTo(EventType.LAST_PING.id))
ClientEventRecordDynamicSqlSupport.type, .and(
SqlBuilder.isNotEqualTo(EventType.LAST_PING.id)) ClientEventRecordDynamicSqlSupport.clientTime,
.and( SqlBuilder.isGreaterThanOrEqualToWhenPresent(filterMap.getClientEventClientTimeFrom()))
ClientEventRecordDynamicSqlSupport.clientTime, .and(
SqlBuilder.isGreaterThanOrEqualToWhenPresent(filterMap.getClientEventClientTimeFrom())) ClientEventRecordDynamicSqlSupport.clientTime,
.and( SqlBuilder.isLessThanOrEqualToWhenPresent(filterMap.getClientEventClientTimeTo()))
ClientEventRecordDynamicSqlSupport.clientTime, .and(
SqlBuilder.isLessThanOrEqualToWhenPresent(filterMap.getClientEventClientTimeTo())) ClientEventRecordDynamicSqlSupport.serverTime,
.and( SqlBuilder.isGreaterThanOrEqualToWhenPresent(filterMap.getClientEventServerTimeFrom()))
ClientEventRecordDynamicSqlSupport.serverTime, .and(
SqlBuilder.isGreaterThanOrEqualToWhenPresent(filterMap.getClientEventServerTimeFrom())) ClientEventRecordDynamicSqlSupport.serverTime,
.and( SqlBuilder.isLessThanOrEqualToWhenPresent(filterMap.getClientEventServerTimeTo()))
ClientEventRecordDynamicSqlSupport.serverTime, .and(
SqlBuilder.isLessThanOrEqualToWhenPresent(filterMap.getClientEventServerTimeTo())) ClientEventRecordDynamicSqlSupport.text,
.and( SqlBuilder.isLikeWhenPresent(filterMap.getClientEventText()))
ClientEventRecordDynamicSqlSupport.text, .build()
SqlBuilder.isLikeWhenPresent(filterMap.getClientEventText())) .execute()
.build() .stream()
.execute() .map(ClientEventDAOImpl::toDomainModel)
.stream() .flatMap(DAOLoggingSupport::logAndSkipOnError)
.map(ClientEventDAOImpl::toDomainModel) .filter(predicate)
.flatMap(DAOLoggingSupport::logAndSkipOnError) .collect(Collectors.toList()));
.filter(predicate)
.collect(Collectors.toList());
});
} }
@Override @Override
@ -166,16 +163,14 @@ public class ClientEventDAOImpl implements ClientEventDAO {
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public Result<Collection<ClientEvent>> allOf(final Set<Long> pks) { public Result<Collection<ClientEvent>> allOf(final Set<Long> pks) {
return Result.tryCatch(() -> { return Result.tryCatch(() -> this.clientEventRecordMapper.selectByExample()
return this.clientEventRecordMapper.selectByExample() .where(ClientEventRecordDynamicSqlSupport.id, isIn(new ArrayList<>(pks)))
.where(ClientEventRecordDynamicSqlSupport.id, isIn(new ArrayList<>(pks))) .build()
.build() .execute()
.execute() .stream()
.stream() .map(ClientEventDAOImpl::toDomainModel)
.map(ClientEventDAOImpl::toDomainModel) .flatMap(DAOLoggingSupport::logAndSkipOnError)
.flatMap(DAOLoggingSupport::logAndSkipOnError) .collect(Collectors.toList()));
.collect(Collectors.toList());
});
} }
@Override @Override
@ -246,20 +241,17 @@ public class ClientEventDAOImpl implements ClientEventDAO {
} }
private static Result<ExtendedClientEvent> toDomainModelExtended(final ConnectionEventJoinRecord record) { private static Result<ExtendedClientEvent> toDomainModelExtended(final ConnectionEventJoinRecord record) {
return Result.tryCatch(() -> { return Result.tryCatch(() -> new ExtendedClientEvent(
record.institution_id,
return new ExtendedClientEvent( record.exam_id,
record.institution_id, record.exam_user_session_identifer,
record.exam_id, record.id,
record.exam_user_session_identifer, record.connection_id,
record.id, (record.type != null) ? EventType.byId(record.type) : EventType.UNKNOWN,
record.connection_id, record.client_time,
(record.type != null) ? EventType.byId(record.type) : EventType.UNKNOWN, record.server_time,
record.client_time, record.numeric_value,
record.server_time, record.text));
(record.numeric_value != null) ? record.numeric_value.doubleValue() : null,
record.text);
});
} }
} }

View file

@ -75,16 +75,14 @@ public class ConfigurationAttributeDAOImpl implements ConfigurationAttributeDAO
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public Result<Collection<ConfigurationAttribute>> allOf(final Set<Long> pks) { public Result<Collection<ConfigurationAttribute>> allOf(final Set<Long> pks) {
return Result.tryCatch(() -> { return Result.tryCatch(() -> this.configurationAttributeRecordMapper.selectByExample()
return this.configurationAttributeRecordMapper.selectByExample() .where(ConfigurationAttributeRecordDynamicSqlSupport.id, isIn(new ArrayList<>(pks)))
.where(ConfigurationAttributeRecordDynamicSqlSupport.id, isIn(new ArrayList<>(pks))) .build()
.build() .execute()
.execute() .stream()
.stream() .map(ConfigurationAttributeDAOImpl::toDomainModel)
.map(ConfigurationAttributeDAOImpl::toDomainModel) .flatMap(DAOLoggingSupport::logAndSkipOnError)
.flatMap(DAOLoggingSupport::logAndSkipOnError) .collect(Collectors.toList()));
.collect(Collectors.toList());
});
} }
@Override @Override

View file

@ -133,7 +133,7 @@ class ConfigurationDAOBatchService {
.build() .build()
.execute(); .execute();
if (count != null && count.longValue() > 0) { if (count != null && count > 0) {
throw new FieldValidationException("name", "configurationNode:name:exists"); throw new FieldValidationException("name", "configurationNode:name:exists");
} }
@ -231,7 +231,7 @@ class ConfigurationDAOBatchService {
oldValRec.getConfigurationAttributeId(), oldValRec.getConfigurationAttributeId(),
oldValRec.getListIndex(), oldValRec.getListIndex(),
oldValRec.getValue())) oldValRec.getValue()))
.forEach(newValRec -> this.batchConfigurationValueRecordMapper.insert(newValRec)); .forEach(this.batchConfigurationValueRecordMapper::insert);
return this.batchConfigurationRecordMapper return this.batchConfigurationRecordMapper
.selectByPrimaryKey(newFollowup.getId()); .selectByPrimaryKey(newFollowup.getId());
@ -337,7 +337,7 @@ class ConfigurationDAOBatchService {
historicValRec.getConfigurationAttributeId(), historicValRec.getConfigurationAttributeId(),
historicValRec.getListIndex(), historicValRec.getListIndex(),
historicValRec.getValue())) historicValRec.getValue()))
.forEach(newValRec -> this.batchConfigurationValueRecordMapper.insert(newValRec)); .forEach(this.batchConfigurationValueRecordMapper::insert);
return followup; return followup;
}) })
@ -361,7 +361,7 @@ class ConfigurationDAOBatchService {
.build() .build()
.execute(); .execute();
if (count != null && count.longValue() > 0) { if (count != null && count > 0) {
throw new FieldValidationException("name", "configurationNode:name:exists"); throw new FieldValidationException("name", "configurationNode:name:exists");
} }
@ -405,9 +405,7 @@ class ConfigurationDAOBatchService {
.execute(); .execute();
if (BooleanUtils.toBoolean(copyInfo.withHistory)) { if (BooleanUtils.toBoolean(copyInfo.withHistory)) {
configs configs.forEach(configRec -> this.copyConfiguration(
.stream()
.forEach(configRec -> this.copyConfiguration(
configRec.getInstitutionId(), configRec.getInstitutionId(),
configRec.getId(), configRec.getId(),
newNodeRec.getId())); newNodeRec.getId()));
@ -540,11 +538,11 @@ class ConfigurationDAOBatchService {
.build() .build()
.execute() .execute()
.stream() .stream()
.collect(Collectors.toMap(rec -> rec.getId(), Function.identity())); .collect(Collectors.toMap(ConfigurationAttributeRecord::getId, Function.identity()));
final List<Long> columnAttributeIds = attributeMap.values() final List<Long> columnAttributeIds = attributeMap.values()
.stream() .stream()
.map(a -> a.getId()) .map(ConfigurationAttributeRecord::getId)
.collect(Collectors.toList()); .collect(Collectors.toList());
// first delete all old values of this table // first delete all old values of this table
@ -705,15 +703,13 @@ class ConfigurationDAOBatchService {
.stream() .stream()
// filter child attributes of tables. No default value for tables. Use templates for that // filter child attributes of tables. No default value for tables. Use templates for that
.filter(ConfigurationDAOBatchService::filterChildAttribute) .filter(ConfigurationDAOBatchService::filterChildAttribute)
.forEach(attrRec -> { .forEach(attrRec -> this.batchConfigurationValueRecordMapper.insert(new ConfigurationValueRecord(
this.batchConfigurationValueRecordMapper.insert(new ConfigurationValueRecord( null,
null, configNode.institutionId,
configNode.institutionId, config.getId(),
config.getId(), attrRec.getId(),
attrRec.getId(), 0,
0, attrRec.getDefaultValue())));
attrRec.getDefaultValue()));
});
// override with template values if available // override with template values if available
if (configNode.templateId == null || configNode.templateId.equals(ConfigurationNode.DEFAULT_TEMPLATE_ID)) { if (configNode.templateId == null || configNode.templateId.equals(ConfigurationNode.DEFAULT_TEMPLATE_ID)) {
@ -747,7 +743,6 @@ class ConfigurationDAOBatchService {
configNode.institutionId, configNode.institutionId,
config.getId(), config.getId(),
attributeMap::get) attributeMap::get)
.stream()
.forEach(value -> { .forEach(value -> {
final ConfigurationValueRecord valueRec = new ConfigurationValueRecord( final ConfigurationValueRecord valueRec = new ConfigurationValueRecord(
null, null,
@ -768,8 +763,7 @@ class ConfigurationDAOBatchService {
final ConfigurationRecord config) { final ConfigurationRecord config) {
final List<ConfigurationValueRecord> templateValues = getTemplateValues(configNode); final List<ConfigurationValueRecord> templateValues = getTemplateValues(configNode);
templateValues.stream() templateValues.forEach(templateValue -> {
.forEach(templateValue -> {
final Long existingId = this.batchConfigurationValueRecordMapper final Long existingId = this.batchConfigurationValueRecordMapper
.selectIdsByExample() .selectIdsByExample()
.where( .where(

View file

@ -96,16 +96,14 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO {
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public Result<Collection<ExamConfigurationMap>> allOf(final Set<Long> pks) { public Result<Collection<ExamConfigurationMap>> allOf(final Set<Long> pks) {
return Result.tryCatch(() -> { return Result.tryCatch(() -> this.examConfigurationMapRecordMapper.selectByExample()
return this.examConfigurationMapRecordMapper.selectByExample() .where(ExamConfigurationMapRecordDynamicSqlSupport.id, isIn(new ArrayList<>(pks)))
.where(ExamConfigurationMapRecordDynamicSqlSupport.id, isIn(new ArrayList<>(pks))) .build()
.build() .execute()
.execute() .stream()
.stream() .map(this::toDomainModel)
.map(this::toDomainModel) .flatMap(DAOLoggingSupport::logAndSkipOnError)
.flatMap(DAOLoggingSupport::logAndSkipOnError) .collect(Collectors.toList()));
.collect(Collectors.toList());
});
} }
@Override @Override
@ -155,7 +153,7 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO {
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public Result<CharSequence> getConfigPasswortCipher(final Long examId, final Long configurationNodeId) { public Result<CharSequence> getConfigPasswordCipher(final Long examId, final Long configurationNodeId) {
return Result.tryCatch(() -> this.examConfigurationMapRecordMapper return Result.tryCatch(() -> this.examConfigurationMapRecordMapper
.selectByExample() .selectByExample()
.where( .where(
@ -185,7 +183,7 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO {
.build() .build()
.execute() .execute()
.stream() .stream()
.map(mapping -> mapping.getConfigurationNodeId()) .map(ExamConfigurationMapRecord::getConfigurationNodeId)
.collect(Utils.toSingleton())); .collect(Utils.toSingleton()));
} }
@ -203,7 +201,7 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO {
.build() .build()
.execute() .execute()
.stream() .stream()
.map(mapping -> mapping.getConfigurationNodeId()) .map(ExamConfigurationMapRecord::getConfigurationNodeId)
.collect(Utils.toSingleton())); .collect(Utils.toSingleton()));
} }
@ -218,7 +216,7 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO {
.build() .build()
.execute() .execute()
.stream() .stream()
.map(mapping -> mapping.getConfigurationNodeId()) .map(ExamConfigurationMapRecord::getConfigurationNodeId)
.collect(Collectors.toList())); .collect(Collectors.toList()));
} }
@ -313,36 +311,32 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO {
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public Result<Collection<Long>> getExamIdsForConfigNodeId(final Long configurationNodeId) { public Result<Collection<Long>> getExamIdsForConfigNodeId(final Long configurationNodeId) {
return Result.tryCatch(() -> { return Result.tryCatch(() -> this.examConfigurationMapRecordMapper.selectByExample()
return this.examConfigurationMapRecordMapper.selectByExample() .where(
.where( ExamConfigurationMapRecordDynamicSqlSupport.configurationNodeId,
ExamConfigurationMapRecordDynamicSqlSupport.configurationNodeId, isEqualTo(configurationNodeId))
isEqualTo(configurationNodeId)) .build()
.build() .execute()
.execute() .stream()
.stream() .map(ExamConfigurationMapRecord::getExamId)
.map(record -> record.getExamId()) .collect(Collectors.toList()));
.collect(Collectors.toList());
});
} }
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public Result<Collection<Long>> getExamIdsForConfigId(final Long configurationId) { public Result<Collection<Long>> getExamIdsForConfigId(final Long configurationId) {
return Result.tryCatch(() -> { return Result.tryCatch(() -> this.configurationNodeRecordMapper.selectIdsByExample()
return this.configurationNodeRecordMapper.selectIdsByExample() .leftJoin(ConfigurationRecordDynamicSqlSupport.configurationRecord)
.leftJoin(ConfigurationRecordDynamicSqlSupport.configurationRecord) .on(
.on( ConfigurationRecordDynamicSqlSupport.configurationNodeId,
ConfigurationRecordDynamicSqlSupport.configurationNodeId, equalTo(ConfigurationNodeRecordDynamicSqlSupport.id))
equalTo(ConfigurationNodeRecordDynamicSqlSupport.id)) .where(
.where( ConfigurationRecordDynamicSqlSupport.id,
ConfigurationRecordDynamicSqlSupport.id, isEqualTo(configurationId))
isEqualTo(configurationId)) .build()
.build() .execute()
.execute() .stream()
.stream() .collect(Utils.toSingleton()))
.collect(Utils.toSingleton());
})
.flatMap(this::getExamIdsForConfigNodeId); .flatMap(this::getExamIdsForConfigNodeId);
} }
@ -379,7 +373,7 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO {
(exam != null) ? exam.type : ExamType.UNDEFINED, (exam != null) ? exam.type : ExamType.UNDEFINED,
record.getConfigurationNodeId(), record.getConfigurationNodeId(),
record.getUserNames(), record.getUserNames(),
null, record.getEncryptSecret(),
null, null,
config.getName(), config.getName(),
config.getDescription(), config.getDescription(),
@ -419,64 +413,56 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO {
} }
private Result<Collection<EntityKey>> allIdsOfInstitution(final EntityKey institutionKey) { private Result<Collection<EntityKey>> allIdsOfInstitution(final EntityKey institutionKey) {
return Result.tryCatch(() -> { return Result.tryCatch(() -> this.examConfigurationMapRecordMapper.selectIdsByExample()
return this.examConfigurationMapRecordMapper.selectIdsByExample() .where(
.where( ExamConfigurationMapRecordDynamicSqlSupport.institutionId,
ExamConfigurationMapRecordDynamicSqlSupport.institutionId, isEqualTo(Long.valueOf(institutionKey.modelId)))
isEqualTo(Long.valueOf(institutionKey.modelId))) .build()
.build() .execute()
.execute() .stream()
.stream() .map(id -> new EntityKey(id, EntityType.EXAM_CONFIGURATION_MAP))
.map(id -> new EntityKey(id, EntityType.EXAM_CONFIGURATION_MAP)) .collect(Collectors.toList()));
.collect(Collectors.toList());
});
} }
private Result<Collection<EntityKey>> allIdsOfLmsSetup(final EntityKey lmsSetupKey) { private Result<Collection<EntityKey>> allIdsOfLmsSetup(final EntityKey lmsSetupKey) {
return Result.tryCatch(() -> { return Result.tryCatch(() -> this.examConfigurationMapRecordMapper.selectIdsByExample()
return this.examConfigurationMapRecordMapper.selectIdsByExample() .leftJoin(ExamRecordDynamicSqlSupport.examRecord)
.leftJoin(ExamRecordDynamicSqlSupport.examRecord) .on(
.on( ExamRecordDynamicSqlSupport.id,
ExamRecordDynamicSqlSupport.id, equalTo(ExamConfigurationMapRecordDynamicSqlSupport.examId))
equalTo(ExamConfigurationMapRecordDynamicSqlSupport.examId))
.where( .where(
ExamRecordDynamicSqlSupport.lmsSetupId, ExamRecordDynamicSqlSupport.lmsSetupId,
isEqualTo(Long.valueOf(lmsSetupKey.modelId))) isEqualTo(Long.valueOf(lmsSetupKey.modelId)))
.build() .build()
.execute() .execute()
.stream() .stream()
.map(id -> new EntityKey(id, EntityType.EXAM_CONFIGURATION_MAP)) .map(id -> new EntityKey(id, EntityType.EXAM_CONFIGURATION_MAP))
.collect(Collectors.toList()); .collect(Collectors.toList()));
});
} }
private Result<Collection<EntityKey>> allIdsOfExam(final EntityKey examKey) { private Result<Collection<EntityKey>> allIdsOfExam(final EntityKey examKey) {
return Result.tryCatch(() -> { return Result.tryCatch(() -> this.examConfigurationMapRecordMapper.selectIdsByExample()
return this.examConfigurationMapRecordMapper.selectIdsByExample() .where(
.where( ExamConfigurationMapRecordDynamicSqlSupport.examId,
ExamConfigurationMapRecordDynamicSqlSupport.examId, isEqualTo(Long.valueOf(examKey.modelId)))
isEqualTo(Long.valueOf(examKey.modelId))) .build()
.build() .execute()
.execute() .stream()
.stream() .map(id -> new EntityKey(id, EntityType.EXAM_CONFIGURATION_MAP))
.map(id -> new EntityKey(id, EntityType.EXAM_CONFIGURATION_MAP)) .collect(Collectors.toList()));
.collect(Collectors.toList());
});
} }
private Result<Collection<EntityKey>> allIdsOfConfig(final EntityKey configKey) { private Result<Collection<EntityKey>> allIdsOfConfig(final EntityKey configKey) {
return Result.tryCatch(() -> { return Result.tryCatch(() -> this.examConfigurationMapRecordMapper.selectIdsByExample()
return this.examConfigurationMapRecordMapper.selectIdsByExample() .where(
.where( ExamConfigurationMapRecordDynamicSqlSupport.configurationNodeId,
ExamConfigurationMapRecordDynamicSqlSupport.configurationNodeId, isEqualTo(Long.valueOf(configKey.modelId)))
isEqualTo(Long.valueOf(configKey.modelId))) .build()
.build() .execute()
.execute() .stream()
.stream() .map(id -> new EntityKey(id, EntityType.EXAM_CONFIGURATION_MAP))
.map(id -> new EntityKey(id, EntityType.EXAM_CONFIGURATION_MAP)) .collect(Collectors.toList()));
.collect(Collectors.toList());
});
} }
private String getEncryptionPassword(final ExamConfigurationMap examConfigurationMap) { private String getEncryptionPassword(final ExamConfigurationMap examConfigurationMap) {

View file

@ -14,7 +14,9 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -33,6 +35,7 @@ import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.EntityKey; 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;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.SebClientConfig.ConfigPurpose;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.gbl.util.Utils;
@ -110,53 +113,47 @@ public class SebClientConfigDAOImpl implements SebClientConfigDAO {
final FilterMap filterMap, final FilterMap filterMap,
final Predicate<SebClientConfig> predicate) { final Predicate<SebClientConfig> predicate) {
return Result.tryCatch(() -> { return Result.tryCatch(() -> this.sebClientConfigRecordMapper
.selectByExample()
return this.sebClientConfigRecordMapper .where(
.selectByExample() SebClientConfigRecordDynamicSqlSupport.institutionId,
.where( isEqualToWhenPresent(filterMap.getInstitutionId()))
SebClientConfigRecordDynamicSqlSupport.institutionId, .and(
isEqualToWhenPresent(filterMap.getInstitutionId())) SebClientConfigRecordDynamicSqlSupport.name,
.and( isLikeWhenPresent(filterMap.getName()))
SebClientConfigRecordDynamicSqlSupport.name, .and(
isLikeWhenPresent(filterMap.getName())) SebClientConfigRecordDynamicSqlSupport.date,
.and( isGreaterThanOrEqualToWhenPresent(filterMap.getSebClientConfigFromTime()))
SebClientConfigRecordDynamicSqlSupport.date, .and(
isGreaterThanOrEqualToWhenPresent(filterMap.getSebClientConfigFromTime())) SebClientConfigRecordDynamicSqlSupport.active,
.and( isEqualToWhenPresent(filterMap.getActiveAsInt()))
SebClientConfigRecordDynamicSqlSupport.active, .build()
isEqualToWhenPresent(filterMap.getActiveAsInt())) .execute()
.build() .stream()
.execute() .map(this::toDomainModel)
.stream() .flatMap(DAOLoggingSupport::logAndSkipOnError)
.map(this::toDomainModel) .filter(predicate)
.flatMap(DAOLoggingSupport::logAndSkipOnError) .collect(Collectors.toList()));
.filter(predicate)
.collect(Collectors.toList());
});
} }
@Override @Override
public Result<SebClientConfig> byClientName(final String clientName) { public Result<SebClientConfig> byClientName(final String clientName) {
return Result.tryCatch(() -> { return Result.tryCatch(() -> this.sebClientConfigRecordMapper
.selectByExample()
return this.sebClientConfigRecordMapper .where(
.selectByExample() SebClientConfigRecordDynamicSqlSupport.clientName,
.where( isEqualTo(clientName))
SebClientConfigRecordDynamicSqlSupport.clientName, .build()
isEqualTo(clientName)) .execute()
.build() .stream()
.execute() .map(this::toDomainModel)
.stream() .flatMap(DAOLoggingSupport::logAndSkipOnError)
.map(this::toDomainModel) .collect(Utils.toSingleton()));
.flatMap(DAOLoggingSupport::logAndSkipOnError)
.collect(Utils.toSingleton());
});
} }
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public Result<CharSequence> getConfigPasswortCipherByClientName(final String clientName) { public Result<CharSequence> getConfigPasswordCipherByClientName(final String clientName) {
return Result.tryCatch(() -> { return Result.tryCatch(() -> {
final SebClientConfigRecord record = this.sebClientConfigRecordMapper final SebClientConfigRecord record = this.sebClientConfigRecordMapper
@ -187,8 +184,7 @@ public class SebClientConfigDAOImpl implements SebClientConfigDAO {
.where(SebClientConfigRecordDynamicSqlSupport.id, isEqualTo(Long.valueOf(modelId))) .where(SebClientConfigRecordDynamicSqlSupport.id, isEqualTo(Long.valueOf(modelId)))
.and(SebClientConfigRecordDynamicSqlSupport.active, isEqualTo(BooleanUtils.toInteger(true))) .and(SebClientConfigRecordDynamicSqlSupport.active, isEqualTo(BooleanUtils.toInteger(true)))
.build() .build()
.execute() .execute() > 0;
.longValue() > 0;
} }
@Override @Override
@ -234,11 +230,7 @@ public class SebClientConfigDAOImpl implements SebClientConfigDAO {
this.sebClientConfigRecordMapper this.sebClientConfigRecordMapper
.insert(newRecord); .insert(newRecord);
this.additionalAttributesDAO.saveAdditionalAttribute( saveAdditionalAttributes(sebClientConfig, newRecord.getId());
EntityType.SEB_CLIENT_CONFIGURATION,
newRecord.getId(),
SebClientConfig.ATTR_FALLBACK_START_URL,
sebClientConfig.fallbackStartURL);
return newRecord; return newRecord;
}) })
@ -266,11 +258,7 @@ public class SebClientConfigDAOImpl implements SebClientConfigDAO {
this.sebClientConfigRecordMapper this.sebClientConfigRecordMapper
.updateByPrimaryKeySelective(newRecord); .updateByPrimaryKeySelective(newRecord);
this.additionalAttributesDAO.saveAdditionalAttribute( saveAdditionalAttributes(sebClientConfig, newRecord.getId());
EntityType.SEB_CLIENT_CONFIGURATION,
newRecord.getId(),
SebClientConfig.ATTR_FALLBACK_START_URL,
sebClientConfig.fallbackStartURL);
return this.sebClientConfigRecordMapper return this.sebClientConfigRecordMapper
.selectByPrimaryKey(sebClientConfig.id); .selectByPrimaryKey(sebClientConfig.id);
@ -300,17 +288,14 @@ public class SebClientConfigDAOImpl implements SebClientConfigDAO {
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public Result<Collection<SebClientConfig>> allOf(final Set<Long> pks) { public Result<Collection<SebClientConfig>> allOf(final Set<Long> pks) {
return Result.tryCatch(() -> { return Result.tryCatch(() -> this.sebClientConfigRecordMapper.selectByExample()
.where(SebClientConfigRecordDynamicSqlSupport.id, isIn(new ArrayList<>(pks)))
return this.sebClientConfigRecordMapper.selectByExample() .build()
.where(SebClientConfigRecordDynamicSqlSupport.id, isIn(new ArrayList<>(pks))) .execute()
.build() .stream()
.execute() .map(this::toDomainModel)
.stream() .flatMap(DAOLoggingSupport::logAndSkipOnError)
.map(this::toDomainModel) .collect(Collectors.toList()));
.flatMap(DAOLoggingSupport::logAndSkipOnError)
.collect(Collectors.toList());
});
} }
@Override @Override
@ -336,28 +321,24 @@ public class SebClientConfigDAOImpl implements SebClientConfigDAO {
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public Result<CharSequence> getConfigPasswortCipher(final String modelId) { public Result<CharSequence> getConfigPasswordCipher(final String modelId) {
return recordByModelId(modelId) return recordByModelId(modelId)
.map(rec -> rec.getEncryptSecret()); .map(SebClientConfigRecord::getEncryptSecret);
} }
private Result<Collection<EntityKey>> allIdsOfInstitution(final EntityKey institutionKey) { private Result<Collection<EntityKey>> allIdsOfInstitution(final EntityKey institutionKey) {
return Result.tryCatch(() -> { return Result.tryCatch(() -> this.sebClientConfigRecordMapper.selectIdsByExample()
return this.sebClientConfigRecordMapper.selectIdsByExample() .where(SebClientConfigRecordDynamicSqlSupport.institutionId,
.where(SebClientConfigRecordDynamicSqlSupport.institutionId, isEqualTo(Long.valueOf(institutionKey.modelId)))
isEqualTo(Long.valueOf(institutionKey.modelId))) .build()
.build() .execute()
.execute() .stream()
.stream() .map(id -> new EntityKey(id, EntityType.SEB_CLIENT_CONFIGURATION))
.map(id -> new EntityKey(id, EntityType.SEB_CLIENT_CONFIGURATION)) .collect(Collectors.toList()));
.collect(Collectors.toList());
});
} }
private Result<SebClientConfigRecord> recordByModelId(final String modelId) { private Result<SebClientConfigRecord> recordByModelId(final String modelId) {
return Result.tryCatch(() -> { return Result.tryCatch(() -> recordById(Long.parseLong(modelId)).getOrThrow());
return recordById(Long.parseLong(modelId)).getOrThrow();
});
} }
private Result<SebClientConfigRecord> recordById(final Long id) { private Result<SebClientConfigRecord> recordById(final Long id) {
@ -375,30 +356,57 @@ public class SebClientConfigDAOImpl implements SebClientConfigDAO {
} }
private Result<SebClientConfig> toDomainModel(final SebClientConfigRecord record) { private Result<SebClientConfig> toDomainModel(final SebClientConfigRecord record) {
final String fallbackURL = this.additionalAttributesDAO.getAdditionalAttributes(
EntityType.SEB_CLIENT_CONFIGURATION, Map<String, AdditionalAttributeRecord> additionalAttributes = this.additionalAttributesDAO
record.getId()) .getAdditionalAttributes(
EntityType.SEB_CLIENT_CONFIGURATION,
record.getId())
.getOrThrow() .getOrThrow()
.stream() .stream()
.filter(rec -> SebClientConfig.ATTR_FALLBACK_START_URL.equals(rec.getName())) .collect(Collectors.toMap(
.findFirst() AdditionalAttributeRecord::getName,
.map(AdditionalAttributeRecord::getValue) Function.identity()));
.orElse(null);
additionalAttributes.get(SebClientConfig.ATTR_CONFIG_PURPOSE);
return Result.tryCatch(() -> new SebClientConfig( return Result.tryCatch(() -> new SebClientConfig(
record.getId(), record.getId(),
record.getInstitutionId(), record.getInstitutionId(),
record.getName(), record.getName(),
fallbackURL, additionalAttributes.containsKey(SebClientConfig.ATTR_CONFIG_PURPOSE)
record.getDate(), ? ConfigPurpose.valueOf(additionalAttributes.get(SebClientConfig.ATTR_CONFIG_PURPOSE).getValue())
: ConfigPurpose.START_EXAM,
additionalAttributes.containsKey(SebClientConfig.ATTR_FALLBACK) &&
BooleanUtils.toBoolean(additionalAttributes.get(SebClientConfig.ATTR_FALLBACK).getValue()),
additionalAttributes.containsKey(SebClientConfig.ATTR_FALLBACK_START_URL)
? additionalAttributes.get(SebClientConfig.ATTR_FALLBACK_START_URL).getValue()
: null,
additionalAttributes.containsKey(SebClientConfig.ATTR_FALLBACK_TIMEOUT)
? Long.parseLong(additionalAttributes.get(SebClientConfig.ATTR_FALLBACK_TIMEOUT).getValue())
: null,
additionalAttributes.containsKey(SebClientConfig.ATTR_FALLBACK_ATTEMPTS)
? Short.parseShort(additionalAttributes.get(SebClientConfig.ATTR_FALLBACK_ATTEMPTS).getValue())
: null,
additionalAttributes.containsKey(SebClientConfig.ATTR_FALLBACK_ATTEMPT_INTERVAL)
? Short.parseShort(additionalAttributes.get(SebClientConfig.ATTR_FALLBACK_ATTEMPT_INTERVAL).getValue())
: null,
additionalAttributes.containsKey(SebClientConfig.ATTR_FALLBACK_PASSWORD)
? additionalAttributes.get(SebClientConfig.ATTR_FALLBACK_PASSWORD).getValue()
: null,
null, null,
additionalAttributes.containsKey(SebClientConfig.ATTR_QUIT_PASSWORD)
? additionalAttributes.get(SebClientConfig.ATTR_QUIT_PASSWORD).getValue()
: null,
null,
record.getDate(),
record.getEncryptSecret(),
null, null,
BooleanUtils.toBooleanObject(record.getActive()))); BooleanUtils.toBooleanObject(record.getActive())));
} }
private String getEncryptionPassword(final SebClientConfig sebClientConfig) { private String getEncryptionPassword(final SebClientConfig sebClientConfig) {
if (sebClientConfig.hasEncryptionSecret() && if (sebClientConfig.hasEncryptionSecret() &&
!sebClientConfig.encryptSecret.equals(sebClientConfig.confirmEncryptSecret)) { !sebClientConfig.encryptSecret.equals(sebClientConfig.encryptSecretConfirm)) {
throw new APIMessageException(ErrorMessage.PASSWORD_MISMATCH); throw new APIMessageException(ErrorMessage.PASSWORD_MISMATCH);
} }
@ -420,11 +428,99 @@ public class SebClientConfigDAOImpl implements SebClientConfigDAO {
.build() .build()
.execute(); .execute();
if (otherWithSameName != null && otherWithSameName.longValue() > 0) { if (otherWithSameName != null && otherWithSameName > 0) {
throw new APIMessageException(APIMessage.fieldValidationError( throw new APIMessageException(APIMessage.fieldValidationError(
Domain.SEB_CLIENT_CONFIGURATION.ATTR_NAME, Domain.SEB_CLIENT_CONFIGURATION.ATTR_NAME,
"clientconfig:name:name.notunique")); "clientconfig:name:name.notunique"));
} }
} }
private void saveAdditionalAttributes(SebClientConfig sebClientConfig, Long configId) {
this.additionalAttributesDAO.saveAdditionalAttribute(
EntityType.SEB_CLIENT_CONFIGURATION,
configId,
SebClientConfig.ATTR_CONFIG_PURPOSE,
(sebClientConfig.configPurpose != null)
? sebClientConfig.configPurpose.name()
: ConfigPurpose.START_EXAM.name());
this.additionalAttributesDAO.saveAdditionalAttribute(
EntityType.SEB_CLIENT_CONFIGURATION,
configId,
SebClientConfig.ATTR_FALLBACK,
String.valueOf(BooleanUtils.isTrue(sebClientConfig.fallback)));
if (BooleanUtils.isTrue(sebClientConfig.fallback)) {
this.additionalAttributesDAO.saveAdditionalAttribute(
EntityType.SEB_CLIENT_CONFIGURATION,
configId,
SebClientConfig.ATTR_FALLBACK_START_URL,
sebClientConfig.fallbackStartURL);
} else {
this.additionalAttributesDAO.delete(
configId,
SebClientConfig.ATTR_FALLBACK_START_URL);
}
if (BooleanUtils.isTrue(sebClientConfig.fallback)) {
this.additionalAttributesDAO.saveAdditionalAttribute(
EntityType.SEB_CLIENT_CONFIGURATION,
configId,
SebClientConfig.ATTR_FALLBACK_TIMEOUT,
sebClientConfig.fallbackTimeout.toString());
} else {
this.additionalAttributesDAO.delete(
configId,
SebClientConfig.ATTR_FALLBACK_TIMEOUT);
}
if (BooleanUtils.isTrue(sebClientConfig.fallback)) {
this.additionalAttributesDAO.saveAdditionalAttribute(
EntityType.SEB_CLIENT_CONFIGURATION,
configId,
SebClientConfig.ATTR_FALLBACK_ATTEMPTS,
sebClientConfig.fallbackAttempts.toString());
} else {
this.additionalAttributesDAO.delete(
configId,
SebClientConfig.ATTR_FALLBACK_ATTEMPTS);
}
if (BooleanUtils.isTrue(sebClientConfig.fallback)) {
this.additionalAttributesDAO.saveAdditionalAttribute(
EntityType.SEB_CLIENT_CONFIGURATION,
configId,
SebClientConfig.ATTR_FALLBACK_ATTEMPT_INTERVAL,
sebClientConfig.fallbackAttemptInterval.toString());
} else {
this.additionalAttributesDAO.delete(
configId,
SebClientConfig.ATTR_FALLBACK_ATTEMPT_INTERVAL);
}
if (BooleanUtils.isTrue(sebClientConfig.fallback) && StringUtils.isNotBlank(sebClientConfig.fallbackPassword)) {
this.additionalAttributesDAO.saveAdditionalAttribute(
EntityType.SEB_CLIENT_CONFIGURATION,
configId,
SebClientConfig.ATTR_FALLBACK_PASSWORD,
this.clientCredentialService.encrypt(sebClientConfig.fallbackPassword).toString());
} else {
this.additionalAttributesDAO.delete(
configId,
SebClientConfig.ATTR_FALLBACK_PASSWORD);
}
if (BooleanUtils.isTrue(sebClientConfig.fallback) && StringUtils.isNotBlank(sebClientConfig.quitPassword)) {
this.additionalAttributesDAO.saveAdditionalAttribute(
EntityType.SEB_CLIENT_CONFIGURATION,
configId,
SebClientConfig.ATTR_QUIT_PASSWORD,
this.clientCredentialService.encrypt(sebClientConfig.quitPassword).toString());
} else {
this.additionalAttributesDAO.delete(
configId,
SebClientConfig.ATTR_QUIT_PASSWORD);
}
}
} }

View file

@ -25,31 +25,7 @@ public interface ClientConfigService {
Logger log = LoggerFactory.getLogger(ClientConfigService.class); Logger log = LoggerFactory.getLogger(ClientConfigService.class);
public static final String EXAM_CLIENT_DETAILS_CACHE = "EXAM_CLIENT_DETAILS_CACHE"; String EXAM_CLIENT_DETAILS_CACHE = "EXAM_CLIENT_DETAILS_CACHE";
static String SEB_CLIENT_CONFIG_EXAMPLE_XML =
" <dict>\r\n" +
" <key>sebMode</key>\r\n" +
" <integer>1</integer>\r\n" +
" <key>sebConfigPurpose</key>\r\n" +
" <integer>1</integer>\r\n" +
" <key>sebServerFallback</key>\r\n" +
" <%s />\r\n" +
" %s" +
" <key>sebServerURL</key>\r\n" +
" <string>%s</string>\r\n" +
" <key>sebServerConfiguration</key>\r\n" +
" <dict>\r\n" +
" <key>institution</key>\r\n" +
" <string>%s</string>\r\n" +
" <key>clientName</key>\r\n" +
" <string>%s</string>\r\n" +
" <key>clientSecret</key>\r\n" +
" <string>%s</string>\r\n" +
" <key>apiDiscovery</key>\r\n" +
" <string>%s</string>\r\n" +
" </dict>\r\n" +
" </dict>\r\n";
/** Indicates if there is any SebClientConfiguration for a specified institution. /** Indicates if there is any SebClientConfiguration for a specified institution.
* *

View file

@ -8,29 +8,6 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl; package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Collections;
import java.util.UUID;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.stereotype.Service;
import ch.ethz.seb.sebserver.WebSecurityConfig; import ch.ethz.seb.sebserver.WebSecurityConfig;
import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.API.BulkActionType; import ch.ethz.seb.sebserver.gbl.api.API.BulkActionType;
@ -54,6 +31,29 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebConfigEncrypti
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ZipService; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ZipService;
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl.SebConfigEncryptionServiceImpl.EncryptionContext; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl.SebConfigEncryptionServiceImpl.EncryptionContext;
import ch.ethz.seb.sebserver.webservice.weblayer.oauth.WebserviceResourceConfiguration; import ch.ethz.seb.sebserver.webservice.weblayer.oauth.WebserviceResourceConfiguration;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Collections;
import java.util.UUID;
@Lazy @Lazy
@Service @Service
@ -62,6 +62,38 @@ public class ClientConfigServiceImpl implements ClientConfigService {
private static final Logger log = LoggerFactory.getLogger(ClientConfigServiceImpl.class); private static final Logger log = LoggerFactory.getLogger(ClientConfigServiceImpl.class);
private static final String SEB_CLIENT_CONFIG_TEMPLATE_XML =
" <dict>\r\n" +
" <key>sebMode</key>\r\n" +
" <integer>1</integer>\r\n" +
" <key>sebConfigPurpose</key>\r\n" +
" <integer>%s</integer>\r\n" +
" <key>sebServerFallback</key>\r\n" +
" <%s />\r\n" +
"%s" +
" <key>sebServerURL</key>\r\n" +
" <string>%s</string>\r\n" +
" <key>sebServerConfiguration</key>\r\n" +
" <dict>\r\n" +
" <key>institution</key>\r\n" +
" <string>%s</string>\r\n" +
" <key>clientName</key>\r\n" +
" <string>%s</string>\r\n" +
" <key>clientSecret</key>\r\n" +
" <string>%s</string>\r\n" +
" <key>apiDiscovery</key>\r\n" +
" <string>%s</string>\r\n" +
" </dict>\r\n" +
" </dict>\r\n";
private final static String SEB_CLIENT_CONFIG_INTEGER_TEMPLATE =
" <key>%s</key>\r\n" +
" <integer>%s</integer>\r\n";
private final static String SEB_CLIENT_CONFIG_STRING_TEMPLATE =
" <key>%s</key>\r\n" +
" <string>%s</string>\r\n";
private final InstitutionDAO institutionDAO; private final InstitutionDAO institutionDAO;
private final SebClientConfigDAO sebClientConfigDAO; private final SebClientConfigDAO sebClientConfigDAO;
private final ClientCredentialService clientCredentialService; private final ClientCredentialService clientCredentialService;
@ -109,6 +141,15 @@ public class ClientConfigServiceImpl implements ClientConfigService {
institutionId, institutionId,
institution.name + "_" + UUID.randomUUID(), institution.name + "_" + UUID.randomUUID(),
null, null,
false,
null,
null,
null,
null,
null,
null,
null,
null,
null, null,
null, null,
null, null,
@ -150,10 +191,10 @@ public class ClientConfigServiceImpl implements ClientConfigService {
.byModelId(modelId).getOrThrow(); .byModelId(modelId).getOrThrow();
final CharSequence encryptionPassword = this.sebClientConfigDAO final CharSequence encryptionPassword = this.sebClientConfigDAO
.getConfigPasswortCipher(config.getModelId()) .getConfigPasswordCipher(config.getModelId())
.getOr(null); .getOr(null);
final String plainTextConfig = getPlainXMLConfig(config); final String plainTextXMLContent = extractXMLContent(config);
PipedOutputStream pOut = null; PipedOutputStream pOut = null;
PipedInputStream pIn = null; PipedInputStream pIn = null;
@ -165,7 +206,7 @@ public class ClientConfigServiceImpl implements ClientConfigService {
Constants.XML_VERSION_HEADER + Constants.XML_VERSION_HEADER +
Constants.XML_DOCTYPE_HEADER + Constants.XML_DOCTYPE_HEADER +
Constants.XML_PLIST_START_V1 + Constants.XML_PLIST_START_V1 +
plainTextConfig + plainTextXMLContent +
Constants.XML_PLIST_END, Constants.XML_PLIST_END,
StandardCharsets.UTF_8.name()); StandardCharsets.UTF_8.name());
@ -206,39 +247,63 @@ public class ClientConfigServiceImpl implements ClientConfigService {
} }
} }
public String getPlainXMLConfig(final SebClientConfig config) { private String extractXMLContent(final SebClientConfig config) {
String fallbackAddition = "";
if (BooleanUtils.isTrue(config.fallback)) {
fallbackAddition += String.format(
SEB_CLIENT_CONFIG_STRING_TEMPLATE,
SebClientConfig.ATTR_FALLBACK_START_URL,
config.fallbackStartURL);
fallbackAddition += String.format(
SEB_CLIENT_CONFIG_INTEGER_TEMPLATE,
SebClientConfig.ATTR_FALLBACK_TIMEOUT,
config.fallbackTimeout);
fallbackAddition += String.format(
SEB_CLIENT_CONFIG_INTEGER_TEMPLATE,
SebClientConfig.ATTR_FALLBACK_ATTEMPTS,
config.fallbackAttempts);
fallbackAddition += String.format(
SEB_CLIENT_CONFIG_INTEGER_TEMPLATE,
SebClientConfig.ATTR_FALLBACK_ATTEMPT_INTERVAL,
config.fallbackAttemptInterval);
if (StringUtils.isNotBlank(config.fallbackPassword)) {
CharSequence decrypt = clientCredentialService.decrypt(config.fallbackPassword);
fallbackAddition += String.format(
SEB_CLIENT_CONFIG_STRING_TEMPLATE,
SebClientConfig.ATTR_FALLBACK_PASSWORD,
Utils.hash_SHA_256_Base_16(decrypt));
}
if (StringUtils.isNotBlank(config.quitPassword)) {
CharSequence decrypt = clientCredentialService.decrypt(config.quitPassword);
fallbackAddition += String.format(
SEB_CLIENT_CONFIG_STRING_TEMPLATE,
SebClientConfig.ATTR_QUIT_PASSWORD,
Utils.hash_SHA_256_Base_16(decrypt));
}
}
final ClientCredentials sebClientCredentials = this.sebClientConfigDAO final ClientCredentials sebClientCredentials = this.sebClientConfigDAO
.getSebClientCredentials(config.getModelId()) .getSebClientCredentials(config.getModelId())
.getOrThrow(); .getOrThrow();
final CharSequence plainClientId = sebClientCredentials.clientId; final CharSequence plainClientId = sebClientCredentials.clientId;
final CharSequence plainClientSecret = this.clientCredentialService final CharSequence plainClientSecret = this.clientCredentialService
.getPlainClientSecret(sebClientCredentials); .getPlainClientSecret(sebClientCredentials);
final String plainTextConfig = extractXML(
config,
plainClientId,
plainClientSecret);
return plainTextConfig;
}
private String extractXML(
final SebClientConfig config,
final CharSequence plainClientId,
final CharSequence plainClientSecret) {
final String plainTextConfig = String.format( final String plainTextConfig = String.format(
SEB_CLIENT_CONFIG_EXAMPLE_XML, SEB_CLIENT_CONFIG_TEMPLATE_XML,
config.configPurpose.ordinal(),
(StringUtils.isNotBlank(config.fallbackStartURL)) (StringUtils.isNotBlank(config.fallbackStartURL))
? "true" ? "true"
: "false", : "false",
(StringUtils.isNotBlank(config.fallbackStartURL)) fallbackAddition,
? "<key>startURL</key>\r\n <string>" + config.fallbackStartURL + "</string>\r\n"
: "",
this.webserviceInfo.getExternalServerURL(), this.webserviceInfo.getExternalServerURL(),
String.valueOf(config.institutionId), config.institutionId,
plainClientId, plainClientId,
plainClientSecret, plainClientSecret,
this.webserviceInfo.getDiscoveryEndpoint()); this.webserviceInfo.getDiscoveryEndpoint());
@ -259,7 +324,6 @@ public class ClientConfigServiceImpl implements ClientConfigService {
bulkAction.type == BulkActionType.HARD_DELETE) { bulkAction.type == BulkActionType.HARD_DELETE) {
bulkAction.extractKeys(EntityType.SEB_CLIENT_CONFIGURATION) bulkAction.extractKeys(EntityType.SEB_CLIENT_CONFIGURATION)
.stream()
.forEach(this::flushClientConfigData); .forEach(this::flushClientConfigData);
} }
@ -275,8 +339,7 @@ public class ClientConfigServiceImpl implements ClientConfigService {
.clientIdAsString(); .clientIdAsString();
final Collection<OAuth2AccessToken> tokensByClientId = this.tokenStore.findTokensByClientId(clientName); final Collection<OAuth2AccessToken> tokensByClientId = this.tokenStore.findTokensByClientId(clientName);
tokensByClientId.stream() tokensByClientId.forEach(this.tokenStore::removeAccessToken);
.forEach(token -> this.tokenStore.removeAccessToken(token));
} catch (final Exception e) { } catch (final Exception e) {
log.error("Unexpected error while trying to flush ClientConfig data for {}", key, e); log.error("Unexpected error while trying to flush ClientConfig data for {}", key, e);
} }
@ -307,7 +370,7 @@ public class ClientConfigServiceImpl implements ClientConfigService {
* @param clientId the clientId/clientName * @param clientId the clientId/clientName
* @return encoded clientSecret for that SebClientConfiguration with clientId or null of not existing */ * @return encoded clientSecret for that SebClientConfiguration with clientId or null of not existing */
private Result<CharSequence> getEncodedClientConfigSecret(final String clientId) { private Result<CharSequence> getEncodedClientConfigSecret(final String clientId) {
return this.sebClientConfigDAO.getConfigPasswortCipherByClientName(clientId) return this.sebClientConfigDAO.getConfigPasswordCipherByClientName(clientId)
.map(cipher -> this.clientPasswordEncoder.encode(this.clientCredentialService.decrypt(cipher))); .map(cipher -> this.clientPasswordEncoder.encode(this.clientCredentialService.decrypt(cipher)));
} }

View file

@ -27,6 +27,7 @@ import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory; import javax.xml.parsers.SAXParserFactory;
import ch.ethz.seb.sebserver.gbl.util.Cryptor;
import org.apache.tomcat.util.http.fileupload.IOUtils; import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -72,19 +73,22 @@ public class ExamConfigIO {
private final ConfigurationDAO configurationDAO; private final ConfigurationDAO configurationDAO;
private final AttributeValueConverterService attributeValueConverterService; private final AttributeValueConverterService attributeValueConverterService;
private final ZipService zipService; private final ZipService zipService;
private final Cryptor cryptor;
protected ExamConfigIO( protected ExamConfigIO(
final ConfigurationAttributeDAO configurationAttributeDAO, final ConfigurationAttributeDAO configurationAttributeDAO,
final ConfigurationValueDAO configurationValueDAO, final ConfigurationValueDAO configurationValueDAO,
final ConfigurationDAO configurationDAO, final ConfigurationDAO configurationDAO,
final AttributeValueConverterService attributeValueConverterService, final AttributeValueConverterService attributeValueConverterService,
final ZipService zipService) { final ZipService zipService,
final Cryptor cryptor) {
this.configurationAttributeDAO = configurationAttributeDAO; this.configurationAttributeDAO = configurationAttributeDAO;
this.configurationValueDAO = configurationValueDAO; this.configurationValueDAO = configurationValueDAO;
this.configurationDAO = configurationDAO; this.configurationDAO = configurationDAO;
this.attributeValueConverterService = attributeValueConverterService; this.attributeValueConverterService = attributeValueConverterService;
this.zipService = zipService; this.zipService = zipService;
this.cryptor = cryptor;
} }
@Async(AsyncServiceSpringConfig.EXECUTOR_BEAN_NAME) @Async(AsyncServiceSpringConfig.EXECUTOR_BEAN_NAME)
@ -188,6 +192,7 @@ public class ExamConfigIO {
// the SAX handler with a ConfigValue sink that saves the values to DB // the SAX handler with a ConfigValue sink that saves the values to DB
// and a attribute-name/id mapping function with pre-created mapping // and a attribute-name/id mapping function with pre-created mapping
final ExamConfigXMLParser examConfigImportHandler = new ExamConfigXMLParser( final ExamConfigXMLParser examConfigImportHandler = new ExamConfigXMLParser(
cryptor,
institutionId, institutionId,
configurationId, configurationId,
value -> this.configurationValueDAO value -> this.configurationValueDAO

View file

@ -174,7 +174,7 @@ public class ExamConfigServiceImpl implements ExamConfigService {
final Long configurationNodeId) { final Long configurationNodeId) {
final CharSequence passwordCipher = this.examConfigurationMapDAO final CharSequence passwordCipher = this.examConfigurationMapDAO
.getConfigPasswortCipher(examId, configurationNodeId) .getConfigPasswordCipher(examId, configurationNodeId)
.getOr(null); .getOr(null);
if (StringUtils.isNotBlank(passwordCipher)) { if (StringUtils.isNotBlank(passwordCipher)) {

View file

@ -15,6 +15,7 @@ import java.util.Stack;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import ch.ethz.seb.sebserver.gbl.util.Cryptor;
import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -85,6 +86,14 @@ public class ExamConfigXMLParser extends DefaultHandler {
private static final Set<String> KNOWN_INLINE_TABLES = new HashSet<>(Arrays.asList( private static final Set<String> KNOWN_INLINE_TABLES = new HashSet<>(Arrays.asList(
"arguments")); "arguments"));
public static final Set<String> PASSWORD_ATTRIBUTES = new HashSet<>(Arrays.asList(
"hashedQuitPassword",
"hashedAdminPassword"
));
public static final String IMPORTED_PASSWORD_MARKER = "_IMPORTED_PASSWORD";
private final Cryptor cryptor;
private final Consumer<ConfigurationValue> valueConsumer; private final Consumer<ConfigurationValue> valueConsumer;
private final Function<String, ConfigurationAttribute> attributeResolver; private final Function<String, ConfigurationAttribute> attributeResolver;
private final Long institutionId; private final Long institutionId;
@ -96,12 +105,14 @@ public class ExamConfigXMLParser extends DefaultHandler {
private Boolean createNewDesktop = null; private Boolean createNewDesktop = null;
public ExamConfigXMLParser( public ExamConfigXMLParser(
final Cryptor cryptor,
final Long institutionId, final Long institutionId,
final Long configId, final Long configId,
final Consumer<ConfigurationValue> valueConsumer, final Consumer<ConfigurationValue> valueConsumer,
final Function<String, ConfigurationAttribute> attributeResolver) { final Function<String, ConfigurationAttribute> attributeResolver) {
super(); super();
this.cryptor = cryptor;
this.valueConsumer = valueConsumer; this.valueConsumer = valueConsumer;
this.attributeResolver = attributeResolver; this.attributeResolver = attributeResolver;
this.institutionId = institutionId; this.institutionId = institutionId;
@ -430,6 +441,21 @@ public class ExamConfigXMLParser extends DefaultHandler {
return null; return null;
} }
if (PASSWORD_ATTRIBUTES.contains(name)) {
// NOTE this is a special case, if a hashed password is imported it is not possible to view this password
// later in plain text to the administrator. Therefore this password hash is marked here as imported
// and internally encrypted as usual. So the password will be decrypted while viewing and is recognizable
// for the export so that the password can be decrypted with internal encryption and then, if import
// marked, just send to the export by removing the marker and do not rehash the already hashed password.
return new ConfigurationValue(
null,
this.institutionId,
this.configId,
attribute.id,
listIndex,
StringUtils.isNotBlank(value) ? cryptor.encrypt(value + IMPORTED_PASSWORD_MARKER).toString() : value);
}
return new ConfigurationValue( return new ConfigurationValue(
null, null,
this.institutionId, this.institutionId,

View file

@ -16,6 +16,8 @@ import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
import ch.ethz.seb.sebserver.webservice.servicelayer.client.ClientCredentialService;
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl.ExamConfigXMLParser;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -40,12 +42,20 @@ public class StringConverter implements AttributeValueConverter {
AttributeType.DECIMAL, AttributeType.DECIMAL,
AttributeType.COMBO_SELECTION))); AttributeType.COMBO_SELECTION)));
private static final String XML_TEMPLATE = "<key>%s</key><string>%s</string>"; private static final String XML_TEMPLATE = "<key>%s</key><string>%s</string>";
private static final String XML_TEMPLATE_EMPTY = "<key>%s</key><string />"; private static final String XML_TEMPLATE_EMPTY = "<key>%s</key><string />";
private static final String JSON_TEMPLATE = "\"%s\":\"%s\""; private static final String JSON_TEMPLATE = "\"%s\":\"%s\"";
private static final String JSON_TEMPLATE_EMPTY = "\"%s\":\"\""; private static final String JSON_TEMPLATE_EMPTY = "\"%s\":\"\"";
private final ClientCredentialService clientCredentialService;
public StringConverter(final ClientCredentialService clientCredentialService) {
this.clientCredentialService = clientCredentialService;
}
@Override @Override
public Set<AttributeType> types() { public Set<AttributeType> types() {
return SUPPORTED_TYPES; return SUPPORTED_TYPES;
@ -85,15 +95,38 @@ public class StringConverter implements AttributeValueConverter {
final String emptyTemplate) throws IOException { final String emptyTemplate) throws IOException {
final String val = (value != null && value.value != null) ? value.value : attribute.getDefaultValue(); final String val = (value != null && value.value != null) ? value.value : attribute.getDefaultValue();
String realName = AttributeValueConverter.extractName(attribute);
if (StringUtils.isNotBlank(val)) { if (StringUtils.isNotBlank(val)) {
out.write(Utils.toByteArray(String.format( out.write(Utils.toByteArray(String.format(
template, template,
AttributeValueConverter.extractName(attribute), realName,
val))); convertPassword(realName, val))));
} else { } else {
out.write(Utils.toByteArray(String.format( out.write(Utils.toByteArray(String.format(
emptyTemplate, emptyTemplate,
AttributeValueConverter.extractName(attribute)))); realName)));
}
}
private CharSequence convertPassword(
final String attributeName,
final String value) {
if (StringUtils.isBlank(value)) {
return value;
}
if (!ExamConfigXMLParser.PASSWORD_ATTRIBUTES.contains(attributeName)) {
return value;
}
// decrypt internally encrypted password and hash it for export
// NOTE: see special case description in ExamConfigXMLParser.createConfigurationValue
String plainText = this.clientCredentialService.decrypt(value).toString();
if (plainText.endsWith(ExamConfigXMLParser.IMPORTED_PASSWORD_MARKER)) {
return plainText.replace(ExamConfigXMLParser.IMPORTED_PASSWORD_MARKER, StringUtils.EMPTY);
} else {
return Utils.hash_SHA_256_Base_16(plainText);
} }
} }

View file

@ -17,6 +17,7 @@ import java.util.function.Function;
import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory; import javax.xml.parsers.SAXParserFactory;
import ch.ethz.seb.sebserver.gbl.util.Cryptor;
import org.apache.tomcat.util.http.fileupload.IOUtils; import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -37,6 +38,12 @@ public class XMLAttributeLoader {
private static final Logger log = LoggerFactory.getLogger(XMLAttributeLoader.class); private static final Logger log = LoggerFactory.getLogger(XMLAttributeLoader.class);
private final Cryptor cryptor;
public XMLAttributeLoader(Cryptor cryptor) {
this.cryptor = cryptor;
}
public Collection<ConfigurationValue> loadFromXML( public Collection<ConfigurationValue> loadFromXML(
final Long institutionId, final Long institutionId,
final Long configurationId, final Long configurationId,
@ -57,6 +64,7 @@ public class XMLAttributeLoader {
final Collection<ConfigurationValue> values = new ArrayList<>(); final Collection<ConfigurationValue> values = new ArrayList<>();
final ExamConfigXMLParser examConfigImportHandler = new ExamConfigXMLParser( final ExamConfigXMLParser examConfigImportHandler = new ExamConfigXMLParser(
cryptor,
institutionId, institutionId,
configurationId, configurationId,
values::add, values::add,

View file

@ -270,13 +270,17 @@ public class ConfigurationNodeController extends EntityController<ConfigurationN
final Result<Configuration> doImport = doImport(password, request, followup); final Result<Configuration> doImport = doImport(password, request, followup);
if (doImport.hasError()) { if (doImport.hasError()) {
// rollback of the new configuration // rollback if the new configuration
this.configurationNodeDAO.delete(new HashSet<>(Arrays.asList(new EntityKey( this.configurationNodeDAO.delete(new HashSet<>(Arrays.asList(new EntityKey(
followup.configurationNodeId, followup.configurationNodeId,
EntityType.CONFIGURATION_NODE)))); EntityType.CONFIGURATION_NODE))));
} }
return doImport Configuration config = doImport
.getOrThrow();
return this.configurationDAO
.saveToHistory(config.configurationNodeId)
.getOrThrow(); .getOrThrow();
} }

View file

@ -11,11 +11,15 @@ package ch.ethz.seb.sebserver.webservice.weblayer.api;
import java.io.IOException; import java.io.IOException;
import java.io.PipedInputStream; import java.io.PipedInputStream;
import java.io.PipedOutputStream; import java.io.PipedOutputStream;
import java.util.ArrayList;
import java.util.Collection;
import javax.servlet.ServletOutputStream; import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.joda.time.DateTimeZone; import org.joda.time.DateTimeZone;
import org.mybatis.dynamic.sql.SqlTable; import org.mybatis.dynamic.sql.SqlTable;
@ -107,14 +111,6 @@ public class SebClientConfigController extends ActivatableEntityController<SebCl
outputStream.flush(); outputStream.flush();
outputStream.close(); outputStream.close();
} }
// final StreamingResponseBody stream = out -> {
// this.sebClientConfigService.exportSebClientConfiguration(
// out,
// modelId);
// };
//
// return new ResponseEntity<>(stream, HttpStatus.OK);
} }
@Override @Override
@ -152,14 +148,67 @@ public class SebClientConfigController extends ActivatableEntityController<SebCl
} }
private SebClientConfig checkPasswordMatch(final SebClientConfig entity) { private SebClientConfig checkPasswordMatch(final SebClientConfig entity) {
if (entity.hasEncryptionSecret() && !entity.encryptSecret.equals(entity.confirmEncryptSecret)) { Collection<APIMessage> errors = new ArrayList<>();
throw new APIMessageException(APIMessage.fieldValidationError( if (entity.hasEncryptionSecret() && !entity.encryptSecret.equals(entity.encryptSecretConfirm)) {
errors.add(APIMessage.fieldValidationError(
new FieldError( new FieldError(
Domain.SEB_CLIENT_CONFIGURATION.TYPE_NAME, Domain.SEB_CLIENT_CONFIGURATION.TYPE_NAME,
PasswordChange.ATTR_NAME_PASSWORD, PasswordChange.ATTR_NAME_PASSWORD,
"clientConfig:confirm_encrypt_secret:password.mismatch"))); "clientConfig:confirm_encrypt_secret:password.mismatch")));
} }
if (entity.hasFallbackPassword() && !entity.fallbackPassword.equals(entity.fallbackPasswordConfirm)) {
errors.add(APIMessage.fieldValidationError(
new FieldError(
Domain.SEB_CLIENT_CONFIGURATION.TYPE_NAME,
SebClientConfig.ATTR_FALLBACK_PASSWORD_CONFIRM,
"clientConfig:sebServerFallbackPasswordHashConfirm:password.mismatch")));
}
if (entity.hasQuitPassword() && !entity.quitPassword.equals(entity.quitPasswordConfirm)) {
errors.add(APIMessage.fieldValidationError(
new FieldError(
Domain.SEB_CLIENT_CONFIGURATION.TYPE_NAME,
SebClientConfig.ATTR_QUIT_PASSWORD_CONFIRM,
"clientConfig:hashedQuitPasswordConfirm:password.mismatch")));
}
if (BooleanUtils.isTrue(entity.fallback) && StringUtils.isBlank(entity.fallbackStartURL)) {
errors.add(APIMessage.fieldValidationError(
new FieldError(
Domain.SEB_CLIENT_CONFIGURATION.TYPE_NAME,
SebClientConfig.ATTR_FALLBACK_START_URL,
"clientConfig:startURL:notNull")));
}
if (BooleanUtils.isTrue(entity.fallback) && entity.fallbackTimeout == null) {
errors.add(APIMessage.fieldValidationError(
new FieldError(
Domain.SEB_CLIENT_CONFIGURATION.TYPE_NAME,
SebClientConfig.ATTR_FALLBACK_TIMEOUT,
"clientConfig:sebServerFallbackTimeout:notNull")));
}
if (BooleanUtils.isTrue(entity.fallback) && entity.fallbackAttempts == null) {
errors.add(APIMessage.fieldValidationError(
new FieldError(
Domain.SEB_CLIENT_CONFIGURATION.TYPE_NAME,
SebClientConfig.ATTR_FALLBACK_ATTEMPTS,
"clientConfig:sebServerFallbackAttempts:notNull")));
}
if (BooleanUtils.isTrue(entity.fallback) && entity.fallbackAttemptInterval == null) {
errors.add(APIMessage.fieldValidationError(
new FieldError(
Domain.SEB_CLIENT_CONFIGURATION.TYPE_NAME,
SebClientConfig.ATTR_FALLBACK_ATTEMPT_INTERVAL,
"clientConfig:sebServerFallbackAttemptInterval:notNull")));
}
if (!errors.isEmpty()) {
throw new APIMessage.APIMessageException(errors);
}
return entity; return entity;
} }

View file

@ -23,6 +23,8 @@ sebserver.overall.action.goAwayFromEditPageConfirm=Are you sure you want to leav
sebserver.overall.action.category.varia= sebserver.overall.action.category.varia=
sebserver.overall.action.category.filter= sebserver.overall.action.category.filter=
sebserver.overall.action.showPassword.tooltip=Show / hide password in plain text.
sebserver.overall.status.active=Active sebserver.overall.status.active=Active
sebserver.overall.status.inactive=Inactive sebserver.overall.status.inactive=Inactive
sebserver.overall.status.all=All sebserver.overall.status.all=All
@ -567,7 +569,7 @@ sebserver.exam.indicator.thresholds.list.add=Add a new threshold
sebserver.exam.indicator.thresholds.list.remove=Delete this threshold sebserver.exam.indicator.thresholds.list.remove=Delete this threshold
################################ ################################
# SEB Client Configuration # SEB client configuration
################################ ################################
sebserver.sebconfig.activity.name=SEB Configuration sebserver.sebconfig.activity.name=SEB Configuration
@ -576,7 +578,7 @@ sebserver.clientconfig.action.list=Client Configuration
sebserver.clientconfig.action.export=Export sebserver.clientconfig.action.export=Export
sebserver.clientconfig.list.empty=There is currently no SEB-Client configuration available. Please create a new one sebserver.clientconfig.list.empty=There is currently no SEB-Client configuration available. Please create a new one
sebserver.clientconfig.list.title=SEB Client Configurations sebserver.clientconfig.list.title=SEB client configurations
sebserver.clientconfig.list.actions= sebserver.clientconfig.list.actions=
sebserver.clientconfig.list.column.institution=Institution sebserver.clientconfig.list.column.institution=Institution
sebserver.clientconfig.list.column.institution.tooltip=The institution of the SEB client configuration.<br/><br/>Use the filter above to specify the institution.<br/>{0} sebserver.clientconfig.list.column.institution.tooltip=The institution of the SEB client configuration.<br/><br/>Use the filter above to specify the institution.<br/>{0}
@ -587,24 +589,31 @@ sebserver.clientconfig.list.column.date.tooltip=The date when the SEB client con
sebserver.clientconfig.list.column.active=Active sebserver.clientconfig.list.column.active=Active
sebserver.clientconfig.list.column.active.tooltip=The activity of SEB client configuration.<br/><br/>Use the filter above to specify the activity.<br/>{0} sebserver.clientconfig.list.column.active.tooltip=The activity of SEB client configuration.<br/><br/>Use the filter above to specify the activity.<br/>{0}
sebserver.clientconfig.info.pleaseSelect=Please select first a Client Configuration from the list sebserver.clientconfig.info.pleaseSelect=Please select first a Client Configuration from the list
sebserver.clientconfig.list.action.no.modify.privilege=No Access: A SEB Client Configuration from other institution cannot be modified. sebserver.clientconfig.list.action.no.modify.privilege=No Access: A SEB client configuration from other institution cannot be modified.
sebserver.clientconfig.form.title.new=Add Client Configuration sebserver.clientconfig.form.title.new=Add Client Configuration
sebserver.clientconfig.form.title=SEB Client Configuration sebserver.clientconfig.form.title=SEB client configuration
sebserver.clientconfig.form.name=Name sebserver.clientconfig.form.name=Name
sebserver.clientconfig.form.name.tooltip=The name of the SEB Client Configuration.<br/>Can be any name that not already exists for another SEB Client Configuration sebserver.clientconfig.form.name.tooltip=The name of the SEB client configuration.<br/>Can be any name that not already exists for another SEB client configuration
sebserver.clientconfig.form.fallback=With Fallback sebserver.clientconfig.form.fallback=With Fallback
sebserver.clientconfig.form.fallback.tooltip=Indicates whether this SEB Client Configuration has a fallback definition or not sebserver.clientconfig.form.fallback.tooltip=Indicates whether this SEB client configuration has a fallback definition or not
sebserver.clientconfig.form.fallback-url=Fallback Start URL sebserver.clientconfig.form.fallback-url=Fallback Start URL
sebserver.clientconfig.form.fallback-url.tooltip=A fallback URL that tells the SEB where to go when the SEB Server service is unavailable. sebserver.clientconfig.form.fallback-url.tooltip=A fallback URL that tells the SEB where to go when the SEB Server service is unavailable.
sebserver.clientconfig.form.sebServerFallbackTimeout=Fallback Timeout sebserver.clientconfig.form.sebServerFallbackTimeout=Fallback Timeout
sebserver.clientconfig.form.sebServerFallbackTimeout.tooltip=Defines the fallback timeout for the SEB Client in milli-seconds. sebserver.clientconfig.form.sebServerFallbackTimeout.tooltip=Defines the fallback timeout for the SEB client in milli-seconds.
sebserver.clientconfig.form.sebServerFallbackAttempts=Fallback Attempts sebserver.clientconfig.form.sebServerFallbackAttempts=Connection Attempts
sebserver.clientconfig.form.sebServerFallbackAttempts.tooltip=The number of connection attempts a SEB Client is trying before switching to fallback case. sebserver.clientconfig.form.sebServerFallbackAttempts.tooltip=The number of connection attempts a SEB client is trying before switching to fallback case.
sebserver.clientconfig.form.sebServerFallbackAttemptInterval=Attempt Interval sebserver.clientconfig.form.sebServerFallbackAttemptInterval=Interval
sebserver.clientconfig.form.sebServerFallbackAttemptInterval.tooltip=The interval (in milli-seconds) between connection attempts a SEB Client shall use. sebserver.clientconfig.form.sebServerFallbackAttemptInterval.tooltip=The interval (in milli-seconds) between connection attempts a SEB client shall use.
sebserver.clientconfig.form.sebServerFallbackPasswordHash=Fallback Password sebserver.clientconfig.form.sebServerFallbackPasswordHash=Fallback Password
sebserver.clientconfig.form.sebServerFallbackPasswordHash.tooltip=A password if set, a SEB Client user must give before the SEB Client starts the fallback procedure. sebserver.clientconfig.form.sebServerFallbackPasswordHash.tooltip=A password if set a SEB Client user must provide before the SEB client starts the fallback procedure.
sebserver.clientconfig.form.sebServerFallbackPasswordHash.confirm=Confirm Fallback Password
sebserver.clientconfig.form.sebServerFallbackPasswordHash.tooltip.confirm=Please confirm the fallback password
sebserver.clientconfig.form.hashedQuitPassword=Quit Password
sebserver.clientconfig.form.hashedQuitPassword.tooltip=A password if set a SEB client user must provide to be able to quit the SEB client.
sebserver.clientconfig.form.hashedQuitPassword.confirm=Confirm Quit Password
sebserver.clientconfig.form.hashedQuitPassword.tooltip.confirm=Please confirm the quit password
sebserver.clientconfig.form.date=Creation Date sebserver.clientconfig.form.date=Creation Date
sebserver.clientconfig.form.date.tooltip=The date when the SEB client configuration was first created. sebserver.clientconfig.form.date.tooltip=The date when the SEB client configuration was first created.
sebserver.clientconfig.form.encryptSecret=Configuration Password sebserver.clientconfig.form.encryptSecret=Configuration Password
@ -614,6 +623,11 @@ sebserver.clientconfig.form.encryptSecret.confirm.tooltip=Please retype the give
sebserver.clientconfig.form.sebConfigPurpose=Configuration Purpose sebserver.clientconfig.form.sebConfigPurpose=Configuration Purpose
sebserver.clientconfig.form.sebConfigPurpose.tooltip=This indicates whether this client configuration shall be used to configure the SEB Client or to start an exam sebserver.clientconfig.form.sebConfigPurpose.tooltip=This indicates whether this client configuration shall be used to configure the SEB Client or to start an exam
sebserver.clientconfig.config.purpose.START_EXAM=Starting an Exam
sebserver.clientconfig.config.purpose.START_EXAM.tooltip=If the SEB client configuration is loaded via a SEB-Link, the local configuration will not be overwritten.
sebserver.clientconfig.config.purpose.CONFIGURE_CLIENT=Configure a Client
sebserver.clientconfig.config.purpose.CONFIGURE_CLIENT.tooltip=If the SEB client configuration is loaded via a SEB-Link, the local configuration will be overwritten by this configuration.
sebserver.clientconfig.action.list.new=Add Configuration sebserver.clientconfig.action.list.new=Add Configuration
sebserver.clientconfig.action.list.view=View Configuration sebserver.clientconfig.action.list.view=View Configuration
sebserver.clientconfig.action.list.modify=Edit Configuration sebserver.clientconfig.action.list.modify=Edit Configuration

View file

@ -320,6 +320,26 @@ Text[MULTI][BORDER]:read-only.inputreadonly {
box-shadow: none; box-shadow: none;
} }
Text.pwdplain,
Text:disabled.pwdplain,
Text:read-only.pwdplain,
Text[BORDER]:disabled.pwdplain,
Text[BORDER]:read-only.pwdplain {
font: 12px Arial, Helvetica, sans-serif;
border: 1px solid #aaaaaa;
border-radius: 0;
padding: 3px 10px 3px 10px;
color: #aaaaaa;
background-repeat: repeat;
background-position: left top;
background-color: #ffffff;
background-image: none;
text-shadow: none;
box-shadow: none;
}
Text:disabled, Text:disabled,
Text:read-only, Text:read-only,
Text[MULTI]:disabled, Text[MULTI]:disabled,

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 B

View file

@ -34,6 +34,7 @@ public class ClientConfigTest extends GuiIntegrationTest {
final Result<SebClientConfig> call = restService.getBuilder(NewClientConfig.class) final Result<SebClientConfig> call = restService.getBuilder(NewClientConfig.class)
.withQueryParam(Domain.SEB_CLIENT_CONFIGURATION.ATTR_NAME, "new client config") .withQueryParam(Domain.SEB_CLIENT_CONFIGURATION.ATTR_NAME, "new client config")
.withFormParam(SebClientConfig.ATTR_CONFIG_PURPOSE, SebClientConfig.ConfigPurpose.START_EXAM.name())
.call(); .call();
assertNotNull(call); assertNotNull(call);
@ -50,6 +51,7 @@ public class ClientConfigTest extends GuiIntegrationTest {
final Result<SebClientConfig> call = restService.getBuilder(NewClientConfig.class) final Result<SebClientConfig> call = restService.getBuilder(NewClientConfig.class)
.withFormParam(Domain.SEB_CLIENT_CONFIGURATION.ATTR_NAME, "new client config") .withFormParam(Domain.SEB_CLIENT_CONFIGURATION.ATTR_NAME, "new client config")
.withFormParam(SebClientConfig.ATTR_CONFIG_PURPOSE, SebClientConfig.ConfigPurpose.START_EXAM.name())
.call(); .call();
assertNotNull(call); assertNotNull(call);
@ -72,6 +74,7 @@ public class ClientConfigTest extends GuiIntegrationTest {
// create one // create one
final SebClientConfig config = restService.getBuilder(NewClientConfig.class) final SebClientConfig config = restService.getBuilder(NewClientConfig.class)
.withQueryParam(Domain.SEB_CLIENT_CONFIGURATION.ATTR_NAME, "new client config") .withQueryParam(Domain.SEB_CLIENT_CONFIGURATION.ATTR_NAME, "new client config")
.withFormParam(SebClientConfig.ATTR_CONFIG_PURPOSE, SebClientConfig.ConfigPurpose.START_EXAM.name())
.call() .call()
.getOrThrow(); .getOrThrow();
@ -104,6 +107,15 @@ public class ClientConfigTest extends GuiIntegrationTest {
config.id, config.id,
config.institutionId, config.institutionId,
"new client config", "new client config",
SebClientConfig.ConfigPurpose.START_EXAM,
null,
null,
null,
null,
null,
null,
null,
null,
null, null,
null, null,
"password", "password",
@ -122,6 +134,15 @@ public class ClientConfigTest extends GuiIntegrationTest {
config.id, config.id,
config.institutionId, config.institutionId,
"new client config", "new client config",
SebClientConfig.ConfigPurpose.START_EXAM,
null,
null,
null,
null,
null,
null,
null,
null,
null, null,
null, null,
"password", "password",
@ -133,7 +154,7 @@ public class ClientConfigTest extends GuiIntegrationTest {
assertEquals(config.id, newConfig.id); assertEquals(config.id, newConfig.id);
assertEquals("new client config", newConfig.name); assertEquals("new client config", newConfig.name);
assertTrue(newConfig.active); assertTrue(newConfig.active);
assertNull(newConfig.getEncryptSecret()); assertNotNull(newConfig.getEncryptSecret());
// deactivate // deactivate
final EntityProcessingReport deactivationReport = restService.getBuilder(DeactivateClientConfig.class) final EntityProcessingReport deactivationReport = restService.getBuilder(DeactivateClientConfig.class)

View file

@ -12,6 +12,7 @@ import static org.junit.Assert.*;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
@ -853,7 +854,12 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
final Result<SebClientConfig> newConfigResponse = restService final Result<SebClientConfig> newConfigResponse = restService
.getBuilder(NewClientConfig.class) .getBuilder(NewClientConfig.class)
.withFormParam(Domain.SEB_CLIENT_CONFIGURATION.ATTR_NAME, "No Password Protection") .withFormParam(Domain.SEB_CLIENT_CONFIGURATION.ATTR_NAME, "No Password Protection")
.withFormParam(SebClientConfig.ATTR_FALLBACK, Constants.TRUE_STRING)
.withFormParam(SebClientConfig.ATTR_FALLBACK_START_URL, "http://fallback.com/fallback") .withFormParam(SebClientConfig.ATTR_FALLBACK_START_URL, "http://fallback.com/fallback")
.withFormParam(SebClientConfig.ATTR_FALLBACK_TIMEOUT, "100")
.withFormParam(SebClientConfig.ATTR_FALLBACK_ATTEMPTS, "5")
.withFormParam(SebClientConfig.ATTR_FALLBACK_ATTEMPT_INTERVAL, "5")
.withFormParam(SebClientConfig.ATTR_CONFIG_PURPOSE, SebClientConfig.ConfigPurpose.START_EXAM.name())
.call(); .call();
assertNotNull(newConfigResponse); assertNotNull(newConfigResponse);
@ -886,9 +892,14 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
final Result<SebClientConfig> configWithPasswordResponse = restService final Result<SebClientConfig> configWithPasswordResponse = restService
.getBuilder(NewClientConfig.class) .getBuilder(NewClientConfig.class)
.withFormParam(Domain.SEB_CLIENT_CONFIGURATION.ATTR_NAME, "With Password Protection") .withFormParam(Domain.SEB_CLIENT_CONFIGURATION.ATTR_NAME, "With Password Protection")
.withFormParam(SebClientConfig.ATTR_CONFIG_PURPOSE, SebClientConfig.ConfigPurpose.START_EXAM.name())
.withFormParam(SebClientConfig.ATTR_FALLBACK, Constants.TRUE_STRING)
.withFormParam(SebClientConfig.ATTR_FALLBACK_START_URL, "http://fallback.com/fallback") .withFormParam(SebClientConfig.ATTR_FALLBACK_START_URL, "http://fallback.com/fallback")
.withFormParam(SebClientConfig.ATTR_FALLBACK_TIMEOUT, "100")
.withFormParam(SebClientConfig.ATTR_FALLBACK_ATTEMPTS, "5")
.withFormParam(SebClientConfig.ATTR_FALLBACK_ATTEMPT_INTERVAL, "5")
.withFormParam(SEB_CLIENT_CONFIGURATION.ATTR_ENCRYPT_SECRET, "123") .withFormParam(SEB_CLIENT_CONFIGURATION.ATTR_ENCRYPT_SECRET, "123")
.withFormParam(SebClientConfig.ATTR_CONFIRM_ENCRYPT_SECRET, "123") .withFormParam(SebClientConfig.ATTR_ENCRYPT_SECRET_CONFIRM, "123")
.call(); .call();
assertNotNull(configWithPasswordResponse); assertNotNull(configWithPasswordResponse);
@ -1091,7 +1102,10 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
restService); restService);
// update a value -- grab first // update a value -- grab first
final ConfigurationValue value = values.get(0); ConfigurationValue value = values.get(0);
if (value.attributeId == 1) {
value = values.get(1);
}
ConfigurationValue newValue = new ConfigurationValue( ConfigurationValue newValue = new ConfigurationValue(
null, value.institutionId, value.configurationId, null, value.institutionId, value.configurationId,
value.attributeId, value.listIndex, "2"); value.attributeId, value.listIndex, "2");
@ -1188,8 +1202,9 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
assertNotNull(valuesResponse); assertNotNull(valuesResponse);
assertFalse(valuesResponse.hasError()); assertFalse(valuesResponse.hasError());
values = valuesResponse.get(); values = valuesResponse.get();
final ConfigurationValue _value = value;
final ConfigurationValue currentValue = final ConfigurationValue currentValue =
values.stream().filter(v -> v.attributeId == value.attributeId).findFirst().orElse(null); values.stream().filter(v -> v.attributeId == _value.attributeId).findFirst().orElse(null);
assertNotNull(currentValue); assertNotNull(currentValue);
assertEquals("2", currentValue.value); assertEquals("2", currentValue.value);
} }
@ -1336,12 +1351,10 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
new GetFollowupConfiguration()); new GetFollowupConfiguration());
// get all configuration attributes // get all configuration attributes
final Collection<ConfigurationAttribute> attributes = restService final Collection<ConfigurationAttribute> attributes = new ArrayList<>(restService
.getBuilder(GetConfigAttributes.class) .getBuilder(GetConfigAttributes.class)
.call() .call()
.getOrThrow() .getOrThrow());
.stream()
.collect(Collectors.toList());
// get configuration page // get configuration page
final Result<Page<ConfigurationNode>> pageResponse = restService final Result<Page<ConfigurationNode>> pageResponse = restService

View file

@ -14,6 +14,7 @@ import java.util.Map;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import ch.ethz.seb.sebserver.gbl.util.Cryptor;
import org.apache.tomcat.util.buf.StringUtils; import org.apache.tomcat.util.buf.StringUtils;
import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.Constants;
@ -24,6 +25,7 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestServiceImpl;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetConfigAttributes; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetConfigAttributes;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetConfigurationTableValues; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetConfigurationTableValues;
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl.init.XMLAttributeLoader; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl.init.XMLAttributeLoader;
import org.mockito.Mockito;
public abstract class UsecaseTestUtils { public abstract class UsecaseTestUtils {
@ -52,7 +54,7 @@ public abstract class UsecaseTestUtils {
.stream() .stream()
.collect(Collectors.toMap(attr -> attr.name, Function.identity())); .collect(Collectors.toMap(attr -> attr.name, Function.identity()));
final XMLAttributeLoader xmlAttributeLoader = new XMLAttributeLoader(); final XMLAttributeLoader xmlAttributeLoader = new XMLAttributeLoader(Mockito.mock(Cryptor.class));
final String configuraedNames = StringUtils.join(xmlAttributeLoader.loadFromXML( final String configuraedNames = StringUtils.join(xmlAttributeLoader.loadFromXML(
1L, 1L,
Long.parseLong(configId), Long.parseLong(configId),
@ -111,7 +113,7 @@ public abstract class UsecaseTestUtils {
.stream() .stream()
.collect(Collectors.toMap(attr -> attr.name, Function.identity())); .collect(Collectors.toMap(attr -> attr.name, Function.identity()));
final XMLAttributeLoader xmlAttributeLoader = new XMLAttributeLoader(); final XMLAttributeLoader xmlAttributeLoader = new XMLAttributeLoader(Mockito.mock(Cryptor.class));
final String configuraedNames = StringUtils.join(xmlAttributeLoader.loadFromXML( final String configuraedNames = StringUtils.join(xmlAttributeLoader.loadFromXML(
1L, 1L,
Long.parseLong(configId), Long.parseLong(configId),

View file

@ -12,6 +12,7 @@ import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import ch.ethz.seb.sebserver.gbl.util.Cryptor;
import org.junit.Test; import org.junit.Test;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
@ -31,23 +32,25 @@ public class ClientCredentialServiceTest {
@Test @Test
public void testEncryptDecryptClientCredentials() { public void testEncryptDecryptClientCredentials() {
final Environment envMock = mock(Environment.class); final Environment envMock = mock(Environment.class);
when(envMock.getRequiredProperty(ClientCredentialServiceImpl.SEBSERVER_WEBSERVICE_INTERNAL_SECRET_KEY)) when(envMock.getRequiredProperty(Cryptor.SEBSERVER_WEBSERVICE_INTERNAL_SECRET_KEY))
.thenReturn("secret1"); .thenReturn("secret1");
Cryptor cryptor = new Cryptor(envMock);
final String clientName = "simpleClientName"; final String clientName = "simpleClientName";
final ClientCredentialServiceImpl service = new ClientCredentialServiceImpl(envMock); final ClientCredentialServiceImpl service = new ClientCredentialServiceImpl(envMock, cryptor);
String encrypted = String encrypted =
service.encrypt(clientName, "secret1").toString(); cryptor.encrypt(clientName, "secret1").toString();
String decrypted = service.decrypt(encrypted, "secret1").toString(); String decrypted = cryptor.decrypt(encrypted, "secret1").toString();
assertEquals(clientName, decrypted); assertEquals(clientName, decrypted);
final String clientSecret = "fbjreij39ru29305ruࣣàèLöäöäü65%(/%(ç87"; final String clientSecret = "fbjreij39ru29305ruࣣàèLöäöäü65%(/%(ç87";
encrypted = encrypted =
service.encrypt(clientSecret, "secret1").toString(); cryptor.encrypt(clientSecret, "secret1").toString();
decrypted = service.decrypt(encrypted, "secret1").toString(); decrypted = cryptor.decrypt(encrypted, "secret1").toString();
assertEquals(clientSecret, decrypted); assertEquals(clientSecret, decrypted);
} }

View file

@ -15,11 +15,13 @@ import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import ch.ethz.seb.sebserver.gbl.util.Cryptor;
import org.junit.Test; import org.junit.Test;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType; import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
import org.mockito.Mockito;
public class ExamConfigImportHandlerTest { public class ExamConfigImportHandlerTest {
@ -41,6 +43,7 @@ public class ExamConfigImportHandlerTest {
public void simpleStringValueTest() throws Exception { public void simpleStringValueTest() throws Exception {
final ValueCollector valueCollector = new ValueCollector(); final ValueCollector valueCollector = new ValueCollector();
final ExamConfigXMLParser candidate = new ExamConfigXMLParser( final ExamConfigXMLParser candidate = new ExamConfigXMLParser(
Mockito.mock(Cryptor.class),
1L, 1L,
1L, 1L,
valueCollector, valueCollector,
@ -73,6 +76,7 @@ public class ExamConfigImportHandlerTest {
public void simpleIntegerValueTest() throws Exception { public void simpleIntegerValueTest() throws Exception {
final ValueCollector valueCollector = new ValueCollector(); final ValueCollector valueCollector = new ValueCollector();
final ExamConfigXMLParser candidate = new ExamConfigXMLParser( final ExamConfigXMLParser candidate = new ExamConfigXMLParser(
Mockito.mock(Cryptor.class),
1L, 1L,
1L, 1L,
valueCollector, valueCollector,
@ -105,6 +109,7 @@ public class ExamConfigImportHandlerTest {
public void simpleBooleanValueTest() throws Exception { public void simpleBooleanValueTest() throws Exception {
final ValueCollector valueCollector = new ValueCollector(); final ValueCollector valueCollector = new ValueCollector();
final ExamConfigXMLParser candidate = new ExamConfigXMLParser( final ExamConfigXMLParser candidate = new ExamConfigXMLParser(
Mockito.mock(Cryptor.class),
1L, 1L,
1L, 1L,
valueCollector, valueCollector,
@ -136,6 +141,7 @@ public class ExamConfigImportHandlerTest {
public void arrayOfStringValueTest() throws Exception { public void arrayOfStringValueTest() throws Exception {
final ValueCollector valueCollector = new ValueCollector(); final ValueCollector valueCollector = new ValueCollector();
final ExamConfigXMLParser candidate = new ExamConfigXMLParser( final ExamConfigXMLParser candidate = new ExamConfigXMLParser(
Mockito.mock(Cryptor.class),
1L, 1L,
1L, 1L,
valueCollector, valueCollector,
@ -187,6 +193,7 @@ public class ExamConfigImportHandlerTest {
return attributeResolver.apply(attrName); return attributeResolver.apply(attrName);
}; };
final ExamConfigXMLParser candidate = new ExamConfigXMLParser( final ExamConfigXMLParser candidate = new ExamConfigXMLParser(
Mockito.mock(Cryptor.class),
1L, 1L,
1L, 1L,
valueCollector, valueCollector,
@ -256,6 +263,7 @@ public class ExamConfigImportHandlerTest {
return attributeResolver.apply(attrName); return attributeResolver.apply(attrName);
}; };
final ExamConfigXMLParser candidate = new ExamConfigXMLParser( final ExamConfigXMLParser candidate = new ExamConfigXMLParser(
Mockito.mock(Cryptor.class),
1L, 1L,
1L, 1L,
valueCollector, valueCollector,

View file

@ -17,6 +17,7 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import ch.ethz.seb.sebserver.webservice.servicelayer.client.ClientCredentialService;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito; import org.mockito.Mockito;
@ -320,8 +321,9 @@ public class TableConverterTest {
} }
private AttributeValueConverterService createAttributeValueConverterService() { private AttributeValueConverterService createAttributeValueConverterService() {
final ClientCredentialService clientCredentialServiceMock = Mockito.mock(ClientCredentialService.class);
final List<AttributeValueConverter> converter = new ArrayList<>(); final List<AttributeValueConverter> converter = new ArrayList<>();
converter.add(new StringConverter()); converter.add(new StringConverter(clientCredentialServiceMock));
return new AttributeValueConverterServiceImpl(converter); return new AttributeValueConverterServiceImpl(converter);
} }