SEBSERV-335 feature complete

This commit is contained in:
anhefti 2022-11-28 16:37:26 +01:00
parent 9d80a94bbf
commit d2d3d3f864
13 changed files with 125 additions and 86 deletions

View file

@ -31,8 +31,6 @@ import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.api.POSTMapper; import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
import ch.ethz.seb.sebserver.gbl.model.Domain.EXAM; import ch.ethz.seb.sebserver.gbl.model.Domain.EXAM;
import ch.ethz.seb.sebserver.gbl.model.GrantEntity; import ch.ethz.seb.sebserver.gbl.model.GrantEntity;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamStatus;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamType;
import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.gbl.util.Utils;
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)

View file

@ -425,6 +425,11 @@ public enum ActionDefinition {
ImageIcon.NO_SHIELD, ImageIcon.NO_SHIELD,
PageStateDefinitionImpl.SECURITY_KEY_EDIT, PageStateDefinitionImpl.SECURITY_KEY_EDIT,
ActionCategory.FORM), ActionCategory.FORM),
EXAM_RELOAD_SECURITY_KEY_VIEW(
new LocTextKey("sebserver.exam.signaturekey.action.edit"),
ImageIcon.SHIELD,
PageStateDefinitionImpl.SECURITY_KEY_EDIT,
ActionCategory.FORM),
EXAM_SECURITY_KEY_SAVE_SETTINGS( EXAM_SECURITY_KEY_SAVE_SETTINGS(
new LocTextKey("sebserver.exam.signaturekey.action.save"), new LocTextKey("sebserver.exam.signaturekey.action.save"),

View file

@ -27,16 +27,18 @@ import ch.ethz.seb.sebserver.gbl.model.institution.AppSignatureKeyInfo;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection; import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
import ch.ethz.seb.sebserver.gui.form.FormBuilder; import ch.ethz.seb.sebserver.gui.form.FormBuilder;
import ch.ethz.seb.sebserver.gui.form.FormHandle; import ch.ethz.seb.sebserver.gui.form.FormHandle;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; 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.ModalInputDialogComposer;
import ch.ethz.seb.sebserver.gui.service.page.PageContext; 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.event.ActionEvent;
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.webservice.api.exam.GetClientConnections; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetClientConnections;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GrantClientConnectionSecurityKey; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.seckey.GrantAppSignatureKey;
import ch.ethz.seb.sebserver.gui.table.ColumnDefinition; import ch.ethz.seb.sebserver.gui.table.ColumnDefinition;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@ -55,6 +57,11 @@ public class AddSecurityKeyGrantPopup {
private static final LocTextKey TITLE_TEXT_FORM_TAG = private static final LocTextKey TITLE_TEXT_FORM_TAG =
new LocTextKey("sebserver.exam.signaturekey.seb.add.tag"); new LocTextKey("sebserver.exam.signaturekey.seb.add.tag");
private static final LocTextKey TABLE_TITLE =
new LocTextKey("sebserver.exam.signaturekey.list.title");
private static final LocTextKey TABLE_TITLE_TOOLTIP =
new LocTextKey("sebserver.exam.signaturekey.list.title" + Constants.TOOLTIP_TEXT_KEY_SUFFIX);
private static final LocTextKey TABLE_COLUMN_NAME = private static final LocTextKey TABLE_COLUMN_NAME =
new LocTextKey("sebserver.exam.signaturekey.list.name"); new LocTextKey("sebserver.exam.signaturekey.list.name");
private static final LocTextKey TABLE_COLUMN_INFO = private static final LocTextKey TABLE_COLUMN_INFO =
@ -77,6 +84,7 @@ public class AddSecurityKeyGrantPopup {
action.pageContext().getParent().getShell(), action.pageContext().getParent().getShell(),
this.pageService.getWidgetFactory()); this.pageService.getWidgetFactory());
dialog.setDialogWidth(800); dialog.setDialogWidth(800);
//dialog.setDialogHeight(600);
final Predicate<FormHandle<?>> applyGrant = formHandle -> applyGrant( final Predicate<FormHandle<?>> applyGrant = formHandle -> applyGrant(
pageContext, pageContext,
@ -144,7 +152,17 @@ public class AddSecurityKeyGrantPopup {
.withQueryParam(API.PARAM_MODEL_ID_LIST, clientConnectionIds) .withQueryParam(API.PARAM_MODEL_ID_LIST, clientConnectionIds)
.call() .call()
.onSuccess(connections -> { .onSuccess(connections -> {
final List<ClientConnection> list = new ArrayList<>();
widgetFactory.addFormSubContextHeader(
formContext.getParent(),
TABLE_TITLE,
TABLE_TITLE_TOOLTIP);
final List<ClientConnection> list = new ArrayList<>(this.pageService
.getRestService()
.getBuilder(GetClientConnections.class)
.withQueryParam(API.PARAM_MODEL_ID_LIST, clientConnectionIds)
.call().getOrThrow());
this.pageService.staticListTableBuilder(list, EntityType.CLIENT_CONNECTION) this.pageService.staticListTableBuilder(list, EntityType.CLIENT_CONNECTION)
.withPaging(10) .withPaging(10)
@ -165,7 +183,8 @@ public class AddSecurityKeyGrantPopup {
TABLE_COLUMN_STATUS, TABLE_COLUMN_STATUS,
row -> this.pageService.getResourceService() row -> this.pageService.getResourceService()
.localizedClientConnectionStatusName(row.getStatus())) .localizedClientConnectionStatusName(row.getStatus()))
.widthProportion(1)); .widthProportion(1))
.compose(formContext);
}); });
@ -184,15 +203,35 @@ public class AddSecurityKeyGrantPopup {
final Long connectioId = appSignatureKeyInfo.connectionIds.keySet().iterator().next(); final Long connectioId = appSignatureKeyInfo.connectionIds.keySet().iterator().next();
return this.pageService final boolean hasValue = this.pageService
.getRestService() .getRestService()
.getBuilder(GrantClientConnectionSecurityKey.class) .getBuilder(GrantAppSignatureKey.class)
.withURIVariable(API.PARAM_PARENT_MODEL_ID, String.valueOf(appSignatureKeyInfo.examId)) .withURIVariable(API.PARAM_PARENT_MODEL_ID, String.valueOf(appSignatureKeyInfo.examId))
.withURIVariable(API.PARAM_MODEL_ID, String.valueOf(connectioId)) .withURIVariable(API.PARAM_MODEL_ID, String.valueOf(connectioId))
.withFormBinding(formHandle.getFormBinding()) .withFormBinding(formHandle.getFormBinding())
.call() .call()
.onError(formHandle::handleError) .onError(error -> {
if (error.getMessage().contains("\"messageCode\":\"1010\"")) {
pageContext.publishInfo(new LocTextKey("sebserver.monitoring.signaturegrant.message.granted"));
} else {
formHandle.handleError(error);
}
})
.hasValue(); .hasValue();
if (hasValue) {
final PageContext reloadContext = pageContext.withEntityKey(pageContext.getParentEntityKey());
final PageAction action = this.pageService.pageActionBuilder(reloadContext)
.newAction(ActionDefinition.EXAM_RELOAD_SECURITY_KEY_VIEW)
.create();
this.pageService.firePageEvent(
new ActionEvent(action),
action.pageContext());
}
return hasValue;
} }
} }

View file

@ -68,8 +68,6 @@ public class ExamSignatureKeyForm implements TemplateComposer {
new LocTextKey("sebserver.exam.signaturekey.keylist.key"); new LocTextKey("sebserver.exam.signaturekey.keylist.key");
private static final LocTextKey APP_SIG_KEY_LIST_NUM_CLIENTS = private static final LocTextKey APP_SIG_KEY_LIST_NUM_CLIENTS =
new LocTextKey("sebserver.exam.signaturekey.keylist.clients"); new LocTextKey("sebserver.exam.signaturekey.keylist.clients");
private static final LocTextKey APP_SIG_KEY_LIST_CLIENT_IDS =
new LocTextKey("sebserver.exam.signaturekey.keylist.clientids");
private static final LocTextKey APP_SIG_KEY_LIST_EMPTY_SELECTION_TEXT_KEY = private static final LocTextKey APP_SIG_KEY_LIST_EMPTY_SELECTION_TEXT_KEY =
new LocTextKey("sebserver.exam.signaturekey.keylist.pleaseSelect"); new LocTextKey("sebserver.exam.signaturekey.keylist.pleaseSelect");
@ -78,7 +76,7 @@ public class ExamSignatureKeyForm implements TemplateComposer {
private static final LocTextKey GRANT_LIST_TITLE_TOOLTIP = private static final LocTextKey GRANT_LIST_TITLE_TOOLTIP =
new LocTextKey("sebserver.exam.signaturekey.grantlist.title" + Constants.TOOLTIP_TEXT_KEY_SUFFIX); new LocTextKey("sebserver.exam.signaturekey.grantlist.title" + Constants.TOOLTIP_TEXT_KEY_SUFFIX);
private static final LocTextKey GRANT_LIST_EMPTY_LIST_TEXT_KEY = private static final LocTextKey GRANT_LIST_EMPTY_LIST_TEXT_KEY =
new LocTextKey("sebserver.exam.signaturekey.grantlist..empty"); new LocTextKey("sebserver.exam.signaturekey.grantlist.empty");
private static final LocTextKey GRANT_LIST_KEY = private static final LocTextKey GRANT_LIST_KEY =
new LocTextKey("sebserver.exam.signaturekey.grantlist.key"); new LocTextKey("sebserver.exam.signaturekey.grantlist.key");
private static final LocTextKey GRANT_LIST_TAG = private static final LocTextKey GRANT_LIST_TAG =

View file

@ -29,7 +29,7 @@ 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.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.webservice.api.session.GrantClientConnectionSecurityKey; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.seckey.GrantAppSignatureKey;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@Lazy @Lazy
@ -131,7 +131,7 @@ public class SignatureKeyGrantPopup {
return this.pageService return this.pageService
.getRestService() .getRestService()
.getBuilder(GrantClientConnectionSecurityKey.class) .getBuilder(GrantAppSignatureKey.class)
.withURIVariable(API.PARAM_PARENT_MODEL_ID, examKey.modelId) .withURIVariable(API.PARAM_PARENT_MODEL_ID, examKey.modelId)
.withURIVariable(API.PARAM_MODEL_ID, connectionKey.modelId) .withURIVariable(API.PARAM_MODEL_ID, connectionKey.modelId)
.withFormBinding(formHandle.getFormBinding()) .withFormBinding(formHandle.getFormBinding())

View file

@ -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.webservice.api.session; package ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.seckey;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
@ -24,9 +24,9 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy @Lazy
@Component @Component
@GuiProfile @GuiProfile
public class GrantClientConnectionSecurityKey extends RestCall<SecurityKey> { public class GrantAppSignatureKey extends RestCall<SecurityKey> {
public GrantClientConnectionSecurityKey() { public GrantAppSignatureKey() {
super(new TypeKey<>( super(new TypeKey<>(
CallType.GET_SINGLE, CallType.GET_SINGLE,
EntityType.SEB_SECURITY_KEY_REGISTRY, EntityType.SEB_SECURITY_KEY_REGISTRY,
@ -34,9 +34,9 @@ public class GrantClientConnectionSecurityKey extends RestCall<SecurityKey> {
}), }),
HttpMethod.POST, HttpMethod.POST,
MediaType.APPLICATION_FORM_URLENCODED, MediaType.APPLICATION_FORM_URLENCODED,
API.EXAM_MONITORING_ENDPOINT + API.EXAM_ADMINISTRATION_ENDPOINT +
API.PARENT_MODEL_ID_VAR_PATH_SEGMENT + API.PARENT_MODEL_ID_VAR_PATH_SEGMENT +
API.EXAM_MONITORING_SIGNATURE_KEY_ENDPOINT + API.EXAM_ADMINISTRATION_SEB_SECURITY_KEY_GRANTS_PATH_SEGMENT +
API.MODEL_ID_VAR_PATH_SEGMENT); API.MODEL_ID_VAR_PATH_SEGMENT);
} }

View file

@ -33,7 +33,7 @@ public class SaveAppSignatureKeySettings extends RestCall<Exam> {
new TypeReference<Exam>() { new TypeReference<Exam>() {
}), }),
HttpMethod.POST, HttpMethod.POST,
MediaType.APPLICATION_JSON, MediaType.APPLICATION_FORM_URLENCODED,
API.EXAM_ADMINISTRATION_ENDPOINT API.EXAM_ADMINISTRATION_ENDPOINT
+ API.PARENT_MODEL_ID_VAR_PATH_SEGMENT + API.PARENT_MODEL_ID_VAR_PATH_SEGMENT
+ API.EXAM_ADMINISTRATION_SEB_SECURITY_KEY_INFO_PATH_SEGMENT); + API.EXAM_ADMINISTRATION_SEB_SECURITY_KEY_INFO_PATH_SEGMENT);

View file

@ -111,6 +111,8 @@ public class ExamAdminServiceImpl implements ExamAdminService {
.error("Failed to store ADDITIONAL_ATTR_STATISTICAL_GRANT_COUNT_THRESHOLD: ", error)); .error("Failed to store ADDITIONAL_ATTR_STATISTICAL_GRANT_COUNT_THRESHOLD: ", error));
} }
this.examDAO.setModified(examId);
}).flatMap(v -> this.examDAO.byPK(examId)); }).flatMap(v -> this.examDAO.byPK(examId));
} }

View file

@ -42,12 +42,6 @@ public interface SecurityKeyService {
* @return Result refer to the list of security key registry entries or to an error when happened */ * @return Result refer to the list of security key registry entries or to an error when happened */
Result<Collection<SecurityKey>> getSecurityKeyEntries(Long institutionId, Long examId, KeyType type); Result<Collection<SecurityKey>> getSecurityKeyEntries(Long institutionId, Long examId, KeyType type);
/** Register a new security key entry in the registry.
*
* @param key The security key data
* @return Result refer to the newly created and stored security key entry or to an error when happened */
Result<SecurityKey> registerSecurityKey(SecurityKey key);
/** Register SEB client connection App-Signature-Key as a new global security key registry entry /** Register SEB client connection App-Signature-Key as a new global security key registry entry
* This is equivalent to make a global grant for specified App-Signature-Key of given SEB client connection. * This is equivalent to make a global grant for specified App-Signature-Key of given SEB client connection.
* *
@ -57,15 +51,14 @@ public interface SecurityKeyService {
* @return Result refer to the newly created security key entry or to an error when happened */ * @return Result refer to the newly created security key entry or to an error when happened */
Result<SecurityKey> registerGlobalAppSignatureKey(Long institutionId, Long connectionId, String tag); Result<SecurityKey> registerGlobalAppSignatureKey(Long institutionId, Long connectionId, String tag);
/** Register SEB client connection App-Signature-Key as a new exam based security key registry entry /** Grants an App-Signature-Key sent by a SEB client and register it within the granted key registry
* This is equivalent to make a exam specific grant for specified App-Signature-Key of given SEB client connection.
* *
* @param institutionId The institution identifier * @param institutionId The institution identifier
* @param examId The exam identifier for the exam based grant * @param examId The exam identifier for the exam based grant
* @param connectionId The client connection identifier * @param connectionId The client connection identifier
* @param tag A Tag for user identification of the grant within the registry * @param tag A Tag for user identification of the grant within the registry
* @return Result refer to the newly created security key entry or to an error when happened */ * @return Result refer to the newly created security key entry or to an error when happened */
Result<SecurityKey> registerExamAppSignatureKey(Long institutionId, Long examId, Long connectionId, String tag); Result<SecurityKey> grantAppSignatureKey(Long institutionId, Long examId, Long connectionId, String tag);
/** Used to apply a SEB client App-signature-Key check for a given App-Signature-Key sent by the SEB. /** Used to apply a SEB client App-signature-Key check for a given App-Signature-Key sent by the SEB.
* Note: This also stores the given App-Signature-Key sent by SEB if not already stored for the SEB connection. * Note: This also stores the given App-Signature-Key sent by SEB if not already stored for the SEB connection.

View file

@ -26,7 +26,9 @@ 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;
import ch.ethz.seb.sebserver.gbl.api.APIMessage.FieldValidationException;
import ch.ethz.seb.sebserver.gbl.api.EntityType; 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.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam; import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
import ch.ethz.seb.sebserver.gbl.model.institution.AppSignatureKeyInfo; import ch.ethz.seb.sebserver.gbl.model.institution.AppSignatureKeyInfo;
@ -121,6 +123,12 @@ public class SecurityKeyServiceImpl implements SecurityKeyService {
final Long connectionId, final Long connectionId,
final String tag) { final String tag) {
if (StringUtils.isEmpty(tag)) {
throw new FieldValidationException(
Domain.SEB_SECURITY_KEY_REGISTRY.ATTR_TAG,
"securityKeyGrant:tag:mandatory");
}
if (log.isDebugEnabled()) { if (log.isDebugEnabled()) {
log.debug("Register app-signature-key global grant. ConnectionId: {} tag: {}", log.debug("Register app-signature-key global grant. ConnectionId: {} tag: {}",
connectionId, connectionId,
@ -138,12 +146,18 @@ public class SecurityKeyServiceImpl implements SecurityKeyService {
} }
@Override @Override
public Result<SecurityKey> registerExamAppSignatureKey( public Result<SecurityKey> grantAppSignatureKey(
final Long institutionId, final Long institutionId,
final Long examId, final Long examId,
final Long connectionId, final Long connectionId,
final String tag) { final String tag) {
if (StringUtils.isEmpty(tag)) {
throw new FieldValidationException(
Domain.SEB_SECURITY_KEY_REGISTRY.ATTR_TAG,
"securityKeyGrant:tag:notNull");
}
if (log.isDebugEnabled()) { if (log.isDebugEnabled()) {
log.debug("Register app-signature-key exam grant. Exam: {} connectionId: {} tag: {}", log.debug("Register app-signature-key exam grant. Exam: {} connectionId: {} tag: {}",
examId, examId,
@ -239,11 +253,11 @@ public class SecurityKeyServiceImpl implements SecurityKeyService {
} }
} }
@Override // @Override
public Result<SecurityKey> registerSecurityKey(final SecurityKey key) { // public Result<SecurityKey> registerSecurityKey(final SecurityKey key) {
return this.encryptInternal(key) // return this.encryptInternal(key)
.flatMap(this.securityKeyRegistryDAO::createNew); // .flatMap(this.securityKeyRegistryDAO::createNew);
} // }
@Override @Override
public Result<EntityKey> deleteSecurityKeyGrant(final Long keyId) { public Result<EntityKey> deleteSecurityKeyGrant(final Long keyId) {
@ -366,7 +380,9 @@ public class SecurityKeyServiceImpl implements SecurityKeyService {
final Long examId, final Long examId,
final String decryptedSignature) { final String decryptedSignature) {
System.out.println("****************** statisticalCheck: " + decryptedSignature); if (log.isDebugEnabled()) {
log.debug("Apply statistical security check update for exam {}", examId);
}
// if there is no exam known yet, no statistical check can be applied // if there is no exam known yet, no statistical check can be applied
if (examId == null) { if (examId == null) {
@ -378,8 +394,7 @@ public class SecurityKeyServiceImpl implements SecurityKeyService {
// TODO if cert encryption is available check if exam has defined cert for decryption // TODO if cert encryption is available check if exam has defined cert for decryption
final Certificate cert = null; final Certificate cert = null;
final int matches = this.clientConnectionDAO final int matches = this.clientConnectionDAO.getConnectionTokens(examId)
.getAllActiveConnectionTokens(examId)
.map(tokens -> tokens.stream() .map(tokens -> tokens.stream()
.map(this.examSessionCacheService::getClientConnection) .map(this.examSessionCacheService::getClientConnection)
.filter(cc -> matchOtherClientConnection(cc.clientConnection, decryptedSignature, cert)) .filter(cc -> matchOtherClientConnection(cc.clientConnection, decryptedSignature, cert))
@ -410,12 +425,20 @@ public class SecurityKeyServiceImpl implements SecurityKeyService {
return false; // NOTE: not supported yet return false; // NOTE: not supported yet
} }
if (cc.status != ConnectionStatus.ACTIVE && cc.status != ConnectionStatus.CLOSED) {
return false;
}
if (log.isDebugEnabled()) {
log.debug("Apply statistical security check update for client connection {}", cc);
}
return Objects.equals( return Objects.equals(
decryptedSignature, decryptedSignature,
decryptStoredSignatureForConnection(cc)); decryptStoredSignatureForConnection(cc));
} catch (final Exception e) { } catch (final Exception e) {
log.warn("Failed to get and decrypt app signature key for client connection: {}", cc, e); log.warn("Failed to apply statistical security check update for client connection: {}", cc, e);
return false; return false;
} }
} }
@ -501,16 +524,16 @@ public class SecurityKeyServiceImpl implements SecurityKeyService {
.getOr(1); .getOr(1);
} }
private Result<SecurityKey> encryptInternal(final SecurityKey key) { // private Result<SecurityKey> encryptInternal(final SecurityKey key) {
return Result.tryCatch(() -> new SecurityKey( // return Result.tryCatch(() -> new SecurityKey(
key.id, // key.id,
key.institutionId, // key.institutionId,
key.keyType, // key.keyType,
Utils.toString(this.cryptor.encrypt(key.key).getOrThrow()), // Utils.toString(this.cryptor.encrypt(key.key).getOrThrow()),
key.tag, // key.tag,
key.examId, // key.examId,
key.examTemplateId)); // key.examTemplateId));
} // }
private Collection<SecurityKey> getKeysForRead(final Collection<SecurityKey> keys) { private Collection<SecurityKey> getKeysForRead(final Collection<SecurityKey> keys) {
return keys.stream() return keys.stream()

View file

@ -17,14 +17,12 @@ import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid; import javax.validation.Valid;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.mybatis.dynamic.sql.SqlTable; import org.mybatis.dynamic.sql.SqlTable;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
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.PathVariable;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
@ -216,7 +214,7 @@ public class ExamAdministrationController extends EntityController<Exam, Exam> {
path = API.PARENT_MODEL_ID_VAR_PATH_SEGMENT path = API.PARENT_MODEL_ID_VAR_PATH_SEGMENT
+ API.EXAM_ADMINISTRATION_SEB_SECURITY_KEY_INFO_PATH_SEGMENT, + API.EXAM_ADMINISTRATION_SEB_SECURITY_KEY_INFO_PATH_SEGMENT,
method = RequestMethod.POST, method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE) consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public void saveAppSignatureKeySettings( public void saveAppSignatureKeySettings(
@PathVariable(name = API.PARAM_PARENT_MODEL_ID, required = true) final Long examId, @PathVariable(name = API.PARAM_PARENT_MODEL_ID, required = true) final Long examId,
@RequestParam( @RequestParam(
@ -258,28 +256,32 @@ public class ExamAdministrationController extends EntityController<Exam, Exam> {
} }
@RequestMapping( @RequestMapping(
path = API.PARENT_MODEL_ID_VAR_PATH_SEGMENT path = API.PARENT_MODEL_ID_VAR_PATH_SEGMENT +
+ API.EXAM_ADMINISTRATION_SEB_SECURITY_KEY_GRANTS_PATH_SEGMENT, API.EXAM_ADMINISTRATION_SEB_SECURITY_KEY_GRANTS_PATH_SEGMENT +
API.MODEL_ID_VAR_PATH_SEGMENT,
method = RequestMethod.POST, method = RequestMethod.POST,
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE) produces = MediaType.APPLICATION_JSON_VALUE)
public SecurityKey newSecurityGrant( public SecurityKey grantAppSignatureKey(
@PathVariable(name = API.PARAM_PARENT_MODEL_ID, required = true) final Long examId,
@RequestParam( @RequestParam(
name = API.PARAM_INSTITUTION_ID, name = API.PARAM_INSTITUTION_ID,
required = true, required = true,
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId, defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
@RequestParam final MultiValueMap<String, String> allRequestParams, @PathVariable(name = API.PARAM_PARENT_MODEL_ID, required = true) final Long examId,
final HttpServletRequest request) { @PathVariable(name = API.PARAM_MODEL_ID, required = true) final Long connectionId,
@RequestParam(name = Domain.SEB_SECURITY_KEY_REGISTRY.ATTR_TAG, required = false) final String tagName) {
this.checkWritePrivilege(institutionId); this.checkWritePrivilege(institutionId);
return this.examDAO.byPK(examId) return this.examDAO.byPK(examId)
.flatMap(this::checkReadAccess) .flatMap(this::checkReadAccess)
.flatMap(exam -> { .flatMap(exam -> this.securityKeyService.grantAppSignatureKey(
final POSTMapper postMap = new POSTMapper(allRequestParams, request.getQueryString()) institutionId,
.putIfAbsent(API.PARAM_INSTITUTION_ID, String.valueOf(institutionId)); examId,
return this.securityKeyService.registerSecurityKey(new SecurityKey(postMap)); connectionId,
}) tagName))
.flatMap(this.userActivityLogDAO::logCreate) .flatMap(this.userActivityLogDAO::logCreate)
.onSuccess(key -> this.securityKeyService.updateAppSignatureKeyGrants(examId))
.getOrThrow(); .getOrThrow();
} }

View file

@ -462,30 +462,6 @@ public class ExamMonitoringController {
} }
} }
@RequestMapping(
path = API.PARENT_MODEL_ID_VAR_PATH_SEGMENT +
API.EXAM_MONITORING_SIGNATURE_KEY_ENDPOINT +
API.MODEL_ID_VAR_PATH_SEGMENT,
method = RequestMethod.POST,
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
public SecurityKey grantAppSignatureKey(
@RequestParam(
name = API.PARAM_INSTITUTION_ID,
required = true,
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
@PathVariable(name = API.PARAM_PARENT_MODEL_ID, required = true) final Long examId,
@PathVariable(name = API.PARAM_MODEL_ID, required = true) final Long connectionId,
@RequestParam(name = Domain.SEB_SECURITY_KEY_REGISTRY.ATTR_TAG, required = true) final String tagName) {
checkPrivileges(institutionId, examId);
return this.securityKeyService
.registerExamAppSignatureKey(institutionId, examId, connectionId, tagName)
.onSuccess(key -> this.securityKeyService.updateAppSignatureKeyGrants(examId))
.getOrThrow();
}
@RequestMapping( @RequestMapping(
path = API.PARENT_MODEL_ID_VAR_PATH_SEGMENT + path = API.PARENT_MODEL_ID_VAR_PATH_SEGMENT +
API.EXAM_MONITORING_SIGNATURE_KEY_ENDPOINT + API.EXAM_MONITORING_SIGNATURE_KEY_ENDPOINT +

View file

@ -819,10 +819,13 @@ sebserver.exam.signaturekey.keylist.clientids.tooltip=List of SEB Client session
sebserver.exam.signaturekey.keylist.pleaseSelect=Please select an App Signature Key from the list. sebserver.exam.signaturekey.keylist.pleaseSelect=Please select an App Signature Key from the list.
sebserver.exam.signaturekey.seb.title=App Signature Key sebserver.exam.signaturekey.seb.title=App Signature Key
sebserver.exam.signaturekey.seb.add.title=Grant App Signature Key
sebserver.exam.signaturekey.seb.add.info=Please set a meaningful Tag Name and use OK to confirm this security key as granted. sebserver.exam.signaturekey.seb.add.info=Please set a meaningful Tag Name and use OK to confirm this security key as granted.
sebserver.exam.signaturekey.seb.add.signature=Key Hash sebserver.exam.signaturekey.seb.add.signature=Key Hash
sebserver.exam.signaturekey.seb.add.tag=Tag Name sebserver.exam.signaturekey.seb.add.tag=Tag Name
sebserver.exam.signaturekey.list.title=SEB client connections
sebserver.exam.signaturekey.list.title.tooltip=List of SEB client connections with this App Signature Key
sebserver.exam.signaturekey.list.name=SEB Session ID sebserver.exam.signaturekey.list.name=SEB Session ID
sebserver.exam.signaturekey.list.info=SEB Client Info sebserver.exam.signaturekey.list.info=SEB Client Info
sebserver.exam.signaturekey.list.status=Connection Status sebserver.exam.signaturekey.list.status=Connection Status