SEBSERV-46 implementation back-end and part of front-end
This commit is contained in:
parent
09b326fdfe
commit
8c8a0944cb
30 changed files with 1341 additions and 107 deletions
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
|
||||
/*
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 |
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue