import, configuration

This commit is contained in:
anhefti 2019-10-10 22:46:14 +02:00
parent f3b44d9cbe
commit 2bb0ae1c0d
17 changed files with 220 additions and 145 deletions

View file

@ -80,4 +80,10 @@ public final class Constants {
public static final int RWT_MOUSE_BUTTON_1 = 1;
public static final int RWT_MOUSE_BUTTON_2 = 2;
public static final int RWT_MOUSE_BUTTON_3 = 3;
public static final int GZIP_HEADER_LENGTH = 10;
public static final int GZIP_ID1 = 0x1F;
public static final int GZIP_ID2 = 0x8B;
public static final int GZIP_CM = 8;
}

View file

@ -306,6 +306,9 @@ public class SebExamConfigPropForm implements TemplateComposer {
final Configuration configuration = pageService.getRestService()
.getBuilder(ImportExamConfig.class)
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
.withHeader(
API.IMPORT_PASSWORD_ATTR_NAME,
form.getFieldValue(API.IMPORT_PASSWORD_ATTR_NAME))
.withBody(inputStream)
.call()
.get(context::notifyError);

View file

@ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.concurrent.Future;
import org.springframework.scheduling.annotation.Async;
@ -80,7 +81,7 @@ public interface SebConfigEncryptionService {
* @param input the input stream to read the cipher text from
* @param context the SebConfigEncryptionContext to access strategy specific data needed for encryption */
@Async(AsyncServiceSpringConfig.EXECUTOR_BEAN_NAME)
void streamDecrypted(
Future<Exception> streamDecrypted(
final OutputStream output,
final InputStream input,
final SebConfigEncryptionContext context);

View file

@ -111,6 +111,6 @@ public interface SebExamConfigService {
* @param input The InputStream to get the SEB config file as byte-stream
* @param password A password is only needed if the file is in an encrypted format
* @return The newly created Configuration instance */
Result<Configuration> importFromXML(Long configNodeId, InputStream input, CharSequence password);
Result<Configuration> importFromSEBFile(Long configNodeId, InputStream input, CharSequence password);
}

View file

@ -166,7 +166,6 @@ public class ExamConfigIO {
* @param in The InputString to constantly read the XML from
* @param institutionId the institionId of the import
* @param configurationId the identifier of the internal configuration to apply the imported values to */
@Async(AsyncServiceSpringConfig.EXECUTOR_BEAN_NAME)
void importPlainXML(final InputStream in, final Long institutionId, final Long configurationId) {
try {
// get all attributes and map the names to ids

View file

@ -15,6 +15,7 @@ import java.util.Stack;
import java.util.function.Consumer;
import java.util.function.Function;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.Attributes;
@ -164,8 +165,8 @@ public class ExamConfigImportHandler extends DefaultHandler {
private void startValueElement(final Type type, final PListNode top) {
final PListNode value = new PListNode(type);
if (top.type == Type.KEY) {
if (Type.isBooleanValue(type)) {
this.stack.pop();
value.name = top.name;
@ -210,40 +211,64 @@ public class ExamConfigImportHandler extends DefaultHandler {
final PListNode grandParent = this.stack.peek();
this.stack.push(parent);
// if we are in an values-array
if (parent.type == Type.ARRAY) {
if (StringUtils.isBlank(parent.value)) {
parent.value = top.value;
} else {
parent.value += "," + top.value;
}
return;
}
final String attrName = (parent.type == Type.DICT && grandParent.type == Type.ARRAY)
? parent.name + "." + top.name
: top.name;
final Long attributeId = this.attributeNameIdResolver.apply(attrName);
if (attributeId == null) {
if (log.isDebugEnabled()) {
log.debug("Skip unknown configuration attribute: {}", attrName);
}
} else {
// TODO use AttributeValueConverterService here. Extend the converters with fromXML functionality
final ConfigurationValue configurationValue = new ConfigurationValue(
null,
this.institutionId,
this.configId,
attributeId,
top.listIndex,
top.value);
if (log.isDebugEnabled()) {
log.debug("Save imported value: {} : {}", attrName, configurationValue);
}
this.valueConsumer.accept(configurationValue);
saveValue(attrName, attributeId, top.listIndex, top.value);
}
}
} else if (top.type == Type.ARRAY && StringUtils.isNoneBlank(top.value)) {
this.stack.pop();
final PListNode parent = this.stack.pop();
final PListNode grandParent = this.stack.peek();
this.stack.push(parent);
final String attrName = (parent.type == Type.DICT && grandParent.type == Type.ARRAY)
? parent.name + "." + top.name
: top.name;
final Long attributeId = this.attributeNameIdResolver.apply(attrName);
saveValue(attrName, attributeId, top.listIndex, top.value);
} else if (!Constants.XML_PLIST_KEY_NAME.equals(qName)) {
this.stack.pop();
}
}
private void saveValue(final String name, final Long attributeId, final int listIndex, final String value) {
// TODO use AttributeValueConverterService here. Extend the converters with fromXML functionality
final ConfigurationValue configurationValue = new ConfigurationValue(
null,
this.institutionId,
this.configId,
attributeId,
listIndex,
value);
if (log.isDebugEnabled()) {
log.debug("Save imported value: {} : {}", name, configurationValue);
}
this.valueConsumer.accept(configurationValue);
}
@Override
public void characters(
final char[] ch,

View file

@ -8,15 +8,20 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.SequenceInputStream;
import java.util.Set;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.cryptonode.jncryptor.AES256JNCryptor;
import org.cryptonode.jncryptor.AES256JNCryptorInputStream;
import org.cryptonode.jncryptor.AES256JNCryptorOutputStream;
import org.cryptonode.jncryptor.CryptorException;
import org.cryptonode.jncryptor.PasswordKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
@ -91,31 +96,66 @@ public class PasswordEncryptor implements SebConfigCryptor {
final InputStream input,
final SebConfigEncryptionContext context) {
if (log.isDebugEnabled()) {
log.debug("*** Start streaming asynchronous decryption");
}
final CharSequence password = context.getPassword();
AES256JNCryptorInputStream encryptInput = null;
try {
final byte[] version = new byte[1];
input.read(version);
encryptInput = new AES256JNCryptorInputStream(
input,
Utils.toCharArray(context.getPassword()));
final SequenceInputStream sequenceInputStream = new SequenceInputStream(
new ByteArrayInputStream(version),
input);
IOUtils.copyLarge(encryptInput, output);
if (version[0] == 3) {
encryptInput.close();
output.flush();
output.close();
if (log.isDebugEnabled()) {
log.debug("*** Start streaming asynchronous decryption");
}
AES256JNCryptorInputStream encryptInput = null;
try {
encryptInput = new AES256JNCryptorInputStream(
sequenceInputStream,
Utils.toCharArray(password));
IOUtils.copyLarge(encryptInput, output);
} catch (final IOException e) {
log.error("Error while trying to read/write form/to streams: ", e);
} finally {
IOUtils.closeQuietly(encryptInput);
}
} else {
// AES256JNCryptorInputStream supports only decryption of AES256 version 3 encrypted data
// Workaround: stop streaming and use AES256JNCryptor which supports both, version 2 and 3
log.info("Trying to decrypt with AES256JNCryptor by load all data into memory...");
try {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
IOUtils.copyLarge(sequenceInputStream, out);
final byte[] ciphertext = out.toByteArray();
final AES256JNCryptor cryptor = new AES256JNCryptor();
cryptor.setPBKDFIterations(10000);
final PasswordKey passwordKey = cryptor.getPasswordKey(Utils.toCharArray(password));
final int versionNumber = cryptor.getVersionNumber();
final byte[] decryptData = cryptor.decryptData(ciphertext, Utils.toCharArray(password));
final ByteArrayInputStream decryptedIn = new ByteArrayInputStream(decryptData);
IOUtils.copyLarge(decryptedIn, output);
} catch (final IOException | CryptorException e) {
log.error("Error while trying to none-streaming decrypt: ", e);
}
}
} catch (final IOException e) {
log.error("Error while trying to read/write form/to streams: ", e);
log.error("Unexpected error while decryption: ", e);
} finally {
try {
if (encryptInput != null)
encryptInput.close();
output.flush();
output.close();
} catch (final IOException e) {
log.error("Failed to close AES256JNCryptorOutputStream: ", e);
log.error("Failed to close streams");
}
if (log.isDebugEnabled()) {

View file

@ -8,23 +8,28 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.SequenceInputStream;
import java.security.cert.Certificate;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.Future;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
@ -99,7 +104,7 @@ public final class SebConfigEncryptionServiceImpl implements SebConfigEncryption
}
@Override
public void streamDecrypted(
public Future<Exception> streamDecrypted(
final OutputStream output,
final InputStream input,
final SebConfigEncryptionContext context) {
@ -110,20 +115,48 @@ public final class SebConfigEncryptionServiceImpl implements SebConfigEncryption
pout = new PipedOutputStream();
pin = new PipedInputStream(pout);
final Strategy strategy = verifyStrategy(input);
Strategy strategy = null;
final byte[] header = new byte[HEADER_SIZE];
input.read(header);
for (final Strategy s : Strategy.values()) {
if (Arrays.equals(s.header, header)) {
strategy = s;
break;
}
}
InputStream newIn = null;
if (strategy == null) {
strategy = Strategy.PLAIN_TEXT;
newIn = new SequenceInputStream(
new ByteArrayInputStream(header),
input);
} else {
newIn = input;
}
if (log.isDebugEnabled()) {
log.debug("Password decryption with strategy: {}", strategy);
}
if ((strategy == Strategy.PASSWORD_PSWD || strategy == Strategy.PASSWORD_PWCC)
&& StringUtils.isBlank(context.getPassword())) {
return new AsyncResult<>(new IllegalArgumentException("Missing Password"));
}
getEncryptor(strategy)
.getOrThrow()
.decrypt(pout, input, context);
.decrypt(pout, newIn, context);
IOUtils.copyLarge(pin, output);
return new AsyncResult<>(null);
} catch (final IOException e) {
log.error("Error while stream decrypted data: ", e);
return new AsyncResult<>(e);
} catch (final Exception iae) {
return new AsyncResult<>(iae);
} finally {
try {
if (pin != null) {
@ -145,22 +178,6 @@ public final class SebConfigEncryptionServiceImpl implements SebConfigEncryption
}
}
private Strategy verifyStrategy(final InputStream input) {
try {
final byte[] header = new byte[HEADER_SIZE];
input.read(header);
for (final Strategy s : Strategy.values()) {
if (Arrays.equals(s.header, header)) {
return s;
}
}
throw new IllegalStateException("Failed to verify decryption strategy from input stream");
} catch (final IOException e) {
log.error("Failed to read decryption strategy from input stream");
throw new IllegalStateException("Failed to verify decryption strategy from input stream");
}
}
private Result<SebConfigCryptor> getEncryptor(final Strategy strategy) {
final SebConfigCryptor encryptor = this.encryptors.get(strategy);
if (encryptor == null) {
@ -233,6 +250,10 @@ public final class SebConfigEncryptionServiceImpl implements SebConfigEncryption
return new EncryptionContext(Strategy.PLAIN_TEXT, null, null);
}
public static SebConfigEncryptionContext contextOf(final CharSequence password) {
return new EncryptionContext(null, password, null);
}
}
}

View file

@ -15,9 +15,9 @@ import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.SequenceInputStream;
import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Future;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -29,6 +29,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException;
import ch.ethz.seb.sebserver.gbl.api.APIMessage.FieldValidationException;
@ -321,7 +322,7 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
}
@Override
public Result<Configuration> importFromXML(
public Result<Configuration> importFromSEBFile(
final Long configNodeId,
final InputStream input,
final CharSequence password) {
@ -332,44 +333,22 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
.saveToHistory(configNodeId)
.getOrThrow();
Future<Exception> streamDecrypted = null;
try {
final byte[] header = new byte[4];
input.read(header);
final InputStream cryptIn = this.unzip(input);
final PipedInputStream plainIn = new PipedInputStream();
final PipedOutputStream cryptOut = new PipedOutputStream(plainIn);
Strategy strategy = null;
try {
strategy = SebConfigEncryptionService.Strategy.getStrategy(header);
} catch (final IllegalArgumentException iae) {
streamDecrypted = this.sebConfigEncryptionService.streamDecrypted(
cryptOut,
cryptIn,
EncryptionContext.contextOf(password));
log.info("{} : Trying to import as unzipped plain text configuration", iae.getMessage());
importPlainOnly(input, newConfig, header);
return newConfig;
}
if (strategy != null) {
final InputStream cryptIn = this.unzip(input);
final PipedInputStream plainIn = new PipedInputStream();
final PipedOutputStream cryptOut = new PipedOutputStream(plainIn);
try {
this.sebConfigEncryptionService.streamDecrypted(
cryptOut,
cryptIn,
EncryptionContext.contextOf(strategy, password));
this.examConfigIO.importPlainXML(
plainIn,
newConfig.institutionId,
newConfig.id);
} finally {
IOUtils.closeQuietly(cryptIn);
IOUtils.closeQuietly(cryptOut);
IOUtils.closeQuietly(plainIn);
}
}
this.examConfigIO.importPlainXML(
plainIn,
newConfig.institutionId,
newConfig.id);
return newConfig;
@ -380,16 +359,26 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
.undo(configNodeId)
.getOrThrow();
throw new RuntimeException("Failed to import SEB configuration. Cause is: " + e.getMessage(), e);
if (streamDecrypted != null) {
final Exception exception = streamDecrypted.get();
if (exception != null) {
throw exception;
}
}
throw new RuntimeException("Failed to import SEB configuration. Cause is: " + e.getMessage());
}
});
}
private InputStream unzip(final InputStream input) throws Exception {
final byte[] zipHeader = new byte[4];
final byte[] zipHeader = new byte[Constants.GZIP_HEADER_LENGTH];
input.read(zipHeader);
final int zipType = ByteBuffer.wrap(zipHeader).getInt();
final boolean isZipped = zipType == 0x504B0304 || zipType == 0x504B0506 || zipType == 0x504B0708;
//final int zipType = ByteBuffer.wrap(zipHeader).getInt();
final boolean isZipped = Byte.toUnsignedInt(zipHeader[0]) == Constants.GZIP_ID1
&& Byte.toUnsignedInt(zipHeader[1]) == Constants.GZIP_ID2
&& Byte.toUnsignedInt(zipHeader[2]) == Constants.GZIP_CM;
if (isZipped) {
@ -409,33 +398,6 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
}
}
private void importPlainOnly(
final InputStream input,
final Configuration newConfig,
final byte[] header) throws IOException {
PipedInputStream plainIn = null;
PipedOutputStream out = null;
try {
plainIn = new PipedInputStream();
out = new PipedOutputStream(plainIn);
this.examConfigIO.importPlainXML(
plainIn,
newConfig.institutionId,
newConfig.id);
out.write(header);
IOUtils.copyLarge(input, out);
} catch (final Exception e) {
log.error("Error while stream plain text SEB Configuration import data: ", e);
throw e;
} finally {
IOUtils.closeQuietly(out);
}
}
private void exportPlainOnly(
final ConfigurationFormat exportFormat,
final OutputStream out,

View file

@ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import org.apache.tomcat.util.http.fileupload.IOUtils;
@ -66,7 +67,31 @@ public class ZipServiceImpl implements ZipService {
@Override
public void read(final OutputStream out, final InputStream in) {
// TODO Auto-generated method stub
if (log.isDebugEnabled()) {
log.debug("*** Start streaming asynchronous unzipping of SEB exam configuration data");
}
GZIPInputStream zipInputStream = null;
try {
zipInputStream = new GZIPInputStream(in);
IOUtils.copyLarge(zipInputStream, out);
} catch (final IOException e) {
log.error("Error while streaming data to unzipped output: ", e);
} finally {
try {
out.flush();
out.close();
} catch (final IOException e) {
log.error("Failed to close OutputStream: ", e);
}
if (log.isDebugEnabled()) {
log.debug("*** Finish streaming asynchronous unzipping of SEB exam configuration data");
}
}
}

View file

@ -176,7 +176,7 @@ public class ConfigurationNodeController extends EntityController<ConfigurationN
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
final HttpServletRequest request) throws IOException {
return this.sebExamConfigService.importFromXML(
return this.sebExamConfigService.importFromSEBFile(
modelId,
request.getInputStream(),
password)

View file

@ -133,7 +133,7 @@ INSERT IGNORE INTO configuration_attribute VALUES
(73, 'permittedProcesses', 'TABLE', null, null, null, null, null),
(74, 'permittedProcesses.active', 'CHECKBOX', 73, null, null, null, 'true'),
(75, 'permittedProcesses.os', 'SINGLE_SELECTION', 73, '0,1', null, null, '0'),
(75, 'permittedProcesses.os', 'SINGLE_SELECTION', 73, '0,1', null, null, '1'),
(76, 'permittedProcesses.title', 'TEXT_FIELD', 73, null, null, null, ''),
(77, 'permittedProcesses.description', 'TEXT_FIELD', 73, null, null, null, ''),
(78, 'permittedProcesses.executable', 'TEXT_FIELD', 73, null, null, null, ''),
@ -152,7 +152,7 @@ INSERT IGNORE INTO configuration_attribute VALUES
(93, 'prohibitedProcesses', 'TABLE', null, null, null, null, null),
(94, 'prohibitedProcesses.active', 'CHECKBOX', 93, null, null, null, 'true'),
(95, 'prohibitedProcesses.os', 'SINGLE_SELECTION', 93, '0,1', null, null, '0'),
(95, 'prohibitedProcesses.os', 'SINGLE_SELECTION', 93, '0,1', null, null, '1'),
(96, 'prohibitedProcesses.executable', 'TEXT_FIELD', 93, null, null, null, ''),
(97, 'prohibitedProcesses.description', 'TEXT_FIELD', 93, null, null, null, ''),
(98, 'prohibitedProcesses.originalName', 'TEXT_FIELD', 93, null, null, null, ''),

View file

@ -107,7 +107,7 @@ INSERT IGNORE INTO configuration_attribute VALUES
(73, 'permittedProcesses', 'TABLE', null, null, null, null, null),
(74, 'permittedProcesses.active', 'CHECKBOX', 73, null, null, null, 'true'),
(75, 'permittedProcesses.os', 'SINGLE_SELECTION', 73, '0,1', null, null, '0'),
(75, 'permittedProcesses.os', 'SINGLE_SELECTION', 73, '0,1', null, null, '1'),
(76, 'permittedProcesses.title', 'TEXT_FIELD', 73, null, null, null, ''),
(77, 'permittedProcesses.description', 'TEXT_FIELD', 73, null, null, null, ''),
(78, 'permittedProcesses.executable', 'TEXT_FIELD', 73, null, null, null, ''),
@ -126,7 +126,7 @@ INSERT IGNORE INTO configuration_attribute VALUES
(93, 'prohibitedProcesses', 'TABLE', null, null, null, null, null),
(94, 'prohibitedProcesses.active', 'CHECKBOX', 93, null, null, null, 'true'),
(95, 'prohibitedProcesses.os', 'SINGLE_SELECTION', 93, '0,1', null, null, '0'),
(95, 'prohibitedProcesses.os', 'SINGLE_SELECTION', 93, '0,1', null, null, '1'),
(96, 'prohibitedProcesses.executable', 'TEXT_FIELD', 93, null, null, null, ''),
(97, 'prohibitedProcesses.description', 'TEXT_FIELD', 93, null, null, null, ''),
(98, 'prohibitedProcesses.originalName', 'TEXT_FIELD', 93, null, null, null, ''),

View file

@ -681,8 +681,8 @@ sebserver.examconfig.props.label.permittedProcesses.active=Active
sebserver.examconfig.props.label.permittedProcesses.active.tooltip=This permitted process item is active.
sebserver.examconfig.props.label.permittedProcesses.os=OS
sebserver.examconfig.props.label.permittedProcesses.os.tooltip=Indicates on which operating system the permitted process runs.
sebserver.examconfig.props.label.permittedProcesses.os.0=Win
sebserver.examconfig.props.label.permittedProcesses.os.1=OS X
sebserver.examconfig.props.label.permittedProcesses.os.0=OS X
sebserver.examconfig.props.label.permittedProcesses.os.1=Win
sebserver.examconfig.props.label.permittedProcesses.title=Title
sebserver.examconfig.props.label.permittedProcesses.title.tooltip=Application title which is displayed in the application chooser. Background processes don't have a title, because they can't be selected by users.
sebserver.examconfig.props.label.permittedProcesses.description=Description
@ -714,8 +714,8 @@ sebserver.examconfig.props.label.prohibitedProcesses=Prohibited Processes
sebserver.examconfig.props.label.prohibitedProcesses.active=Active
sebserver.examconfig.props.label.prohibitedProcesses.active.tooltip=Indicates if this prohibited process item is active.
sebserver.examconfig.props.label.prohibitedProcesses.os=OS
sebserver.examconfig.props.label.prohibitedProcesses.os.0=Win
sebserver.examconfig.props.label.prohibitedProcesses.os.1=OS X
sebserver.examconfig.props.label.prohibitedProcesses.os.0=OS X
sebserver.examconfig.props.label.prohibitedProcesses.os.1=Win
sebserver.examconfig.props.label.prohibitedProcesses.description=Description
sebserver.examconfig.props.label.prohibitedProcesses.description.tooltip=Optional, to explain what kind of process this is, because this might not be obvious only from the executable's name.
sebserver.examconfig.props.label.prohibitedProcesses.executable=Executable

View file

@ -15,7 +15,7 @@ import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
import org.junit.Test;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;

View file

@ -15,7 +15,7 @@ import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import org.junit.jupiter.api.Test;
import org.junit.Test;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
@ -155,18 +155,11 @@ public class ExamConfigImportHandlerTest {
candidate.endElement(null, null, "plist");
assertFalse(valueCollector.values.isEmpty());
assertTrue(valueCollector.values.size() == 3);
assertTrue(valueCollector.values.size() == 1);
final ConfigurationValue configurationValue1 = valueCollector.values.get(0);
assertEquals("val1", configurationValue1.value);
assertEquals("val1,val2,val3", configurationValue1.value);
assertTrue(configurationValue1.listIndex == 0);
final ConfigurationValue configurationValue2 = valueCollector.values.get(1);
assertEquals("val2", configurationValue2.value);
assertTrue(configurationValue2.listIndex == 1);
final ConfigurationValue configurationValue3 = valueCollector.values.get(2);
assertEquals("val3", configurationValue3.value);
assertTrue(configurationValue3.listIndex == 2);
}
@Test

View file

@ -114,7 +114,7 @@ INSERT IGNORE INTO configuration_attribute VALUES
(73, 'permittedProcesses', 'TABLE', null, null, null, null, null),
(74, 'permittedProcesses.active', 'CHECKBOX', 73, null, null, null, 'true'),
(75, 'permittedProcesses.os', 'SINGLE_SELECTION', 73, '0,1', null, null, '0'),
(75, 'permittedProcesses.os', 'SINGLE_SELECTION', 73, '0,1', null, null, '1'),
(76, 'permittedProcesses.title', 'TEXT_FIELD', 73, null, null, null, ''),
(77, 'permittedProcesses.description', 'TEXT_FIELD', 73, null, null, null, ''),
(78, 'permittedProcesses.executable', 'TEXT_FIELD', 73, null, null, null, ''),
@ -133,7 +133,7 @@ INSERT IGNORE INTO configuration_attribute VALUES
(93, 'prohibitedProcesses', 'TABLE', null, null, null, null, null),
(94, 'prohibitedProcesses.active', 'CHECKBOX', 93, null, null, null, 'true'),
(95, 'prohibitedProcesses.os', 'SINGLE_SELECTION', 93, '0,1', null, null, '0'),
(95, 'prohibitedProcesses.os', 'SINGLE_SELECTION', 93, '0,1', null, null, '1'),
(96, 'prohibitedProcesses.executable', 'TEXT_FIELD', 93, null, null, null, ''),
(97, 'prohibitedProcesses.description', 'TEXT_FIELD', 93, null, null, null, ''),
(98, 'prohibitedProcesses.originalName', 'TEXT_FIELD', 93, null, null, null, ''),