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_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 BLACK_RGB = new RGB(0, 0, 0);

View file

@ -67,6 +67,15 @@ public class POSTMapper {
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) {
final String value = this.params.getFirst(name);
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 static final String ATTR_FALLBACK_START_URL = "fallback_start_url";
public static final String ATTR_CONFIRM_ENCRYPT_SECRET = "confirm_encrypt_secret";
public static final String ATTR_CONFIG_PURPOSE = "sebConfigPurpose";
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 enum ConfigPurpose {
START_EXAM,
CONFIGURE_CLIENT
}
@JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_ID)
public final Long id;
@ -47,18 +61,46 @@ public final class SebClientConfig implements GrantEntity, Activatable {
@JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_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)
@URL(message = "clientconfig:fallback_start_url:invalidURL")
@URL(message = "clientconfig:startURL:invalidURL")
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)
public final DateTime date;
@JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_ENCRYPT_SECRET)
public final CharSequence encryptSecret;
@JsonProperty(ATTR_CONFIRM_ENCRYPT_SECRET)
public final CharSequence confirmEncryptSecret;
@JsonProperty(ATTR_ENCRYPT_SECRET_CONFIRM)
public final CharSequence encryptSecretConfirm;
@JsonProperty(SEB_CLIENT_CONFIGURATION.ATTR_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_INSTITUTION_ID) final Long institutionId,
@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_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_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) {
this.id = id;
this.institutionId = institutionId;
this.name = name;
this.configPurpose = configPurpose;
this.fallback = fallback;
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.encryptSecret = encryptSecret;
this.confirmEncryptSecret = confirmEncryptSecret;
this.encryptSecretConfirm = encryptSecretConfirm;
this.active = active;
}
@ -88,10 +148,19 @@ public final class SebClientConfig implements GrantEntity, Activatable {
this.id = null;
this.institutionId = institutionId;
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.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.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;
}
@ -130,18 +199,55 @@ public final class SebClientConfig implements GrantEntity, Activatable {
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() {
return this.date;
}
@JsonIgnore
public CharSequence getEncryptSecret() {
return this.encryptSecret;
}
@JsonIgnore
public CharSequence getConfirmEncryptSecret() {
return this.confirmEncryptSecret;
public CharSequence getEncryptSecretConfirm() {
return this.encryptSecretConfirm;
}
@JsonIgnore
@ -149,47 +255,78 @@ public final class SebClientConfig implements GrantEntity, Activatable {
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() {
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
public Entity printSecureCopy() {
return new SebClientConfig(
this.id,
this.institutionId,
this.name,
this.configPurpose,
this.fallback,
this.fallbackStartURL,
this.fallbackTimeout,
this.fallbackAttempts,
this.fallbackAttemptInterval,
Constants.EMPTY_NOTE,
Constants.EMPTY_NOTE,
Constants.EMPTY_NOTE,
Constants.EMPTY_NOTE,
this.date,
Constants.EMPTY_NOTE,
Constants.EMPTY_NOTE,
this.active);
}
@Override
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) {
public static SebClientConfig createNew(final Long institutionId) {
return new SebClientConfig(
null,
institutionId,
null,
ConfigPurpose.START_EXAM,
false,
null,
null,
null,
null,
null,
null,
null,
null,
DateTime.now(DateTimeZone.UTC),
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.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@ -28,6 +30,7 @@ import java.util.function.Predicate;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringEscapeUtils;
import org.eclipse.swt.graphics.RGB;
@ -478,6 +481,20 @@ public final class Utils {
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")
public static <T> Predicate<T> truePredicate() {
return (Predicate<T>) TRUE_PREDICATE;

View file

@ -228,14 +228,15 @@ final class ExamToConfigBindingForm {
resourceService.localizedExamConfigStatusName(examConfigurationMap))
.readonly(true))
.addField(FormBuilder.text(
.addField(FormBuilder.password(
Domain.EXAM_CONFIGURATION_MAP.ATTR_ENCRYPT_SECRET,
FORM_ENCRYPT_SECRET_TEXT_KEY)
.asPasswordField())
.addField(FormBuilder.text(
FORM_ENCRYPT_SECRET_TEXT_KEY,
examConfigurationMap.encryptSecret))
.addField(FormBuilder.password(
ExamConfigurationMap.ATTR_CONFIRM_ENCRYPT_SECRET,
FORM_CONFIRM_ENCRYPT_SECRET_TEXT_KEY)
.asPasswordField())
FORM_CONFIRM_ENCRYPT_SECRET_TEXT_KEY,
examConfigurationMap.encryptSecret))
.build();

View file

@ -8,8 +8,14 @@
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.client.service.UrlLauncher;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.springframework.beans.factory.annotation.Value;
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.widget.WidgetFactory;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
@Lazy
@Component
@GuiProfile
@ -53,15 +63,51 @@ public class SebClientConfigForm implements TemplateComposer {
new LocTextKey("sebserver.clientconfig.form.title");
private static final LocTextKey FORM_NAME_TEXT_KEY =
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 =
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 =
new LocTextKey("sebserver.clientconfig.form.encryptSecret");
private static final LocTextKey FORM_CONFIRM_ENCRYPT_SECRET_TEXT_KEY =
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 RestService restService;
private final CurrentUser currentUser;
@ -131,32 +177,147 @@ public class SebClientConfigForm implements TemplateComposer {
.putStaticValue(
Domain.SEB_CLIENT_CONFIGURATION.ATTR_INSTITUTION_ID,
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,
() -> FormBuilder.text(
Domain.SEB_CLIENT_CONFIGURATION.ATTR_DATE,
FORM_DATE_TEXT_KEY,
i18nSupport.formatDisplayDateWithTimeZone(clientConfig.date))
.readonly(true))
.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,
FORM_ENCRYPT_SECRET_TEXT_KEY)
.asPasswordField())
FORM_ENCRYPT_SECRET_TEXT_KEY,
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(
SebClientConfig.ATTR_CONFIRM_ENCRYPT_SECRET,
FORM_CONFIRM_ENCRYPT_SECRET_TEXT_KEY)
.asPasswordField())
SebClientConfig.ATTR_FALLBACK_START_URL,
FALLBACK_URL_TEXT_KEY,
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)
? this.restService.getRestCall(NewClientConfig.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);
this.pageService.pageActionBuilder(formContext.clearEntityKeys())
@ -208,4 +369,14 @@ public class SebClientConfigForm implements TemplateComposer {
.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 {
final Form form = formHandle.getForm();
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();
// Ad-hoc field validation
@ -270,7 +270,7 @@ final class SebExamConfigImportPopup {
void cancelUpload() {
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) {
((FileUploadSelection) fieldControl).close();
}

View file

@ -13,20 +13,27 @@ import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import java.util.function.Consumer;
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);
}
@Override
void build(final FormBuilder builder) {
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 Button checkbox = builder.widgetFactory.buttonLocalized(
fieldGrid,

View file

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

View file

@ -13,6 +13,7 @@ import java.util.Collection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
@ -34,7 +35,7 @@ public class FileUploadFieldBuilder extends FieldBuilder<String> {
@Override
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 FileUploadSelection fileUpload = builder.widgetFactory.fileUploadSelection(
fieldGrid,

View file

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

View file

@ -12,8 +12,10 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.BooleanSupplier;
import java.util.function.Consumer;
import java.util.function.Supplier;
import ch.ethz.seb.sebserver.gbl.util.Cryptor;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
@ -40,6 +42,7 @@ public class FormBuilder {
private static final Logger log = LoggerFactory.getLogger(FormBuilder.class);
final Cryptor cryptor;
final I18nSupport i18nSupport;
final PageService pageService;
final WidgetFactory widgetFactory;
@ -56,13 +59,15 @@ public class FormBuilder {
public FormBuilder(
final PageService pageService,
final PageContext pageContext,
final Cryptor cryptor,
final int rows) {
this.cryptor = cryptor;
this.i18nSupport = pageService.getI18nSupport();
this.pageService = pageService;
this.widgetFactory = pageService.getWidgetFactory();
this.pageContext = pageContext;
this.form = new Form(pageService.getJSONMapper());
this.form = new Form(pageService.getJSONMapper(), cryptor);
this.formParent = this.widgetFactory.formGrid(
pageContext.getParent(),
@ -214,11 +219,18 @@ public class FormBuilder {
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) {
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(
final String name,
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.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
@ -37,7 +38,7 @@ public final class ImageUploadFieldBuilder extends FieldBuilder<String> {
@Override
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 ImageUploadSelection imageUpload = builder.widgetFactory.imageUploadLocalized(
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
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) {
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 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 */
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) {
final Composite composite = new Composite(builder.formParent, SWT.NONE);
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.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
@ -88,7 +89,7 @@ public final class TextFieldBuilder extends FieldBuilder<String> {
@Override
void build(final FormBuilder builder) {
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);
if (readonly && this.isHTML) {
@ -117,7 +118,7 @@ public final class TextFieldBuilder extends FieldBuilder<String> {
gridData.minimumHeight = this.areaMinHeight;
} else if (this.isColorBox) {
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);
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.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import ch.ethz.seb.sebserver.gbl.Constants;
@ -39,7 +40,7 @@ public class ThresholdListBuilder extends FieldBuilder<Collection<Threshold>> {
@Override
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) {
// No read-only view needed for this so far?
return;

View file

@ -20,6 +20,7 @@ import java.util.function.Function;
import java.util.function.Supplier;
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.Utils;
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 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_CLIENT_CONFIG_PURPOSE_PREFIX = "sebserver.clientconfig.config.purpose.";
public static final EnumSet<AttributeType> ATTRIBUTE_TYPES_NOT_DISPLAYED = EnumSet.of(
AttributeType.LABEL,
@ -656,4 +658,16 @@ public class ResourceService {
.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;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.apache.commons.codec.binary.Hex;
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.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.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Text;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
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
@Component
@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 =
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
public boolean builderFor(
final ConfigurationAttribute attribute,
@ -69,24 +76,26 @@ public class PassworFieldBuilder implements InputFieldBuilder {
final Composite innerGrid = InputFieldBuilder
.createInnerGrid(parent, attribute, orientation);
final Text passwordInput = new Text(innerGrid, SWT.LEFT | SWT.BORDER | SWT.PASSWORD);
final GridData passwordInputLD = new GridData(SWT.FILL, SWT.FILL, true, false);
final PasswordInput passwordInput = new PasswordInput(innerGrid, widgetFactory);
final GridData passwordInputLD = new GridData(SWT.FILL, SWT.FILL, true, true);
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;
confirmInput.setLayoutData(gridData);
innerGrid.setData("isPlainText", false);
final PasswordInputField passwordInputField = new PasswordInputField(
attribute,
orientation,
passwordInput,
confirmInput,
FieldBuilder.createErrorLabel(innerGrid));
FieldBuilder.createErrorLabel(innerGrid),
cryptor);
if (viewContext.readonly) {
passwordInput.setEditable(false);
passwordInput.setData(RWT.CUSTOM_VARIANT, CustomVariant.CONFIG_INPUT_READONLY.key);
passwordInputLD.heightHint = WidgetFactory.TEXT_INPUT_MIN_HEIGHT;
confirmInput.setEditable(false);
confirmInput.setData(RWT.CUSTOM_VARIANT, CustomVariant.CONFIG_INPUT_READONLY.key);
@ -95,8 +104,8 @@ public class PassworFieldBuilder implements InputFieldBuilder {
final Listener valueChangeEventListener = event -> {
passwordInputField.clearError();
final String pwd = passwordInput.getText();
final String confirm = confirmInput.getText();
final CharSequence pwd = passwordInput.getValue();
final CharSequence confirm = confirmInput.getValue();
if (passwordInputField.initValue != null && passwordInputField.initValue.equals(pwd)) {
return;
@ -127,50 +136,44 @@ public class PassworFieldBuilder implements InputFieldBuilder {
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(
final ConfigurationAttribute attribute,
final Orientation orientation,
final Text control,
final Text confirm,
final Label errorLabel) {
final PasswordInput control,
final PasswordInput confirm,
final Label errorLabel,
final Cryptor cryptor) {
super(attribute, orientation, control, errorLabel);
this.confirm = confirm;
this.cryptor = cryptor;
}
@Override
protected void setValueToControl(final String value) {
// TODO clarify setting some "fake" input when a password is set (like in config tool)
if (value != null) {
this.control.setText(value);
this.confirm.setText(value);
if (StringUtils.isNotBlank(value)) {
CharSequence pwd = cryptor.decrypt(value);
this.control.setValue(pwd.toString());
this.confirm.setValue(pwd.toString());
} else {
this.control.setValue(StringUtils.EMPTY);
this.confirm.setValue(StringUtils.EMPTY);
}
}
@Override
public String getValue() {
String hashedPWD;
try {
hashedPWD = hashPassword(this.control.getText());
} catch (final NoSuchAlgorithmException e) {
log.error("Failed to hash password: ", e);
showError("Failed to hash password");
hashedPWD = null;
final CharSequence pwd = this.control.getValue();
if (StringUtils.isNotBlank(pwd)) {
return cryptor.encrypt(pwd).toString();
}
return hashedPWD;
}
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);
return StringUtils.EMPTY;
}
}

View file

@ -20,6 +20,7 @@ import java.util.function.Supplier;
import javax.servlet.http.HttpSession;
import ch.ethz.seb.sebserver.gbl.util.Cryptor;
import org.eclipse.rap.rwt.RWT;
import org.slf4j.Logger;
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 ListenerComparator LIST_COMPARATOR = new ListenerComparator();
private final Cryptor cryptor;
private final JSONMapper jsonMapper;
private final WidgetFactory widgetFactory;
private final PolyglotPageService polyglotPageService;
@ -85,12 +87,14 @@ public class PageServiceImpl implements PageService {
private final CurrentUser currentUser;
public PageServiceImpl(
final Cryptor cryptor,
final JSONMapper jsonMapper,
final WidgetFactory widgetFactory,
final PolyglotPageService polyglotPageService,
final ResourceService resourceService,
final CurrentUser currentUser) {
this.cryptor = cryptor;
this.jsonMapper = jsonMapper;
this.widgetFactory = widgetFactory;
this.polyglotPageService = polyglotPageService;
@ -337,7 +341,7 @@ public class PageServiceImpl implements PageService {
@Override
public FormBuilder formBuilder(final PageContext pageContext, final int rows) {
return new FormBuilder(this, pageContext, rows);
return new FormBuilder(this, pageContext, cryptor, rows);
}
@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"),
LOCK("lock.png"),
UNLOCK("unlock.png"),
RESTRICTION("restriction.png");
RESTRICTION("restriction.png"),
VISIBILITY("visibility.png"),
VISIBILITY_OFF("visibility_off.png");
public String fileName;
private ImageData image = null;
@ -189,7 +191,9 @@ public class WidgetFactory {
LOGIN_BACK("login-back"),
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) {
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);

View file

@ -8,22 +8,20 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.client;
import java.io.UnsupportedEncodingException;
import java.nio.CharBuffer;
import java.security.SecureRandom;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Cryptor;
import ch.ethz.seb.sebserver.gbl.util.Result;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
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;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Result;
import java.io.UnsupportedEncodingException;
import java.nio.CharBuffer;
import java.security.SecureRandom;
@Lazy
@Service
@ -32,12 +30,15 @@ public class ClientCredentialServiceImpl implements ClientCredentialService {
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 Cryptor cryptor;
protected ClientCredentialServiceImpl(
final Environment environment,
final Cryptor cryptor) {
protected ClientCredentialServiceImpl(final Environment environment) {
this.environment = environment;
this.cryptor = cryptor;
}
@Override
@ -63,15 +64,15 @@ public class ClientCredentialServiceImpl implements ClientCredentialService {
final CharSequence accessTokenPlaintext) {
final CharSequence secret = this.environment
.getProperty(SEBSERVER_WEBSERVICE_INTERNAL_SECRET_KEY);
.getProperty(Cryptor.SEBSERVER_WEBSERVICE_INTERNAL_SECRET_KEY);
return new ClientCredentials(
clientIdPlaintext,
(StringUtils.isNoneBlank(secretPlaintext))
? encrypt(secretPlaintext, secret).toString()
? Cryptor.encrypt(secretPlaintext, secret).toString()
: null,
(StringUtils.isNoneBlank(accessTokenPlaintext))
? encrypt(accessTokenPlaintext, secret).toString()
? Cryptor.encrypt(accessTokenPlaintext, secret).toString()
: null);
}
@ -82,8 +83,8 @@ public class ClientCredentialServiceImpl implements ClientCredentialService {
}
final CharSequence secret = this.environment
.getProperty(SEBSERVER_WEBSERVICE_INTERNAL_SECRET_KEY);
return this.decrypt(credentials.secret, secret);
.getProperty(Cryptor.SEBSERVER_WEBSERVICE_INTERNAL_SECRET_KEY);
return Cryptor.decrypt(credentials.secret, secret);
}
@Override
@ -93,100 +94,39 @@ public class ClientCredentialServiceImpl implements ClientCredentialService {
}
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
public CharSequence encrypt(final CharSequence text) {
final CharSequence secret = this.environment
.getProperty(SEBSERVER_WEBSERVICE_INTERNAL_SECRET_KEY);
return encrypt(text, secret);
return cryptor.encrypt(text);
}
@Override
public CharSequence decrypt(final CharSequence 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;
}
return cryptor.decrypt(text);
}
private final static char[] possibleCharacters =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~`!@#$%^*()-_=+[{]}?"
.toCharArray();
public final static CharSequence generateClientId() {
public static CharSequence generateClientId() {
return RandomStringUtils.random(
16, 0, possibleCharacters.length - 1, false, false,
possibleCharacters, new SecureRandom());
}
public final 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
public static CharSequence generateClientSecret() throws UnsupportedEncodingException {
// TODO find a better way to generate a random char array instead of using RandomStringUtils.random which uses a String
return RandomStringUtils.random(
64, 0, possibleCharacters.length - 1, false, false,
possibleCharacters, new SecureRandom());
}
public final static void clearChars(final CharSequence sequence) {
public static void clearChars(final CharSequence sequence) {
if (sequence == null) {
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 configurationNodeId the ConfigurationNode mapping identifier
* @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
* the Exam with specified identifier.

View file

@ -43,7 +43,7 @@ public interface SebClientConfigDAO extends
*
* @param modelId the model
* @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.
* 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
* @return encrypted configuration password */
Result<CharSequence> getConfigPasswortCipherByClientName(String clientName);
Result<CharSequence> getConfigPasswordCipherByClientName(String clientName);
@Override
@CacheEvict(

View file

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

View file

@ -77,9 +77,7 @@ public class ClientEventDAOImpl implements ClientEventDAO {
final FilterMap filterMap,
final Predicate<ClientEvent> predicate) {
return Result.tryCatch(() -> {
return this.clientEventRecordMapper
return Result.tryCatch(() -> this.clientEventRecordMapper
.selectByExample()
.where(
ClientEventRecordDynamicSqlSupport.clientConnectionId,
@ -111,8 +109,7 @@ public class ClientEventDAOImpl implements ClientEventDAO {
.map(ClientEventDAOImpl::toDomainModel)
.flatMap(DAOLoggingSupport::logAndSkipOnError)
.filter(predicate)
.collect(Collectors.toList());
});
.collect(Collectors.toList()));
}
@Override
@ -166,16 +163,14 @@ public class ClientEventDAOImpl implements ClientEventDAO {
@Override
@Transactional(readOnly = true)
public Result<Collection<ClientEvent>> allOf(final Set<Long> pks) {
return Result.tryCatch(() -> {
return this.clientEventRecordMapper.selectByExample()
return Result.tryCatch(() -> this.clientEventRecordMapper.selectByExample()
.where(ClientEventRecordDynamicSqlSupport.id, isIn(new ArrayList<>(pks)))
.build()
.execute()
.stream()
.map(ClientEventDAOImpl::toDomainModel)
.flatMap(DAOLoggingSupport::logAndSkipOnError)
.collect(Collectors.toList());
});
.collect(Collectors.toList()));
}
@Override
@ -246,9 +241,7 @@ public class ClientEventDAOImpl implements ClientEventDAO {
}
private static Result<ExtendedClientEvent> toDomainModelExtended(final ConnectionEventJoinRecord record) {
return Result.tryCatch(() -> {
return new ExtendedClientEvent(
return Result.tryCatch(() -> new ExtendedClientEvent(
record.institution_id,
record.exam_id,
record.exam_user_session_identifer,
@ -257,9 +250,8 @@ public class ClientEventDAOImpl implements ClientEventDAO {
(record.type != null) ? EventType.byId(record.type) : EventType.UNKNOWN,
record.client_time,
record.server_time,
(record.numeric_value != null) ? record.numeric_value.doubleValue() : null,
record.text);
});
record.numeric_value,
record.text));
}
}

View file

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

View file

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

View file

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

View file

@ -14,7 +14,9 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
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.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.SebClientConfig;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.SebClientConfig.ConfigPurpose;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.gbl.util.Utils;
@ -110,9 +113,7 @@ public class SebClientConfigDAOImpl implements SebClientConfigDAO {
final FilterMap filterMap,
final Predicate<SebClientConfig> predicate) {
return Result.tryCatch(() -> {
return this.sebClientConfigRecordMapper
return Result.tryCatch(() -> this.sebClientConfigRecordMapper
.selectByExample()
.where(
SebClientConfigRecordDynamicSqlSupport.institutionId,
@ -132,15 +133,12 @@ public class SebClientConfigDAOImpl implements SebClientConfigDAO {
.map(this::toDomainModel)
.flatMap(DAOLoggingSupport::logAndSkipOnError)
.filter(predicate)
.collect(Collectors.toList());
});
.collect(Collectors.toList()));
}
@Override
public Result<SebClientConfig> byClientName(final String clientName) {
return Result.tryCatch(() -> {
return this.sebClientConfigRecordMapper
return Result.tryCatch(() -> this.sebClientConfigRecordMapper
.selectByExample()
.where(
SebClientConfigRecordDynamicSqlSupport.clientName,
@ -150,13 +148,12 @@ public class SebClientConfigDAOImpl implements SebClientConfigDAO {
.stream()
.map(this::toDomainModel)
.flatMap(DAOLoggingSupport::logAndSkipOnError)
.collect(Utils.toSingleton());
});
.collect(Utils.toSingleton()));
}
@Override
@Transactional(readOnly = true)
public Result<CharSequence> getConfigPasswortCipherByClientName(final String clientName) {
public Result<CharSequence> getConfigPasswordCipherByClientName(final String clientName) {
return Result.tryCatch(() -> {
final SebClientConfigRecord record = this.sebClientConfigRecordMapper
@ -187,8 +184,7 @@ public class SebClientConfigDAOImpl implements SebClientConfigDAO {
.where(SebClientConfigRecordDynamicSqlSupport.id, isEqualTo(Long.valueOf(modelId)))
.and(SebClientConfigRecordDynamicSqlSupport.active, isEqualTo(BooleanUtils.toInteger(true)))
.build()
.execute()
.longValue() > 0;
.execute() > 0;
}
@Override
@ -234,11 +230,7 @@ public class SebClientConfigDAOImpl implements SebClientConfigDAO {
this.sebClientConfigRecordMapper
.insert(newRecord);
this.additionalAttributesDAO.saveAdditionalAttribute(
EntityType.SEB_CLIENT_CONFIGURATION,
newRecord.getId(),
SebClientConfig.ATTR_FALLBACK_START_URL,
sebClientConfig.fallbackStartURL);
saveAdditionalAttributes(sebClientConfig, newRecord.getId());
return newRecord;
})
@ -266,11 +258,7 @@ public class SebClientConfigDAOImpl implements SebClientConfigDAO {
this.sebClientConfigRecordMapper
.updateByPrimaryKeySelective(newRecord);
this.additionalAttributesDAO.saveAdditionalAttribute(
EntityType.SEB_CLIENT_CONFIGURATION,
newRecord.getId(),
SebClientConfig.ATTR_FALLBACK_START_URL,
sebClientConfig.fallbackStartURL);
saveAdditionalAttributes(sebClientConfig, newRecord.getId());
return this.sebClientConfigRecordMapper
.selectByPrimaryKey(sebClientConfig.id);
@ -300,17 +288,14 @@ public class SebClientConfigDAOImpl implements SebClientConfigDAO {
@Override
@Transactional(readOnly = true)
public Result<Collection<SebClientConfig>> allOf(final Set<Long> pks) {
return Result.tryCatch(() -> {
return this.sebClientConfigRecordMapper.selectByExample()
return Result.tryCatch(() -> this.sebClientConfigRecordMapper.selectByExample()
.where(SebClientConfigRecordDynamicSqlSupport.id, isIn(new ArrayList<>(pks)))
.build()
.execute()
.stream()
.map(this::toDomainModel)
.flatMap(DAOLoggingSupport::logAndSkipOnError)
.collect(Collectors.toList());
});
.collect(Collectors.toList()));
}
@Override
@ -336,28 +321,24 @@ public class SebClientConfigDAOImpl implements SebClientConfigDAO {
@Override
@Transactional(readOnly = true)
public Result<CharSequence> getConfigPasswortCipher(final String modelId) {
public Result<CharSequence> getConfigPasswordCipher(final String modelId) {
return recordByModelId(modelId)
.map(rec -> rec.getEncryptSecret());
.map(SebClientConfigRecord::getEncryptSecret);
}
private Result<Collection<EntityKey>> allIdsOfInstitution(final EntityKey institutionKey) {
return Result.tryCatch(() -> {
return this.sebClientConfigRecordMapper.selectIdsByExample()
return Result.tryCatch(() -> this.sebClientConfigRecordMapper.selectIdsByExample()
.where(SebClientConfigRecordDynamicSqlSupport.institutionId,
isEqualTo(Long.valueOf(institutionKey.modelId)))
.build()
.execute()
.stream()
.map(id -> new EntityKey(id, EntityType.SEB_CLIENT_CONFIGURATION))
.collect(Collectors.toList());
});
.collect(Collectors.toList()));
}
private Result<SebClientConfigRecord> recordByModelId(final String modelId) {
return Result.tryCatch(() -> {
return recordById(Long.parseLong(modelId)).getOrThrow();
});
return Result.tryCatch(() -> recordById(Long.parseLong(modelId)).getOrThrow());
}
private Result<SebClientConfigRecord> recordById(final Long id) {
@ -375,30 +356,57 @@ public class SebClientConfigDAOImpl implements SebClientConfigDAO {
}
private Result<SebClientConfig> toDomainModel(final SebClientConfigRecord record) {
final String fallbackURL = this.additionalAttributesDAO.getAdditionalAttributes(
Map<String, AdditionalAttributeRecord> additionalAttributes = this.additionalAttributesDAO
.getAdditionalAttributes(
EntityType.SEB_CLIENT_CONFIGURATION,
record.getId())
.getOrThrow()
.stream()
.filter(rec -> SebClientConfig.ATTR_FALLBACK_START_URL.equals(rec.getName()))
.findFirst()
.map(AdditionalAttributeRecord::getValue)
.orElse(null);
.collect(Collectors.toMap(
AdditionalAttributeRecord::getName,
Function.identity()));
additionalAttributes.get(SebClientConfig.ATTR_CONFIG_PURPOSE);
return Result.tryCatch(() -> new SebClientConfig(
record.getId(),
record.getInstitutionId(),
record.getName(),
fallbackURL,
record.getDate(),
additionalAttributes.containsKey(SebClientConfig.ATTR_CONFIG_PURPOSE)
? 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,
additionalAttributes.containsKey(SebClientConfig.ATTR_QUIT_PASSWORD)
? additionalAttributes.get(SebClientConfig.ATTR_QUIT_PASSWORD).getValue()
: null,
null,
record.getDate(),
record.getEncryptSecret(),
null,
BooleanUtils.toBooleanObject(record.getActive())));
}
private String getEncryptionPassword(final SebClientConfig sebClientConfig) {
if (sebClientConfig.hasEncryptionSecret() &&
!sebClientConfig.encryptSecret.equals(sebClientConfig.confirmEncryptSecret)) {
!sebClientConfig.encryptSecret.equals(sebClientConfig.encryptSecretConfirm)) {
throw new APIMessageException(ErrorMessage.PASSWORD_MISMATCH);
}
@ -420,11 +428,99 @@ public class SebClientConfigDAOImpl implements SebClientConfigDAO {
.build()
.execute();
if (otherWithSameName != null && otherWithSameName.longValue() > 0) {
if (otherWithSameName != null && otherWithSameName > 0) {
throw new APIMessageException(APIMessage.fieldValidationError(
Domain.SEB_CLIENT_CONFIGURATION.ATTR_NAME,
"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);
public static final 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";
String EXAM_CLIENT_DETAILS_CACHE = "EXAM_CLIENT_DETAILS_CACHE";
/** 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;
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.gbl.Constants;
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.impl.SebConfigEncryptionServiceImpl.EncryptionContext;
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
@Service
@ -62,6 +62,38 @@ public class ClientConfigServiceImpl implements ClientConfigService {
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 SebClientConfigDAO sebClientConfigDAO;
private final ClientCredentialService clientCredentialService;
@ -109,6 +141,15 @@ public class ClientConfigServiceImpl implements ClientConfigService {
institutionId,
institution.name + "_" + UUID.randomUUID(),
null,
false,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
@ -150,10 +191,10 @@ public class ClientConfigServiceImpl implements ClientConfigService {
.byModelId(modelId).getOrThrow();
final CharSequence encryptionPassword = this.sebClientConfigDAO
.getConfigPasswortCipher(config.getModelId())
.getConfigPasswordCipher(config.getModelId())
.getOr(null);
final String plainTextConfig = getPlainXMLConfig(config);
final String plainTextXMLContent = extractXMLContent(config);
PipedOutputStream pOut = null;
PipedInputStream pIn = null;
@ -165,7 +206,7 @@ public class ClientConfigServiceImpl implements ClientConfigService {
Constants.XML_VERSION_HEADER +
Constants.XML_DOCTYPE_HEADER +
Constants.XML_PLIST_START_V1 +
plainTextConfig +
plainTextXMLContent +
Constants.XML_PLIST_END,
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
.getSebClientCredentials(config.getModelId())
.getOrThrow();
final CharSequence plainClientId = sebClientCredentials.clientId;
final CharSequence plainClientSecret = this.clientCredentialService
.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(
SEB_CLIENT_CONFIG_EXAMPLE_XML,
SEB_CLIENT_CONFIG_TEMPLATE_XML,
config.configPurpose.ordinal(),
(StringUtils.isNotBlank(config.fallbackStartURL))
? "true"
: "false",
(StringUtils.isNotBlank(config.fallbackStartURL))
? "<key>startURL</key>\r\n <string>" + config.fallbackStartURL + "</string>\r\n"
: "",
fallbackAddition,
this.webserviceInfo.getExternalServerURL(),
String.valueOf(config.institutionId),
config.institutionId,
plainClientId,
plainClientSecret,
this.webserviceInfo.getDiscoveryEndpoint());
@ -259,7 +324,6 @@ public class ClientConfigServiceImpl implements ClientConfigService {
bulkAction.type == BulkActionType.HARD_DELETE) {
bulkAction.extractKeys(EntityType.SEB_CLIENT_CONFIGURATION)
.stream()
.forEach(this::flushClientConfigData);
}
@ -275,8 +339,7 @@ public class ClientConfigServiceImpl implements ClientConfigService {
.clientIdAsString();
final Collection<OAuth2AccessToken> tokensByClientId = this.tokenStore.findTokensByClientId(clientName);
tokensByClientId.stream()
.forEach(token -> this.tokenStore.removeAccessToken(token));
tokensByClientId.forEach(this.tokenStore::removeAccessToken);
} catch (final Exception 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
* @return encoded clientSecret for that SebClientConfiguration with clientId or null of not existing */
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)));
}

View file

@ -27,6 +27,7 @@ import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import ch.ethz.seb.sebserver.gbl.util.Cryptor;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -72,19 +73,22 @@ public class ExamConfigIO {
private final ConfigurationDAO configurationDAO;
private final AttributeValueConverterService attributeValueConverterService;
private final ZipService zipService;
private final Cryptor cryptor;
protected ExamConfigIO(
final ConfigurationAttributeDAO configurationAttributeDAO,
final ConfigurationValueDAO configurationValueDAO,
final ConfigurationDAO configurationDAO,
final AttributeValueConverterService attributeValueConverterService,
final ZipService zipService) {
final ZipService zipService,
final Cryptor cryptor) {
this.configurationAttributeDAO = configurationAttributeDAO;
this.configurationValueDAO = configurationValueDAO;
this.configurationDAO = configurationDAO;
this.attributeValueConverterService = attributeValueConverterService;
this.zipService = zipService;
this.cryptor = cryptor;
}
@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
// and a attribute-name/id mapping function with pre-created mapping
final ExamConfigXMLParser examConfigImportHandler = new ExamConfigXMLParser(
cryptor,
institutionId,
configurationId,
value -> this.configurationValueDAO

View file

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

View file

@ -15,6 +15,7 @@ import java.util.Stack;
import java.util.function.Consumer;
import java.util.function.Function;
import ch.ethz.seb.sebserver.gbl.util.Cryptor;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
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(
"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 Function<String, ConfigurationAttribute> attributeResolver;
private final Long institutionId;
@ -96,12 +105,14 @@ public class ExamConfigXMLParser extends DefaultHandler {
private Boolean createNewDesktop = null;
public ExamConfigXMLParser(
final Cryptor cryptor,
final Long institutionId,
final Long configId,
final Consumer<ConfigurationValue> valueConsumer,
final Function<String, ConfigurationAttribute> attributeResolver) {
super();
this.cryptor = cryptor;
this.valueConsumer = valueConsumer;
this.attributeResolver = attributeResolver;
this.institutionId = institutionId;
@ -430,6 +441,21 @@ public class ExamConfigXMLParser extends DefaultHandler {
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(
null,
this.institutionId,

View file

@ -16,6 +16,8 @@ import java.util.HashSet;
import java.util.Set;
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.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
@ -40,12 +42,20 @@ public class StringConverter implements AttributeValueConverter {
AttributeType.DECIMAL,
AttributeType.COMBO_SELECTION)));
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 JSON_TEMPLATE = "\"%s\":\"%s\"";
private static final String JSON_TEMPLATE_EMPTY = "\"%s\":\"\"";
private final ClientCredentialService clientCredentialService;
public StringConverter(final ClientCredentialService clientCredentialService) {
this.clientCredentialService = clientCredentialService;
}
@Override
public Set<AttributeType> types() {
return SUPPORTED_TYPES;
@ -85,15 +95,38 @@ public class StringConverter implements AttributeValueConverter {
final String emptyTemplate) throws IOException {
final String val = (value != null && value.value != null) ? value.value : attribute.getDefaultValue();
String realName = AttributeValueConverter.extractName(attribute);
if (StringUtils.isNotBlank(val)) {
out.write(Utils.toByteArray(String.format(
template,
AttributeValueConverter.extractName(attribute),
val)));
realName,
convertPassword(realName, val))));
} else {
out.write(Utils.toByteArray(String.format(
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.SAXParserFactory;
import ch.ethz.seb.sebserver.gbl.util.Cryptor;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -37,6 +38,12 @@ public class XMLAttributeLoader {
private static final Logger log = LoggerFactory.getLogger(XMLAttributeLoader.class);
private final Cryptor cryptor;
public XMLAttributeLoader(Cryptor cryptor) {
this.cryptor = cryptor;
}
public Collection<ConfigurationValue> loadFromXML(
final Long institutionId,
final Long configurationId,
@ -57,6 +64,7 @@ public class XMLAttributeLoader {
final Collection<ConfigurationValue> values = new ArrayList<>();
final ExamConfigXMLParser examConfigImportHandler = new ExamConfigXMLParser(
cryptor,
institutionId,
configurationId,
values::add,

View file

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

View file

@ -11,11 +11,15 @@ package ch.ethz.seb.sebserver.webservice.weblayer.api;
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.util.ArrayList;
import java.util.Collection;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
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.DateTimeZone;
import org.mybatis.dynamic.sql.SqlTable;
@ -107,14 +111,6 @@ public class SebClientConfigController extends ActivatableEntityController<SebCl
outputStream.flush();
outputStream.close();
}
// final StreamingResponseBody stream = out -> {
// this.sebClientConfigService.exportSebClientConfiguration(
// out,
// modelId);
// };
//
// return new ResponseEntity<>(stream, HttpStatus.OK);
}
@Override
@ -152,14 +148,67 @@ public class SebClientConfigController extends ActivatableEntityController<SebCl
}
private SebClientConfig checkPasswordMatch(final SebClientConfig entity) {
if (entity.hasEncryptionSecret() && !entity.encryptSecret.equals(entity.confirmEncryptSecret)) {
throw new APIMessageException(APIMessage.fieldValidationError(
Collection<APIMessage> errors = new ArrayList<>();
if (entity.hasEncryptionSecret() && !entity.encryptSecret.equals(entity.encryptSecretConfirm)) {
errors.add(APIMessage.fieldValidationError(
new FieldError(
Domain.SEB_CLIENT_CONFIGURATION.TYPE_NAME,
PasswordChange.ATTR_NAME_PASSWORD,
"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;
}

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.filter=
sebserver.overall.action.showPassword.tooltip=Show / hide password in plain text.
sebserver.overall.status.active=Active
sebserver.overall.status.inactive=Inactive
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
################################
# SEB Client Configuration
# SEB client configuration
################################
sebserver.sebconfig.activity.name=SEB Configuration
@ -576,7 +578,7 @@ sebserver.clientconfig.action.list=Client Configuration
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.title=SEB Client Configurations
sebserver.clientconfig.list.title=SEB client configurations
sebserver.clientconfig.list.actions=
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}
@ -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.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.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=SEB Client Configuration
sebserver.clientconfig.form.title=SEB client configuration
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.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.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.tooltip=Defines the fallback timeout for the SEB Client in milli-seconds.
sebserver.clientconfig.form.sebServerFallbackAttempts=Fallback Attempts
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.tooltip=The interval (in milli-seconds) between connection attempts a SEB Client shall use.
sebserver.clientconfig.form.sebServerFallbackTimeout.tooltip=Defines the fallback timeout for the SEB client in milli-seconds.
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.sebServerFallbackAttemptInterval=Interval
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.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.tooltip=The date when the SEB client configuration was first created.
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.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.view=View Configuration
sebserver.clientconfig.action.list.modify=Edit Configuration

View file

@ -320,6 +320,26 @@ Text[MULTI][BORDER]:read-only.inputreadonly {
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:read-only,
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)
.withQueryParam(Domain.SEB_CLIENT_CONFIGURATION.ATTR_NAME, "new client config")
.withFormParam(SebClientConfig.ATTR_CONFIG_PURPOSE, SebClientConfig.ConfigPurpose.START_EXAM.name())
.call();
assertNotNull(call);
@ -50,6 +51,7 @@ public class ClientConfigTest extends GuiIntegrationTest {
final Result<SebClientConfig> call = restService.getBuilder(NewClientConfig.class)
.withFormParam(Domain.SEB_CLIENT_CONFIGURATION.ATTR_NAME, "new client config")
.withFormParam(SebClientConfig.ATTR_CONFIG_PURPOSE, SebClientConfig.ConfigPurpose.START_EXAM.name())
.call();
assertNotNull(call);
@ -72,6 +74,7 @@ public class ClientConfigTest extends GuiIntegrationTest {
// create one
final SebClientConfig config = restService.getBuilder(NewClientConfig.class)
.withQueryParam(Domain.SEB_CLIENT_CONFIGURATION.ATTR_NAME, "new client config")
.withFormParam(SebClientConfig.ATTR_CONFIG_PURPOSE, SebClientConfig.ConfigPurpose.START_EXAM.name())
.call()
.getOrThrow();
@ -104,6 +107,15 @@ public class ClientConfigTest extends GuiIntegrationTest {
config.id,
config.institutionId,
"new client config",
SebClientConfig.ConfigPurpose.START_EXAM,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
"password",
@ -122,6 +134,15 @@ public class ClientConfigTest extends GuiIntegrationTest {
config.id,
config.institutionId,
"new client config",
SebClientConfig.ConfigPurpose.START_EXAM,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
"password",
@ -133,7 +154,7 @@ public class ClientConfigTest extends GuiIntegrationTest {
assertEquals(config.id, newConfig.id);
assertEquals("new client config", newConfig.name);
assertTrue(newConfig.active);
assertNull(newConfig.getEncryptSecret());
assertNotNull(newConfig.getEncryptSecret());
// deactivate
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.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
@ -853,7 +854,12 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
final Result<SebClientConfig> newConfigResponse = restService
.getBuilder(NewClientConfig.class)
.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_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();
assertNotNull(newConfigResponse);
@ -886,9 +892,14 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
final Result<SebClientConfig> configWithPasswordResponse = restService
.getBuilder(NewClientConfig.class)
.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_TIMEOUT, "100")
.withFormParam(SebClientConfig.ATTR_FALLBACK_ATTEMPTS, "5")
.withFormParam(SebClientConfig.ATTR_FALLBACK_ATTEMPT_INTERVAL, "5")
.withFormParam(SEB_CLIENT_CONFIGURATION.ATTR_ENCRYPT_SECRET, "123")
.withFormParam(SebClientConfig.ATTR_CONFIRM_ENCRYPT_SECRET, "123")
.withFormParam(SebClientConfig.ATTR_ENCRYPT_SECRET_CONFIRM, "123")
.call();
assertNotNull(configWithPasswordResponse);
@ -1091,7 +1102,10 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
restService);
// 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(
null, value.institutionId, value.configurationId,
value.attributeId, value.listIndex, "2");
@ -1188,8 +1202,9 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
assertNotNull(valuesResponse);
assertFalse(valuesResponse.hasError());
values = valuesResponse.get();
final ConfigurationValue _value = value;
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);
assertEquals("2", currentValue.value);
}
@ -1336,12 +1351,10 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
new GetFollowupConfiguration());
// get all configuration attributes
final Collection<ConfigurationAttribute> attributes = restService
final Collection<ConfigurationAttribute> attributes = new ArrayList<>(restService
.getBuilder(GetConfigAttributes.class)
.call()
.getOrThrow()
.stream()
.collect(Collectors.toList());
.getOrThrow());
// get configuration page
final Result<Page<ConfigurationNode>> pageResponse = restService

View file

@ -14,6 +14,7 @@ import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import ch.ethz.seb.sebserver.gbl.util.Cryptor;
import org.apache.tomcat.util.buf.StringUtils;
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.GetConfigurationTableValues;
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl.init.XMLAttributeLoader;
import org.mockito.Mockito;
public abstract class UsecaseTestUtils {
@ -52,7 +54,7 @@ public abstract class UsecaseTestUtils {
.stream()
.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(
1L,
Long.parseLong(configId),
@ -111,7 +113,7 @@ public abstract class UsecaseTestUtils {
.stream()
.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(
1L,
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.when;
import ch.ethz.seb.sebserver.gbl.util.Cryptor;
import org.junit.Test;
import org.springframework.core.env.Environment;
@ -31,23 +32,25 @@ public class ClientCredentialServiceTest {
@Test
public void testEncryptDecryptClientCredentials() {
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");
Cryptor cryptor = new Cryptor(envMock);
final String clientName = "simpleClientName";
final ClientCredentialServiceImpl service = new ClientCredentialServiceImpl(envMock);
final ClientCredentialServiceImpl service = new ClientCredentialServiceImpl(envMock, cryptor);
String encrypted =
service.encrypt(clientName, "secret1").toString();
String decrypted = service.decrypt(encrypted, "secret1").toString();
cryptor.encrypt(clientName, "secret1").toString();
String decrypted = cryptor.decrypt(encrypted, "secret1").toString();
assertEquals(clientName, decrypted);
final String clientSecret = "fbjreij39ru29305ruࣣàèLöäöäü65%(/%(ç87";
encrypted =
service.encrypt(clientSecret, "secret1").toString();
decrypted = service.decrypt(encrypted, "secret1").toString();
cryptor.encrypt(clientSecret, "secret1").toString();
decrypted = cryptor.decrypt(encrypted, "secret1").toString();
assertEquals(clientSecret, decrypted);
}

View file

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

View file

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