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()); |         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; | 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 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.context.annotation.Lazy; | ||||||
| import org.springframework.stereotype.Service; | import org.springframework.stereotype.Service; | ||||||
| 
 | 
 | ||||||
|  | @ -17,6 +25,8 @@ import org.springframework.stereotype.Service; | ||||||
| @Service | @Service | ||||||
| public class AsyncService { | public class AsyncService { | ||||||
| 
 | 
 | ||||||
|  |     private static final Logger log = LoggerFactory.getLogger(AsyncService.class); | ||||||
|  | 
 | ||||||
|     private final AsyncRunner asyncRunner; |     private final AsyncRunner asyncRunner; | ||||||
| 
 | 
 | ||||||
|     protected AsyncService(final AsyncRunner asyncRunner) { |     protected AsyncService(final AsyncRunner asyncRunner) { | ||||||
|  | @ -62,4 +72,41 @@ public class AsyncService { | ||||||
|                 momoized); |                 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.Bean; | ||||||
| import org.springframework.context.annotation.Configuration; | import org.springframework.context.annotation.Configuration; | ||||||
|  | import org.springframework.scheduling.annotation.AsyncConfigurer; | ||||||
|  | import org.springframework.scheduling.annotation.EnableAsync; | ||||||
| import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; | import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; | ||||||
| 
 | 
 | ||||||
| @Configuration | @Configuration | ||||||
| public class AsyncServiceSpringConfig { | @EnableAsync | ||||||
|  | public class AsyncServiceSpringConfig implements AsyncConfigurer { | ||||||
| 
 | 
 | ||||||
|     public static final String EXECUTOR_BEAN_NAME = "AsyncServiceExecutorBean"; |     public static final String EXECUTOR_BEAN_NAME = "AsyncServiceExecutorBean"; | ||||||
| 
 | 
 | ||||||
|  | @ -30,4 +33,9 @@ public class AsyncServiceSpringConfig { | ||||||
|         return executor; |         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 type is a list of a composite of single types */ | ||||||
|     TABLE(COMPOSITE_LIST), |     TABLE(COMPOSITE_LIST), | ||||||
|     ; | 
 | ||||||
|  |     STATIC_TABLE(COMPOSITE_LIST); | ||||||
| 
 | 
 | ||||||
|     public final AttributeValueType attributeValueType; |     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.ConfigurationValue; | ||||||
| import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation; | import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation; | ||||||
| 
 | 
 | ||||||
|  | /** Adapter interface for SEB Exam Configuration based input fields. */ | ||||||
| public interface InputField { | public interface InputField { | ||||||
| 
 | 
 | ||||||
|     ConfigurationAttribute getAttribute(); |     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) { |         private void applyTableRowValues(final int index) { | ||||||
|             final TableItem item = this.control.getItem(index); |             final TableItem item = this.control.getItem(index); | ||||||
|             final Map<Long, TableValue> rowValues = this.values.get(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.Composite; | ||||||
| import org.eclipse.swt.widgets.Event; | import org.eclipse.swt.widgets.Event; | ||||||
| import org.eclipse.swt.widgets.Label; | import org.eclipse.swt.widgets.Label; | ||||||
|  | import org.eclipse.swt.widgets.Listener; | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| 
 | 
 | ||||||
|  | @ -37,6 +38,8 @@ public class ColorSelection extends Composite implements Selection { | ||||||
|     private final Composite colorField; |     private final Composite colorField; | ||||||
|     private RGB selection; |     private RGB selection; | ||||||
| 
 | 
 | ||||||
|  |     private Listener listener = null; | ||||||
|  | 
 | ||||||
|     ColorSelection(final Composite parent, final WidgetFactory widgetFactory) { |     ColorSelection(final Composite parent, final WidgetFactory widgetFactory) { | ||||||
|         super(parent, SWT.NONE); |         super(parent, SWT.NONE); | ||||||
|         final GridLayout gridLayout = new GridLayout(2, false); |         final GridLayout gridLayout = new GridLayout(2, false); | ||||||
|  | @ -67,6 +70,11 @@ public class ColorSelection extends Composite implements Selection { | ||||||
|         this.addListener(SWT.Resize, this::adaptColumnWidth); |         this.addListener(SWT.Resize, this::adaptColumnWidth); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     @Override | ||||||
|  |     public void setSelectionListener(final Listener listener) { | ||||||
|  |         this.listener = listener; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     @Override |     @Override | ||||||
|     public Type type() { |     public Type type() { | ||||||
|         return Type.COLOR; |         return Type.COLOR; | ||||||
|  | @ -100,6 +108,9 @@ public class ColorSelection extends Composite implements Selection { | ||||||
| 
 | 
 | ||||||
|             this.selection = this.colorDialog.getRGB(); |             this.selection = this.colorDialog.getRGB(); | ||||||
|             applySelection(); |             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.layout.GridLayout; | ||||||
| import org.eclipse.swt.widgets.Composite; | import org.eclipse.swt.widgets.Composite; | ||||||
| import org.eclipse.swt.widgets.Label; | 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.Constants; | ||||||
| import ch.ethz.seb.sebserver.gbl.util.Tuple; | 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> labels = new ArrayList<>(); | ||||||
|     private final List<Label> selected = new ArrayList<>(); |     private final List<Label> selected = new ArrayList<>(); | ||||||
| 
 | 
 | ||||||
|  |     private Listener listener = null; | ||||||
|  | 
 | ||||||
|     MultiSelection(final Composite parent) { |     MultiSelection(final Composite parent) { | ||||||
|         super(parent, SWT.NONE); |         super(parent, SWT.NONE); | ||||||
|         final GridLayout gridLayout = new GridLayout(1, true); |         final GridLayout gridLayout = new GridLayout(1, true); | ||||||
|  | @ -49,6 +52,11 @@ public class MultiSelection extends Composite implements Selection { | ||||||
|         return Type.MULTI; |         return Type.MULTI; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     @Override | ||||||
|  |     public void setSelectionListener(final Listener listener) { | ||||||
|  |         this.listener = listener; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     @Override |     @Override | ||||||
|     public void applyNewMapping(final List<Tuple<String>> mapping) { |     public void applyNewMapping(final List<Tuple<String>> mapping) { | ||||||
|         final String selectionValue = getSelectionValue(); |         final String selectionValue = getSelectionValue(); | ||||||
|  | @ -71,6 +79,9 @@ public class MultiSelection extends Composite implements Selection { | ||||||
|                     l.setData(RWT.CUSTOM_VARIANT, CustomVariant.SELECTED.key); |                     l.setData(RWT.CUSTOM_VARIANT, CustomVariant.SELECTED.key); | ||||||
|                     this.selected.add(l); |                     this.selected.add(l); | ||||||
|                 } |                 } | ||||||
|  |                 if (this.listener != null) { | ||||||
|  |                     this.listener.handleEvent(event); | ||||||
|  |                 } | ||||||
|             }); |             }); | ||||||
|             this.labels.add(label); |             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.Control; | ||||||
| import org.eclipse.swt.widgets.Event; | import org.eclipse.swt.widgets.Event; | ||||||
| import org.eclipse.swt.widgets.Label; | import org.eclipse.swt.widgets.Label; | ||||||
|  | import org.eclipse.swt.widgets.Listener; | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| 
 | 
 | ||||||
|  | @ -51,6 +52,8 @@ public class MultiSelectionCombo extends Composite implements Selection { | ||||||
|     private final GridData comboCell; |     private final GridData comboCell; | ||||||
|     private final GridData actionCell; |     private final GridData actionCell; | ||||||
| 
 | 
 | ||||||
|  |     private Listener listener = null; | ||||||
|  | 
 | ||||||
|     MultiSelectionCombo(final Composite parent, final WidgetFactory widgetFactory) { |     MultiSelectionCombo(final Composite parent, final WidgetFactory widgetFactory) { | ||||||
|         super(parent, SWT.NONE); |         super(parent, SWT.NONE); | ||||||
|         this.widgetFactory = widgetFactory; |         this.widgetFactory = widgetFactory; | ||||||
|  | @ -83,6 +86,11 @@ public class MultiSelectionCombo extends Composite implements Selection { | ||||||
|         return Type.MULTI_COMBO; |         return Type.MULTI_COMBO; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     @Override | ||||||
|  |     public void setSelectionListener(final Listener listener) { | ||||||
|  |         this.listener = listener; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     @Override |     @Override | ||||||
|     public void applyNewMapping(final List<Tuple<String>> mapping) { |     public void applyNewMapping(final List<Tuple<String>> mapping) { | ||||||
|         this.mapping.putAll(mapping.stream() |         this.mapping.putAll(mapping.stream() | ||||||
|  | @ -153,6 +161,9 @@ public class MultiSelectionCombo extends Composite implements Selection { | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         addSelection(findFirst.get().getKey()); |         addSelection(findFirst.get().getKey()); | ||||||
|  |         if (this.listener != null) { | ||||||
|  |             this.listener.handleEvent(event); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private void addSelection(final String itemKey) { |     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.combo.add(value._2, this.combo.getItemCount()); | ||||||
| 
 | 
 | ||||||
|         this.getParent().layout(); |         this.getParent().layout(); | ||||||
|  |         if (this.listener != null) { | ||||||
|  |             this.listener.handleEvent(event); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private void adaptColumnWidth(final Event event) { |     private void adaptColumnWidth(final Event event) { | ||||||
|  |  | ||||||
|  | @ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.gui.widget; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| 
 | 
 | ||||||
| import org.eclipse.swt.widgets.Control; | import org.eclipse.swt.widgets.Control; | ||||||
|  | import org.eclipse.swt.widgets.Listener; | ||||||
| 
 | 
 | ||||||
| import ch.ethz.seb.sebserver.gbl.util.Tuple; | import ch.ethz.seb.sebserver.gbl.util.Tuple; | ||||||
| 
 | 
 | ||||||
|  | @ -35,10 +36,15 @@ public interface Selection { | ||||||
| 
 | 
 | ||||||
|     void setVisible(boolean visible); |     void setVisible(boolean visible); | ||||||
| 
 | 
 | ||||||
|  |     void setSelectionListener(Listener listener); | ||||||
|  | 
 | ||||||
|     default Control adaptToControl() { |     default Control adaptToControl() { | ||||||
|         return (Control) this; |         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.SWT; | ||||||
| import org.eclipse.swt.widgets.Combo; | import org.eclipse.swt.widgets.Combo; | ||||||
| import org.eclipse.swt.widgets.Composite; | import org.eclipse.swt.widgets.Composite; | ||||||
|  | import org.eclipse.swt.widgets.Listener; | ||||||
| 
 | 
 | ||||||
| import ch.ethz.seb.sebserver.gbl.util.Tuple; | import ch.ethz.seb.sebserver.gbl.util.Tuple; | ||||||
| 
 | 
 | ||||||
|  | @ -77,4 +78,9 @@ public class SingleSelection extends Combo implements Selection { | ||||||
|         return Type.SINGLE; |         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; | 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.model.sebconfig.SebClientConfig; | ||||||
| import ch.ethz.seb.sebserver.gbl.util.Result; | import ch.ethz.seb.sebserver.gbl.util.Result; | ||||||
|  | @ -55,6 +55,8 @@ public interface SebClientConfigService { | ||||||
| 
 | 
 | ||||||
|     Result<SebClientConfig> autoCreateSebClientConfigurationForInstitution(Long institutionId); |     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; | package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig; | ||||||
| 
 | 
 | ||||||
|  | import java.io.InputStream; | ||||||
|  | import java.io.OutputStream; | ||||||
| import java.nio.ByteBuffer; | import java.nio.ByteBuffer; | ||||||
| import java.util.Set; | import java.util.Set; | ||||||
| 
 | 
 | ||||||
|  | @ -43,4 +45,14 @@ public interface SebConfigCryptor { | ||||||
|             final ByteBuffer cipher, |             final ByteBuffer cipher, | ||||||
|             final SebConfigEncryptionContext context); |             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; | package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig; | ||||||
| 
 | 
 | ||||||
|  | import java.io.InputStream; | ||||||
|  | import java.io.OutputStream; | ||||||
| import java.nio.ByteBuffer; | import java.nio.ByteBuffer; | ||||||
| import java.security.cert.Certificate; | import java.security.cert.Certificate; | ||||||
| import java.util.function.Function; | import java.util.function.Function; | ||||||
|  | @ -60,16 +62,11 @@ public interface SebConfigEncryptionService { | ||||||
|      *         case */ |      *         case */ | ||||||
|     Result<ByteBuffer> plainText(CharSequence plainTextConfig); |     Result<ByteBuffer> plainText(CharSequence plainTextConfig); | ||||||
| 
 | 
 | ||||||
|     /** Use this to create a password encrypted SEB Configuration file from configuration text |     void streamEncryption( | ||||||
|      * with the given Strategy |             final OutputStream output, | ||||||
|      * |             final InputStream input, | ||||||
|      * @param plainTextConfig plainTextConfig plain text SEB Configuration as CharSequence |             final Strategy strategy, | ||||||
|      * @return Result of password encoded ByteBuffer or a reference to an Exception on error |             final CharSequence password); | ||||||
|      *         case */ |  | ||||||
|     Result<ByteBuffer> encryptWithPassword( |  | ||||||
|             CharSequence plainTextConfig, |  | ||||||
|             Strategy strategy, |  | ||||||
|             CharSequence password); |  | ||||||
| 
 | 
 | ||||||
|     Result<ByteBuffer> encryptWithCertificate( |     Result<ByteBuffer> encryptWithCertificate( | ||||||
|             CharSequence plainTextConfig, |             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; | 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.nio.ByteBuffer; | ||||||
| import java.util.Set; | 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.cryptonode.jncryptor.JNCryptor; | ||||||
|  | import org.slf4j.Logger; | ||||||
|  | import org.slf4j.LoggerFactory; | ||||||
| import org.springframework.context.annotation.Lazy; | import org.springframework.context.annotation.Lazy; | ||||||
|  | import org.springframework.scheduling.annotation.Async; | ||||||
| import org.springframework.stereotype.Component; | 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.profile.WebServiceProfile; | ||||||
| import ch.ethz.seb.sebserver.gbl.util.Result; | import ch.ethz.seb.sebserver.gbl.util.Result; | ||||||
| import ch.ethz.seb.sebserver.gbl.util.Utils; | 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.SebConfigEncryptionContext; | ||||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebConfigEncryptionService.Strategy; | import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebConfigEncryptionService.Strategy; | ||||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebConfigCryptor; |  | ||||||
| 
 | 
 | ||||||
| @Lazy | @Lazy | ||||||
| @Component | @Component | ||||||
| @WebServiceProfile | @WebServiceProfile | ||||||
| public class PasswordEncryptor implements SebConfigCryptor { | public class PasswordEncryptor implements SebConfigCryptor { | ||||||
| 
 | 
 | ||||||
|  |     private static final Logger log = LoggerFactory.getLogger(PasswordEncryptor.class); | ||||||
|  | 
 | ||||||
|     private static final Set<Strategy> STRATEGIES = Utils.immutableSetOf( |     private static final Set<Strategy> STRATEGIES = Utils.immutableSetOf( | ||||||
|             Strategy.PASSWORD_PSWD, |             Strategy.PASSWORD_PSWD, | ||||||
|             Strategy.PASSWORD_PWCC); |             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; | package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl; | ||||||
| 
 | 
 | ||||||
| import java.io.ByteArrayInputStream; | import java.io.ByteArrayInputStream; | ||||||
|  | import java.io.IOException; | ||||||
| import java.io.InputStream; | import java.io.InputStream; | ||||||
|  | import java.io.OutputStream; | ||||||
|  | import java.io.PipedInputStream; | ||||||
|  | import java.io.PipedOutputStream; | ||||||
| import java.nio.ByteBuffer; | import java.nio.ByteBuffer; | ||||||
| import java.util.Collection; | import java.util.Collection; | ||||||
| import java.util.UUID; | import java.util.UUID; | ||||||
| 
 | 
 | ||||||
|  | import org.apache.commons.io.IOUtils; | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| import org.springframework.beans.factory.annotation.Value; | 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.SebClientConfigService; | ||||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebConfigEncryptionService; | 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.SebConfigEncryptionService.Strategy; | ||||||
|  | import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ZipService; | ||||||
| 
 | 
 | ||||||
| @Lazy | @Lazy | ||||||
| @Service | @Service | ||||||
|  | @ -46,6 +52,7 @@ public class SebClientConfigServiceImpl implements SebClientConfigService { | ||||||
|     private final SebClientConfigDAO sebClientConfigDAO; |     private final SebClientConfigDAO sebClientConfigDAO; | ||||||
|     private final ClientCredentialService clientCredentialService; |     private final ClientCredentialService clientCredentialService; | ||||||
|     private final SebConfigEncryptionService sebConfigEncryptionService; |     private final SebConfigEncryptionService sebConfigEncryptionService; | ||||||
|  |     private final ZipService zipService; | ||||||
|     private final String httpScheme; |     private final String httpScheme; | ||||||
|     private final String serverAddress; |     private final String serverAddress; | ||||||
|     private final String serverPort; |     private final String serverPort; | ||||||
|  | @ -56,6 +63,7 @@ public class SebClientConfigServiceImpl implements SebClientConfigService { | ||||||
|             final SebClientConfigDAO sebClientConfigDAO, |             final SebClientConfigDAO sebClientConfigDAO, | ||||||
|             final ClientCredentialService clientCredentialService, |             final ClientCredentialService clientCredentialService, | ||||||
|             final SebConfigEncryptionService sebConfigEncryptionService, |             final SebConfigEncryptionService sebConfigEncryptionService, | ||||||
|  |             final ZipService zipService, | ||||||
|             @Value("${sebserver.webservice.http.scheme}") final String httpScheme, |             @Value("${sebserver.webservice.http.scheme}") final String httpScheme, | ||||||
|             @Value("${server.address}") final String serverAddress, |             @Value("${server.address}") final String serverAddress, | ||||||
|             @Value("${server.port}") final String serverPort, |             @Value("${server.port}") final String serverPort, | ||||||
|  | @ -65,6 +73,7 @@ public class SebClientConfigServiceImpl implements SebClientConfigService { | ||||||
|         this.sebClientConfigDAO = sebClientConfigDAO; |         this.sebClientConfigDAO = sebClientConfigDAO; | ||||||
|         this.clientCredentialService = clientCredentialService; |         this.clientCredentialService = clientCredentialService; | ||||||
|         this.sebConfigEncryptionService = sebConfigEncryptionService; |         this.sebConfigEncryptionService = sebConfigEncryptionService; | ||||||
|  |         this.zipService = zipService; | ||||||
|         this.httpScheme = httpScheme; |         this.httpScheme = httpScheme; | ||||||
|         this.serverAddress = serverAddress; |         this.serverAddress = serverAddress; | ||||||
|         this.serverPort = serverPort; |         this.serverPort = serverPort; | ||||||
|  | @ -97,21 +106,12 @@ public class SebClientConfigServiceImpl implements SebClientConfigService { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     public Result<InputStream> exportSebClientConfiguration(final String modelId) { |     public void exportSebClientConfiguration( | ||||||
|         return this.sebClientConfigDAO.byModelId(modelId) |             final OutputStream output, | ||||||
|                 .flatMap(this::createExport); |             final String modelId) { | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     private final Result<InputStream> createExport(final SebClientConfig config) { |         final SebClientConfig config = this.sebClientConfigDAO | ||||||
|         // TODO implementation of creation of SEB client configuration for specified Institution |                 .byModelId(modelId).getOrThrow(); | ||||||
|         // 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 |  | ||||||
| 
 |  | ||||||
|         return Result.tryCatch(() -> { |  | ||||||
| 
 | 
 | ||||||
|         final String serverURL = UriComponentsBuilder.newInstance() |         final String serverURL = UriComponentsBuilder.newInstance() | ||||||
|                 .scheme(this.httpScheme) |                 .scheme(this.httpScheme) | ||||||
|  | @ -144,30 +144,75 @@ public class SebClientConfigServiceImpl implements SebClientConfigService { | ||||||
|                 this.sebClientAPIEndpoint + API.EXAM_API_PING_ENDPOINT, |                 this.sebClientAPIEndpoint + API.EXAM_API_PING_ENDPOINT, | ||||||
|                 this.sebClientAPIEndpoint + API.EXAM_API_EVENT_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) { |             if (encryptionPassword != null) { | ||||||
| 
 |                 passwordEncryption(output, encryptionPassword, pIn); | ||||||
|                 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)); |  | ||||||
|             } else { |             } else { | ||||||
|  |                 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"); |         log.debug("Serve plain text seb configuration with specified header"); | ||||||
| 
 | 
 | ||||||
|         final ByteBuffer encryptedConfig = this.sebConfigEncryptionService.plainText(plainTextConfig) |         final ByteBuffer encryptedConfig = this.sebConfigEncryptionService.plainText(plainTextConfig) | ||||||
|                 .getOrThrow(); |                 .getOrThrow(); | ||||||
| 
 | 
 | ||||||
|                 return new ByteArrayInputStream(Utils.toByteArray(encryptedConfig)); |         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; | 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.nio.ByteBuffer; | ||||||
| import java.security.cert.Certificate; | import java.security.cert.Certificate; | ||||||
| import java.util.Arrays; | import java.util.Arrays; | ||||||
|  | @ -17,6 +22,7 @@ import java.util.function.Function; | ||||||
| import java.util.function.Supplier; | import java.util.function.Supplier; | ||||||
| import java.util.stream.Collectors; | import java.util.stream.Collectors; | ||||||
| 
 | 
 | ||||||
|  | import org.apache.commons.io.IOUtils; | ||||||
| import org.apache.commons.lang3.tuple.ImmutablePair; | import org.apache.commons.lang3.tuple.ImmutablePair; | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
|  | @ -66,20 +72,49 @@ public final class SebConfigEncryptionServiceImpl implements SebConfigEncryption | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     public Result<ByteBuffer> encryptWithPassword( |     public void streamEncryption( | ||||||
|             final CharSequence plainTextConfig, |             final OutputStream output, | ||||||
|  |             final InputStream input, | ||||||
|             final Strategy strategy, |             final Strategy strategy, | ||||||
|             final CharSequence password) { |             final CharSequence password) { | ||||||
| 
 | 
 | ||||||
|  |         PipedOutputStream pout = null; | ||||||
|  |         PipedInputStream pin = null; | ||||||
|  |         try { | ||||||
|  |             pout = new PipedOutputStream(); | ||||||
|  |             pin = new PipedInputStream(pout); | ||||||
|  | 
 | ||||||
|             if (log.isDebugEnabled()) { |             if (log.isDebugEnabled()) { | ||||||
|                 log.debug("Password encryption with strategy: {}", strategy); |                 log.debug("Password encryption with strategy: {}", strategy); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|         return getEncryptor(strategy) |             pout.write(strategy.header); | ||||||
|                 .flatMap(encryptor -> encryptor.encrypt( |             getEncryptor(strategy) | ||||||
|                         plainTextConfig, |                     .getOrThrow() | ||||||
|                         EncryptionContext.contextOf(strategy, password))) |                     .encrypt(pout, | ||||||
|                 .map(bb -> addHeader(bb, strategy)); |                             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 |     @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; | 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.DateTime; | ||||||
| import org.joda.time.DateTimeZone; | import org.joda.time.DateTimeZone; | ||||||
| import org.mybatis.dynamic.sql.SqlTable; | import org.mybatis.dynamic.sql.SqlTable; | ||||||
|  | import org.slf4j.Logger; | ||||||
|  | import org.slf4j.LoggerFactory; | ||||||
| import org.springframework.http.HttpStatus; | import org.springframework.http.HttpStatus; | ||||||
| import org.springframework.http.MediaType; | import org.springframework.http.MediaType; | ||||||
|  | import org.springframework.http.ResponseEntity; | ||||||
|  | import org.springframework.scheduling.annotation.EnableAsync; | ||||||
| import org.springframework.validation.FieldError; | import org.springframework.validation.FieldError; | ||||||
| import org.springframework.web.bind.annotation.PathVariable; | import org.springframework.web.bind.annotation.PathVariable; | ||||||
| import org.springframework.web.bind.annotation.RequestMapping; | import org.springframework.web.bind.annotation.RequestMapping; | ||||||
| import org.springframework.web.bind.annotation.RequestMethod; | import org.springframework.web.bind.annotation.RequestMethod; | ||||||
| import org.springframework.web.bind.annotation.RestController; | 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.Constants; | ||||||
| import ch.ethz.seb.sebserver.gbl.api.API; | import ch.ethz.seb.sebserver.gbl.api.API; | ||||||
|  | @ -45,9 +45,12 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe | ||||||
| 
 | 
 | ||||||
| @WebServiceProfile | @WebServiceProfile | ||||||
| @RestController | @RestController | ||||||
|  | @EnableAsync | ||||||
| @RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + API.SEB_CLIENT_CONFIG_ENDPOINT) | @RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + API.SEB_CLIENT_CONFIG_ENDPOINT) | ||||||
| public class SebClientConfigController extends ActivatableEntityController<SebClientConfig, SebClientConfig> { | public class SebClientConfigController extends ActivatableEntityController<SebClientConfig, SebClientConfig> { | ||||||
| 
 | 
 | ||||||
|  |     private static final Logger log = LoggerFactory.getLogger(SebClientConfigController.class); | ||||||
|  | 
 | ||||||
|     private final SebClientConfigService sebClientConfigService; |     private final SebClientConfigService sebClientConfigService; | ||||||
| 
 | 
 | ||||||
|     public SebClientConfigController( |     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, |             path = API.SEB_CLIENT_CONFIG_DOWNLOAD_PATH_SEGMENT + API.MODEL_ID_VAR_PATH_SEGMENT, | ||||||
|             method = RequestMethod.GET, |             method = RequestMethod.GET, | ||||||
|             produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) |             produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) | ||||||
|     public void downloadSEBConfig( |     public ResponseEntity<StreamingResponseBody> downloadSEBConfig( | ||||||
|             @PathVariable final String modelId, |             @PathVariable final String modelId) { | ||||||
|             final HttpServletResponse response) throws Exception { |  | ||||||
| 
 | 
 | ||||||
|         this.entityDAO.byModelId(modelId) |         this.entityDAO.byModelId(modelId) | ||||||
|                 .map(this.authorization::checkWrite); |                 .map(this.authorization::checkWrite); | ||||||
| 
 | 
 | ||||||
|         response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); |         final StreamingResponseBody stream = out -> this.sebClientConfigService | ||||||
|         response.setStatus(HttpStatus.OK.value()); |                 .exportSebClientConfiguration(out, modelId); | ||||||
| 
 | 
 | ||||||
|         final InputStream sebConfigFileIn = this.sebClientConfigService |         return new ResponseEntity<>(stream, HttpStatus.OK); | ||||||
|                 .exportSebClientConfiguration(modelId) |  | ||||||
|                 .getOrThrow(); |  | ||||||
| 
 |  | ||||||
|         IOUtils.copyLarge(sebConfigFileIn, response.getOutputStream()); |  | ||||||
|         response.flushBuffer(); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Override |     @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 static org.junit.Assert.*; | ||||||
| 
 | 
 | ||||||
|  | import java.io.ByteArrayOutputStream; | ||||||
|  | import java.io.IOException; | ||||||
| import java.nio.ByteBuffer; | import java.nio.ByteBuffer; | ||||||
| import java.util.Arrays; | import java.util.Arrays; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| 
 | 
 | ||||||
|  | import org.apache.commons.io.IOUtils; | ||||||
| import org.cryptonode.jncryptor.AES256JNCryptor; | import org.cryptonode.jncryptor.AES256JNCryptor; | ||||||
| import org.cryptonode.jncryptor.JNCryptor; | import org.cryptonode.jncryptor.JNCryptor; | ||||||
| import org.junit.Test; | import org.junit.Test; | ||||||
| 
 | 
 | ||||||
| import ch.ethz.seb.sebserver.gbl.util.Result; | import ch.ethz.seb.sebserver.gbl.util.Result; | ||||||
| import ch.ethz.seb.sebserver.gbl.util.Utils; | 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.SebConfigCryptor; | ||||||
|  | import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebConfigEncryptionService.Strategy; | ||||||
| 
 | 
 | ||||||
| public class SebConfigEncryptionServiceImplTest { | public class SebConfigEncryptionServiceImplTest { | ||||||
| 
 | 
 | ||||||
|  | @ -42,19 +45,24 @@ public class SebConfigEncryptionServiceImplTest { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Test |     @Test | ||||||
|     public void testPasswordEncryption() { |     public void testPasswordEncryption() throws IOException { | ||||||
|         final SebConfigEncryptionServiceImpl sebConfigEncryptionServiceImpl = sebConfigEncryptionServiceImpl(); |         final SebConfigEncryptionServiceImpl sebConfigEncryptionServiceImpl = sebConfigEncryptionServiceImpl(); | ||||||
| 
 | 
 | ||||||
|         final String config = "<TestConfig></TestConfig>"; |         final String config = "<TestConfig></TestConfig>"; | ||||||
|         final String pwd = "password"; |         final String pwd = "password"; | ||||||
| 
 | 
 | ||||||
|         final Result<ByteBuffer> plainText = sebConfigEncryptionServiceImpl.encryptWithPassword( |         final ByteArrayOutputStream out = new ByteArrayOutputStream(1024); | ||||||
|                 config, | 
 | ||||||
|  |         sebConfigEncryptionServiceImpl.streamEncryption( | ||||||
|  |                 out, | ||||||
|  |                 IOUtils.toInputStream(config, "UTF-8"), | ||||||
|                 Strategy.PASSWORD_PWCC, |                 Strategy.PASSWORD_PWCC, | ||||||
|                 pwd); |                 pwd); | ||||||
| 
 | 
 | ||||||
|         assertFalse(plainText.hasError()); |         final byte[] byteArray = out.toByteArray(); | ||||||
|         final ByteBuffer cipher = plainText.get(); | 
 | ||||||
|  |         //assertFalse(plainText.hasError()); | ||||||
|  |         final ByteBuffer cipher = ByteBuffer.wrap(byteArray); | ||||||
|         assertTrue(Utils.toString(cipher).startsWith(Utils.toString(Strategy.PASSWORD_PWCC.header))); |         assertTrue(Utils.toString(cipher).startsWith(Utils.toString(Strategy.PASSWORD_PWCC.header))); | ||||||
| 
 | 
 | ||||||
|         final Result<ByteBuffer> decrypt = sebConfigEncryptionServiceImpl.decrypt(cipher, () -> pwd, null); |         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.api.exam.refreshTokenValiditySeconds=-1 | ||||||
| sebserver.webservice.internalSecret=TO_SET | sebserver.webservice.internalSecret=TO_SET | ||||||
| sebserver.webservice.api.redirect.unauthorized=none | sebserver.webservice.api.redirect.unauthorized=none | ||||||
|  | 
 | ||||||
|  | management.endpoints.web.base-path=/actuator | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 anhefti
						anhefti