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_1 = 1;
public static final int RWT_MOUSE_BUTTON_2 = 2; public static final int RWT_MOUSE_BUTTON_2 = 2;
public static final int RWT_MOUSE_BUTTON_3 = 3; 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() final Configuration configuration = pageService.getRestService()
.getBuilder(ImportExamConfig.class) .getBuilder(ImportExamConfig.class)
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
.withHeader(
API.IMPORT_PASSWORD_ATTR_NAME,
form.getFieldValue(API.IMPORT_PASSWORD_ATTR_NAME))
.withBody(inputStream) .withBody(inputStream)
.call() .call()
.get(context::notifyError); .get(context::notifyError);

View file

@ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Arrays; import java.util.Arrays;
import java.util.concurrent.Future;
import org.springframework.scheduling.annotation.Async; 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 input the input stream to read the cipher text from
* @param context the SebConfigEncryptionContext to access strategy specific data needed for encryption */ * @param context the SebConfigEncryptionContext to access strategy specific data needed for encryption */
@Async(AsyncServiceSpringConfig.EXECUTOR_BEAN_NAME) @Async(AsyncServiceSpringConfig.EXECUTOR_BEAN_NAME)
void streamDecrypted( Future<Exception> streamDecrypted(
final OutputStream output, final OutputStream output,
final InputStream input, final InputStream input,
final SebConfigEncryptionContext context); 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 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 * @param password A password is only needed if the file is in an encrypted format
* @return The newly created Configuration instance */ * @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 in The InputString to constantly read the XML from
* @param institutionId the institionId of the import * @param institutionId the institionId of the import
* @param configurationId the identifier of the internal configuration to apply the imported values to */ * @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) { void importPlainXML(final InputStream in, final Long institutionId, final Long configurationId) {
try { try {
// get all attributes and map the names to ids // 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.Consumer;
import java.util.function.Function; import java.util.function.Function;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.xml.sax.Attributes; import org.xml.sax.Attributes;
@ -164,8 +165,8 @@ public class ExamConfigImportHandler extends DefaultHandler {
private void startValueElement(final Type type, final PListNode top) { private void startValueElement(final Type type, final PListNode top) {
final PListNode value = new PListNode(type); final PListNode value = new PListNode(type);
if (top.type == Type.KEY) { if (top.type == Type.KEY) {
if (Type.isBooleanValue(type)) { if (Type.isBooleanValue(type)) {
this.stack.pop(); this.stack.pop();
value.name = top.name; value.name = top.name;
@ -210,40 +211,64 @@ public class ExamConfigImportHandler extends DefaultHandler {
final PListNode grandParent = this.stack.peek(); final PListNode grandParent = this.stack.peek();
this.stack.push(parent); 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) final String attrName = (parent.type == Type.DICT && grandParent.type == Type.ARRAY)
? parent.name + "." + top.name ? parent.name + "." + top.name
: top.name; : top.name;
final Long attributeId = this.attributeNameIdResolver.apply(attrName); final Long attributeId = this.attributeNameIdResolver.apply(attrName);
if (attributeId == null) { if (attributeId == null) {
if (log.isDebugEnabled()) { if (log.isDebugEnabled()) {
log.debug("Skip unknown configuration attribute: {}", attrName); log.debug("Skip unknown configuration attribute: {}", attrName);
} }
} else { } else {
saveValue(attrName, attributeId, top.listIndex, top.value);
// 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);
} }
} }
} 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)) { } else if (!Constants.XML_PLIST_KEY_NAME.equals(qName)) {
this.stack.pop(); 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 @Override
public void characters( public void characters(
final char[] ch, final char[] ch,

View file

@ -8,15 +8,20 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl; package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.SequenceInputStream;
import java.util.Set; import java.util.Set;
import org.apache.tomcat.util.http.fileupload.IOUtils; import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.cryptonode.jncryptor.AES256JNCryptor;
import org.cryptonode.jncryptor.AES256JNCryptorInputStream; import org.cryptonode.jncryptor.AES256JNCryptorInputStream;
import org.cryptonode.jncryptor.AES256JNCryptorOutputStream; import org.cryptonode.jncryptor.AES256JNCryptorOutputStream;
import org.cryptonode.jncryptor.CryptorException; import org.cryptonode.jncryptor.CryptorException;
import org.cryptonode.jncryptor.PasswordKey;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
@ -91,31 +96,66 @@ public class PasswordEncryptor implements SebConfigCryptor {
final InputStream input, final InputStream input,
final SebConfigEncryptionContext context) { final SebConfigEncryptionContext context) {
if (log.isDebugEnabled()) { final CharSequence password = context.getPassword();
log.debug("*** Start streaming asynchronous decryption");
}
AES256JNCryptorInputStream encryptInput = null;
try { try {
final byte[] version = new byte[1];
input.read(version);
encryptInput = new AES256JNCryptorInputStream( final SequenceInputStream sequenceInputStream = new SequenceInputStream(
input, new ByteArrayInputStream(version),
Utils.toCharArray(context.getPassword())); input);
IOUtils.copyLarge(encryptInput, output); if (version[0] == 3) {
encryptInput.close(); if (log.isDebugEnabled()) {
output.flush(); log.debug("*** Start streaming asynchronous decryption");
output.close(); }
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) { } catch (final IOException e) {
log.error("Error while trying to read/write form/to streams: ", e); log.error("Unexpected error while decryption: ", e);
} finally { } finally {
try { try {
if (encryptInput != null) output.flush();
encryptInput.close(); output.close();
} catch (final IOException e) { } catch (final IOException e) {
log.error("Failed to close AES256JNCryptorOutputStream: ", e); log.error("Failed to close streams");
} }
if (log.isDebugEnabled()) { if (log.isDebugEnabled()) {

View file

@ -8,23 +8,28 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl; package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl;
import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.PipedInputStream; import java.io.PipedInputStream;
import java.io.PipedOutputStream; import java.io.PipedOutputStream;
import java.io.SequenceInputStream;
import java.security.cert.Certificate; import java.security.cert.Certificate;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Map; import java.util.Map;
import java.util.concurrent.Future;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.ImmutablePair;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
@ -99,7 +104,7 @@ public final class SebConfigEncryptionServiceImpl implements SebConfigEncryption
} }
@Override @Override
public void streamDecrypted( public Future<Exception> streamDecrypted(
final OutputStream output, final OutputStream output,
final InputStream input, final InputStream input,
final SebConfigEncryptionContext context) { final SebConfigEncryptionContext context) {
@ -110,20 +115,48 @@ public final class SebConfigEncryptionServiceImpl implements SebConfigEncryption
pout = new PipedOutputStream(); pout = new PipedOutputStream();
pin = new PipedInputStream(pout); 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()) { if (log.isDebugEnabled()) {
log.debug("Password decryption with strategy: {}", strategy); 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) getEncryptor(strategy)
.getOrThrow() .getOrThrow()
.decrypt(pout, input, context); .decrypt(pout, newIn, context);
IOUtils.copyLarge(pin, output); IOUtils.copyLarge(pin, output);
return new AsyncResult<>(null);
} catch (final IOException e) { } catch (final IOException e) {
log.error("Error while stream decrypted data: ", e); log.error("Error while stream decrypted data: ", e);
return new AsyncResult<>(e);
} catch (final Exception iae) {
return new AsyncResult<>(iae);
} finally { } finally {
try { try {
if (pin != null) { 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) { private Result<SebConfigCryptor> getEncryptor(final Strategy strategy) {
final SebConfigCryptor encryptor = this.encryptors.get(strategy); final SebConfigCryptor encryptor = this.encryptors.get(strategy);
if (encryptor == null) { if (encryptor == null) {
@ -233,6 +250,10 @@ public final class SebConfigEncryptionServiceImpl implements SebConfigEncryption
return new EncryptionContext(Strategy.PLAIN_TEXT, null, null); 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.PipedInputStream;
import java.io.PipedOutputStream; import java.io.PipedOutputStream;
import java.io.SequenceInputStream; import java.io.SequenceInputStream;
import java.nio.ByteBuffer;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.concurrent.Future;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -29,6 +29,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service; 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;
import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException; import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException;
import ch.ethz.seb.sebserver.gbl.api.APIMessage.FieldValidationException; import ch.ethz.seb.sebserver.gbl.api.APIMessage.FieldValidationException;
@ -321,7 +322,7 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
} }
@Override @Override
public Result<Configuration> importFromXML( public Result<Configuration> importFromSEBFile(
final Long configNodeId, final Long configNodeId,
final InputStream input, final InputStream input,
final CharSequence password) { final CharSequence password) {
@ -332,44 +333,22 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
.saveToHistory(configNodeId) .saveToHistory(configNodeId)
.getOrThrow(); .getOrThrow();
Future<Exception> streamDecrypted = null;
try { try {
final byte[] header = new byte[4]; final InputStream cryptIn = this.unzip(input);
input.read(header); final PipedInputStream plainIn = new PipedInputStream();
final PipedOutputStream cryptOut = new PipedOutputStream(plainIn);
Strategy strategy = null; streamDecrypted = this.sebConfigEncryptionService.streamDecrypted(
try { cryptOut,
strategy = SebConfigEncryptionService.Strategy.getStrategy(header); cryptIn,
} catch (final IllegalArgumentException iae) { EncryptionContext.contextOf(password));
log.info("{} : Trying to import as unzipped plain text configuration", iae.getMessage()); this.examConfigIO.importPlainXML(
plainIn,
importPlainOnly(input, newConfig, header); newConfig.institutionId,
return newConfig; newConfig.id);
}
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);
}
}
return newConfig; return newConfig;
@ -380,16 +359,26 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
.undo(configNodeId) .undo(configNodeId)
.getOrThrow(); .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 { 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); 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) { 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( private void exportPlainOnly(
final ConfigurationFormat exportFormat, final ConfigurationFormat exportFormat,
final OutputStream out, 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.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream; import java.util.zip.GZIPOutputStream;
import org.apache.tomcat.util.http.fileupload.IOUtils; import org.apache.tomcat.util.http.fileupload.IOUtils;
@ -66,7 +67,31 @@ public class ZipServiceImpl implements ZipService {
@Override @Override
public void read(final OutputStream out, final InputStream in) { 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, defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
final HttpServletRequest request) throws IOException { final HttpServletRequest request) throws IOException {
return this.sebExamConfigService.importFromXML( return this.sebExamConfigService.importFromSEBFile(
modelId, modelId,
request.getInputStream(), request.getInputStream(),
password) password)

View file

@ -133,7 +133,7 @@ INSERT IGNORE INTO configuration_attribute VALUES
(73, 'permittedProcesses', 'TABLE', null, null, null, null, null), (73, 'permittedProcesses', 'TABLE', null, null, null, null, null),
(74, 'permittedProcesses.active', 'CHECKBOX', 73, null, null, null, 'true'), (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, ''), (76, 'permittedProcesses.title', 'TEXT_FIELD', 73, null, null, null, ''),
(77, 'permittedProcesses.description', 'TEXT_FIELD', 73, null, null, null, ''), (77, 'permittedProcesses.description', 'TEXT_FIELD', 73, null, null, null, ''),
(78, 'permittedProcesses.executable', '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), (93, 'prohibitedProcesses', 'TABLE', null, null, null, null, null),
(94, 'prohibitedProcesses.active', 'CHECKBOX', 93, null, null, null, 'true'), (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, ''), (96, 'prohibitedProcesses.executable', 'TEXT_FIELD', 93, null, null, null, ''),
(97, 'prohibitedProcesses.description', 'TEXT_FIELD', 93, null, null, null, ''), (97, 'prohibitedProcesses.description', 'TEXT_FIELD', 93, null, null, null, ''),
(98, 'prohibitedProcesses.originalName', '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), (73, 'permittedProcesses', 'TABLE', null, null, null, null, null),
(74, 'permittedProcesses.active', 'CHECKBOX', 73, null, null, null, 'true'), (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, ''), (76, 'permittedProcesses.title', 'TEXT_FIELD', 73, null, null, null, ''),
(77, 'permittedProcesses.description', 'TEXT_FIELD', 73, null, null, null, ''), (77, 'permittedProcesses.description', 'TEXT_FIELD', 73, null, null, null, ''),
(78, 'permittedProcesses.executable', '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), (93, 'prohibitedProcesses', 'TABLE', null, null, null, null, null),
(94, 'prohibitedProcesses.active', 'CHECKBOX', 93, null, null, null, 'true'), (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, ''), (96, 'prohibitedProcesses.executable', 'TEXT_FIELD', 93, null, null, null, ''),
(97, 'prohibitedProcesses.description', 'TEXT_FIELD', 93, null, null, null, ''), (97, 'prohibitedProcesses.description', 'TEXT_FIELD', 93, null, null, null, ''),
(98, 'prohibitedProcesses.originalName', '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.active.tooltip=This permitted process item is active.
sebserver.examconfig.props.label.permittedProcesses.os=OS 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.tooltip=Indicates on which operating system the permitted process runs.
sebserver.examconfig.props.label.permittedProcesses.os.0=Win sebserver.examconfig.props.label.permittedProcesses.os.0=OS X
sebserver.examconfig.props.label.permittedProcesses.os.1=OS X sebserver.examconfig.props.label.permittedProcesses.os.1=Win
sebserver.examconfig.props.label.permittedProcesses.title=Title 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.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 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=Active
sebserver.examconfig.props.label.prohibitedProcesses.active.tooltip=Indicates if this prohibited process item is 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=OS
sebserver.examconfig.props.label.prohibitedProcesses.os.0=Win sebserver.examconfig.props.label.prohibitedProcesses.os.0=OS X
sebserver.examconfig.props.label.prohibitedProcesses.os.1=OS X sebserver.examconfig.props.label.prohibitedProcesses.os.1=Win
sebserver.examconfig.props.label.prohibitedProcesses.description=Description 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.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 sebserver.examconfig.props.label.prohibitedProcesses.executable=Executable

View file

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

View file

@ -114,7 +114,7 @@ INSERT IGNORE INTO configuration_attribute VALUES
(73, 'permittedProcesses', 'TABLE', null, null, null, null, null), (73, 'permittedProcesses', 'TABLE', null, null, null, null, null),
(74, 'permittedProcesses.active', 'CHECKBOX', 73, null, null, null, 'true'), (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, ''), (76, 'permittedProcesses.title', 'TEXT_FIELD', 73, null, null, null, ''),
(77, 'permittedProcesses.description', 'TEXT_FIELD', 73, null, null, null, ''), (77, 'permittedProcesses.description', 'TEXT_FIELD', 73, null, null, null, ''),
(78, 'permittedProcesses.executable', '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), (93, 'prohibitedProcesses', 'TABLE', null, null, null, null, null),
(94, 'prohibitedProcesses.active', 'CHECKBOX', 93, null, null, null, 'true'), (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, ''), (96, 'prohibitedProcesses.executable', 'TEXT_FIELD', 93, null, null, null, ''),
(97, 'prohibitedProcesses.description', 'TEXT_FIELD', 93, null, null, null, ''), (97, 'prohibitedProcesses.description', 'TEXT_FIELD', 93, null, null, null, ''),
(98, 'prohibitedProcesses.originalName', 'TEXT_FIELD', 93, null, null, null, ''), (98, 'prohibitedProcesses.originalName', 'TEXT_FIELD', 93, null, null, null, ''),