asynchronous zip end encryption streaming for seb configs download
This commit is contained in:
parent
8867721a8a
commit
06da2d026b
25 changed files with 773 additions and 119 deletions
|
@ -31,4 +31,9 @@ public class AsyncRunner {
|
|||
return new AsyncResult<>(supplier.get());
|
||||
}
|
||||
|
||||
@Async(AsyncServiceSpringConfig.EXECUTOR_BEAN_NAME)
|
||||
public void runAsync(final Runnable block) {
|
||||
block.run();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,8 +8,16 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.gbl.async;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PipedInputStream;
|
||||
import java.io.PipedOutputStream;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
|
@ -17,6 +25,8 @@ import org.springframework.stereotype.Service;
|
|||
@Service
|
||||
public class AsyncService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(AsyncService.class);
|
||||
|
||||
private final AsyncRunner asyncRunner;
|
||||
|
||||
protected AsyncService(final AsyncRunner asyncRunner) {
|
||||
|
@ -62,4 +72,41 @@ public class AsyncService {
|
|||
momoized);
|
||||
}
|
||||
|
||||
public void pipeToOutputStream(
|
||||
final OutputStream output,
|
||||
final Consumer<PipedOutputStream> consumer) {
|
||||
|
||||
this.asyncRunner.runAsync(() -> {
|
||||
|
||||
PipedOutputStream pout = null;
|
||||
PipedInputStream pin = null;
|
||||
try {
|
||||
pout = new PipedOutputStream();
|
||||
pin = new PipedInputStream(pout);
|
||||
|
||||
consumer.accept(pout);
|
||||
|
||||
IOUtils.copyLarge(pin, output);
|
||||
|
||||
pin.close();
|
||||
pout.flush();
|
||||
pout.close();
|
||||
|
||||
} catch (final IOException e) {
|
||||
log.error("Error while pipe stream data: ", e);
|
||||
} finally {
|
||||
try {
|
||||
pin.close();
|
||||
} catch (final IOException e1) {
|
||||
log.error("Failed to close PipedInputStream: ", e1);
|
||||
}
|
||||
try {
|
||||
pout.close();
|
||||
} catch (final IOException e1) {
|
||||
log.error("Failed to close PipedOutputStream: ", e1);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -12,10 +12,13 @@ import java.util.concurrent.Executor;
|
|||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.annotation.AsyncConfigurer;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
@Configuration
|
||||
public class AsyncServiceSpringConfig {
|
||||
@EnableAsync
|
||||
public class AsyncServiceSpringConfig implements AsyncConfigurer {
|
||||
|
||||
public static final String EXECUTOR_BEAN_NAME = "AsyncServiceExecutorBean";
|
||||
|
||||
|
@ -30,4 +33,9 @@ public class AsyncServiceSpringConfig {
|
|||
return executor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Executor getAsyncExecutor() {
|
||||
return threadPoolTaskExecutor();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -41,7 +41,8 @@ public enum AttributeType {
|
|||
|
||||
/** Table type is a list of a composite of single types */
|
||||
TABLE(COMPOSITE_LIST),
|
||||
;
|
||||
|
||||
STATIC_TABLE(COMPOSITE_LIST);
|
||||
|
||||
public final AttributeValueType attributeValueType;
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
|
|||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
|
||||
|
||||
/** Adapter interface for SEB Exam Configuration based input fields. */
|
||||
public interface InputField {
|
||||
|
||||
ConfigurationAttribute getAttribute();
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* 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.examconfig.impl;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.swt.widgets.Label;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Tuple;
|
||||
import ch.ethz.seb.sebserver.gui.service.examconfig.ExamConfigurationService;
|
||||
import ch.ethz.seb.sebserver.gui.service.examconfig.InputField;
|
||||
import ch.ethz.seb.sebserver.gui.service.examconfig.InputFieldBuilder;
|
||||
import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport;
|
||||
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
|
||||
import ch.ethz.seb.sebserver.gui.widget.Selection;
|
||||
import ch.ethz.seb.sebserver.gui.widget.SingleSelection;
|
||||
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@GuiProfile
|
||||
public class SingleSelectionFieldBuilder implements InputFieldBuilder {
|
||||
|
||||
private final WidgetFactory widgetFactory;
|
||||
|
||||
protected SingleSelectionFieldBuilder(final WidgetFactory widgetFactory) {
|
||||
this.widgetFactory = widgetFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean builderFor(
|
||||
final ConfigurationAttribute attribute,
|
||||
final Orientation orientation) {
|
||||
|
||||
return attribute.type == AttributeType.SINGLE_SELECTION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputField createInputField(
|
||||
final Composite parent,
|
||||
final ConfigurationAttribute attribute,
|
||||
final ViewContext viewContext) {
|
||||
|
||||
final Orientation orientation = viewContext
|
||||
.getOrientation(attribute.id);
|
||||
final Composite innerGrid = InputFieldBuilder
|
||||
.createInnerGrid(parent, orientation);
|
||||
|
||||
final SingleSelection selection = this.widgetFactory.selectionLocalized(
|
||||
Selection.Type.SINGLE,
|
||||
innerGrid,
|
||||
() -> this.getLocalizedResources(attribute))
|
||||
.getTypeInstance();
|
||||
|
||||
final SingleSelectionInputField singleSelectionInputField = new SingleSelectionInputField(
|
||||
attribute,
|
||||
orientation,
|
||||
selection,
|
||||
InputFieldBuilder.createErrorLabel(innerGrid));
|
||||
|
||||
selection.setSelectionListener(event -> {
|
||||
singleSelectionInputField.clearError();
|
||||
viewContext.getValueChangeListener().valueChanged(
|
||||
viewContext,
|
||||
attribute,
|
||||
String.valueOf(selection.getSelectionValue()),
|
||||
singleSelectionInputField.listIndex);
|
||||
});
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<Tuple<String>> getLocalizedResources(final ConfigurationAttribute attribute) {
|
||||
if (attribute == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final I18nSupport i18nSupport = this.widgetFactory.getI18nSupport();
|
||||
final String prefix = ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + attribute.name + ".";
|
||||
return Arrays.asList(StringUtils.split(
|
||||
attribute.resources,
|
||||
Constants.LIST_SEPARATOR))
|
||||
.stream()
|
||||
.map(value -> new Tuple<>(
|
||||
value,
|
||||
i18nSupport.getText(
|
||||
new LocTextKey(prefix + value),
|
||||
value)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
static final class SingleSelectionInputField extends AbstractInputField<SingleSelection> {
|
||||
|
||||
protected SingleSelectionInputField(
|
||||
final ConfigurationAttribute attribute,
|
||||
final Orientation orientation,
|
||||
final SingleSelection control,
|
||||
final Label errorLabel) {
|
||||
|
||||
super(attribute, orientation, control, errorLabel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return this.control.getSelectionValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setValueToControl(final String value) {
|
||||
this.control.select(value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -270,17 +270,6 @@ public class TableFieldBuilder implements InputFieldBuilder {
|
|||
|
||||
}
|
||||
|
||||
// private void applyRowValues(
|
||||
// final int rowIndex,
|
||||
// final Map<Long, TableValue> rowValues) {
|
||||
//
|
||||
// // set the new values
|
||||
// this.values.set(rowIndex, rowValues);
|
||||
// // update table row
|
||||
// applyTableRowValues(rowIndex);
|
||||
//
|
||||
// }
|
||||
|
||||
private void applyTableRowValues(final int index) {
|
||||
final TableItem item = this.control.getItem(index);
|
||||
final Map<Long, TableValue> rowValues = this.values.get(index);
|
||||
|
|
|
@ -20,6 +20,7 @@ import org.eclipse.swt.widgets.ColorDialog;
|
|||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.swt.widgets.Event;
|
||||
import org.eclipse.swt.widgets.Label;
|
||||
import org.eclipse.swt.widgets.Listener;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -37,6 +38,8 @@ public class ColorSelection extends Composite implements Selection {
|
|||
private final Composite colorField;
|
||||
private RGB selection;
|
||||
|
||||
private Listener listener = null;
|
||||
|
||||
ColorSelection(final Composite parent, final WidgetFactory widgetFactory) {
|
||||
super(parent, SWT.NONE);
|
||||
final GridLayout gridLayout = new GridLayout(2, false);
|
||||
|
@ -67,6 +70,11 @@ public class ColorSelection extends Composite implements Selection {
|
|||
this.addListener(SWT.Resize, this::adaptColumnWidth);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelectionListener(final Listener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type type() {
|
||||
return Type.COLOR;
|
||||
|
@ -100,6 +108,9 @@ public class ColorSelection extends Composite implements Selection {
|
|||
|
||||
this.selection = this.colorDialog.getRGB();
|
||||
applySelection();
|
||||
if (this.listener != null) {
|
||||
this.listener.handleEvent(event);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ import org.eclipse.swt.layout.GridData;
|
|||
import org.eclipse.swt.layout.GridLayout;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.swt.widgets.Label;
|
||||
import org.eclipse.swt.widgets.Listener;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Tuple;
|
||||
|
@ -34,6 +35,8 @@ public class MultiSelection extends Composite implements Selection {
|
|||
private final List<Label> labels = new ArrayList<>();
|
||||
private final List<Label> selected = new ArrayList<>();
|
||||
|
||||
private Listener listener = null;
|
||||
|
||||
MultiSelection(final Composite parent) {
|
||||
super(parent, SWT.NONE);
|
||||
final GridLayout gridLayout = new GridLayout(1, true);
|
||||
|
@ -49,6 +52,11 @@ public class MultiSelection extends Composite implements Selection {
|
|||
return Type.MULTI;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelectionListener(final Listener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyNewMapping(final List<Tuple<String>> mapping) {
|
||||
final String selectionValue = getSelectionValue();
|
||||
|
@ -71,6 +79,9 @@ public class MultiSelection extends Composite implements Selection {
|
|||
l.setData(RWT.CUSTOM_VARIANT, CustomVariant.SELECTED.key);
|
||||
this.selected.add(l);
|
||||
}
|
||||
if (this.listener != null) {
|
||||
this.listener.handleEvent(event);
|
||||
}
|
||||
});
|
||||
this.labels.add(label);
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.eclipse.swt.widgets.Composite;
|
|||
import org.eclipse.swt.widgets.Control;
|
||||
import org.eclipse.swt.widgets.Event;
|
||||
import org.eclipse.swt.widgets.Label;
|
||||
import org.eclipse.swt.widgets.Listener;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -51,6 +52,8 @@ public class MultiSelectionCombo extends Composite implements Selection {
|
|||
private final GridData comboCell;
|
||||
private final GridData actionCell;
|
||||
|
||||
private Listener listener = null;
|
||||
|
||||
MultiSelectionCombo(final Composite parent, final WidgetFactory widgetFactory) {
|
||||
super(parent, SWT.NONE);
|
||||
this.widgetFactory = widgetFactory;
|
||||
|
@ -83,6 +86,11 @@ public class MultiSelectionCombo extends Composite implements Selection {
|
|||
return Type.MULTI_COMBO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelectionListener(final Listener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyNewMapping(final List<Tuple<String>> mapping) {
|
||||
this.mapping.putAll(mapping.stream()
|
||||
|
@ -153,6 +161,9 @@ public class MultiSelectionCombo extends Composite implements Selection {
|
|||
}
|
||||
|
||||
addSelection(findFirst.get().getKey());
|
||||
if (this.listener != null) {
|
||||
this.listener.handleEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
private void addSelection(final String itemKey) {
|
||||
|
@ -203,6 +214,9 @@ public class MultiSelectionCombo extends Composite implements Selection {
|
|||
this.combo.add(value._2, this.combo.getItemCount());
|
||||
|
||||
this.getParent().layout();
|
||||
if (this.listener != null) {
|
||||
this.listener.handleEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
private void adaptColumnWidth(final Event event) {
|
||||
|
|
|
@ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.gui.widget;
|
|||
import java.util.List;
|
||||
|
||||
import org.eclipse.swt.widgets.Control;
|
||||
import org.eclipse.swt.widgets.Listener;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.util.Tuple;
|
||||
|
||||
|
@ -35,10 +36,15 @@ public interface Selection {
|
|||
|
||||
void setVisible(boolean visible);
|
||||
|
||||
void setSelectionListener(Listener listener);
|
||||
|
||||
default Control adaptToControl() {
|
||||
return (Control) this;
|
||||
}
|
||||
|
||||
//<T extends Selection> T getTypeInstance();
|
||||
@SuppressWarnings("unchecked")
|
||||
default <T extends Selection> T getTypeInstance() {
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import java.util.stream.Collectors;
|
|||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.widgets.Combo;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.swt.widgets.Listener;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.util.Tuple;
|
||||
|
||||
|
@ -77,4 +78,9 @@ public class SingleSelection extends Combo implements Selection {
|
|||
return Type.SINGLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelectionListener(final Listener listener) {
|
||||
super.addListener(SWT.Selection, listener);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.SebClientConfig;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
|
@ -55,6 +55,8 @@ public interface SebClientConfigService {
|
|||
|
||||
Result<SebClientConfig> autoCreateSebClientConfigurationForInstitution(Long institutionId);
|
||||
|
||||
Result<InputStream> exportSebClientConfiguration(final String modelId);
|
||||
void exportSebClientConfiguration(
|
||||
OutputStream out,
|
||||
final String modelId);
|
||||
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -43,4 +45,14 @@ public interface SebConfigCryptor {
|
|||
final ByteBuffer cipher,
|
||||
final SebConfigEncryptionContext context);
|
||||
|
||||
void encrypt(
|
||||
final OutputStream encryptedOutput,
|
||||
final InputStream plainTextInputStream,
|
||||
final SebConfigEncryptionContext context);
|
||||
|
||||
void decrypt(
|
||||
final OutputStream plainTextOutput,
|
||||
final InputStream cipherInputStream,
|
||||
final SebConfigEncryptionContext context);
|
||||
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.cert.Certificate;
|
||||
import java.util.function.Function;
|
||||
|
@ -60,16 +62,11 @@ public interface SebConfigEncryptionService {
|
|||
* case */
|
||||
Result<ByteBuffer> plainText(CharSequence plainTextConfig);
|
||||
|
||||
/** Use this to create a password encrypted SEB Configuration file from configuration text
|
||||
* with the given Strategy
|
||||
*
|
||||
* @param plainTextConfig plainTextConfig plain text SEB Configuration as CharSequence
|
||||
* @return Result of password encoded ByteBuffer or a reference to an Exception on error
|
||||
* case */
|
||||
Result<ByteBuffer> encryptWithPassword(
|
||||
CharSequence plainTextConfig,
|
||||
Strategy strategy,
|
||||
CharSequence password);
|
||||
void streamEncryption(
|
||||
final OutputStream output,
|
||||
final InputStream input,
|
||||
final Strategy strategy,
|
||||
final CharSequence password);
|
||||
|
||||
Result<ByteBuffer> encryptWithCertificate(
|
||||
CharSequence plainTextConfig,
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public interface ZipService {
|
||||
|
||||
void write(OutputStream out, InputStream in);
|
||||
|
||||
void read(OutputStream out, InputStream in);
|
||||
}
|
|
@ -8,25 +8,37 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.tomcat.util.http.fileupload.IOUtils;
|
||||
import org.cryptonode.jncryptor.AES256JNCryptorOutputStream;
|
||||
import org.cryptonode.jncryptor.CryptorException;
|
||||
import org.cryptonode.jncryptor.JNCryptor;
|
||||
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 ch.ethz.seb.sebserver.gbl.async.AsyncServiceSpringConfig;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebConfigCryptor;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebConfigEncryptionContext;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebConfigEncryptionService.Strategy;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebConfigCryptor;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@WebServiceProfile
|
||||
public class PasswordEncryptor implements SebConfigCryptor {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(PasswordEncryptor.class);
|
||||
|
||||
private static final Set<Strategy> STRATEGIES = Utils.immutableSetOf(
|
||||
Strategy.PASSWORD_PSWD,
|
||||
Strategy.PASSWORD_PWCC);
|
||||
|
@ -60,4 +72,58 @@ public class PasswordEncryptor implements SebConfigCryptor {
|
|||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@Async(AsyncServiceSpringConfig.EXECUTOR_BEAN_NAME)
|
||||
public void encrypt(
|
||||
final OutputStream output,
|
||||
final InputStream input,
|
||||
final SebConfigEncryptionContext context) {
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("*** Start streaming asynchronous encryption of SEB exam configuration data");
|
||||
}
|
||||
|
||||
AES256JNCryptorOutputStream encryptOutput = null;
|
||||
try {
|
||||
|
||||
encryptOutput = new AES256JNCryptorOutputStream(
|
||||
output,
|
||||
Utils.toCharArray(context.getPassword()));
|
||||
|
||||
IOUtils.copyLarge(input, encryptOutput);
|
||||
|
||||
encryptOutput.close();
|
||||
encryptOutput.flush();
|
||||
encryptOutput.close();
|
||||
output.flush();
|
||||
|
||||
} catch (final CryptorException e) {
|
||||
log.error("Error while trying to stream and encrypt seb exam configuration data: ", e);
|
||||
} catch (final IOException e) {
|
||||
log.error("Error while trying to read/write form/to streams: ", e);
|
||||
} finally {
|
||||
try {
|
||||
if (encryptOutput != null)
|
||||
encryptOutput.close();
|
||||
} catch (final IOException e) {
|
||||
log.error("Failed to close AES256JNCryptorOutputStream: ", e);
|
||||
}
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("*** Finish streaming asynchronous encryption of SEB exam configuration data");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Async(AsyncServiceSpringConfig.EXECUTOR_BEAN_NAME)
|
||||
public void decrypt(
|
||||
final OutputStream plainTextOutput,
|
||||
final InputStream cipherInputStream,
|
||||
final SebConfigEncryptionContext context) {
|
||||
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,11 +9,16 @@
|
|||
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.nio.ByteBuffer;
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
|
@ -34,6 +39,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.SebClientConfigDAO;
|
|||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebClientConfigService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebConfigEncryptionService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebConfigEncryptionService.Strategy;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ZipService;
|
||||
|
||||
@Lazy
|
||||
@Service
|
||||
|
@ -46,6 +52,7 @@ public class SebClientConfigServiceImpl implements SebClientConfigService {
|
|||
private final SebClientConfigDAO sebClientConfigDAO;
|
||||
private final ClientCredentialService clientCredentialService;
|
||||
private final SebConfigEncryptionService sebConfigEncryptionService;
|
||||
private final ZipService zipService;
|
||||
private final String httpScheme;
|
||||
private final String serverAddress;
|
||||
private final String serverPort;
|
||||
|
@ -56,6 +63,7 @@ public class SebClientConfigServiceImpl implements SebClientConfigService {
|
|||
final SebClientConfigDAO sebClientConfigDAO,
|
||||
final ClientCredentialService clientCredentialService,
|
||||
final SebConfigEncryptionService sebConfigEncryptionService,
|
||||
final ZipService zipService,
|
||||
@Value("${sebserver.webservice.http.scheme}") final String httpScheme,
|
||||
@Value("${server.address}") final String serverAddress,
|
||||
@Value("${server.port}") final String serverPort,
|
||||
|
@ -65,6 +73,7 @@ public class SebClientConfigServiceImpl implements SebClientConfigService {
|
|||
this.sebClientConfigDAO = sebClientConfigDAO;
|
||||
this.clientCredentialService = clientCredentialService;
|
||||
this.sebConfigEncryptionService = sebConfigEncryptionService;
|
||||
this.zipService = zipService;
|
||||
this.httpScheme = httpScheme;
|
||||
this.serverAddress = serverAddress;
|
||||
this.serverPort = serverPort;
|
||||
|
@ -97,77 +106,113 @@ public class SebClientConfigServiceImpl implements SebClientConfigService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Result<InputStream> exportSebClientConfiguration(final String modelId) {
|
||||
return this.sebClientConfigDAO.byModelId(modelId)
|
||||
.flatMap(this::createExport);
|
||||
}
|
||||
public void exportSebClientConfiguration(
|
||||
final OutputStream output,
|
||||
final String modelId) {
|
||||
|
||||
private final Result<InputStream> createExport(final SebClientConfig config) {
|
||||
// TODO implementation of creation of SEB client configuration for specified Institution
|
||||
// A SEB start configuration should at least contain the SEB-Client-Credentials to access the SEB Server API
|
||||
// and the SEB Server URL
|
||||
//
|
||||
// To Clarify : The format of a SEB start configuration
|
||||
// To Clarify : How the file should be encrypted (use case) maybe we need another encryption-secret for this that can be given by
|
||||
// an administrator on SEB start configuration creation time
|
||||
final SebClientConfig config = this.sebClientConfigDAO
|
||||
.byModelId(modelId).getOrThrow();
|
||||
|
||||
return Result.tryCatch(() -> {
|
||||
final String serverURL = UriComponentsBuilder.newInstance()
|
||||
.scheme(this.httpScheme)
|
||||
.host(this.serverAddress)
|
||||
.port(this.serverPort)
|
||||
.toUriString();
|
||||
|
||||
final String serverURL = UriComponentsBuilder.newInstance()
|
||||
.scheme(this.httpScheme)
|
||||
.host(this.serverAddress)
|
||||
.port(this.serverPort)
|
||||
.toUriString();
|
||||
final ClientCredentials sebClientCredentials = this.sebClientConfigDAO
|
||||
.getSebClientCredentials(config.getModelId())
|
||||
.getOrThrow();
|
||||
|
||||
final ClientCredentials sebClientCredentials = this.sebClientConfigDAO
|
||||
.getSebClientCredentials(config.getModelId())
|
||||
.getOrThrow();
|
||||
final CharSequence encryptionPassword = this.sebClientConfigDAO
|
||||
.getConfigPasswortCipher(config.getModelId())
|
||||
.getOrThrow();
|
||||
|
||||
final CharSequence encryptionPassword = this.sebClientConfigDAO
|
||||
.getConfigPasswortCipher(config.getModelId())
|
||||
.getOrThrow();
|
||||
final CharSequence plainClientId = this.clientCredentialService
|
||||
.getPlainClientId(sebClientCredentials);
|
||||
final CharSequence plainClientSecret = this.clientCredentialService
|
||||
.getPlainClientSecret(sebClientCredentials);
|
||||
|
||||
final CharSequence plainClientId = this.clientCredentialService
|
||||
.getPlainClientId(sebClientCredentials);
|
||||
final CharSequence plainClientSecret = this.clientCredentialService
|
||||
.getPlainClientSecret(sebClientCredentials);
|
||||
final String plainTextConfig = String.format(
|
||||
SEB_CLIENT_CONFIG_EXAMPLE_XML,
|
||||
serverURL,
|
||||
String.valueOf(config.institutionId),
|
||||
plainClientId,
|
||||
plainClientSecret,
|
||||
API.OAUTH_TOKEN_ENDPOINT,
|
||||
this.sebClientAPIEndpoint + API.EXAM_API_HANDSHAKE_ENDPOINT,
|
||||
this.sebClientAPIEndpoint + API.EXAM_API_CONFIGURATION_REQUEST_ENDPOINT,
|
||||
this.sebClientAPIEndpoint + API.EXAM_API_PING_ENDPOINT,
|
||||
this.sebClientAPIEndpoint + API.EXAM_API_EVENT_ENDPOINT);
|
||||
|
||||
final String plainTextConfig = String.format(
|
||||
SEB_CLIENT_CONFIG_EXAMPLE_XML,
|
||||
serverURL,
|
||||
String.valueOf(config.institutionId),
|
||||
plainClientId,
|
||||
plainClientSecret,
|
||||
API.OAUTH_TOKEN_ENDPOINT,
|
||||
this.sebClientAPIEndpoint + API.EXAM_API_HANDSHAKE_ENDPOINT,
|
||||
this.sebClientAPIEndpoint + API.EXAM_API_CONFIGURATION_REQUEST_ENDPOINT,
|
||||
this.sebClientAPIEndpoint + API.EXAM_API_PING_ENDPOINT,
|
||||
this.sebClientAPIEndpoint + API.EXAM_API_EVENT_ENDPOINT);
|
||||
PipedOutputStream pOut = null;
|
||||
PipedInputStream pIn = null;
|
||||
try {
|
||||
// zip the plain text
|
||||
final InputStream plainIn = IOUtils.toInputStream(plainTextConfig, "UTF-8");
|
||||
pOut = new PipedOutputStream();
|
||||
pIn = new PipedInputStream(pOut);
|
||||
|
||||
this.zipService.write(pOut, plainIn);
|
||||
|
||||
if (encryptionPassword != null) {
|
||||
|
||||
log.debug("Try to encrypt seb client configuration with password based encryption");
|
||||
|
||||
final CharSequence encryptionPasswordPlaintext = this.clientCredentialService
|
||||
.decrypt(encryptionPassword);
|
||||
|
||||
final ByteBuffer encryptedConfig = this.sebConfigEncryptionService.encryptWithPassword(
|
||||
plainTextConfig,
|
||||
Strategy.PASSWORD_PSWD,
|
||||
encryptionPasswordPlaintext)
|
||||
.getOrThrow();
|
||||
|
||||
return new ByteArrayInputStream(Utils.toByteArray(encryptedConfig));
|
||||
passwordEncryption(output, encryptionPassword, pIn);
|
||||
} else {
|
||||
|
||||
log.debug("Serve plain text seb configuration with specified header");
|
||||
|
||||
final ByteBuffer encryptedConfig = this.sebConfigEncryptionService.plainText(plainTextConfig)
|
||||
.getOrThrow();
|
||||
|
||||
return new ByteArrayInputStream(Utils.toByteArray(encryptedConfig));
|
||||
noEncryption(output, plainTextConfig);
|
||||
}
|
||||
});
|
||||
|
||||
} catch (final Exception e) {
|
||||
log.error("Error while zip and encrypt seb client config stream: ", e);
|
||||
try {
|
||||
if (pIn != null)
|
||||
pIn.close();
|
||||
} catch (final IOException e1) {
|
||||
log.error("Failed to close PipedInputStream: ", e1);
|
||||
}
|
||||
try {
|
||||
if (pOut != null)
|
||||
pOut.close();
|
||||
} catch (final IOException e1) {
|
||||
log.error("Failed to close PipedOutputStream: ", e1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void noEncryption(
|
||||
final OutputStream output,
|
||||
final String plainTextConfig) throws IOException {
|
||||
|
||||
log.debug("Serve plain text seb configuration with specified header");
|
||||
|
||||
final ByteBuffer encryptedConfig = this.sebConfigEncryptionService.plainText(plainTextConfig)
|
||||
.getOrThrow();
|
||||
|
||||
IOUtils.copyLarge(
|
||||
new ByteArrayInputStream(Utils.toByteArray(encryptedConfig)),
|
||||
output);
|
||||
|
||||
}
|
||||
|
||||
private void passwordEncryption(
|
||||
final OutputStream output,
|
||||
final CharSequence encryptionPassword,
|
||||
final InputStream input) {
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("*** Seb client configuration with password based encryption");
|
||||
}
|
||||
|
||||
final CharSequence encryptionPasswordPlaintext = this.clientCredentialService
|
||||
.decrypt(encryptionPassword);
|
||||
|
||||
this.sebConfigEncryptionService.streamEncryption(
|
||||
output,
|
||||
input,
|
||||
Strategy.PASSWORD_PSWD,
|
||||
encryptionPasswordPlaintext);
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("*** Finished Seb client configuration with password based encryption");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,6 +8,11 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PipedInputStream;
|
||||
import java.io.PipedOutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.cert.Certificate;
|
||||
import java.util.Arrays;
|
||||
|
@ -17,6 +22,7 @@ import java.util.function.Function;
|
|||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -66,20 +72,49 @@ public final class SebConfigEncryptionServiceImpl implements SebConfigEncryption
|
|||
}
|
||||
|
||||
@Override
|
||||
public Result<ByteBuffer> encryptWithPassword(
|
||||
final CharSequence plainTextConfig,
|
||||
public void streamEncryption(
|
||||
final OutputStream output,
|
||||
final InputStream input,
|
||||
final Strategy strategy,
|
||||
final CharSequence password) {
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Password encryption with strategy: {}", strategy);
|
||||
}
|
||||
PipedOutputStream pout = null;
|
||||
PipedInputStream pin = null;
|
||||
try {
|
||||
pout = new PipedOutputStream();
|
||||
pin = new PipedInputStream(pout);
|
||||
|
||||
return getEncryptor(strategy)
|
||||
.flatMap(encryptor -> encryptor.encrypt(
|
||||
plainTextConfig,
|
||||
EncryptionContext.contextOf(strategy, password)))
|
||||
.map(bb -> addHeader(bb, strategy));
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Password encryption with strategy: {}", strategy);
|
||||
}
|
||||
|
||||
pout.write(strategy.header);
|
||||
getEncryptor(strategy)
|
||||
.getOrThrow()
|
||||
.encrypt(pout,
|
||||
input,
|
||||
EncryptionContext.contextOf(strategy, password));
|
||||
|
||||
IOUtils.copyLarge(pin, output);
|
||||
|
||||
pin.close();
|
||||
pout.flush();
|
||||
pout.close();
|
||||
|
||||
} catch (final IOException e) {
|
||||
log.error("Error while stream encrypted data: ", e);
|
||||
} finally {
|
||||
try {
|
||||
pin.close();
|
||||
} catch (final IOException e1) {
|
||||
log.error("Failed to close PipedInputStream: ", e1);
|
||||
}
|
||||
try {
|
||||
pout.close();
|
||||
} catch (final IOException e1) {
|
||||
log.error("Failed to close PipedOutputStream: ", e1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* 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.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
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 ch.ethz.seb.sebserver.gbl.async.AsyncServiceSpringConfig;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ZipService;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@WebServiceProfile
|
||||
public class ZipServiceImpl implements ZipService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ZipServiceImpl.class);
|
||||
|
||||
@Override
|
||||
@Async(AsyncServiceSpringConfig.EXECUTOR_BEAN_NAME)
|
||||
public void write(final OutputStream out, final InputStream in) {
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("*** Start streaming asynchronous zipping of SEB exam configuration data");
|
||||
}
|
||||
|
||||
GZIPOutputStream zipOutputStream = null;
|
||||
try {
|
||||
|
||||
zipOutputStream = new GZIPOutputStream(out);
|
||||
|
||||
IOUtils.copyLarge(in, zipOutputStream);
|
||||
|
||||
in.close();
|
||||
zipOutputStream.flush();
|
||||
zipOutputStream.close();
|
||||
|
||||
} catch (final IOException e) {
|
||||
log.error("Error while streaming data to zipped output: ", e);
|
||||
} finally {
|
||||
try {
|
||||
if (zipOutputStream != null)
|
||||
zipOutputStream.close();
|
||||
} catch (final IOException e) {
|
||||
log.error("Failed to close ZipOutputStream: ", e);
|
||||
}
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("*** Finish streaming asynchronous zipping of SEB exam configuration data");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Async(AsyncServiceSpringConfig.EXECUTOR_BEAN_NAME)
|
||||
public void read(final OutputStream out, final InputStream in) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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.weblayer.api;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.task.AsyncTaskExecutor;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
|
||||
@EnableAsync
|
||||
@Configuration
|
||||
@WebServiceProfile
|
||||
public class ControllerConfig implements WebMvcConfigurer {
|
||||
|
||||
@Override
|
||||
public void configureAsyncSupport(final AsyncSupportConfigurer configurer) {
|
||||
configurer.setTaskExecutor(threadPoolTaskExecutor());
|
||||
configurer.setDefaultTimeout(30_000);
|
||||
}
|
||||
|
||||
public AsyncTaskExecutor threadPoolTaskExecutor() {
|
||||
final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
executor.setCorePoolSize(7);
|
||||
executor.setMaxPoolSize(42);
|
||||
executor.setQueueCapacity(11);
|
||||
executor.setThreadNamePrefix("mvc-");
|
||||
executor.initialize();
|
||||
return executor;
|
||||
}
|
||||
|
||||
}
|
|
@ -8,21 +8,21 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.webservice.weblayer.api;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.tomcat.util.http.fileupload.IOUtils;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.mybatis.dynamic.sql.SqlTable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.validation.FieldError;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||
|
@ -45,9 +45,12 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe
|
|||
|
||||
@WebServiceProfile
|
||||
@RestController
|
||||
@EnableAsync
|
||||
@RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + API.SEB_CLIENT_CONFIG_ENDPOINT)
|
||||
public class SebClientConfigController extends ActivatableEntityController<SebClientConfig, SebClientConfig> {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SebClientConfigController.class);
|
||||
|
||||
private final SebClientConfigService sebClientConfigService;
|
||||
|
||||
public SebClientConfigController(
|
||||
|
@ -73,22 +76,16 @@ public class SebClientConfigController extends ActivatableEntityController<SebCl
|
|||
path = API.SEB_CLIENT_CONFIG_DOWNLOAD_PATH_SEGMENT + API.MODEL_ID_VAR_PATH_SEGMENT,
|
||||
method = RequestMethod.GET,
|
||||
produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
|
||||
public void downloadSEBConfig(
|
||||
@PathVariable final String modelId,
|
||||
final HttpServletResponse response) throws Exception {
|
||||
public ResponseEntity<StreamingResponseBody> downloadSEBConfig(
|
||||
@PathVariable final String modelId) {
|
||||
|
||||
this.entityDAO.byModelId(modelId)
|
||||
.map(this.authorization::checkWrite);
|
||||
|
||||
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
|
||||
response.setStatus(HttpStatus.OK.value());
|
||||
final StreamingResponseBody stream = out -> this.sebClientConfigService
|
||||
.exportSebClientConfiguration(out, modelId);
|
||||
|
||||
final InputStream sebConfigFileIn = this.sebClientConfigService
|
||||
.exportSebClientConfiguration(modelId)
|
||||
.getOrThrow();
|
||||
|
||||
IOUtils.copyLarge(sebConfigFileIn, response.getOutputStream());
|
||||
response.flushBuffer();
|
||||
return new ResponseEntity<>(stream, HttpStatus.OK);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* 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.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.cryptonode.jncryptor.AES256JNCryptor;
|
||||
import org.cryptonode.jncryptor.AES256JNCryptorOutputStream;
|
||||
import org.cryptonode.jncryptor.JNCryptor;
|
||||
import org.junit.Test;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebConfigEncryptionContext;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebConfigEncryptionService.Strategy;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl.SebConfigEncryptionServiceImpl.EncryptionContext;
|
||||
|
||||
public class PasswordEncryptorTest {
|
||||
|
||||
@Test
|
||||
public void conversionTest() throws IOException {
|
||||
final String text = "ojnjbiboijnlkncokdnvoiwjife";
|
||||
final byte[] byteArray = Utils.toByteArray(text);
|
||||
final byte[] otherByteArray = new byte[byteArray.length];
|
||||
final InputStream inputStream = IOUtils.toInputStream(text, "UTF-8");
|
||||
inputStream.read(otherByteArray);
|
||||
|
||||
assertTrue(Arrays.equals(byteArray, otherByteArray));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUsingPassword() throws Exception {
|
||||
|
||||
final String config = "<TestConfig></TestConfig>";
|
||||
final byte[] plaintext = Utils.toByteArray(config);//getRandomBytes(127);
|
||||
|
||||
final String password = "Testing1234";
|
||||
|
||||
final ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
|
||||
final AES256JNCryptorOutputStream cryptorStream = new AES256JNCryptorOutputStream(
|
||||
byteStream, password.toCharArray());
|
||||
cryptorStream.write(plaintext);
|
||||
cryptorStream.close();
|
||||
|
||||
final byte[] encrypted = byteStream.toByteArray();
|
||||
|
||||
final JNCryptor cryptor = new AES256JNCryptor();
|
||||
|
||||
final byte[] result = cryptor.decryptData(encrypted, password.toCharArray());
|
||||
assertArrayEquals(plaintext, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test1() {
|
||||
final JNCryptor jnCryptor = new AES256JNCryptor();
|
||||
jnCryptor.setPBKDFIterations(10000);
|
||||
final PasswordEncryptor encryptor = new PasswordEncryptor(jnCryptor);
|
||||
|
||||
final String config = "<TestConfig></TestConfig>";
|
||||
final String pwd = "password";
|
||||
|
||||
final SebConfigEncryptionContext context = EncryptionContext.contextOf(
|
||||
Strategy.PASSWORD_PWCC,
|
||||
pwd);
|
||||
|
||||
final Result<ByteBuffer> encrypt = encryptor.encrypt(config, context);
|
||||
assertFalse(encrypt.hasError());
|
||||
final ByteBuffer cipher = encrypt.getOrThrow();
|
||||
final byte[] byteArray = Utils.toByteArray(cipher);
|
||||
|
||||
final Result<ByteBuffer> decrypt = encryptor.decrypt(cipher, context);
|
||||
assertFalse(decrypt.hasError());
|
||||
|
||||
final String decryptedConfig = Utils.toString(decrypt.getOrThrow());
|
||||
assertEquals(config, decryptedConfig);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test2() throws IOException {
|
||||
final JNCryptor jnCryptor = new AES256JNCryptor();
|
||||
jnCryptor.setPBKDFIterations(10000);
|
||||
final PasswordEncryptor encryptor = new PasswordEncryptor(jnCryptor);
|
||||
|
||||
final String config = "<TestConfig></TestConfig>";
|
||||
final String pwd = "password";
|
||||
final ByteArrayOutputStream out = new ByteArrayOutputStream(512);
|
||||
|
||||
final SebConfigEncryptionContext context = EncryptionContext.contextOf(
|
||||
Strategy.PASSWORD_PWCC,
|
||||
pwd);
|
||||
|
||||
encryptor.encrypt(
|
||||
out,
|
||||
IOUtils.toInputStream(config, "UTF-8"),
|
||||
context);
|
||||
|
||||
final byte[] byteArray = out.toByteArray();
|
||||
|
||||
final Result<ByteBuffer> decrypt = encryptor.decrypt(
|
||||
ByteBuffer.wrap(byteArray),
|
||||
context);
|
||||
assertFalse(decrypt.hasError());
|
||||
|
||||
final ByteBuffer buffer = decrypt.getOrThrow();
|
||||
buffer.rewind();
|
||||
final String decryptedConfig = Utils.toString(buffer);
|
||||
assertEquals(config, decryptedConfig);
|
||||
}
|
||||
|
||||
}
|
|
@ -10,18 +10,21 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl;
|
|||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.cryptonode.jncryptor.AES256JNCryptor;
|
||||
import org.cryptonode.jncryptor.JNCryptor;
|
||||
import org.junit.Test;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebConfigEncryptionService.Strategy;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebConfigCryptor;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebConfigEncryptionService.Strategy;
|
||||
|
||||
public class SebConfigEncryptionServiceImplTest {
|
||||
|
||||
|
@ -42,19 +45,24 @@ public class SebConfigEncryptionServiceImplTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testPasswordEncryption() {
|
||||
public void testPasswordEncryption() throws IOException {
|
||||
final SebConfigEncryptionServiceImpl sebConfigEncryptionServiceImpl = sebConfigEncryptionServiceImpl();
|
||||
|
||||
final String config = "<TestConfig></TestConfig>";
|
||||
final String pwd = "password";
|
||||
|
||||
final Result<ByteBuffer> plainText = sebConfigEncryptionServiceImpl.encryptWithPassword(
|
||||
config,
|
||||
final ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
|
||||
|
||||
sebConfigEncryptionServiceImpl.streamEncryption(
|
||||
out,
|
||||
IOUtils.toInputStream(config, "UTF-8"),
|
||||
Strategy.PASSWORD_PWCC,
|
||||
pwd);
|
||||
|
||||
assertFalse(plainText.hasError());
|
||||
final ByteBuffer cipher = plainText.get();
|
||||
final byte[] byteArray = out.toByteArray();
|
||||
|
||||
//assertFalse(plainText.hasError());
|
||||
final ByteBuffer cipher = ByteBuffer.wrap(byteArray);
|
||||
assertTrue(Utils.toString(cipher).startsWith(Utils.toString(Strategy.PASSWORD_PWCC.header)));
|
||||
|
||||
final Result<ByteBuffer> decrypt = sebConfigEncryptionServiceImpl.decrypt(cipher, () -> pwd, null);
|
||||
|
|
|
@ -19,3 +19,5 @@ sebserver.webservice.api.exam.accessTokenValiditySeconds=1800
|
|||
sebserver.webservice.api.exam.refreshTokenValiditySeconds=-1
|
||||
sebserver.webservice.internalSecret=TO_SET
|
||||
sebserver.webservice.api.redirect.unauthorized=none
|
||||
|
||||
management.endpoints.web.base-path=/actuator
|
Loading…
Reference in a new issue