added encryption for SEB Exam config download
This commit is contained in:
parent
79725f9924
commit
a7097d8b81
23 changed files with 467 additions and 83 deletions
|
@ -6,7 +6,7 @@ ARG SEBSERVER_VERSION
|
||||||
WORKDIR /demo
|
WORKDIR /demo
|
||||||
RUN if [ "x${GIT_TAG}" = "x" ] ; \
|
RUN if [ "x${GIT_TAG}" = "x" ] ; \
|
||||||
then git clone --depth 1 https://github.com/SafeExamBrowser/seb-server.git ; \
|
then git clone --depth 1 https://github.com/SafeExamBrowser/seb-server.git ; \
|
||||||
else git clone -b "v$GIT_TAG" --depth 1 https://github.com/SafeExamBrowser/seb-server.git ; fi
|
else git clone -b "$GIT_TAG" --depth 1 https://github.com/SafeExamBrowser/seb-server.git ; fi
|
||||||
|
|
||||||
FROM maven:3.5-jdk-8-alpine
|
FROM maven:3.5-jdk-8-alpine
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,8 @@ services:
|
||||||
container_name: seb-server-mariadb
|
container_name: seb-server-mariadb
|
||||||
environment:
|
environment:
|
||||||
MYSQL_ROOT_PASSWORD: somePW
|
MYSQL_ROOT_PASSWORD: somePW
|
||||||
|
volumes:
|
||||||
|
- seb-server-mariadb-data:/var/lib/mysql
|
||||||
ports:
|
ports:
|
||||||
- 3306:3306
|
- 3306:3306
|
||||||
networks:
|
networks:
|
||||||
|
@ -33,4 +35,7 @@ services:
|
||||||
- "mariadb"
|
- "mariadb"
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
ralph:
|
ralph:
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
seb-server-mariadb-data:
|
|
@ -27,6 +27,7 @@ public final class API {
|
||||||
|
|
||||||
public static final String INSTITUTION_VAR_PATH_SEGMENT = "/{" + PARAM_INSTITUTION_ID + "}";
|
public static final String INSTITUTION_VAR_PATH_SEGMENT = "/{" + PARAM_INSTITUTION_ID + "}";
|
||||||
public static final String MODEL_ID_VAR_PATH_SEGMENT = "/{" + PARAM_MODEL_ID + "}";
|
public static final String MODEL_ID_VAR_PATH_SEGMENT = "/{" + PARAM_MODEL_ID + "}";
|
||||||
|
public static final String PARENT_MODEL_ID_VAR_PATH_SEGMENT = "/{" + PARAM_PARENT_MODEL_ID + "}";
|
||||||
|
|
||||||
public static final String OAUTH_ENDPOINT = "/oauth";
|
public static final String OAUTH_ENDPOINT = "/oauth";
|
||||||
public static final String OAUTH_TOKEN_ENDPOINT = OAUTH_ENDPOINT + "/token"; // TODO to config properties?
|
public static final String OAUTH_TOKEN_ENDPOINT = OAUTH_ENDPOINT + "/token"; // TODO to config properties?
|
||||||
|
@ -100,6 +101,7 @@ public final class API {
|
||||||
public static final String QUIZ_DISCOVERY_ENDPOINT = "/quiz";
|
public static final String QUIZ_DISCOVERY_ENDPOINT = "/quiz";
|
||||||
|
|
||||||
public static final String EXAM_ADMINISTRATION_ENDPOINT = "/exam";
|
public static final String EXAM_ADMINISTRATION_ENDPOINT = "/exam";
|
||||||
|
public static final String EXAM_ADMINISTRATION_DOWNLOAD_CONFIG_PATH_SEGMENT = "/downloadConfig";
|
||||||
|
|
||||||
public static final String EXAM_INDICATOR_ENDPOINT = "/indicator";
|
public static final String EXAM_INDICATOR_ENDPOINT = "/indicator";
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.web.context.WebApplicationContext;
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
import org.springframework.web.context.support.WebApplicationContextUtils;
|
import org.springframework.web.context.support.WebApplicationContextUtils;
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.DownloadService;
|
import ch.ethz.seb.sebserver.gui.service.remote.download.DownloadService;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.SEBServerAuthorizationContext;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.SEBServerAuthorizationContext;
|
||||||
|
|
||||||
|
|
|
@ -17,9 +17,12 @@ import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.apache.commons.lang3.BooleanUtils;
|
import org.apache.commons.lang3.BooleanUtils;
|
||||||
import org.apache.tomcat.util.buf.StringUtils;
|
import org.apache.tomcat.util.buf.StringUtils;
|
||||||
|
import org.eclipse.rap.rwt.RWT;
|
||||||
|
import org.eclipse.rap.rwt.client.service.UrlLauncher;
|
||||||
import org.eclipse.swt.widgets.Composite;
|
import org.eclipse.swt.widgets.Composite;
|
||||||
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.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@ -50,6 +53,8 @@ import ch.ethz.seb.sebserver.gui.service.page.PageService.PageActionBuilder;
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
|
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent;
|
import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent;
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction;
|
import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.remote.download.DownloadService;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.remote.download.SebExamConfigDownload;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.DeleteExamConfigMapping;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.DeleteExamConfigMapping;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.DeleteIndicator;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.DeleteIndicator;
|
||||||
|
@ -73,9 +78,6 @@ public class ExamForm implements TemplateComposer {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(ExamForm.class);
|
private static final Logger log = LoggerFactory.getLogger(ExamForm.class);
|
||||||
|
|
||||||
private final PageService pageService;
|
|
||||||
private final ResourceService resourceService;
|
|
||||||
|
|
||||||
private static final LocTextKey CONFIG_EMPTY_LIST_MESSAGE =
|
private static final LocTextKey CONFIG_EMPTY_LIST_MESSAGE =
|
||||||
new LocTextKey("sebserver.exam.configuration.list.empty");
|
new LocTextKey("sebserver.exam.configuration.list.empty");
|
||||||
private static final LocTextKey INDICATOR_EMPTY_LIST_MESSAGE =
|
private static final LocTextKey INDICATOR_EMPTY_LIST_MESSAGE =
|
||||||
|
@ -123,12 +125,21 @@ public class ExamForm implements TemplateComposer {
|
||||||
private final static LocTextKey INDICATOR_EMPTY_SELECTION_TEXT_KEY =
|
private final static LocTextKey INDICATOR_EMPTY_SELECTION_TEXT_KEY =
|
||||||
new LocTextKey("sebserver.exam.indicator.list.pleaseSelect");
|
new LocTextKey("sebserver.exam.indicator.list.pleaseSelect");
|
||||||
|
|
||||||
|
private final PageService pageService;
|
||||||
|
private final ResourceService resourceService;
|
||||||
|
private final DownloadService downloadService;
|
||||||
|
private final String downloadFileName;
|
||||||
|
|
||||||
protected ExamForm(
|
protected ExamForm(
|
||||||
final PageService pageService,
|
final PageService pageService,
|
||||||
final ResourceService resourceService) {
|
final ResourceService resourceService,
|
||||||
|
final DownloadService downloadService,
|
||||||
|
@Value("${sebserver.gui.seb.exam.config.download.filename}") final String downloadFileName) {
|
||||||
|
|
||||||
this.pageService = pageService;
|
this.pageService = pageService;
|
||||||
this.resourceService = resourceService;
|
this.resourceService = resourceService;
|
||||||
|
this.downloadService = downloadService;
|
||||||
|
this.downloadFileName = downloadFileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -305,19 +316,7 @@ public class ExamForm implements TemplateComposer {
|
||||||
this.resourceService::localizedExamConfigStatusName))
|
this.resourceService::localizedExamConfigStatusName))
|
||||||
.withDefaultActionIf(
|
.withDefaultActionIf(
|
||||||
() -> editable,
|
() -> editable,
|
||||||
t -> actionBuilder
|
this::viewExamConfigPageAction)
|
||||||
.newAction(ActionDefinition.EXAM_CONFIGURATION_EXAM_CONFIG_VIEW_PROP)
|
|
||||||
.withSelectionSupplier(() -> {
|
|
||||||
final ExamConfigurationMap selectedROWData = t.getSelectedROWData();
|
|
||||||
final HashSet<EntityKey> result = new HashSet<>();
|
|
||||||
if (selectedROWData != null) {
|
|
||||||
result.add(new EntityKey(
|
|
||||||
selectedROWData.configurationNodeId,
|
|
||||||
EntityType.CONFIGURATION_NODE));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
})
|
|
||||||
.create())
|
|
||||||
|
|
||||||
.compose(pageContext.copyOf(content));
|
.compose(pageContext.copyOf(content));
|
||||||
|
|
||||||
|
@ -356,6 +355,15 @@ public class ExamForm implements TemplateComposer {
|
||||||
CONFIG_EMPTY_SELECTION_TEXT_KEY)
|
CONFIG_EMPTY_SELECTION_TEXT_KEY)
|
||||||
.publishIf(() -> modifyGrant && configurationTable.hasAnyContent() && editable)
|
.publishIf(() -> modifyGrant && configurationTable.hasAnyContent() && editable)
|
||||||
|
|
||||||
|
.newAction(ActionDefinition.EXAM_CONFIGURATION_EXPORT)
|
||||||
|
.withParentEntityKey(entityKey)
|
||||||
|
.withSelect(
|
||||||
|
getConfigSelection(configurationTable),
|
||||||
|
this::downloadExamConfigAction,
|
||||||
|
CONFIG_EMPTY_SELECTION_TEXT_KEY)
|
||||||
|
.noEventPropagation()
|
||||||
|
.publishIf(() -> userGrantCheck.r() && configurationTable.hasAnyContent())
|
||||||
|
|
||||||
.newAction(ActionDefinition.EXAM_CONFIGURATION_GET_CONFIG_KEY)
|
.newAction(ActionDefinition.EXAM_CONFIGURATION_GET_CONFIG_KEY)
|
||||||
.withSelect(
|
.withSelect(
|
||||||
getConfigSelection(configurationTable),
|
getConfigSelection(configurationTable),
|
||||||
|
@ -423,6 +431,41 @@ public class ExamForm implements TemplateComposer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private PageAction viewExamConfigPageAction(final EntityTable<ExamConfigurationMap> table) {
|
||||||
|
|
||||||
|
final PageActionBuilder actionBuilder = this.pageService.pageActionBuilder(table.getPageContext()
|
||||||
|
.clearEntityKeys()
|
||||||
|
.removeAttribute(AttributeKeys.IMPORT_FROM_QUIZ_DATA));
|
||||||
|
|
||||||
|
return actionBuilder
|
||||||
|
.newAction(ActionDefinition.EXAM_CONFIGURATION_EXAM_CONFIG_VIEW_PROP)
|
||||||
|
.withSelectionSupplier(() -> {
|
||||||
|
final ExamConfigurationMap selectedROWData = table.getSelectedROWData();
|
||||||
|
final HashSet<EntityKey> result = new HashSet<>();
|
||||||
|
if (selectedROWData != null) {
|
||||||
|
result.add(new EntityKey(
|
||||||
|
selectedROWData.configurationNodeId,
|
||||||
|
EntityType.CONFIGURATION_NODE));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
})
|
||||||
|
.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
private PageAction downloadExamConfigAction(final PageAction action) {
|
||||||
|
final UrlLauncher urlLauncher = RWT.getClient().getService(UrlLauncher.class);
|
||||||
|
final EntityKey selection = action.getSingleSelection();
|
||||||
|
if (selection != null) {
|
||||||
|
final String downloadURL = this.downloadService.createDownloadURL(
|
||||||
|
selection.modelId,
|
||||||
|
action.pageContext().getParentEntityKey().modelId,
|
||||||
|
SebExamConfigDownload.class,
|
||||||
|
this.downloadFileName);
|
||||||
|
urlLauncher.openURL(downloadURL);
|
||||||
|
}
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
|
||||||
private Supplier<Set<EntityKey>> getConfigMappingSelection(
|
private Supplier<Set<EntityKey>> getConfigMappingSelection(
|
||||||
final EntityTable<ExamConfigurationMap> configurationTable) {
|
final EntityTable<ExamConfigurationMap> configurationTable) {
|
||||||
return () -> {
|
return () -> {
|
||||||
|
|
|
@ -32,8 +32,8 @@ import ch.ethz.seb.sebserver.gui.service.page.PageContext;
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.PageService;
|
import ch.ethz.seb.sebserver.gui.service.page.PageService;
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
|
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.impl.PageUtils;
|
import ch.ethz.seb.sebserver.gui.service.page.impl.PageUtils;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.DownloadService;
|
import ch.ethz.seb.sebserver.gui.service.remote.download.DownloadService;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.SebClientConfigDownload;
|
import ch.ethz.seb.sebserver.gui.service.remote.download.SebClientConfigDownload;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.ActivateClientConfig;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.ActivateClientConfig;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.DeactivateClientConfig;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.DeactivateClientConfig;
|
||||||
|
|
|
@ -39,8 +39,8 @@ import ch.ethz.seb.sebserver.gui.service.page.PageService;
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
|
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.impl.ModalInputDialog;
|
import ch.ethz.seb.sebserver.gui.service.page.impl.ModalInputDialog;
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction;
|
import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.DownloadService;
|
import ch.ethz.seb.sebserver.gui.service.remote.download.DownloadService;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.SebExamConfigDownload;
|
import ch.ethz.seb.sebserver.gui.service.remote.download.SebExamConfigPlaintextDownload;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.ExportConfigKey;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.ExportConfigKey;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetExamConfigNode;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetExamConfigNode;
|
||||||
|
@ -188,7 +188,7 @@ public class SebExamConfigPropForm implements TemplateComposer {
|
||||||
.withExec(action -> {
|
.withExec(action -> {
|
||||||
final String downloadURL = this.downloadService.createDownloadURL(
|
final String downloadURL = this.downloadService.createDownloadURL(
|
||||||
entityKey.modelId,
|
entityKey.modelId,
|
||||||
SebExamConfigDownload.class,
|
SebExamConfigPlaintextDownload.class,
|
||||||
this.downloadFileName);
|
this.downloadFileName);
|
||||||
urlLauncher.openURL(downloadURL);
|
urlLauncher.openURL(downloadURL);
|
||||||
return action;
|
return action;
|
||||||
|
|
|
@ -247,8 +247,13 @@ public enum ActionDefinition {
|
||||||
ImageIcon.DELETE,
|
ImageIcon.DELETE,
|
||||||
PageStateDefinition.EXAM_VIEW,
|
PageStateDefinition.EXAM_VIEW,
|
||||||
ActionCategory.EXAM_CONFIG_MAPPING_LIST),
|
ActionCategory.EXAM_CONFIG_MAPPING_LIST),
|
||||||
|
EXAM_CONFIGURATION_EXPORT(
|
||||||
|
new LocTextKey("sebserver.exam.configuration.action.export-config"),
|
||||||
|
ImageIcon.EXPORT,
|
||||||
|
PageStateDefinition.EXAM_VIEW,
|
||||||
|
ActionCategory.EXAM_CONFIG_MAPPING_LIST),
|
||||||
EXAM_CONFIGURATION_GET_CONFIG_KEY(
|
EXAM_CONFIGURATION_GET_CONFIG_KEY(
|
||||||
new LocTextKey("sebserver.examconfig.action.get-config-key"),
|
new LocTextKey("sebserver.exam.configuration.action.get-config-key"),
|
||||||
ImageIcon.SECURE,
|
ImageIcon.SECURE,
|
||||||
ActionCategory.EXAM_CONFIG_MAPPING_LIST),
|
ActionCategory.EXAM_CONFIG_MAPPING_LIST),
|
||||||
EXAM_CONFIGURATION_SAVE(
|
EXAM_CONFIGURATION_SAVE(
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package ch.ethz.seb.sebserver.gui.service.remote;
|
package ch.ethz.seb.sebserver.gui.service.remote.download;
|
||||||
|
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
@ -39,44 +39,34 @@ public abstract class AbstractDownloadServiceHandler implements DownloadServiceH
|
||||||
|
|
||||||
log.debug("download requested... trying to get needed parameter from request");
|
log.debug("download requested... trying to get needed parameter from request");
|
||||||
|
|
||||||
final String configId = request.getParameter(API.PARAM_MODEL_ID);
|
final String modelId = request.getParameter(API.PARAM_MODEL_ID);
|
||||||
if (StringUtils.isBlank(configId)) {
|
if (StringUtils.isBlank(modelId)) {
|
||||||
log.error(
|
log.error(
|
||||||
"Mandatory modelId parameter not found within HttpServletRequest. Download request is ignored");
|
"Mandatory modelId parameter not found within HttpServletRequest. Download request is ignored");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug(
|
if (log.isDebugEnabled()) {
|
||||||
"Found modelId: {} for {} download. Trying to request webservice...",
|
log.debug(
|
||||||
configId,
|
"Found modelId: {} for {} download. Trying to request webservice...",
|
||||||
downloadFileName);
|
modelId,
|
||||||
|
downloadFileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
final String parentModelId = request.getParameter(API.PARAM_PARENT_MODEL_ID);
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
log.debug(
|
||||||
|
"Found parentModelId: {} for {} download. Trying to request webservice...",
|
||||||
|
modelId,
|
||||||
|
downloadFileName);
|
||||||
|
}
|
||||||
|
|
||||||
final String header =
|
final String header =
|
||||||
"attachment; filename=\"" + Utils.preventResponseSplittingAttack(downloadFileName) + "\"";
|
"attachment; filename=\"" + Utils.preventResponseSplittingAttack(downloadFileName) + "\"";
|
||||||
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, header);
|
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, header);
|
||||||
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
|
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
|
||||||
|
|
||||||
webserviceCall(configId, response.getOutputStream());
|
webserviceCall(modelId, parentModelId, response.getOutputStream());
|
||||||
|
|
||||||
// final byte[] configFile = webserviceCall(configId);
|
|
||||||
//
|
|
||||||
// if (configFile == null) {
|
|
||||||
// log.error("No or empty download received from webservice. Download request is ignored");
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// log.debug("Sucessfully downloaded from webservice. File size: {}", configFile.length);
|
|
||||||
//
|
|
||||||
// response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
|
|
||||||
// response.setContentLength(configFile.length);
|
|
||||||
//
|
|
||||||
// final String header =
|
|
||||||
// "attachment; filename=\"" + Utils.preventResponseSplittingAttack(downloadFileName) + "\"";
|
|
||||||
// response.setHeader(HttpHeaders.CONTENT_DISPOSITION, header);
|
|
||||||
//
|
|
||||||
// log.debug("Write the download data to response output");
|
|
||||||
//
|
|
||||||
// response.getOutputStream().write(configFile);
|
|
||||||
|
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
log.error(
|
log.error(
|
||||||
|
@ -85,6 +75,6 @@ public abstract class AbstractDownloadServiceHandler implements DownloadServiceH
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void webserviceCall(String configId, OutputStream downloadOut);
|
protected abstract void webserviceCall(String modelId, String parentModelId, OutputStream downloadOut);
|
||||||
|
|
||||||
}
|
}
|
|
@ -6,7 +6,7 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package ch.ethz.seb.sebserver.gui.service.remote;
|
package ch.ethz.seb.sebserver.gui.service.remote.download;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -81,6 +81,15 @@ public class DownloadService implements ServiceHandler {
|
||||||
final Class<? extends DownloadServiceHandler> handlerClass,
|
final Class<? extends DownloadServiceHandler> handlerClass,
|
||||||
final String downloadFileName) {
|
final String downloadFileName) {
|
||||||
|
|
||||||
|
return createDownloadURL(modelId, null, handlerClass, downloadFileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String createDownloadURL(
|
||||||
|
final String modelId,
|
||||||
|
final String parentModelId,
|
||||||
|
final Class<? extends DownloadServiceHandler> handlerClass,
|
||||||
|
final String downloadFileName) {
|
||||||
|
|
||||||
final StringBuilder url = new StringBuilder()
|
final StringBuilder url = new StringBuilder()
|
||||||
.append(RWT.getServiceManager()
|
.append(RWT.getServiceManager()
|
||||||
.getServiceHandlerUrl(DownloadService.DOWNLOAD_SERVICE_NAME))
|
.getServiceHandlerUrl(DownloadService.DOWNLOAD_SERVICE_NAME))
|
||||||
|
@ -96,6 +105,14 @@ public class DownloadService implements ServiceHandler {
|
||||||
.append(DownloadService.DOWNLOAD_FILE_NAME)
|
.append(DownloadService.DOWNLOAD_FILE_NAME)
|
||||||
.append(Constants.FORM_URL_ENCODED_NAME_VALUE_SEPARATOR)
|
.append(Constants.FORM_URL_ENCODED_NAME_VALUE_SEPARATOR)
|
||||||
.append(downloadFileName);
|
.append(downloadFileName);
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(parentModelId)) {
|
||||||
|
url.append(Constants.FORM_URL_ENCODED_SEPARATOR)
|
||||||
|
.append(API.PARAM_PARENT_MODEL_ID)
|
||||||
|
.append(Constants.FORM_URL_ENCODED_NAME_VALUE_SEPARATOR)
|
||||||
|
.append(parentModelId);
|
||||||
|
}
|
||||||
|
|
||||||
return url.toString();
|
return url.toString();
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package ch.ethz.seb.sebserver.gui.service.remote;
|
package ch.ethz.seb.sebserver.gui.service.remote.download;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
|
@ -6,7 +6,7 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package ch.ethz.seb.sebserver.gui.service.remote;
|
package ch.ethz.seb.sebserver.gui.service.remote.download;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
@ -43,7 +43,7 @@ public class SebClientConfigDownload extends AbstractDownloadServiceHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void webserviceCall(final String modelId, final OutputStream downloadOut) {
|
protected void webserviceCall(final String modelId, final String parentModelId, final OutputStream downloadOut) {
|
||||||
|
|
||||||
final InputStream input = this.restService.getBuilder(ExportClientConfig.class)
|
final InputStream input = this.restService.getBuilder(ExportClientConfig.class)
|
||||||
.withURIVariable(API.PARAM_MODEL_ID, modelId)
|
.withURIVariable(API.PARAM_MODEL_ID, modelId)
|
|
@ -6,7 +6,7 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package ch.ethz.seb.sebserver.gui.service.remote;
|
package ch.ethz.seb.sebserver.gui.service.remote.download;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
@ -21,7 +21,7 @@ import org.springframework.stereotype.Component;
|
||||||
import ch.ethz.seb.sebserver.gbl.api.API;
|
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.ExportPlainXML;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.ExportExamConfig;
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
@Component
|
@Component
|
||||||
|
@ -37,10 +37,11 @@ public class SebExamConfigDownload extends AbstractDownloadServiceHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void webserviceCall(final String modelId, final OutputStream downloadOut) {
|
protected void webserviceCall(final String modelId, final String parentModelId, final OutputStream downloadOut) {
|
||||||
|
|
||||||
final InputStream input = this.restService.getBuilder(ExportPlainXML.class)
|
final InputStream input = this.restService.getBuilder(ExportExamConfig.class)
|
||||||
.withURIVariable(API.PARAM_MODEL_ID, modelId)
|
.withURIVariable(API.PARAM_MODEL_ID, modelId)
|
||||||
|
.withURIVariable(API.PARAM_PARENT_MODEL_ID, parentModelId)
|
||||||
.call()
|
.call()
|
||||||
.getOrThrow();
|
.getOrThrow();
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ch.ethz.seb.sebserver.gui.service.remote.download;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
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.stereotype.Component;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.ExportPlainXML;
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Component
|
||||||
|
@GuiProfile
|
||||||
|
public class SebExamConfigPlaintextDownload extends AbstractDownloadServiceHandler {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(SebExamConfigPlaintextDownload.class);
|
||||||
|
|
||||||
|
private final RestService restService;
|
||||||
|
|
||||||
|
protected SebExamConfigPlaintextDownload(final RestService restService) {
|
||||||
|
this.restService = restService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void webserviceCall(final String modelId, final String parentModelId, final OutputStream downloadOut) {
|
||||||
|
|
||||||
|
final InputStream input = this.restService.getBuilder(ExportPlainXML.class)
|
||||||
|
.withURIVariable(API.PARAM_MODEL_ID, modelId)
|
||||||
|
.call()
|
||||||
|
.getOrThrow();
|
||||||
|
|
||||||
|
try {
|
||||||
|
IOUtils.copyLarge(input, downloadOut);
|
||||||
|
} catch (final IOException e) {
|
||||||
|
log.error(
|
||||||
|
"Unexpected error while streaming incomming config data from web-service to output-stream of download response: ",
|
||||||
|
e);
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
downloadOut.flush();
|
||||||
|
downloadOut.close();
|
||||||
|
} catch (final IOException e) {
|
||||||
|
log.error("Unexpected error while trying to close download output-stream");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.AbstractExportCall;
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Component
|
||||||
|
@GuiProfile
|
||||||
|
public class ExportExamConfig extends AbstractExportCall {
|
||||||
|
|
||||||
|
public ExportExamConfig() {
|
||||||
|
super(new TypeKey<>(
|
||||||
|
CallType.UNDEFINED,
|
||||||
|
EntityType.EXAM,
|
||||||
|
new TypeReference<InputStream>() {
|
||||||
|
}),
|
||||||
|
HttpMethod.GET,
|
||||||
|
MediaType.APPLICATION_FORM_URLENCODED,
|
||||||
|
API.EXAM_ADMINISTRATION_ENDPOINT
|
||||||
|
+ API.MODEL_ID_VAR_PATH_SEGMENT
|
||||||
|
+ API.EXAM_ADMINISTRATION_DOWNLOAD_CONFIG_PATH_SEGMENT
|
||||||
|
+ API.PARENT_MODEL_ID_VAR_PATH_SEGMENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -189,6 +189,14 @@ public class EntityTable<ROW extends Entity> {
|
||||||
this.sortOrder);
|
this.sortOrder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PageContext getPageContext() {
|
||||||
|
if (this.pageContext == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.pageContext.copy();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean hasAnyContent() {
|
public boolean hasAnyContent() {
|
||||||
return this.table.getItemCount() > 0;
|
return this.table.getItemCount() > 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,11 +55,6 @@ public class BatisConfig {
|
||||||
factoryBean.setDataSource(dataSource);
|
factoryBean.setDataSource(dataSource);
|
||||||
final SqlSessionFactory factory = factoryBean.getObject();
|
final SqlSessionFactory factory = factoryBean.getObject();
|
||||||
|
|
||||||
factory.getConfiguration()
|
|
||||||
.addMappers("ch.ethz.seb.sebserver.webservice.datalayer.batis");
|
|
||||||
factory.getConfiguration()
|
|
||||||
.addMappers("ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper");
|
|
||||||
|
|
||||||
return factory;
|
return factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,20 @@ public interface ExamConfigurationMapDAO extends
|
||||||
EntityDAO<ExamConfigurationMap, ExamConfigurationMap>,
|
EntityDAO<ExamConfigurationMap, ExamConfigurationMap>,
|
||||||
BulkActionSupportDAO<ExamConfigurationMap> {
|
BulkActionSupportDAO<ExamConfigurationMap> {
|
||||||
|
|
||||||
|
/** Get a specific ExamConfigurationMap by the mapping identifiers
|
||||||
|
*
|
||||||
|
* @param examId The Exam mapping identifier
|
||||||
|
* @param configurationNodeId the ConfigurationNode mapping identifier
|
||||||
|
* @return Result refer to the ExamConfigurationMap with specified mapping or to an exception if happened */
|
||||||
|
public Result<ExamConfigurationMap> byMapping(Long examId, Long configurationNodeId);
|
||||||
|
|
||||||
|
/** Get the password cipher of a specific ExamConfigurationMap by the mapping identifiers
|
||||||
|
*
|
||||||
|
* @param examId The Exam mapping identifier
|
||||||
|
* @param configurationNodeId the ConfigurationNode mapping identifier
|
||||||
|
* @return Result refer to the password cipher of specified mapping or to an exception if happened */
|
||||||
|
public Result<CharSequence> getConfigPasswortCipher(Long examId, Long configurationNodeId);
|
||||||
|
|
||||||
/** Get the ConfigurationNode identifier of the default Exam Configuration of
|
/** Get the ConfigurationNode identifier of the default Exam Configuration of
|
||||||
* the Exam with specified identifier.
|
* the Exam with specified identifier.
|
||||||
*
|
*
|
||||||
|
|
|
@ -126,6 +126,43 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO {
|
||||||
.collect(Collectors.toList()));
|
.collect(Collectors.toList()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public Result<ExamConfigurationMap> byMapping(final Long examId, final Long configurationNodeId) {
|
||||||
|
return Result.tryCatch(() -> this.examConfigurationMapRecordMapper
|
||||||
|
.selectByExample()
|
||||||
|
.where(
|
||||||
|
ExamConfigurationMapRecordDynamicSqlSupport.examId,
|
||||||
|
SqlBuilder.isEqualTo(examId))
|
||||||
|
.and(
|
||||||
|
ExamConfigurationMapRecordDynamicSqlSupport.configurationNodeId,
|
||||||
|
SqlBuilder.isEqualTo(configurationNodeId))
|
||||||
|
.build()
|
||||||
|
.execute()
|
||||||
|
.stream()
|
||||||
|
.map(this::toDomainModel)
|
||||||
|
.flatMap(DAOLoggingSupport::logAndSkipOnError)
|
||||||
|
.collect(Utils.toSingleton()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public Result<CharSequence> getConfigPasswortCipher(final Long examId, final Long configurationNodeId) {
|
||||||
|
return Result.tryCatch(() -> this.examConfigurationMapRecordMapper
|
||||||
|
.selectByExample()
|
||||||
|
.where(
|
||||||
|
ExamConfigurationMapRecordDynamicSqlSupport.examId,
|
||||||
|
SqlBuilder.isEqualTo(examId))
|
||||||
|
.and(
|
||||||
|
ExamConfigurationMapRecordDynamicSqlSupport.configurationNodeId,
|
||||||
|
SqlBuilder.isEqualTo(configurationNodeId))
|
||||||
|
.build()
|
||||||
|
.execute()
|
||||||
|
.stream()
|
||||||
|
.collect(Utils.toSingleton()))
|
||||||
|
.map(ExamConfigurationMapRecord::getEncryptSecret);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public Result<Long> getDefaultConfigurationForExam(final Long examId) {
|
public Result<Long> getDefaultConfigurationForExam(final Long examId) {
|
||||||
|
|
|
@ -61,7 +61,7 @@ public interface SebExamConfigService {
|
||||||
* @param examId the exam identifier
|
* @param examId the exam identifier
|
||||||
* @return The configuration node identifier (PK) */
|
* @return The configuration node identifier (PK) */
|
||||||
default Long exportForExam(final OutputStream out, final Long institutionId, final Long examId) {
|
default Long exportForExam(final OutputStream out, final Long institutionId, final Long examId) {
|
||||||
return exportForExam(out, institutionId, examId, null);
|
return exportForExam(out, institutionId, examId, (String) null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Used to export the default SEB Exam Configuration for a given exam identifier.
|
/** Used to export the default SEB Exam Configuration for a given exam identifier.
|
||||||
|
@ -75,7 +75,23 @@ public interface SebExamConfigService {
|
||||||
* @return The configuration node identifier (PK) */
|
* @return The configuration node identifier (PK) */
|
||||||
Long exportForExam(OutputStream out, Long institutionId, Long examId, String userId);
|
Long exportForExam(OutputStream out, Long institutionId, Long examId, String userId);
|
||||||
|
|
||||||
/** TODO */
|
/** Used to export the default SEB Exam Configuration for a given exam identifier.
|
||||||
|
* either with encryption if defined or as plain text within the SEB Configuration format
|
||||||
|
* as described here: https://www.safeexambrowser.org/developer/seb-file-format.html
|
||||||
|
*
|
||||||
|
* @param out The output stream to write the export data to
|
||||||
|
* @param institutionId The identifier of the institution of the requesting user
|
||||||
|
* @param examId the exam identifier that defines the mapping
|
||||||
|
* @param configurationNodeId the configurationNodeId that defines the mapping
|
||||||
|
* @return The configuration node identifier (PK) */
|
||||||
|
Long exportForExam(OutputStream out, Long institutionId, Long examId, Long configurationNodeId);
|
||||||
|
|
||||||
|
/** Generates a Config-Key form the SEB exam configuration defined by configurationNodeId.
|
||||||
|
* See https://www.safeexambrowser.org/developer/seb-config-key.html for more information about the Config-Key
|
||||||
|
*
|
||||||
|
* @param institutionId the institutional id
|
||||||
|
* @param configurationNodeId the configurationNodeId
|
||||||
|
* @return Result refer to the generated Config-Key or to an error if happened. */
|
||||||
Result<String> generateConfigKey(Long institutionId, Long configurationNodeId);
|
Result<String> generateConfigKey(Long institutionId, Long configurationNodeId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,11 +33,16 @@ import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValues;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
|
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
|
||||||
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.webservice.servicelayer.client.ClientCredentialService;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationAttributeDAO;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationAttributeDAO;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamConfigurationMapDAO;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamConfigurationMapDAO;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ConfigurationFormat;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ConfigurationFormat;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ConfigurationValueValidator;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ConfigurationValueValidator;
|
||||||
|
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.SebExamConfigService;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebExamConfigService;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ZipService;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl.SebConfigEncryptionServiceImpl.EncryptionContext;
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
@Service
|
@Service
|
||||||
|
@ -50,17 +55,26 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
|
||||||
private final ConfigurationAttributeDAO configurationAttributeDAO;
|
private final ConfigurationAttributeDAO configurationAttributeDAO;
|
||||||
private final ExamConfigurationMapDAO examConfigurationMapDAO;
|
private final ExamConfigurationMapDAO examConfigurationMapDAO;
|
||||||
private final Collection<ConfigurationValueValidator> validators;
|
private final Collection<ConfigurationValueValidator> validators;
|
||||||
|
private final ClientCredentialService clientCredentialService;
|
||||||
|
private final ZipService zipService;
|
||||||
|
private final SebConfigEncryptionService sebConfigEncryptionService;
|
||||||
|
|
||||||
protected SebExamConfigServiceImpl(
|
protected SebExamConfigServiceImpl(
|
||||||
final ExamConfigIO examConfigIO,
|
final ExamConfigIO examConfigIO,
|
||||||
final ConfigurationAttributeDAO configurationAttributeDAO,
|
final ConfigurationAttributeDAO configurationAttributeDAO,
|
||||||
final ExamConfigurationMapDAO examConfigurationMapDAO,
|
final ExamConfigurationMapDAO examConfigurationMapDAO,
|
||||||
final Collection<ConfigurationValueValidator> validators) {
|
final Collection<ConfigurationValueValidator> validators,
|
||||||
|
final ClientCredentialService clientCredentialService,
|
||||||
|
final ZipService zipService,
|
||||||
|
final SebConfigEncryptionService sebConfigEncryptionService) {
|
||||||
|
|
||||||
this.examConfigIO = examConfigIO;
|
this.examConfigIO = examConfigIO;
|
||||||
this.configurationAttributeDAO = configurationAttributeDAO;
|
this.configurationAttributeDAO = configurationAttributeDAO;
|
||||||
this.examConfigurationMapDAO = examConfigurationMapDAO;
|
this.examConfigurationMapDAO = examConfigurationMapDAO;
|
||||||
this.validators = validators;
|
this.validators = validators;
|
||||||
|
this.clientCredentialService = clientCredentialService;
|
||||||
|
this.zipService = zipService;
|
||||||
|
this.sebConfigEncryptionService = sebConfigEncryptionService;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,7 +128,7 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
|
||||||
final Long institutionId,
|
final Long institutionId,
|
||||||
final Long configurationNodeId) {
|
final Long configurationNodeId) {
|
||||||
|
|
||||||
this.exportPlain(ConfigurationFormat.XML, out, institutionId, configurationNodeId);
|
this.exportPlainOnly(ConfigurationFormat.XML, out, institutionId, configurationNodeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -123,7 +137,7 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
|
||||||
final Long institutionId,
|
final Long institutionId,
|
||||||
final Long configurationNodeId) {
|
final Long configurationNodeId) {
|
||||||
|
|
||||||
this.exportPlain(ConfigurationFormat.JSON, out, institutionId, configurationNodeId);
|
this.exportPlainOnly(ConfigurationFormat.JSON, out, institutionId, configurationNodeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result<Long> getDefaultConfigurationIdForExam(final Long examId) {
|
public Result<Long> getDefaultConfigurationIdForExam(final Long examId) {
|
||||||
|
@ -147,9 +161,83 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
|
||||||
: getUserConfigurationIdForExam(examId, userId)
|
: getUserConfigurationIdForExam(examId, userId)
|
||||||
.getOrThrow();
|
.getOrThrow();
|
||||||
|
|
||||||
// TODO add header, zip and encrypt if needed
|
return exportForExam(out, institutionId, examId, configurationNodeId);
|
||||||
|
}
|
||||||
|
|
||||||
this.exportPlainXML(out, institutionId, configurationNodeId);
|
@Override
|
||||||
|
public Long exportForExam(
|
||||||
|
final OutputStream out,
|
||||||
|
final Long institutionId,
|
||||||
|
final Long examId,
|
||||||
|
final Long configurationNodeId) {
|
||||||
|
|
||||||
|
final CharSequence passwordCipher = this.examConfigurationMapDAO
|
||||||
|
.getConfigPasswortCipher(examId, configurationNodeId)
|
||||||
|
.getOr(null);
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(passwordCipher)) {
|
||||||
|
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
log.debug("*** Seb exam configuration with password based encryption");
|
||||||
|
}
|
||||||
|
|
||||||
|
final CharSequence encryptionPasswordPlaintext = this.clientCredentialService
|
||||||
|
.decrypt(passwordCipher);
|
||||||
|
|
||||||
|
PipedOutputStream plainOut = null;
|
||||||
|
PipedInputStream zipIn = null;
|
||||||
|
|
||||||
|
PipedOutputStream zipOut = null;
|
||||||
|
PipedInputStream cryptIn = null;
|
||||||
|
|
||||||
|
PipedOutputStream cryptOut = null;
|
||||||
|
PipedInputStream in = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
plainOut = new PipedOutputStream();
|
||||||
|
zipIn = new PipedInputStream(plainOut);
|
||||||
|
|
||||||
|
zipOut = new PipedOutputStream();
|
||||||
|
cryptIn = new PipedInputStream(zipOut);
|
||||||
|
|
||||||
|
cryptOut = new PipedOutputStream();
|
||||||
|
in = new PipedInputStream(cryptOut);
|
||||||
|
|
||||||
|
// streaming...
|
||||||
|
// export plain text
|
||||||
|
this.examConfigIO.exportPlain(
|
||||||
|
ConfigurationFormat.XML,
|
||||||
|
plainOut,
|
||||||
|
institutionId,
|
||||||
|
configurationNodeId);
|
||||||
|
// zip the plain text
|
||||||
|
this.zipService.write(zipOut, zipIn);
|
||||||
|
// encrypt the zipped plain text
|
||||||
|
this.sebConfigEncryptionService.streamEncrypted(
|
||||||
|
cryptOut,
|
||||||
|
cryptIn,
|
||||||
|
EncryptionContext.contextOf(
|
||||||
|
Strategy.PASSWORD_PSWD,
|
||||||
|
encryptionPasswordPlaintext));
|
||||||
|
|
||||||
|
// copy to output
|
||||||
|
IOUtils.copyLarge(in, out);
|
||||||
|
|
||||||
|
} catch (final Exception e) {
|
||||||
|
log.error("Error while zip and encrypt seb exam config stream: ", e);
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeQuietly(zipIn);
|
||||||
|
IOUtils.closeQuietly(plainOut);
|
||||||
|
IOUtils.closeQuietly(cryptIn);
|
||||||
|
IOUtils.closeQuietly(zipOut);
|
||||||
|
IOUtils.closeQuietly(in);
|
||||||
|
IOUtils.closeQuietly(cryptOut);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// just export in plain text XML format
|
||||||
|
this.exportPlainXML(out, institutionId, configurationNodeId);
|
||||||
|
}
|
||||||
|
|
||||||
return configurationNodeId;
|
return configurationNodeId;
|
||||||
}
|
}
|
||||||
|
@ -224,7 +312,7 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void exportPlain(
|
private void exportPlainOnly(
|
||||||
final ConfigurationFormat exportFormat,
|
final ConfigurationFormat exportFormat,
|
||||||
final OutputStream out,
|
final OutputStream out,
|
||||||
final Long institutionId,
|
final Long institutionId,
|
||||||
|
|
|
@ -8,17 +8,25 @@
|
||||||
|
|
||||||
package ch.ethz.seb.sebserver.webservice.weblayer.api;
|
package ch.ethz.seb.sebserver.webservice.weblayer.api;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import javax.servlet.ServletOutputStream;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
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.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.util.MultiValueMap;
|
import org.springframework.util.MultiValueMap;
|
||||||
import org.springframework.validation.FieldError;
|
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.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
@ -50,6 +58,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserDAO;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserDAO;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebExamConfigService;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService;
|
||||||
|
|
||||||
@WebServiceProfile
|
@WebServiceProfile
|
||||||
|
@ -57,9 +66,12 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe
|
||||||
@RequestMapping("${sebserver.webservice.api.admin.endpoint}" + API.EXAM_ADMINISTRATION_ENDPOINT)
|
@RequestMapping("${sebserver.webservice.api.admin.endpoint}" + API.EXAM_ADMINISTRATION_ENDPOINT)
|
||||||
public class ExamAdministrationController extends ActivatableEntityController<Exam, Exam> {
|
public class ExamAdministrationController extends ActivatableEntityController<Exam, Exam> {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(ExamAdministrationController.class);
|
||||||
|
|
||||||
private final ExamDAO examDAO;
|
private final ExamDAO examDAO;
|
||||||
private final UserDAO userDAO;
|
private final UserDAO userDAO;
|
||||||
private final LmsAPIService lmsAPIService;
|
private final LmsAPIService lmsAPIService;
|
||||||
|
private final SebExamConfigService sebExamConfigService;
|
||||||
|
|
||||||
public ExamAdministrationController(
|
public ExamAdministrationController(
|
||||||
final AuthorizationService authorization,
|
final AuthorizationService authorization,
|
||||||
|
@ -69,7 +81,8 @@ public class ExamAdministrationController extends ActivatableEntityController<Ex
|
||||||
final BulkActionService bulkActionService,
|
final BulkActionService bulkActionService,
|
||||||
final BeanValidationService beanValidationService,
|
final BeanValidationService beanValidationService,
|
||||||
final LmsAPIService lmsAPIService,
|
final LmsAPIService lmsAPIService,
|
||||||
final UserDAO userDAO) {
|
final UserDAO userDAO,
|
||||||
|
final SebExamConfigService sebExamConfigService) {
|
||||||
|
|
||||||
super(authorization,
|
super(authorization,
|
||||||
bulkActionService,
|
bulkActionService,
|
||||||
|
@ -81,6 +94,7 @@ public class ExamAdministrationController extends ActivatableEntityController<Ex
|
||||||
this.examDAO = examDAO;
|
this.examDAO = examDAO;
|
||||||
this.userDAO = userDAO;
|
this.userDAO = userDAO;
|
||||||
this.lmsAPIService = lmsAPIService;
|
this.lmsAPIService = lmsAPIService;
|
||||||
|
this.sebExamConfigService = sebExamConfigService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -133,6 +147,46 @@ public class ExamAdministrationController extends ActivatableEntityController<Ex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequestMapping(
|
||||||
|
path = API.MODEL_ID_VAR_PATH_SEGMENT
|
||||||
|
+ API.EXAM_ADMINISTRATION_DOWNLOAD_CONFIG_PATH_SEGMENT
|
||||||
|
+ API.PARENT_MODEL_ID_VAR_PATH_SEGMENT,
|
||||||
|
method = RequestMethod.GET,
|
||||||
|
produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
|
||||||
|
public void downloadPlainXMLConfig(
|
||||||
|
@PathVariable final Long modelId,
|
||||||
|
@PathVariable final Long parentModelId,
|
||||||
|
@RequestParam(
|
||||||
|
name = API.PARAM_INSTITUTION_ID,
|
||||||
|
required = true,
|
||||||
|
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
|
||||||
|
final HttpServletResponse response) throws IOException {
|
||||||
|
|
||||||
|
this.entityDAO.byPK(modelId)
|
||||||
|
.flatMap(this.authorization::checkRead)
|
||||||
|
.flatMap(this.userActivityLogDAO::logExport);
|
||||||
|
|
||||||
|
final ServletOutputStream outputStream = response.getOutputStream();
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
this.sebExamConfigService.exportForExam(
|
||||||
|
outputStream,
|
||||||
|
institutionId,
|
||||||
|
parentModelId,
|
||||||
|
modelId);
|
||||||
|
|
||||||
|
response.setStatus(HttpStatus.OK.value());
|
||||||
|
|
||||||
|
} catch (final Exception e) {
|
||||||
|
log.error("Unexpected error while trying to downstream exam config: ", e);
|
||||||
|
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
|
||||||
|
} finally {
|
||||||
|
outputStream.flush();
|
||||||
|
outputStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static Page<Exam> buildSortedExamPage(
|
public static Page<Exam> buildSortedExamPage(
|
||||||
final Integer pageNumber,
|
final Integer pageNumber,
|
||||||
final Integer pageSize,
|
final Integer pageSize,
|
||||||
|
|
|
@ -311,8 +311,8 @@ sebserver.exam.status.UP_COMING=Up Coming
|
||||||
sebserver.exam.status.RUNNING=Running
|
sebserver.exam.status.RUNNING=Running
|
||||||
sebserver.exam.status.FINISHED=Finished
|
sebserver.exam.status.FINISHED=Finished
|
||||||
|
|
||||||
sebserver.exam.configuration.list.actions=SEB Configuration
|
sebserver.exam.configuration.list.actions=SEB Exam Configuration
|
||||||
sebserver.exam.configuration.list.title=SEB Configuration
|
sebserver.exam.configuration.list.title=SEB Exam Configuration
|
||||||
sebserver.exam.configuration.list.column.name=Name
|
sebserver.exam.configuration.list.column.name=Name
|
||||||
sebserver.exam.configuration.list.column.description=Description
|
sebserver.exam.configuration.list.column.description=Description
|
||||||
sebserver.exam.configuration.list.column.status=Status
|
sebserver.exam.configuration.list.column.status=Status
|
||||||
|
@ -320,10 +320,12 @@ sebserver.exam.configuration.list.empty=There is currently no SEB Configuration
|
||||||
sebserver.exam.configuration.list.pleaseSelect=Please Select a SEB Configuration first
|
sebserver.exam.configuration.list.pleaseSelect=Please Select a SEB Configuration first
|
||||||
sebserver.exam.configuration.action.noconfig.message=There is currently no SEB exam configuration to select.<br/>Please create one in SEB Configuration / Exam Configuration
|
sebserver.exam.configuration.action.noconfig.message=There is currently no SEB exam configuration to select.<br/>Please create one in SEB Configuration / Exam Configuration
|
||||||
|
|
||||||
sebserver.exam.configuration.action.list.new=Add
|
sebserver.exam.configuration.action.list.new=Add Configuration
|
||||||
sebserver.exam.configuration.action.list.modify=Edit
|
sebserver.exam.configuration.action.list.modify=Edit Configuration
|
||||||
sebserver.exam.configuration.action.list.delete=Delete
|
sebserver.exam.configuration.action.list.delete=Delete Configuration
|
||||||
sebserver.exam.configuration.action.save=Save
|
sebserver.exam.configuration.action.save=Save Configuration
|
||||||
|
sebserver.exam.configuration.action.export-config=Export Configuration
|
||||||
|
sebserver.exam.configuration.action.get-config-key=Export Config-Key
|
||||||
|
|
||||||
sebserver.exam.configuration.form.title.new=Add SEB Configuration Mapping
|
sebserver.exam.configuration.form.title.new=Add SEB Configuration Mapping
|
||||||
sebserver.exam.configuration.form.title=SEB Configuration Mapping
|
sebserver.exam.configuration.form.title=SEB Configuration Mapping
|
||||||
|
|
Loading…
Reference in a new issue