fixed client config export to configure a client with password
added possibility to export client config on exam side with including examId in the config
This commit is contained in:
parent
14334f0d7e
commit
6c8aa7b12c
12 changed files with 334 additions and 35 deletions
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
* Copyright (c) 2020 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.content;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
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.Label;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
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.Domain;
|
||||
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigCreationInfo;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig.ConfigPurpose;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Tuple;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
import ch.ethz.seb.sebserver.gui.form.FormBuilder;
|
||||
import ch.ethz.seb.sebserver.gui.form.FormHandle;
|
||||
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
|
||||
import ch.ethz.seb.sebserver.gui.service.page.ModalInputDialogComposer;
|
||||
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
|
||||
import ch.ethz.seb.sebserver.gui.service.page.PageService;
|
||||
import ch.ethz.seb.sebserver.gui.service.page.impl.ModalInputDialog;
|
||||
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.SEBClientConfigDownload;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.GetClientConfigs;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@GuiProfile
|
||||
public class ExamCreateClientConfigPopup {
|
||||
|
||||
private static final LocTextKey TITLE_KEY = new LocTextKey("sebserver.exam.form.export.config.popup.title");
|
||||
private static final LocTextKey CONFIG_NAME_KEY = new LocTextKey("sebserver.exam.form.export.config.name");
|
||||
private static final LocTextKey CONFIG_TEXT_KEY = new LocTextKey("sebserver.exam.form.export.config.popup.text");
|
||||
private static final LocTextKey NO_CONFIG_TEXT_KEY =
|
||||
new LocTextKey("sebserver.exam.form.export.config.popup.noconfig");
|
||||
|
||||
private final PageService pageService;
|
||||
private final DownloadService downloadService;
|
||||
private final String downloadFileName;
|
||||
|
||||
public ExamCreateClientConfigPopup(
|
||||
final PageService pageService,
|
||||
final DownloadService downloadService,
|
||||
@Value("${sebserver.gui.seb.exam.config.download.filename}") final String downloadFileName) {
|
||||
|
||||
this.pageService = pageService;
|
||||
this.downloadService = downloadService;
|
||||
this.downloadFileName = downloadFileName;
|
||||
}
|
||||
|
||||
public Function<PageAction, PageAction> exportFunction() {
|
||||
|
||||
return action -> {
|
||||
|
||||
final ModalInputDialog<FormHandle<?>> dialog =
|
||||
new ModalInputDialog<FormHandle<?>>(
|
||||
action.pageContext().getParent().getShell(),
|
||||
this.pageService.getWidgetFactory())
|
||||
.setLargeDialogWidth();
|
||||
|
||||
final CreationFormContext creationFormContext = new CreationFormContext(
|
||||
this.pageService,
|
||||
action.pageContext());
|
||||
|
||||
final Predicate<FormHandle<?>> doCreate = formHandle -> doCreate(
|
||||
this.pageService,
|
||||
action.pageContext(),
|
||||
action.getEntityKey(),
|
||||
formHandle);
|
||||
|
||||
dialog.open(
|
||||
TITLE_KEY,
|
||||
doCreate,
|
||||
Utils.EMPTY_EXECUTION,
|
||||
creationFormContext);
|
||||
|
||||
return action;
|
||||
};
|
||||
}
|
||||
|
||||
private boolean doCreate(
|
||||
final PageService pageService,
|
||||
final PageContext pageContext,
|
||||
final EntityKey examKey,
|
||||
final FormHandle<?> formHandle) {
|
||||
|
||||
if (formHandle == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final UrlLauncher urlLauncher = RWT.getClient().getService(UrlLauncher.class);
|
||||
final String modelId = formHandle.getForm().getFieldValue(Domain.SEB_CLIENT_CONFIGURATION.ATTR_ID);
|
||||
final String downloadURL = this.downloadService.createDownloadURL(
|
||||
modelId,
|
||||
examKey.modelId,
|
||||
SEBClientConfigDownload.class,
|
||||
this.downloadFileName);
|
||||
urlLauncher.openURL(downloadURL);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private final class CreationFormContext implements ModalInputDialogComposer<FormHandle<?>> {
|
||||
|
||||
private final PageService pageService;
|
||||
private final PageContext pageContext;
|
||||
|
||||
protected CreationFormContext(
|
||||
final PageService pageService,
|
||||
final PageContext pageContext) {
|
||||
|
||||
this.pageService = pageService;
|
||||
this.pageContext = pageContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Supplier<FormHandle<?>> compose(final Composite parent) {
|
||||
|
||||
final List<Tuple<String>> configs = this.pageService.getRestService().getBuilder(GetClientConfigs.class)
|
||||
.withQueryParam(SEBClientConfig.FILTER_ATTR_ACTIVE, Constants.TRUE_STRING)
|
||||
.call()
|
||||
.getOrThrow()
|
||||
.stream()
|
||||
.filter(config -> config.configPurpose == ConfigPurpose.START_EXAM)
|
||||
.map(config -> new Tuple<>(config.getModelId(), config.name))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (configs.isEmpty()) {
|
||||
final Label text = this.pageService
|
||||
.getWidgetFactory()
|
||||
.labelLocalized(parent, NO_CONFIG_TEXT_KEY);
|
||||
text.setData(RWT.MARKUP_ENABLED, true);
|
||||
return null;
|
||||
} else {
|
||||
|
||||
final Label text = this.pageService
|
||||
.getWidgetFactory()
|
||||
.labelLocalized(parent, CONFIG_TEXT_KEY);
|
||||
text.setData(RWT.MARKUP_ENABLED, true);
|
||||
|
||||
final FormHandle<ConfigCreationInfo> formHandle = this.pageService.formBuilder(
|
||||
this.pageContext.copyOf(parent))
|
||||
.readonly(false)
|
||||
.addField(FormBuilder.singleSelection(
|
||||
Domain.SEB_CLIENT_CONFIGURATION.ATTR_ID,
|
||||
CONFIG_NAME_KEY,
|
||||
configs.get(0)._1,
|
||||
() -> configs))
|
||||
|
||||
.build();
|
||||
return () -> formHandle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -166,11 +166,13 @@ public class ExamForm implements TemplateComposer {
|
|||
private final String downloadFileName;
|
||||
private final WidgetFactory widgetFactory;
|
||||
private final RestService restService;
|
||||
private final ExamCreateClientConfigPopup examCreateClientConfigPopup;
|
||||
|
||||
protected ExamForm(
|
||||
final PageService pageService,
|
||||
final ResourceService resourceService,
|
||||
final DownloadService downloadService,
|
||||
final ExamCreateClientConfigPopup examCreateClientConfigPopup,
|
||||
@Value("${sebserver.gui.seb.exam.config.download.filename}") final String downloadFileName) {
|
||||
|
||||
this.pageService = pageService;
|
||||
|
@ -179,6 +181,7 @@ public class ExamForm implements TemplateComposer {
|
|||
this.downloadFileName = downloadFileName;
|
||||
this.widgetFactory = pageService.getWidgetFactory();
|
||||
this.restService = this.resourceService.getRestService();
|
||||
this.examCreateClientConfigPopup = examCreateClientConfigPopup;
|
||||
|
||||
this.consistencyMessageMapping = new HashMap<>();
|
||||
this.consistencyMessageMapping.put(
|
||||
|
@ -245,6 +248,7 @@ public class ExamForm implements TemplateComposer {
|
|||
final boolean modifyGrant = userGrantCheck.m();
|
||||
final ExamStatus examStatus = exam.getStatus();
|
||||
final boolean isExamRunning = examStatus == ExamStatus.RUNNING;
|
||||
final boolean writeGrant = userGrantCheck.w();
|
||||
final boolean editable = examStatus == ExamStatus.UP_COMING
|
||||
|| examStatus == ExamStatus.RUNNING
|
||||
&& currentUser.get().hasRole(UserRole.EXAM_ADMIN);
|
||||
|
@ -391,6 +395,11 @@ public class ExamForm implements TemplateComposer {
|
|||
.withExec(this.cancelModifyFunction())
|
||||
.publishIf(() -> !readonly)
|
||||
|
||||
.newAction(ActionDefinition.EXAM_SEB_CLIENT_CONFIG_EXPORT)
|
||||
.withEntityKey(entityKey)
|
||||
.withExec(this.examCreateClientConfigPopup.exportFunction())
|
||||
.publishIf(() -> writeGrant && readonly)
|
||||
|
||||
.newAction(ActionDefinition.EXAM_MODIFY_SEB_RESTRICTION_DETAILS)
|
||||
.withEntityKey(entityKey)
|
||||
.withExec(ExamSEBRestrictionSettings.settingsFunction(this.pageService))
|
||||
|
|
|
@ -306,6 +306,7 @@ public class SEBClientConfigForm implements TemplateComposer {
|
|||
FALLBACK_ATTRIBUTES::contains,
|
||||
ffa -> ffa.setVisible(BooleanUtils.isTrue(clientConfig.fallback)));
|
||||
|
||||
if (!isReadonly) {
|
||||
formHandle.getForm().getFieldInput(SEBClientConfig.ATTR_FALLBACK)
|
||||
.addListener(SWT.Selection, event -> formHandle.process(
|
||||
FALLBACK_ATTRIBUTES::contains,
|
||||
|
@ -317,6 +318,7 @@ public class SEBClientConfigForm implements TemplateComposer {
|
|||
ffa.setStringValue(StringUtils.EMPTY);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
final UrlLauncher urlLauncher = RWT.getClient().getService(UrlLauncher.class);
|
||||
this.pageService.pageActionBuilder(formContext.clearEntityKeys())
|
||||
|
|
|
@ -353,6 +353,11 @@ public enum ActionDefinition {
|
|||
ImageIcon.CANCEL,
|
||||
PageStateDefinitionImpl.EXAM_VIEW,
|
||||
ActionCategory.FORM),
|
||||
EXAM_SEB_CLIENT_CONFIG_EXPORT(
|
||||
new LocTextKey("sebserver.exam.action.createClientToStartExam"),
|
||||
ImageIcon.EXPORT,
|
||||
PageStateDefinitionImpl.EXAM_VIEW,
|
||||
ActionCategory.FORM),
|
||||
|
||||
SEB_CLIENT_CONFIG_LIST(
|
||||
new LocTextKey("sebserver.clientconfig.list.title"),
|
||||
|
|
|
@ -13,6 +13,7 @@ import java.io.InputStream;
|
|||
import java.io.OutputStream;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
|
@ -20,7 +21,9 @@ 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.model.Domain.EXAM;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.ExportClientConfig;
|
||||
|
||||
|
@ -45,8 +48,15 @@ public class SEBClientConfigDownload extends AbstractDownloadServiceHandler {
|
|||
@Override
|
||||
protected void webserviceCall(final String modelId, final String parentModelId, final OutputStream downloadOut) {
|
||||
|
||||
final InputStream input = this.restService.getBuilder(ExportClientConfig.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, modelId)
|
||||
final RestCall<InputStream>.RestCallBuilder restCallBuilder = this.restService
|
||||
.getBuilder(ExportClientConfig.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, modelId);
|
||||
|
||||
if (StringUtils.isNotBlank(parentModelId)) {
|
||||
restCallBuilder.withQueryParam(EXAM.ATTR_ID, parentModelId);
|
||||
}
|
||||
|
||||
final InputStream input = restCallBuilder
|
||||
.call()
|
||||
.getOrThrow();
|
||||
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.PageToListCallAdapter;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@GuiProfile
|
||||
public class GetClientConfigs extends PageToListCallAdapter<SEBClientConfig> {
|
||||
|
||||
public GetClientConfigs() {
|
||||
super(
|
||||
GetClientConfigPage.class,
|
||||
EntityType.SEB_CLIENT_CONFIGURATION,
|
||||
new TypeReference<List<SEBClientConfig>>() {
|
||||
},
|
||||
API.SEB_CLIENT_CONFIG_ENDPOINT);
|
||||
}
|
||||
|
||||
}
|
|
@ -31,9 +31,9 @@ import org.springframework.transaction.annotation.Transactional;
|
|||
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
|
||||
import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException;
|
||||
import ch.ethz.seb.sebserver.gbl.api.APIMessage.ErrorMessage;
|
||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||
import ch.ethz.seb.sebserver.gbl.client.ClientCredentialService;
|
||||
import ch.ethz.seb.sebserver.gbl.client.ClientCredentials;
|
||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
||||
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig;
|
||||
|
@ -245,18 +245,20 @@ public class SEBClientConfigDAOImpl implements SEBClientConfigDAO {
|
|||
|
||||
checkUniqueName(sebClientConfig);
|
||||
|
||||
final SebClientConfigRecord record =
|
||||
this.sebClientConfigRecordMapper.selectByPrimaryKey(sebClientConfig.id);
|
||||
|
||||
final SebClientConfigRecord newRecord = new SebClientConfigRecord(
|
||||
sebClientConfig.id,
|
||||
null,
|
||||
record.getInstitutionId(),
|
||||
sebClientConfig.name,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
record.getDate(),
|
||||
record.getClientName(),
|
||||
record.getClientSecret(),
|
||||
getEncryptionPassword(sebClientConfig),
|
||||
null);
|
||||
record.getActive());
|
||||
|
||||
this.sebClientConfigRecordMapper
|
||||
.updateByPrimaryKeySelective(newRecord);
|
||||
this.sebClientConfigRecordMapper.updateByPrimaryKey(newRecord);
|
||||
|
||||
saveAdditionalAttributes(sebClientConfig, newRecord.getId());
|
||||
|
||||
|
|
|
@ -45,10 +45,12 @@ public interface ClientConfigService {
|
|||
* as described here: https://www.safeexambrowser.org/developer/seb-file-format.html
|
||||
*
|
||||
* @param out OutputStream to write the export to
|
||||
* @param modelId the model identifier of the SEBClientConfiguration to export */
|
||||
* @param modelId the model identifier of the SEBClientConfiguration to export
|
||||
* @param examId The exam identifier. May be null, if not the exported client config will contain the exam information*/
|
||||
void exportSEBClientConfiguration(
|
||||
OutputStream out,
|
||||
final String modelId);
|
||||
final String modelId,
|
||||
final Long examId);
|
||||
|
||||
/** Get the ClientDetails for given client name that identifies a SEBClientConfiguration entry.
|
||||
*
|
||||
|
|
|
@ -13,6 +13,8 @@ import java.io.OutputStream;
|
|||
import java.io.PipedInputStream;
|
||||
import java.io.PipedOutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Base64;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
@ -21,6 +23,7 @@ import java.util.UUID;
|
|||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.bouncycastle.util.encoders.Hex;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
@ -46,6 +49,7 @@ import ch.ethz.seb.sebserver.gbl.client.ClientCredentialService;
|
|||
import ch.ethz.seb.sebserver.gbl.client.ClientCredentials;
|
||||
import ch.ethz.seb.sebserver.gbl.model.institution.Institution;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig.ConfigPurpose;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
|
@ -66,6 +70,7 @@ public class ClientConfigServiceImpl implements ClientConfigService {
|
|||
|
||||
private static final Logger log = LoggerFactory.getLogger(ClientConfigServiceImpl.class);
|
||||
|
||||
private static final String SEB_CLIENT_CONFIG_EXAM_PROP_NAME = "exam";
|
||||
private static final String SEB_CLIENT_CONFIG_TEMPLATE_XML =
|
||||
" <dict>%n" +
|
||||
" <key>sebMode</key>%n" +
|
||||
|
@ -80,7 +85,7 @@ public class ClientConfigServiceImpl implements ClientConfigService {
|
|||
" <key>sebServerConfiguration</key>%n" +
|
||||
" <dict>%n" +
|
||||
" <key>institution</key>%n" +
|
||||
" <string>%s</string>%n" +
|
||||
" <string>%s</string>%n%s" +
|
||||
" <key>clientName</key>%n" +
|
||||
" <string>%s</string>%n" +
|
||||
" <key>clientSecret</key>%n" +
|
||||
|
@ -186,16 +191,17 @@ public class ClientConfigServiceImpl implements ClientConfigService {
|
|||
@Override
|
||||
public void exportSEBClientConfiguration(
|
||||
final OutputStream output,
|
||||
final String modelId) {
|
||||
final String modelId,
|
||||
final Long examId) {
|
||||
|
||||
final SEBClientConfig config = this.sebClientConfigDAO
|
||||
.byModelId(modelId).getOrThrow();
|
||||
|
||||
final CharSequence encryptionPassword = this.sebClientConfigDAO
|
||||
.getConfigPasswordCipher(config.getModelId())
|
||||
.getOr(StringUtils.EMPTY);
|
||||
.getOr((config.getConfigPurpose() == ConfigPurpose.START_EXAM) ? null : StringUtils.EMPTY);
|
||||
|
||||
final String plainTextXMLContent = extractXMLContent(config);
|
||||
final String plainTextXMLContent = extractXMLContent(config, examId);
|
||||
|
||||
PipedOutputStream pOut = null;
|
||||
PipedInputStream pIn = null;
|
||||
|
@ -223,7 +229,7 @@ public class ClientConfigServiceImpl implements ClientConfigService {
|
|||
|
||||
if (encryptionPassword != null) {
|
||||
// encrypt zipped plain text and add header
|
||||
passwordEncryption(zipOut, encryptionPassword, pIn);
|
||||
passwordEncryption(zipOut, encryptionPassword, config.getConfigPurpose(), pIn);
|
||||
} else {
|
||||
// just add plain text header
|
||||
this.sebConfigEncryptionService.streamEncrypted(
|
||||
|
@ -248,7 +254,7 @@ public class ClientConfigServiceImpl implements ClientConfigService {
|
|||
}
|
||||
}
|
||||
|
||||
private String extractXMLContent(final SEBClientConfig config) {
|
||||
private String extractXMLContent(final SEBClientConfig config, final Long examId) {
|
||||
|
||||
String fallbackAddition = "";
|
||||
if (BooleanUtils.isTrue(config.fallback)) {
|
||||
|
@ -289,6 +295,14 @@ public class ClientConfigServiceImpl implements ClientConfigService {
|
|||
}
|
||||
}
|
||||
|
||||
String examIdAddition = "";
|
||||
if (examId != null) {
|
||||
examIdAddition = String.format(
|
||||
SEB_CLIENT_CONFIG_STRING_TEMPLATE,
|
||||
SEB_CLIENT_CONFIG_EXAM_PROP_NAME,
|
||||
examId);
|
||||
}
|
||||
|
||||
final ClientCredentials sebClientCredentials = this.sebClientConfigDAO
|
||||
.getSEBClientCredentials(config.getModelId())
|
||||
.getOrThrow();
|
||||
|
@ -305,6 +319,7 @@ public class ClientConfigServiceImpl implements ClientConfigService {
|
|||
fallbackAddition,
|
||||
this.webserviceInfo.getExternalServerURL(),
|
||||
config.institutionId,
|
||||
examIdAddition,
|
||||
plainClientId,
|
||||
plainClientSecret,
|
||||
this.webserviceInfo.getDiscoveryEndpoint());
|
||||
|
@ -375,22 +390,50 @@ public class ClientConfigServiceImpl implements ClientConfigService {
|
|||
private void passwordEncryption(
|
||||
final OutputStream output,
|
||||
final CharSequence encryptionPassword,
|
||||
final ConfigPurpose configPurpose,
|
||||
final InputStream input) {
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("*** SEB client configuration with password based encryption");
|
||||
}
|
||||
|
||||
final CharSequence encryptionPasswordPlaintext = (encryptionPassword == StringUtils.EMPTY)
|
||||
? StringUtils.EMPTY
|
||||
: this.clientCredentialService.decrypt(encryptionPassword);
|
||||
final CharSequence plainTextPassword = getPlainTextPassword(
|
||||
encryptionPassword,
|
||||
configPurpose);
|
||||
|
||||
this.sebConfigEncryptionService.streamEncrypted(
|
||||
output,
|
||||
input,
|
||||
EncryptionContext.contextOf(
|
||||
(encryptionPassword == StringUtils.EMPTY) ? Strategy.PASSWORD_PWCC : Strategy.PASSWORD_PSWD,
|
||||
encryptionPasswordPlaintext));
|
||||
(configPurpose == ConfigPurpose.CONFIGURE_CLIENT)
|
||||
? Strategy.PASSWORD_PWCC
|
||||
: Strategy.PASSWORD_PSWD,
|
||||
plainTextPassword));
|
||||
}
|
||||
|
||||
private CharSequence getPlainTextPassword(
|
||||
final CharSequence encryptionPassword,
|
||||
final ConfigPurpose configPurpose) {
|
||||
|
||||
CharSequence plainTextPassword = (encryptionPassword == StringUtils.EMPTY)
|
||||
? StringUtils.EMPTY
|
||||
: this.clientCredentialService.decrypt(encryptionPassword);
|
||||
|
||||
if (configPurpose == ConfigPurpose.CONFIGURE_CLIENT && plainTextPassword != StringUtils.EMPTY) {
|
||||
MessageDigest digest;
|
||||
try {
|
||||
digest = MessageDigest.getInstance("SHA-256");
|
||||
final byte[] hash = digest.digest(
|
||||
plainTextPassword.toString().getBytes(StandardCharsets.UTF_8));
|
||||
final byte[] encode = Hex.encode(hash);
|
||||
plainTextPassword = new String(encode, StandardCharsets.UTF_8);
|
||||
|
||||
} catch (final NoSuchAlgorithmException e) {
|
||||
log.error("Failed to generate password hash for config encryption.", e);
|
||||
plainTextPassword = StringUtils.EMPTY;
|
||||
}
|
||||
}
|
||||
return plainTextPassword;
|
||||
}
|
||||
|
||||
/** Get a encoded clientSecret for the SEBClientConfiguration with specified clientId/clientName.
|
||||
|
|
|
@ -69,6 +69,7 @@ public class PasswordEncryptor implements SEBConfigCryptor {
|
|||
try {
|
||||
|
||||
final CharSequence password = context.getPassword();
|
||||
|
||||
if (password.length() == 0) {
|
||||
encryptOutput = new AES256JNCryptorOutputStreamEmptyPwdSupport(
|
||||
output,
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.springframework.validation.FieldError;
|
|||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
|
@ -37,6 +38,7 @@ import ch.ethz.seb.sebserver.gbl.api.API;
|
|||
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
|
||||
import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Domain.EXAM;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig;
|
||||
import ch.ethz.seb.sebserver.gbl.model.user.PasswordChange;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
|
@ -83,6 +85,7 @@ public class SEBClientConfigController extends ActivatableEntityController<SEBCl
|
|||
produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
|
||||
public void downloadSEBConfig(
|
||||
@PathVariable final String modelId,
|
||||
@RequestParam(name = EXAM.ATTR_ID, required = false) final Long examId,
|
||||
final HttpServletResponse response) throws IOException {
|
||||
|
||||
this.entityDAO.byModelId(modelId)
|
||||
|
@ -98,7 +101,8 @@ public class SEBClientConfigController extends ActivatableEntityController<SEBCl
|
|||
|
||||
this.sebClientConfigService.exportSEBClientConfiguration(
|
||||
pout,
|
||||
modelId);
|
||||
modelId,
|
||||
examId);
|
||||
|
||||
IOUtils.copyLarge(pin, outputStream);
|
||||
|
||||
|
|
|
@ -423,6 +423,7 @@ sebserver.exam.action.deactivate=Deactivate Exam
|
|||
sebserver.exam.action.sebrestriction.enable=Apply SEB Lock
|
||||
sebserver.exam.action.sebrestriction.disable=Release SEB Lock
|
||||
sebserver.exam.action.sebrestriction.details=SEB Restriction Details
|
||||
sebserver.exam.action.createClientToStartExam=Export Start Exam Config
|
||||
|
||||
sebserver.exam.info.pleaseSelect=At first please select an Exam from the list
|
||||
|
||||
|
@ -450,6 +451,12 @@ sebserver.exam.form.type.tooltip=The type of the exam.<br/><br/>This has only de
|
|||
sebserver.exam.form.supporter=Exam Supporter
|
||||
sebserver.exam.form.supporter.tooltip=A list of users that are allowed to support this exam<br/><br/>To add a user in edit mode click into the field on the right-hand side and start typing the first letters of the username.<br/>A filtered choice will drop down. Select a specific username in the dropdown list to add the user to the list.<br/>To remove a user from the list, just double-click the username on the list.
|
||||
|
||||
sebserver.exam.form.export.config.popup.title=Export SEB Client Configuration for Starting the Exam
|
||||
sebserver.exam.form.export.config.name=Name
|
||||
sebserver.exam.form.export.config.name.tooltip=The name of the SEB Client Configuration
|
||||
sebserver.exam.form.export.config.popup.text=Please select the SEB Client Configuration you want to use for starting this exam<br/>and click OK to start the download.
|
||||
sebserver.exam.form.export.config.popup.noconfig=There is currently no active SEB Client Configuration for the purpose of "Starting an Exam".<br/><br/>Please go the SEB Client Configuration section and create a new one."
|
||||
|
||||
sebserver.exam.form.sebrestriction.title=SEB Restriction Details
|
||||
sebserver.exam.form.sebrestriction.title.subtitle=
|
||||
sebserver.exam.form.sebrestriction.info=Info
|
||||
|
|
Loading…
Reference in a new issue