SEBSERV-46 implementation back-end and part of front-end

This commit is contained in:
anhefti 2019-10-03 16:44:27 +02:00
parent 09b326fdfe
commit 8c8a0944cb
30 changed files with 1341 additions and 107 deletions

View file

@ -60,6 +60,15 @@ public final class Constants {
public static final String XML_DICT_END =
"</dict>";
public static final String XML_PLIST_NAME = "plist";
public static final String XML_PLIST_DICT_NAME = "dict";
public static final String XML_PLIST_ARRAY_NAME = "array";
public static final String XML_PLIST_KEY_NAME = "key";
public static final String XML_PLIST_BOOLEAN_TRUE = "true";
public static final String XML_PLIST_BOOLEAN_FALSE = "false";
public static final String XML_PLIST_STRING = "string";
public static final String XML_PLIST_INTEGER = "integer";
public static final String OAUTH2_GRANT_TYPE_PASSWORD = "password";
public static final String OAUTH2_GRANT_TYPE_REFRESH_TOKEN = "refresh_token";
public static final String OAUTH2_GRANT_TYPE_CLIENT_CREDENTIALS = "client_credentials";

View file

@ -16,6 +16,7 @@ public final class API {
ACTIVATE;
}
public static final String SEB_FILE_EXTENSION = "seb";
public static final String PARAM_LOGO_IMAGE = "logoImageBase64";
public static final String PARAM_INSTITUTION_ID = "institutionId";
public static final String PARAM_MODEL_ID = "modelId";
@ -119,6 +120,7 @@ public final class API {
public static final String CONFIGURATION_TABLE_VALUE_PATH_SEGMENT = "/table";
public static final String CONFIGURATION_ATTRIBUTE_ENDPOINT = "/configuration_attribute";
public static final String CONFIGURATION_PLAIN_XML_DOWNLOAD_PATH_SEGMENT = "/downloadxml";
public static final String CONFIGURATION_IMPORT_PATH_SEGMENT = "/import";
public static final String ORIENTATION_ENDPOINT = "/orientation";
public static final String VIEW_ENDPOINT = ORIENTATION_ENDPOINT + "/view";

View file

@ -46,6 +46,8 @@ public final class Utils {
public static final Predicate<?> TRUE_PREDICATE = v -> true;
public static final Predicate<?> FALSE_PREDICATE = v -> false;
public static final Runnable EMPTY_EXECUTION = () -> {
};
private static final Logger log = LoggerFactory.getLogger(Utils.class);

View file

@ -8,12 +8,15 @@
package ch.ethz.seb.sebserver.gui.content;
import java.io.InputStream;
import java.util.function.Function;
import java.util.function.Supplier;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.rap.rwt.client.service.UrlLauncher;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Text;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -29,11 +32,14 @@ import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode.ConfigurationType;
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
import ch.ethz.seb.sebserver.gui.form.Form;
import ch.ethz.seb.sebserver.gui.form.FormBuilder;
import ch.ethz.seb.sebserver.gui.form.FormHandle;
import ch.ethz.seb.sebserver.gui.service.ResourceService;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.page.ModalInputDialogComposer;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.PageService;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
@ -48,6 +54,7 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.Ne
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.SaveExamConfig;
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.FileUploadSelection;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant;
@ -58,6 +65,8 @@ public class SebExamConfigPropForm implements TemplateComposer {
private static final Logger log = LoggerFactory.getLogger(SebExamConfigPropForm.class);
private static final String PASSWORD_ATTR_NAME = "importFilePassword";
private static final String IMPORT_FILE_ATTR_NAME = "importFile";
private static final LocTextKey FORM_TITLE_NEW =
new LocTextKey("sebserver.examconfig.form.title.new");
private static final LocTextKey FORM_TITLE =
@ -68,7 +77,12 @@ public class SebExamConfigPropForm implements TemplateComposer {
new LocTextKey("sebserver.examconfig.form.description");
private static final LocTextKey FORM_STATUS_TEXT_KEY =
new LocTextKey("sebserver.examconfig.form.status");
private static final LocTextKey FORM_IMPORT_TEXT_KEY =
new LocTextKey("sebserver.examconfig.action.import-config");
private static final LocTextKey FORM_IMPORT_SELECT_TEXT_KEY =
new LocTextKey("sebserver.examconfig.action.import-file-select");
private static final LocTextKey FORM_IMPORT_PASSWORD_TEXT_KEY =
new LocTextKey("sebserver.examconfig.action.import-file-password");
private static final LocTextKey CONFIG_KEY_TITLE_TEXT_KEY =
new LocTextKey("sebserver.examconfig.form.config-key.title");
@ -201,6 +215,12 @@ public class SebExamConfigPropForm implements TemplateComposer {
.noEventPropagation()
.publishIf(() -> readGrant && isReadonly)
.newAction(ActionDefinition.SEB_EXAM_CONFIG_IMPORT_CONFIG)
.withEntityKey(entityKey)
.withExec(SebExamConfigPropForm.importConfigFunction(this.pageService))
.noEventPropagation()
.publishIf(() -> readGrant && isReadonly)
.newAction(ActionDefinition.SEB_EXAM_CONFIG_SAVE)
.withEntityKey(entityKey)
.withExec(formHandle::processFormSave)
@ -247,4 +267,71 @@ public class SebExamConfigPropForm implements TemplateComposer {
};
}
private static Function<PageAction, PageAction> importConfigFunction(final PageService pageService) {
return action -> {
final ModalInputDialog<FormHandle<ConfigurationNode>> dialog =
new ModalInputDialog<FormHandle<ConfigurationNode>>(
action.pageContext().getParent().getShell(),
pageService.getWidgetFactory())
.setDialogWidth(600);
final ImportFormBuilder importFormBuilder = new ImportFormBuilder(
pageService,
action.pageContext());
dialog.open(
FORM_IMPORT_TEXT_KEY,
SebExamConfigPropForm::doImport,
Utils.EMPTY_EXECUTION,
importFormBuilder);
return action;
};
}
// TODO
private static final void doImport(final FormHandle<ConfigurationNode> formHandle) {
final Form form = formHandle.getForm();
final EntityKey entityKey = formHandle.getContext().getEntityKey();
final Control fieldControl = form.getFieldControl(IMPORT_FILE_ATTR_NAME);
if (fieldControl != null && fieldControl instanceof FileUploadSelection) {
final InputStream inputStream = ((FileUploadSelection) fieldControl).getInputStream();
if (inputStream != null) {
// TODO
}
}
}
private static final class ImportFormBuilder implements ModalInputDialogComposer<FormHandle<ConfigurationNode>> {
private final PageService pageService;
private final PageContext pageContext;
protected ImportFormBuilder(final PageService pageService, final PageContext pageContext) {
this.pageService = pageService;
this.pageContext = pageContext;
}
@Override
public Supplier<FormHandle<ConfigurationNode>> compose(final Composite parent) {
final FormHandle<ConfigurationNode> formHandle = this.pageService.formBuilder(
this.pageContext.copyOf(parent), 4)
.readonly(false)
.addField(FormBuilder.fileUpload(
IMPORT_FILE_ATTR_NAME,
FORM_IMPORT_SELECT_TEXT_KEY,
null,
API.SEB_FILE_EXTENSION))
.addField(FormBuilder.text(
PASSWORD_ATTR_NAME,
FORM_IMPORT_PASSWORD_TEXT_KEY,
"").asPasswordField())
.build();
return () -> formHandle;
}
}
}

View file

@ -393,6 +393,10 @@ public enum ActionDefinition {
new LocTextKey("sebserver.examconfig.action.get-config-key"),
ImageIcon.SECURE,
ActionCategory.FORM),
SEB_EXAM_CONFIG_IMPORT_CONFIG(
new LocTextKey("sebserver.examconfig.action.import-config"),
ImageIcon.IMPORT,
ActionCategory.FORM),
SEB_EXAM_CONFIG_MODIFY_FROM_LIST(
new LocTextKey("sebserver.examconfig.action.list.modify"),

View file

@ -0,0 +1,58 @@
/*
* Copyright (c) 2019 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 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.Label;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.widget.FileUploadSelection;
public class FileUploadFieldBuilder extends FieldBuilder<String> {
private final Collection<String> supportedFiles;
FileUploadFieldBuilder(
final String name,
final LocTextKey label,
final String value,
final Collection<String> supportedFiles) {
super(name, label, value);
this.supportedFiles = supportedFiles;
}
@Override
void build(final FormBuilder builder) {
final Label lab = builder.labelLocalized(
builder.formParent,
this.label,
this.defaultLabel,
1);
final Composite fieldGrid = Form.createFieldGrid(builder.formParent, this.spanInput);
final FileUploadSelection fileUpload = builder.widgetFactory.fileUploadSelection(
fieldGrid,
builder.readonly || this.readonly,
this.supportedFiles);
final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false);
fileUpload.setLayoutData(gridData);
fileUpload.setFileName(this.value);
final Label errorLabel = Form.createErrorLabel(fieldGrid);
builder.form.putField(this.name, lab, fileUpload, errorLabel);
builder.setFieldVisible(this.visible, this.name);
}
}

View file

@ -40,7 +40,8 @@ import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.Threshold;
import ch.ethz.seb.sebserver.gbl.util.Tuple;
import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.FormBinding;
import ch.ethz.seb.sebserver.gui.widget.ImageUpload;
import ch.ethz.seb.sebserver.gui.widget.FileUploadSelection;
import ch.ethz.seb.sebserver.gui.widget.ImageUploadSelection;
import ch.ethz.seb.sebserver.gui.widget.Selection;
import ch.ethz.seb.sebserver.gui.widget.Selection.Type;
import ch.ethz.seb.sebserver.gui.widget.ThresholdList;
@ -137,12 +138,19 @@ public final class Form implements FormBinding {
this.formFields.add(name, createAccessor(label, field, errorLabel));
}
void putField(final String name, final Label label, final ImageUpload imageUpload, final Label errorLabel) {
void putField(final String name, final Label label, final ImageUploadSelection imageUpload,
final Label errorLabel) {
final FormFieldAccessor createAccessor = createAccessor(label, imageUpload, errorLabel);
imageUpload.setErrorHandler(createAccessor::setError);
this.formFields.add(name, createAccessor);
}
void putField(final String name, final Label label, final FileUploadSelection fileUpload, final Label errorLabel) {
final FormFieldAccessor createAccessor = createAccessor(label, fileUpload, errorLabel);
fileUpload.setErrorHandler(createAccessor::setError);
this.formFields.add(name, createAccessor);
}
public String getFieldValue(final String attributeName) {
final FormFieldAccessor fieldAccessor = this.formFields.getFirst(attributeName);
if (fieldAccessor == null) {
@ -152,6 +160,15 @@ public final class Form implements FormBinding {
return fieldAccessor.getStringValue();
}
public Control getFieldControl(final String attributeName) {
final FormFieldAccessor fieldAccessor = this.formFields.getFirst(attributeName);
if (fieldAccessor == null) {
return null;
}
return fieldAccessor.control;
}
public void setFieldValue(final String attributeName, final String attributeValue) {
final FormFieldAccessor fieldAccessor = this.formFields.getFirst(attributeName);
if (fieldAccessor == null) {
@ -291,11 +308,16 @@ public final class Form implements FormBinding {
}
};
}
private FormFieldAccessor createAccessor(final Label label, final ImageUpload imageUpload, final Label errorLabel) {
private FormFieldAccessor createAccessor(final Label 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) {
return new FormFieldAccessor(label, fileUpload, errorLabel) {
@Override public String getStringValue() { return fileUpload.getFileName(); }
};
}
//@formatter:on
/*

View file

@ -8,7 +8,9 @@
package ch.ethz.seb.sebserver.gui.form;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.function.BooleanSupplier;
import java.util.function.Supplier;
@ -66,9 +68,9 @@ public class FormBuilder {
this.pageContext = pageContext;
this.form = new Form(pageService.getJSONMapper());
this.formParent = this.widgetFactory
.formGrid(pageContext.getParent(), rows);
this.formParent.setData("TEST");
this.formParent = this.widgetFactory.formGrid(
pageContext.getParent(),
rows);
}
public FormBuilder readonly(final boolean readonly) {
@ -252,6 +254,19 @@ public class FormBuilder {
return new ImageUploadFieldBuilder(name, label, value);
}
public static FileUploadFieldBuilder fileUpload(
final String name,
final LocTextKey label,
final String value,
final String... supportedFiles) {
return new FileUploadFieldBuilder(
name,
label,
value,
(supportedFiles != null) ? Arrays.asList(supportedFiles) : Collections.emptyList());
}
Label labelLocalized(
final Composite parent,
final LocTextKey locTextKey,

View file

@ -54,6 +54,10 @@ public class FormHandle<T extends Entity> {
this.i18nSupport = pageService.getI18nSupport();
}
public PageContext getContext() {
return this.pageContext;
}
public FormBinding getFormBinding() {
return this.form;
}

View file

@ -14,7 +14,7 @@ import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.widget.ImageUpload;
import ch.ethz.seb.sebserver.gui.widget.ImageUploadSelection;
public final class ImageUploadFieldBuilder extends FieldBuilder<String> {
@ -45,7 +45,7 @@ public final class ImageUploadFieldBuilder extends FieldBuilder<String> {
1);
final Composite fieldGrid = Form.createFieldGrid(builder.formParent, this.spanInput);
final ImageUpload imageUpload = builder.widgetFactory.imageUploadLocalized(
final ImageUploadSelection imageUpload = builder.widgetFactory.imageUploadLocalized(
fieldGrid,
new LocTextKey("sebserver.overall.upload"),
builder.readonly || this.readonly,

View file

@ -23,7 +23,7 @@ import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.widget.ImageUpload;
import ch.ethz.seb.sebserver.gui.widget.ImageUploadSelection;
public interface PolyglotPageService {
@ -49,7 +49,7 @@ public interface PolyglotPageService {
* @param locale the Locale to set */
void setPageLocale(Composite root, Locale locale);
void injectI18n(ImageUpload imageUpload, LocTextKey locTextKey);
void injectI18n(ImageUploadSelection imageUpload, LocTextKey locTextKey);
void injectI18n(Label label, LocTextKey locTextKey);

View file

@ -35,7 +35,7 @@ import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService;
import ch.ethz.seb.sebserver.gui.service.page.ComposerService;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.widget.ImageUpload;
import ch.ethz.seb.sebserver.gui.widget.ImageUploadSelection;
/** Service that supports page language change on the fly */
@Lazy
@ -72,8 +72,8 @@ public final class PolyglotPageServiceImpl implements PolyglotPageService {
}
@Override
public void injectI18n(final ImageUpload imageUpload, final LocTextKey locTextKey) {
final Consumer<ImageUpload> imageUploadFunction = iu -> {
public void injectI18n(final ImageUploadSelection imageUpload, final LocTextKey locTextKey) {
final Consumer<ImageUploadSelection> imageUploadFunction = iu -> {
if (locTextKey != null) {
iu.setSelectionText(this.i18nSupport.getText(locTextKey));
}

View file

@ -9,6 +9,7 @@
package ch.ethz.seb.sebserver.gui.service.remote.webservice.api;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
@ -185,6 +186,8 @@ public abstract class RestCall<T> {
private UriComponentsBuilder uriComponentsBuilder;
private final HttpHeaders httpHeaders;
private String body = null;
private InputStream streamingBody = null;
private final MultiValueMap<String, String> queryParams;
private final Map<String, String> uriVariables;
@ -247,6 +250,11 @@ public abstract class RestCall<T> {
return this;
}
if (body instanceof InputStream) {
this.streamingBody = (InputStream) body;
return this;
}
try {
this.body = RestCall.this.jsonMapper.writeValueAsString(body);
} catch (final JsonProcessingException e) {
@ -325,7 +333,9 @@ public abstract class RestCall<T> {
}
public HttpEntity<?> buildRequestEntity() {
if (this.body != null) {
if (this.streamingBody != null) {
return new HttpEntity<>(this.streamingBody, this.httpHeaders);
} else if (this.body != null) {
return new HttpEntity<>(this.body, this.httpHeaders);
} else {
return new HttpEntity<>(this.httpHeaders);

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2019 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.service.remote.webservice.api.seb.examconfig;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.core.type.TypeReference;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Configuration;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class ImportExamConfig extends RestCall<Configuration> {
public ImportExamConfig() {
super(new TypeKey<>(
CallType.UNDEFINED,
EntityType.CONFIGURATION,
new TypeReference<Configuration>() {
}),
HttpMethod.POST,
MediaType.APPLICATION_FORM_URLENCODED,
API.CONFIGURATION_NODE_ENDPOINT
+ API.MODEL_ID_VAR_PATH_SEGMENT
+ API.CONFIGURATION_IMPORT_PATH_SEGMENT);
}
}

View file

@ -0,0 +1,153 @@
/*
* Copyright (c) 2019 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 java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import org.eclipse.rap.fileupload.FileDetails;
import org.eclipse.rap.fileupload.FileUploadHandler;
import org.eclipse.rap.fileupload.FileUploadReceiver;
import org.eclipse.rap.rwt.widgets.FileUpload;
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 ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.push.ServerPushService;
public class FileUploadSelection extends Composite {
private static final long serialVersionUID = 5800153475027387363L;
private static final LocTextKey PLEASE_SELECT_TEXT =
new LocTextKey("sebserver.overall.upload");
private final I18nSupport i18nSupport;
private final ServerPushService serverPushService;
private final List<String> supportedFileExtensions = new ArrayList<>();
private final boolean readonly;
private final FileUpload fileUpload;
private final Label fileName;
private Consumer<String> errorHandler;
private InputStream inputStream;
public FileUploadSelection(
final Composite parent,
final ServerPushService serverPushService,
final I18nSupport i18nSupport,
final boolean readonly) {
super(parent, SWT.NONE);
final GridLayout gridLayout = new GridLayout(2, false);
gridLayout.horizontalSpacing = 0;
gridLayout.marginHeight = 0;
gridLayout.marginWidth = 0;
gridLayout.verticalSpacing = 0;
super.setLayout(gridLayout);
this.i18nSupport = i18nSupport;
this.serverPushService = serverPushService;
this.readonly = readonly;
if (readonly) {
this.fileName = new Label(this, SWT.NONE);
this.fileName.setText(i18nSupport.getText(PLEASE_SELECT_TEXT));
this.fileName.setLayoutData(new GridData());
this.fileUpload = null;
} else {
this.fileUpload = new FileUpload(this, SWT.NONE);
this.fileUpload.setImage(WidgetFactory.ImageIcon.IMPORT.getImage(parent.getDisplay()));
this.fileUpload.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
this.fileUpload.setToolTipText(this.i18nSupport.getText(PLEASE_SELECT_TEXT));
final FileUploadHandler uploadHandler = new FileUploadHandler(new InputReceiver());
this.fileUpload.addListener(SWT.Selection, event -> {
final String fileName = FileUploadSelection.this.fileUpload.getFileName();
if (fileName == null || !fileSupported(fileName)) {
if (FileUploadSelection.this.errorHandler != null) {
final String text = i18nSupport.getText(new LocTextKey(
"sebserver.overall.upload.unsupported.file",
this.supportedFileExtensions.toString()),
"Unsupported image file type selected");
FileUploadSelection.this.errorHandler.accept(text);
}
return;
}
FileUploadSelection.this.fileUpload.submit(uploadHandler.getUploadUrl());
});
this.fileName = new Label(this, SWT.NONE);
this.fileName.setText(i18nSupport.getText(PLEASE_SELECT_TEXT));
this.fileName.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false));
}
}
public String getFileName() {
if (this.fileName != null) {
return this.fileName.getText();
}
return Constants.EMPTY_NOTE;
}
public void setFileName(final String fileName) {
if (this.fileName != null && fileName != null) {
this.fileName.setText(fileName);
}
}
public InputStream getInputStream() {
return this.inputStream;
}
@Override
public void update() {
if (this.inputStream != null) {
this.fileName.setText(this.i18nSupport.getText(PLEASE_SELECT_TEXT));
}
if (!this.readonly) {
this.fileUpload.setToolTipText(this.i18nSupport.getText(PLEASE_SELECT_TEXT));
}
}
public FileUploadSelection setErrorHandler(final Consumer<String> errorHandler) {
this.errorHandler = errorHandler;
return this;
}
public FileUploadSelection withSupportFor(final String fileExtension) {
this.supportedFileExtensions.add(fileExtension);
return this;
}
private boolean fileSupported(final String fileName) {
return this.supportedFileExtensions
.stream()
.filter(fileType -> fileName.toUpperCase().endsWith(fileType.toUpperCase()))
.findFirst()
.isPresent();
}
private final class InputReceiver extends FileUploadReceiver {
@Override
public void receive(final InputStream stream, final FileDetails details) throws IOException {
FileUploadSelection.this.inputStream = stream;
}
}
}

View file

@ -28,8 +28,6 @@ import org.eclipse.rap.fileupload.FileUploadReceiver;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.rap.rwt.widgets.FileUpload;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.Rectangle;
@ -43,10 +41,10 @@ import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport;
import ch.ethz.seb.sebserver.gui.service.push.ServerPushContext;
import ch.ethz.seb.sebserver.gui.service.push.ServerPushService;
public final class ImageUpload extends Composite {
public final class ImageUploadSelection extends Composite {
private static final long serialVersionUID = 368264811155804533L;
private static final Logger log = LoggerFactory.getLogger(ImageUpload.class);
private static final Logger log = LoggerFactory.getLogger(ImageUploadSelection.class);
public static final Set<String> SUPPORTED_IMAGE_FILES = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
".png",
@ -65,7 +63,7 @@ public final class ImageUpload extends Composite {
private boolean loadNewImage = false;
private boolean imageLoaded = false;
ImageUpload(
ImageUploadSelection(
final Composite parent,
final ServerPushService serverPushService,
final I18nSupport i18nSupport,
@ -74,7 +72,12 @@ public final class ImageUpload extends Composite {
final int maxHeight) {
super(parent, SWT.NONE);
super.setLayout(new GridLayout(1, false));
final GridLayout gridLayout = new GridLayout(1, false);
gridLayout.horizontalSpacing = 0;
gridLayout.marginHeight = 0;
gridLayout.marginWidth = 0;
gridLayout.verticalSpacing = 0;
super.setLayout(gridLayout);
this.serverPushService = serverPushService;
this.maxWidth = maxWidth;
@ -85,55 +88,29 @@ public final class ImageUpload extends Composite {
this.fileUpload.setImage(WidgetFactory.ImageIcon.IMPORT.getImage(parent.getDisplay()));
this.fileUpload.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
final FileUploadHandler uploadHandler = new FileUploadHandler(new FileUploadReceiver() {
@Override
public void receive(final InputStream stream, final FileDetails details) throws IOException {
try {
final String contentType = details.getContentType();
if (contentType != null && contentType.startsWith("image")) {
ImageUpload.this.imageBase64 = Base64.getEncoder()
.encodeToString(IOUtils.toByteArray(stream));
}
} catch (final Exception e) {
log.error("Error while trying to upload image", e);
} finally {
ImageUpload.this.imageLoaded = true;
stream.close();
final FileUploadHandler uploadHandler = new FileUploadHandler(new ImageReceiver());
this.fileUpload.addListener(SWT.Selection, event -> {
final String fileName = ImageUploadSelection.this.fileUpload.getFileName();
if (fileName == null || !fileSupported(fileName)) {
if (ImageUploadSelection.this.errorHandler != null) {
final String text = i18nSupport.getText(
"sebserver.institution.form.logoImage.unsupportedFileType",
"Unsupported image file type selected");
ImageUploadSelection.this.errorHandler.accept(text);
}
log.warn("Unsupported image file selected: {}", fileName);
return;
}
});
this.fileUpload.addSelectionListener(new SelectionAdapter() {
private static final long serialVersionUID = -6776734104137568801L;
@Override
public void widgetSelected(final SelectionEvent event) {
final String fileName = ImageUpload.this.fileUpload.getFileName();
if (fileName == null || !fileSupported(fileName)) {
if (ImageUpload.this.errorHandler != null) {
final String text = i18nSupport.getText(
"sebserver.institution.form.logoImage.unsupportedFileType",
"Unsupported image file type selected");
ImageUpload.this.errorHandler.accept(text);
}
log.warn("Unsupported image file selected: {}", fileName);
return;
}
ImageUpload.this.loadNewImage = true;
ImageUpload.this.imageLoaded = false;
ImageUpload.this.fileUpload.submit(uploadHandler.getUploadUrl());
ImageUpload.this.serverPushService.runServerPush(
new ServerPushContext(ImageUpload.this, ImageUpload::uploadInProgress),
200,
ImageUpload::update);
}
ImageUploadSelection.this.loadNewImage = true;
ImageUploadSelection.this.imageLoaded = false;
ImageUploadSelection.this.fileUpload.submit(uploadHandler.getUploadUrl());
ImageUploadSelection.this.serverPushService.runServerPush(
new ServerPushContext(ImageUploadSelection.this, ImageUploadSelection::uploadInProgress),
200,
ImageUploadSelection::update);
});
} else {
this.fileUpload = null;
@ -142,7 +119,6 @@ public final class ImageUpload extends Composite {
this.imageCanvas = new Composite(this, SWT.NONE);
final GridData canvas = new GridData(SWT.FILL, SWT.FILL, true, true);
this.imageCanvas.setLayoutData(canvas);
}
public void setErrorHandler(final Consumer<String> errorHandler) {
@ -172,12 +148,12 @@ public final class ImageUpload extends Composite {
}
private static final boolean uploadInProgress(final ServerPushContext context) {
final ImageUpload imageUpload = (ImageUpload) context.getAnchor();
final ImageUploadSelection imageUpload = (ImageUploadSelection) context.getAnchor();
return imageUpload.loadNewImage && !imageUpload.imageLoaded;
}
private static final void update(final ServerPushContext context) {
final ImageUpload imageUpload = (ImageUpload) context.getAnchor();
final ImageUploadSelection imageUpload = (ImageUploadSelection) context.getAnchor();
if (imageUpload.imageBase64 != null
&& imageUpload.loadNewImage
&& imageUpload.imageLoaded) {
@ -195,7 +171,7 @@ public final class ImageUpload extends Composite {
}
}
private static void setImage(final ImageUpload imageUpload, final Base64InputStream input) {
private static void setImage(final ImageUploadSelection imageUpload, final Base64InputStream input) {
imageUpload.imageCanvas.setData(RWT.CUSTOM_VARIANT, "bgLogoNoImage");
final Image image = new Image(imageUpload.imageCanvas.getDisplay(), input);
@ -218,4 +194,23 @@ public final class ImageUpload extends Composite {
.isPresent();
}
private final class ImageReceiver extends FileUploadReceiver {
@Override
public void receive(final InputStream stream, final FileDetails details) throws IOException {
try {
final String contentType = details.getContentType();
if (contentType != null && contentType.startsWith("image")) {
ImageUploadSelection.this.imageBase64 = Base64.getEncoder()
.encodeToString(IOUtils.toByteArray(stream));
}
} catch (final Exception e) {
log.error("Error while trying to upload image", e);
} finally {
ImageUploadSelection.this.imageLoaded = true;
stream.close();
}
}
}
}

View file

@ -585,7 +585,7 @@ public class WidgetFactory {
return thresholdList;
}
public ImageUpload logoImageUploadLocalized(
public ImageUploadSelection logoImageUploadLocalized(
final Composite parent,
final LocTextKey locTextKey,
final boolean readonly) {
@ -598,14 +598,14 @@ public class WidgetFactory {
DefaultPageLayout.LOGO_IMAGE_MAX_HEIGHT);
}
public ImageUpload imageUploadLocalized(
public ImageUploadSelection imageUploadLocalized(
final Composite parent,
final LocTextKey locTextKey,
final boolean readonly,
final int maxWidth,
final int maxHeight) {
final ImageUpload imageUpload = new ImageUpload(
final ImageUploadSelection imageUpload = new ImageUploadSelection(
parent,
this.serverPushService,
this.i18nSupport,
@ -617,4 +617,17 @@ public class WidgetFactory {
return imageUpload;
}
public FileUploadSelection fileUploadSelection(
final Composite parent,
final boolean readonly,
final Collection<String> supportedFiles) {
final FileUploadSelection fileUploadSelection =
new FileUploadSelection(parent, null, this.i18nSupport, readonly);
if (supportedFiles != null) {
supportedFiles.forEach(ext -> fileUploadSelection.withSupportFor(ext));
}
return fileUploadSelection;
}
}

View file

@ -10,9 +10,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.cert.Certificate;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.Arrays;
import org.springframework.scheduling.annotation.Async;
@ -54,6 +52,15 @@ public interface SebConfigEncryptionService {
this.header = Utils.toByteArray(headerKey);
}
public static Strategy getStrategy(final byte[] header) {
return Arrays.asList(Strategy.values())
.stream()
.filter(strategy -> Arrays.equals(strategy.header, header))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException(
"No Strategy for header: " + Utils.toString(header) + " found."));
}
}
/** This can be used to stream incoming plain text data to encrypted cipher data output stream.
@ -76,7 +83,6 @@ public interface SebConfigEncryptionService {
void streamDecrypted(
final OutputStream output,
final InputStream input,
Supplier<CharSequence> passwordSupplier,
Function<CharSequence, Certificate> certificateStore);
final SebConfigEncryptionContext context);
}

View file

@ -8,9 +8,11 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig;
import java.io.InputStream;
import java.io.OutputStream;
import ch.ethz.seb.sebserver.gbl.api.APIMessage.FieldValidationException;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Configuration;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValues;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
import ch.ethz.seb.sebserver.gbl.util.Result;
@ -94,4 +96,21 @@ public interface SebExamConfigService {
* @return Result refer to the generated Config-Key or to an error if happened. */
Result<String> generateConfigKey(Long institutionId, Long configurationNodeId);
/** Imports a SEB Exam Configuration from a SEB File of the format:
* https://www.safeexambrowser.org/developer/seb-file-format.html
*
* First tries to read the file from the given input stream and detect the file format. A password
* is needed if the file is in an encrypted format.
*
* Then loads the ConfigurationNode on which the import should take place and performs a "save in histroy"
* action first to allow to make an easy rollback or even later an undo by the user.
*
* Then parses the XML and adds each attribute to the new Configuration.
*
* @param configNodeId The identifier of the configuration node on which the import should take place
* @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);
}

View file

@ -19,12 +19,17 @@ import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.xml.sax.SAXException;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.async.AsyncServiceSpringConfig;
@ -35,6 +40,7 @@ import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationAttributeDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationValueDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.AttributeValueConverter;
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.AttributeValueConverterService;
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ConfigurationFormat;
@ -153,6 +159,50 @@ public class ExamConfigIO {
}
}
/** This parses the XML from given InputStream with a SAX parser to avoid keeping the
* whole XML file in memory and keep up with the streaming approach of SEB Exam Configuration
* to avoid trouble with big SEB Exam Configuration in the future.
*
* @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
final Map<String, Long> attributeMap = this.configurationAttributeDAO
.allMatching(new FilterMap())
.getOrThrow()
.stream()
.collect(Collectors.toMap(attr -> attr.name, attr -> attr.id));
// 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 ExamConfigImportHandler examConfigImportHandler = new ExamConfigImportHandler(
institutionId,
configurationId,
value -> this.configurationValueDAO.save(value),
attributeMap::get);
// SAX parsing
final SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
final SAXParser parser = saxParserFactory.newSAXParser();
parser.parse(in, examConfigImportHandler);
} catch (final ParserConfigurationException e) {
log.error("Unexpected error while trying to parse imported SEB Config XML: ", e);
throw new RuntimeException(e);
} catch (final SAXException e) {
log.error("Unexpected error while trying to parse imported SEB Config XML: ", e);
throw new RuntimeException(e);
} catch (final IOException e) {
log.error("Unexpected error while trying to parse imported SEB Config XML: ", e);
throw new RuntimeException(e);
} finally {
IOUtils.closeQuietly(in);
}
}
private Predicate<ConfigurationAttribute> exportFormatBasedAttributeFilter(final ConfigurationFormat format) {
// Filter originatorVersion according to: https://www.safeexambrowser.org/developer/seb-config-key.html
return attr -> !("originatorVersion".equals(attr.getName()) && format == ConfigurationFormat.JSON);
@ -206,11 +256,6 @@ public class ExamConfigIO {
}
}
@Async(AsyncServiceSpringConfig.EXECUTOR_BEAN_NAME)
void importPlainXML(final InputStream in, final Long institutionId, final Long configurationNodeId) {
// TODO version 1
}
private Function<ConfigurationAttribute, ConfigurationValue> getConfigurationValueSupplier(
final Long institutionId,
final Long configurationId) {

View file

@ -0,0 +1,286 @@
/*
* Copyright (c) 2019 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.webservice.servicelayer.sebconfig.impl;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.Stack;
import java.util.function.Consumer;
import java.util.function.Function;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl.ExamConfigImportHandler.PListNode.Type;
public class ExamConfigImportHandler extends DefaultHandler {
private static final Set<String> VALUE_ELEMENTS = new HashSet<>(Arrays.asList(
Constants.XML_PLIST_BOOLEAN_FALSE,
Constants.XML_PLIST_BOOLEAN_TRUE,
Constants.XML_PLIST_STRING,
Constants.XML_PLIST_INTEGER));
private final Consumer<ConfigurationValue> valueConsumer;
private final Function<String, Long> attributeNameIdResolver;
private final Long institutionId;
private final Long configId;
private final Stack<PListNode> stack = new Stack<>();
protected ExamConfigImportHandler(
final Long institutionId,
final Long configId,
final Consumer<ConfigurationValue> valueConsumer,
final Function<String, Long> attributeNameIdResolver) {
super();
this.valueConsumer = valueConsumer;
this.attributeNameIdResolver = attributeNameIdResolver;
this.institutionId = institutionId;
this.configId = configId;
}
@Override
public void startElement(
final String uri,
final String localName,
final String qName,
final Attributes attributes) throws SAXException {
final Type type = Type.getType(qName);
final PListNode top = (this.stack.isEmpty()) ? null : this.stack.peek();
switch (type) {
case PLIST:
startPList(type);
break;
case DICT:
startDict(type, top);
break;
case ARRAY:
startArray(type, top);
break;
case KEY:
startKey(type, top);
break;
case VALUE_BOOLEAN_FALSE:
case VALUE_BOOLEAN_TRUE:
case VALUE_STRING:
case VALUE_INTEGER:
startValueElement(type, top);
break;
}
}
private void startKey(final Type type, final PListNode top) {
final PListNode key = new PListNode(type);
switch (top.type) {
case DICT: {
key.listIndex = top.listIndex;
this.stack.push(key);
break;
}
default:
throw new IllegalStateException();
}
}
private void startArray(final Type type, final PListNode top) {
final PListNode array = new PListNode(type);
switch (top.type) {
case KEY: {
array.name = top.name;
array.listIndex = top.listIndex;
this.stack.pop();
this.stack.push(array);
break;
}
default:
throw new IllegalStateException();
}
}
private void startDict(final Type type, final PListNode top) {
final PListNode dict = new PListNode(type);
switch (top.type) {
case PLIST: {
this.stack.push(dict);
break;
}
case ARRAY: {
dict.name = top.name;
dict.listIndex = top.arrayCounter++;
this.stack.push(dict);
break;
}
case KEY: {
dict.name = top.name;
dict.listIndex = top.listIndex;
this.stack.pop();
this.stack.push(dict);
break;
}
default:
throw new IllegalStateException();
}
}
private void startPList(final Type type) {
if (this.stack.isEmpty()) {
this.stack.push(new PListNode(type));
} else {
throw new IllegalStateException();
}
}
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;
value.listIndex = top.listIndex;
value.value = type == Type.VALUE_BOOLEAN_TRUE
? Constants.XML_PLIST_BOOLEAN_TRUE
: Constants.XML_PLIST_BOOLEAN_FALSE;
this.stack.push(value);
} else {
this.stack.pop();
value.name = top.name;
value.listIndex = top.listIndex;
this.stack.push(value);
}
} else if (top.type == Type.ARRAY) {
if (Type.isBooleanValue(type)) {
value.name = top.name;
value.listIndex = top.arrayCounter++;
value.value = type == Type.VALUE_BOOLEAN_TRUE
? Constants.XML_PLIST_BOOLEAN_TRUE
: Constants.XML_PLIST_BOOLEAN_FALSE;
this.stack.push(value);
} else {
value.name = top.name;
value.listIndex = top.arrayCounter++;
this.stack.push(value);
}
}
}
@Override
public void endElement(
final String uri,
final String localName,
final String qName) throws SAXException {
final PListNode top = this.stack.peek();
if (VALUE_ELEMENTS.contains(qName)) {
if (top.type.isValueType) {
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;
this.valueConsumer.accept(new ConfigurationValue(
null,
this.institutionId,
this.configId,
this.attributeNameIdResolver.apply(attrName),
top.listIndex,
top.value));
}
} else if (!Constants.XML_PLIST_KEY_NAME.equals(qName)) {
this.stack.pop();
}
}
@Override
public void characters(
final char[] ch,
final int start,
final int length) throws SAXException {
final PListNode top = this.stack.peek();
if (top.type == Type.VALUE_STRING) {
top.value = String.valueOf(ch);
} else if (top.type == Type.VALUE_INTEGER) {
top.value = String.valueOf(ch);
} else if (top.type == Type.KEY) {
top.name = String.valueOf(ch);
}
}
final static class PListNode {
enum Type {
PLIST(false, Constants.XML_PLIST_NAME),
DICT(false, Constants.XML_PLIST_DICT_NAME),
ARRAY(false, Constants.XML_PLIST_ARRAY_NAME),
KEY(false, Constants.XML_PLIST_KEY_NAME),
VALUE_BOOLEAN_TRUE(true, Constants.XML_PLIST_BOOLEAN_TRUE),
VALUE_BOOLEAN_FALSE(true, Constants.XML_PLIST_BOOLEAN_FALSE),
VALUE_STRING(true, Constants.XML_PLIST_STRING),
VALUE_INTEGER(true, Constants.XML_PLIST_INTEGER);
private final boolean isValueType;
private final String typeName;
private Type(final boolean isValueType, final String typeName) {
this.isValueType = isValueType;
this.typeName = typeName;
}
public static boolean isBooleanValue(final Type type) {
return type == VALUE_BOOLEAN_TRUE || type == VALUE_BOOLEAN_FALSE;
}
public static Type getType(final String qName) {
return Arrays.asList(Type.values()).stream()
.filter(type -> type.typeName.equals(qName))
.findFirst()
.orElse(null);
}
}
final Type type;
String name;
int arrayCounter = 0;
int listIndex = 0;
String value;
protected PListNode(final Type type) {
this.type = type;
}
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append("PListNode [type=");
builder.append(this.type);
builder.append(", name=");
builder.append(this.name);
builder.append(", listIndex=");
builder.append(this.listIndex);
builder.append(", value=");
builder.append(this.value);
builder.append("]");
return builder.toString();
}
}
}

View file

@ -18,7 +18,6 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.apache.commons.io.IOUtils;
@ -103,8 +102,7 @@ public final class SebConfigEncryptionServiceImpl implements SebConfigEncryption
public void streamDecrypted(
final OutputStream output,
final InputStream input,
final Supplier<CharSequence> passwordSupplier,
final Function<CharSequence, Certificate> certificateStore) {
final SebConfigEncryptionContext context) {
PipedOutputStream pout = null;
PipedInputStream pin = null;
@ -118,11 +116,6 @@ public final class SebConfigEncryptionServiceImpl implements SebConfigEncryption
log.debug("Password decryption with strategy: {}", strategy);
}
final EncryptionContext context = new EncryptionContext(
strategy,
(passwordSupplier != null) ? passwordSupplier.get() : null,
certificateStore);
getEncryptor(strategy)
.getOrThrow()
.decrypt(pout, input, context);

View file

@ -8,10 +8,14 @@
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.nio.ByteBuffer;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
@ -28,6 +32,7 @@ import org.springframework.stereotype.Service;
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;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Configuration;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValues;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
@ -35,6 +40,7 @@ import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.servicelayer.client.ClientCredentialService;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationAttributeDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamConfigurationMapDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ConfigurationFormat;
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ConfigurationValueValidator;
@ -53,6 +59,7 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
private final ExamConfigIO examConfigIO;
private final ConfigurationAttributeDAO configurationAttributeDAO;
private final ConfigurationDAO configurationDAO;
private final ExamConfigurationMapDAO examConfigurationMapDAO;
private final Collection<ConfigurationValueValidator> validators;
private final ClientCredentialService clientCredentialService;
@ -62,6 +69,7 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
protected SebExamConfigServiceImpl(
final ExamConfigIO examConfigIO,
final ConfigurationAttributeDAO configurationAttributeDAO,
final ConfigurationDAO configurationDAO,
final ExamConfigurationMapDAO examConfigurationMapDAO,
final Collection<ConfigurationValueValidator> validators,
final ClientCredentialService clientCredentialService,
@ -70,12 +78,12 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
this.examConfigIO = examConfigIO;
this.configurationAttributeDAO = configurationAttributeDAO;
this.configurationDAO = configurationDAO;
this.examConfigurationMapDAO = examConfigurationMapDAO;
this.validators = validators;
this.clientCredentialService = clientCredentialService;
this.zipService = zipService;
this.sebConfigEncryptionService = sebConfigEncryptionService;
}
@Override
@ -248,7 +256,7 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
final Long configurationNodeId) {
if (log.isDebugEnabled()) {
log.debug("Start to stream plain JSON SEB clonfiguration data for Config-Key generation");
log.debug("Start to stream plain JSON SEB Configuration data for Config-Key generation");
}
if (log.isTraceEnabled()) {
@ -288,7 +296,7 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
return Result.of(configKey);
} catch (final Exception e) {
log.error("Error while stream plain JSON SEB clonfiguration data for Config-Key generation: ", e);
log.error("Error while stream plain JSON SEB Configuration data for Config-Key generation: ", e);
return Result.ofError(e);
} finally {
try {
@ -307,11 +315,116 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
}
if (log.isDebugEnabled()) {
log.debug("Finished to stream plain JSON SEB clonfiguration data for Config-Key generation");
log.debug("Finished to stream plain JSON SEB Configuration data for Config-Key generation");
}
}
}
@Override
public Result<Configuration> importFromXML(
final Long configNodeId,
final InputStream input,
final CharSequence password) {
return Result.tryCatch(() -> {
final Configuration newConfig = this.configurationDAO
.saveToHistory(configNodeId)
.getOrThrow();
try {
final byte[] header = new byte[4];
input.read(header);
final Strategy strategy = SebConfigEncryptionService.Strategy.getStrategy(header);
if (strategy == null) {
importPlainOnly(input, newConfig, header);
} else {
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;
} catch (final Exception e) {
log.error("Unexpected error while trying to import SEB Exam Configuration: ", e);
log.debug("Make an undo on the ConfigurationNode to rollback the changes");
return this.configurationDAO
.undo(configNodeId)
.getOrThrow();
}
});
}
private InputStream unzip(final InputStream input) throws Exception {
final byte[] zipHeader = new byte[4];
input.read(zipHeader);
final int zipType = ByteBuffer.wrap(zipHeader).getInt();
final boolean isZipped = zipType == 0x504B0304 || zipType == 0x504B0506 || zipType == 0x504B0708;
if (isZipped) {
final InputStream sequencedInput = new SequenceInputStream(
new ByteArrayInputStream(zipHeader),
input);
final PipedInputStream pipedIn = new PipedInputStream();
final PipedOutputStream pipedOut = new PipedOutputStream(pipedIn);
this.zipService.read(pipedOut, sequencedInput);
return pipedIn;
} else {
return new SequenceInputStream(
new ByteArrayInputStream(zipHeader),
input);
}
}
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);
IOUtils.closeQuietly(out);
} catch (final Exception e) {
log.error("Error while stream plain text SEB Configuration import data: ", e);
throw e;
} finally {
IOUtils.closeQuietly(out);
IOUtils.closeQuietly(plainIn);
}
}
private void exportPlainOnly(
final ConfigurationFormat exportFormat,
final OutputStream out,
@ -319,7 +432,7 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
final Long configurationNodeId) {
if (log.isDebugEnabled()) {
log.debug("Start to stream plain text SEB clonfiguration data");
log.debug("Start to stream plain text SEB Configuration data");
}
PipedOutputStream pout = null;
@ -337,7 +450,7 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
IOUtils.copyLarge(pin, out);
} catch (final Exception e) {
log.error("Error while stream plain text SEB clonfiguration data: ", e);
log.error("Error while stream plain text SEB Configuration export data: ", e);
} finally {
try {
if (pin != null) {
@ -356,7 +469,7 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
}
if (log.isDebugEnabled()) {
log.debug("Finished to stream plain text SEB clonfiguration data");
log.debug("Finished to stream plain text SEB Configuration export data");
}
}
}

View file

@ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.webservice.weblayer.api;
import java.io.IOException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.mybatis.dynamic.sql.SqlTable;
@ -19,6 +20,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
@ -160,4 +162,24 @@ public class ConfigurationNodeController extends EntityController<ConfigurationN
}
}
@RequestMapping(
path = API.MODEL_ID_VAR_PATH_SEGMENT + API.CONFIGURATION_IMPORT_PATH_SEGMENT,
method = RequestMethod.GET,
produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public Configuration importExamConfig(
@PathVariable final Long modelId,
@RequestHeader final String password,
@RequestParam(
name = API.PARAM_INSTITUTION_ID,
required = true,
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
final HttpServletRequest request) throws IOException {
return this.sebExamConfigService.importFromXML(
modelId,
request.getInputStream(),
password)
.getOrThrow();
}
}

View file

@ -7,7 +7,8 @@ sebserver.overall.help=Documentation
sebserver.overall.help.link=https://www.safeexambrowser.org/news_en.html
sebserver.overall.message.leave.without.save=You have unsaved changes!\nAre you sure you want to leave the page?\The changes will be lost.
sebserver.overall.upload=Please Select
sebserver.overall.upload=Please select a file
sebserver.overall.upload.unsupported.file=This file type is not supported. Supported files are: {0}
sebserver.overall.action.modify.cancel=Cancel
sebserver.overall.action.modify.cancel.confirm=Are you sure you want to cancel? Modifications will be lost.
sebserver.overall.action.filter=Apply filter
@ -438,8 +439,11 @@ sebserver.examconfig.action.saveToHistory=Save In History
sebserver.examconfig.action.saveToHistory.success=Successfully saved in history
sebserver.examconfig.action.undo=Undo
sebserver.examconfig.action.undo.success=Successfully reverted to last saved state
sebserver.examconfig.action.export.plainxml=Export XML
sebserver.examconfig.action.export.plainxml=Export Configuration
sebserver.examconfig.action.get-config-key=Export Config-Key
sebserver.examconfig.action.import-config=Import Configuration
sebserver.examconfig.action.import-file-select=Import From File
sebserver.examconfig.action.import-file-password=Password
sebserver.examconfig.form.title.new=Add Exam Configuration
sebserver.examconfig.form.title=Exam Configuration

View file

@ -7,7 +7,7 @@ sebserver.overall.help=Documentation
sebserver.overall.help.link=https://www.safeexambrowser.org/news_en.html
sebserver.overall.message.leave.without.save=You have unsaved changes!\nAre you sure you want to leave the page?\The changes will be lost.
sebserver.overall.upload=Please Select
sebserver.overall.upload=Please select a file
sebserver.overall.action.modify.cancel=Cancel
sebserver.overall.action.modify.cancel.confirm=Are you sure you want to cancel? Modifications will be lost.
sebserver.overall.action.filter=Apply filter

Binary file not shown.

Before

Width:  |  Height:  |  Size: 143 B

After

Width:  |  Height:  |  Size: 170 B

View file

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

View file

@ -0,0 +1,332 @@
/*
* Copyright (c) 2019 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.webservice.servicelayer.sebconfig.impl;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import org.junit.jupiter.api.Test;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
public class ExamConfigImportHandlerTest {
@Test
public void simpleStringValueTest() throws Exception {
final ValueCollector valueCollector = new ValueCollector();
final ExamConfigImportHandler candidate = new ExamConfigImportHandler(
1L,
1L,
valueCollector,
name -> Long.parseLong(String.valueOf(name.charAt(name.length() - 1))));
final String attribute = "param1";
final String value = "value1";
candidate.startElement(null, null, "plist", null);
candidate.startElement(null, null, "dict", null);
candidate.startElement(null, null, "key", null);
candidate.characters(attribute.toCharArray(), 0, attribute.length());
candidate.endElement(null, null, "key");
candidate.startElement(null, null, "string", null);
candidate.characters(value.toCharArray(), 0, value.length());
candidate.endElement(null, null, "string");
candidate.endElement(null, null, "dict");
candidate.endElement(null, null, "plist");
assertFalse(valueCollector.values.isEmpty());
final ConfigurationValue configurationValue = valueCollector.values.get(0);
assertNotNull(configurationValue);
assertTrue(1L == configurationValue.attributeId);
assertEquals("value1", configurationValue.value);
}
@Test
public void simpleIntegerValueTest() throws Exception {
final ValueCollector valueCollector = new ValueCollector();
final ExamConfigImportHandler candidate = new ExamConfigImportHandler(
1L,
1L,
valueCollector,
name -> Long.parseLong(String.valueOf(name.charAt(name.length() - 1))));
final String attribute = "param2";
final String value = "22";
candidate.startElement(null, null, "plist", null);
candidate.startElement(null, null, "dict", null);
candidate.startElement(null, null, "key", null);
candidate.characters(attribute.toCharArray(), 0, attribute.length());
candidate.endElement(null, null, "key");
candidate.startElement(null, null, "integer", null);
candidate.characters(value.toCharArray(), 0, value.length());
candidate.endElement(null, null, "integer");
candidate.endElement(null, null, "dict");
candidate.endElement(null, null, "plist");
assertFalse(valueCollector.values.isEmpty());
final ConfigurationValue configurationValue = valueCollector.values.get(0);
assertNotNull(configurationValue);
assertTrue(2L == configurationValue.attributeId);
assertEquals("22", configurationValue.value);
}
@Test
public void simpleBooleanValueTest() throws Exception {
final ValueCollector valueCollector = new ValueCollector();
final ExamConfigImportHandler candidate = new ExamConfigImportHandler(
1L,
1L,
valueCollector,
name -> Long.parseLong(String.valueOf(name.charAt(name.length() - 1))));
final String attribute = "param3";
final String value = "true";
candidate.startElement(null, null, "plist", null);
candidate.startElement(null, null, "dict", null);
candidate.startElement(null, null, "key", null);
candidate.characters(attribute.toCharArray(), 0, attribute.length());
candidate.endElement(null, null, "key");
candidate.startElement(null, null, value, null);
candidate.endElement(null, null, value);
candidate.endElement(null, null, "dict");
candidate.endElement(null, null, "plist");
assertFalse(valueCollector.values.isEmpty());
final ConfigurationValue configurationValue = valueCollector.values.get(0);
assertNotNull(configurationValue);
assertTrue(3L == configurationValue.attributeId);
assertEquals("true", configurationValue.value);
}
@Test
public void arrayOfStringValueTest() throws Exception {
final ValueCollector valueCollector = new ValueCollector();
final ExamConfigImportHandler candidate = new ExamConfigImportHandler(
1L,
1L,
valueCollector,
name -> Long.parseLong(String.valueOf(name.charAt(name.length() - 1))));
final String attribute = "array1";
final String value1 = "val1";
final String value2 = "val2";
final String value3 = "val3";
candidate.startElement(null, null, "plist", null);
candidate.startElement(null, null, "dict", null);
candidate.startElement(null, null, "key", null);
candidate.characters(attribute.toCharArray(), 0, attribute.length());
candidate.endElement(null, null, "key");
candidate.startElement(null, null, "array", null);
candidate.startElement(null, null, "string", null);
candidate.characters(value1.toCharArray(), 0, value1.length());
candidate.endElement(null, null, "string");
candidate.startElement(null, null, "string", null);
candidate.characters(value2.toCharArray(), 0, value2.length());
candidate.endElement(null, null, "string");
candidate.startElement(null, null, "string", null);
candidate.characters(value3.toCharArray(), 0, value3.length());
candidate.endElement(null, null, "string");
candidate.endElement(null, null, "array");
candidate.endElement(null, null, "dict");
candidate.endElement(null, null, "plist");
assertFalse(valueCollector.values.isEmpty());
assertTrue(valueCollector.values.size() == 3);
final ConfigurationValue configurationValue1 = valueCollector.values.get(0);
assertEquals("val1", 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
public void dictOfValuesTest() throws Exception {
final ValueCollector valueCollector = new ValueCollector();
final List<String> attrNamesCollector = new ArrayList<>();
final Function<String, Long> attrConverter = attrName -> {
attrNamesCollector.add(attrName);
return Long.parseLong(String.valueOf(attrName.charAt(attrName.length() - 1)));
};
final ExamConfigImportHandler candidate = new ExamConfigImportHandler(
1L,
1L,
valueCollector,
attrConverter);
final String attribute = "dict1";
final String attr1 = "attr1";
final String attr2 = "attr2";
final String attr3 = "attr3";
final String value1 = "val1";
final String value2 = "2";
candidate.startElement(null, null, "plist", null);
candidate.startElement(null, null, "dict", null);
candidate.startElement(null, null, "key", null);
candidate.characters(attribute.toCharArray(), 0, attribute.length());
candidate.endElement(null, null, "key");
candidate.startElement(null, null, "dict", null);
candidate.startElement(null, null, "key", null);
candidate.characters(attr1.toCharArray(), 0, attr1.length());
candidate.endElement(null, null, "key");
candidate.startElement(null, null, "string", null);
candidate.characters(value1.toCharArray(), 0, value1.length());
candidate.endElement(null, null, "string");
candidate.startElement(null, null, "key", null);
candidate.characters(attr2.toCharArray(), 0, attr2.length());
candidate.endElement(null, null, "key");
candidate.startElement(null, null, "integer", null);
candidate.characters(value2.toCharArray(), 0, value2.length());
candidate.endElement(null, null, "integer");
candidate.startElement(null, null, "key", null);
candidate.characters(attr3.toCharArray(), 0, attr3.length());
candidate.endElement(null, null, "key");
candidate.startElement(null, null, "true", null);
candidate.endElement(null, null, "true");
candidate.endElement(null, null, "dict");
candidate.endElement(null, null, "dict");
candidate.endElement(null, null, "plist");
assertFalse(valueCollector.values.isEmpty());
assertTrue(valueCollector.values.size() == 3);
assertEquals(
"[ConfigurationValue [id=null, institutionId=1, configurationId=1, attributeId=1, listIndex=0, value=val1], "
+ "ConfigurationValue [id=null, institutionId=1, configurationId=1, attributeId=2, listIndex=0, value=2], "
+ "ConfigurationValue [id=null, institutionId=1, configurationId=1, attributeId=3, listIndex=0, value=true]]",
valueCollector.values.toString());
assertEquals(
"[attr1, attr2, attr3]",
attrNamesCollector.toString());
}
@Test
public void arrayOfDictOfValuesTest() throws Exception {
final ValueCollector valueCollector = new ValueCollector();
final List<String> attrNamesCollector = new ArrayList<>();
final Function<String, Long> attrConverter = attrName -> {
attrNamesCollector.add(attrName);
return Long.parseLong(String.valueOf(attrName.charAt(attrName.length() - 1)));
};
final ExamConfigImportHandler candidate = new ExamConfigImportHandler(
1L,
1L,
valueCollector,
attrConverter);
final String attribute = "attribute";
final String attr1 = "attr1";
final String attr2 = "attr2";
final String attr3 = "attr3";
final String value1 = "val1";
final String value2 = "2";
candidate.startElement(null, null, "plist", null);
candidate.startElement(null, null, "dict", null);
candidate.startElement(null, null, "key", null);
candidate.characters(attribute.toCharArray(), 0, attribute.length());
candidate.endElement(null, null, "key");
candidate.startElement(null, null, "array", null);
for (int i = 0; i < 3; i++) {
candidate.startElement(null, null, "dict", null);
candidate.startElement(null, null, "key", null);
candidate.characters(attr1.toCharArray(), 0, attr1.length());
candidate.endElement(null, null, "key");
candidate.startElement(null, null, "string", null);
candidate.characters(value1.toCharArray(), 0, value1.length());
candidate.endElement(null, null, "string");
candidate.startElement(null, null, "key", null);
candidate.characters(attr2.toCharArray(), 0, attr2.length());
candidate.endElement(null, null, "key");
candidate.startElement(null, null, "integer", null);
candidate.characters(value2.toCharArray(), 0, value2.length());
candidate.endElement(null, null, "integer");
candidate.startElement(null, null, "key", null);
candidate.characters(attr3.toCharArray(), 0, attr3.length());
candidate.endElement(null, null, "key");
candidate.startElement(null, null, "true", null);
candidate.endElement(null, null, "true");
candidate.endElement(null, null, "dict");
}
candidate.endElement(null, null, "array");
candidate.endElement(null, null, "dict");
candidate.endElement(null, null, "plist");
assertFalse(valueCollector.values.isEmpty());
assertTrue(valueCollector.values.size() == 9);
assertEquals(
"[ConfigurationValue [id=null, institutionId=1, configurationId=1, attributeId=1, listIndex=0, value=val1], "
+ "ConfigurationValue [id=null, institutionId=1, configurationId=1, attributeId=2, listIndex=0, value=2], "
+ "ConfigurationValue [id=null, institutionId=1, configurationId=1, attributeId=3, listIndex=0, value=true], "
+ "ConfigurationValue [id=null, institutionId=1, configurationId=1, attributeId=1, listIndex=1, value=val1], "
+ "ConfigurationValue [id=null, institutionId=1, configurationId=1, attributeId=2, listIndex=1, value=2], "
+ "ConfigurationValue [id=null, institutionId=1, configurationId=1, attributeId=3, listIndex=1, value=true], "
+ "ConfigurationValue [id=null, institutionId=1, configurationId=1, attributeId=1, listIndex=2, value=val1], "
+ "ConfigurationValue [id=null, institutionId=1, configurationId=1, attributeId=2, listIndex=2, value=2], "
+ "ConfigurationValue [id=null, institutionId=1, configurationId=1, attributeId=3, listIndex=2, value=true]]",
valueCollector.values.toString());
assertEquals(
"[attribute.attr1, attribute.attr2, attribute.attr3, "
+ "attribute.attr1, attribute.attr2, attribute.attr3, "
+ "attribute.attr1, attribute.attr2, attribute.attr3]",
attrNamesCollector.toString());
}
private static final class ValueCollector implements Consumer<ConfigurationValue> {
List<ConfigurationValue> values = new ArrayList<>();
@Override
public void accept(final ConfigurationValue value) {
this.values.add(value);
}
}
}

View file

@ -48,8 +48,7 @@ public class SebConfigEncryptionServiceImplTest {
sebConfigEncryptionServiceImpl.streamDecrypted(
out2,
new ByteArrayInputStream(plainWithHeader),
null,
null);
EncryptionContext.contextOf(Strategy.PASSWORD_PSWD, (CharSequence) null));
out2.close();
@ -86,8 +85,7 @@ public class SebConfigEncryptionServiceImplTest {
sebConfigEncryptionServiceImpl.streamDecrypted(
out2,
new ByteArrayInputStream(byteArray),
() -> pwd,
null);
EncryptionContext.contextOf(Strategy.PASSWORD_PSWD, pwd));
final byte[] byteArray2 = out2.toByteArray();
assertNotNull(byteArray2);