SEBSERV-473 fixed list and sorting

This commit is contained in:
anhefti 2023-11-21 15:08:57 +01:00
parent 73c0c8627c
commit 6be552b8d3
14 changed files with 73 additions and 68 deletions

View file

@ -173,10 +173,10 @@ public class UserActivityLogs implements TemplateComposer {
}
});
final Consumer<Boolean> deleteActionActivation = this.pageService.getActionActiviationPublisher(
final Consumer<Boolean> deleteActionActivation = this.pageService.getActionActivationPublisher(
pageContext,
ActionDefinition.LOGS_USER_ACTIVITY_DELETE_ALL);
final Consumer<Boolean> detailsActionActivation = this.pageService.getActionActiviationPublisher(
final Consumer<Boolean> detailsActionActivation = this.pageService.getActionActivationPublisher(
pageContext,
ActionDefinition.LOGS_USER_ACTIVITY_SHOW_DETAILS);
final Consumer<Integer> contentChangeListener = contentSize -> {

View file

@ -14,6 +14,7 @@ import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.swt.widgets.Composite;
import org.springframework.context.annotation.Lazy;
@ -173,6 +174,7 @@ public class AddSecurityKeyGrantPopup {
.getBuilder(GetClientConnections.class)
.withQueryParam(API.PARAM_MODEL_ID_LIST, clientConnectionIds)
.call().getOrThrow());
list.sort((cc1, cc2) -> ObjectUtils.compare(cc1.userSessionId, cc2.userSessionId));
this.pageService.staticListTableBuilder(list, EntityType.CLIENT_CONNECTION)
.withPaging(10)

View file

@ -63,7 +63,7 @@ public class SecurityKeyGrantPopup {
return action;
}
private final class PopupComposer {
private static final class PopupComposer {
private final PageService pageService;
private final SecurityKey securityKey;

View file

@ -150,10 +150,10 @@ public class SEBClientEvents implements TemplateComposer {
final boolean writeGrant = this.pageService.getCurrentUser()
.hasInstitutionalPrivilege(PrivilegeType.WRITE, EntityType.CLIENT_EVENT);
final Consumer<Boolean> deleteActionActivation = this.pageService.getActionActiviationPublisher(
final Consumer<Boolean> deleteActionActivation = this.pageService.getActionActivationPublisher(
pageContext,
ActionDefinition.LOGS_SEB_CLIENT_DELETE_ALL);
final Consumer<Boolean> detailsActionActivation = this.pageService.getActionActiviationPublisher(
final Consumer<Boolean> detailsActionActivation = this.pageService.getActionActivationPublisher(
pageContext,
ActionDefinition.LOGS_SEB_CLIENT_SHOW_DETAILS);
final Consumer<Integer> contentChangeListener = contentSize -> {

View file

@ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.gui.service.page;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.function.BooleanSupplier;
import java.util.function.Consumer;
import java.util.function.Function;
@ -138,7 +139,7 @@ public interface PageService {
* @param noSelectionText LocTextKey for missing selection message
* @param testBeforeActivation a function to test before activation. This function shall throw an error if test
* fails.
* My be null if no specific test is needed before activation
* May be null if no specific test is needed before activation
* @return page action execution function for switching the activity */
<T extends Entity & Activatable> Function<PageAction, PageAction> activationToggleActionFunction(
EntityTable<T> table,
@ -161,7 +162,7 @@ public interface PageService {
*
* @param entity the entity instance
* @return a message supplier to notify deactivation dependencies to the user */
<T extends Entity & Activatable> Supplier<LocTextKey> confirmDeactivation(final T entity);
<T extends Entity & Activatable> Supplier<LocTextKey> confirmDeactivation(T entity);
/** Get a message supplier to notify deactivation dependencies to the user for given entity table selection
*
@ -188,17 +189,17 @@ public interface PageService {
*
* @param pageContext the current PageContext
* @param actionDefinitions list of action definitions that activity should be toggled on table selection
* @return the action activation publisher that can be used to control the activity of an certain action */
default Consumer<Boolean> getActionActiviationPublisher(
* @return the action activation publisher that can be used to control the activity of a certain action */
default Consumer<Boolean> getActionActivationPublisher(
final PageContext pageContext,
final ActionDefinition... actionDefinitions) {
return avtivate -> firePageEvent(
new ActionActivationEvent(avtivate, actionDefinitions),
return activate -> firePageEvent(
new ActionActivationEvent(activate, actionDefinitions),
pageContext);
}
/** Use this to get an table selection action publisher that processes the action
/** Use this to get a table selection action publisher that processes the action
* activation on table selection.
*
* @param pageContext the current PageContext
@ -213,10 +214,10 @@ public interface PageService {
pageContext);
}
/** Use this to get an table selection action publisher that processes the action
/** Use this to get a table selection action publisher that processes the action
* activation on table selection.
* </p>
* This additional has the ability to define a entity activity action that is toggles in
* This additional has the ability to define an entity activity action that is toggles in
* case of the selected entity
*
* @param toggle the base entity activity action definition
@ -254,7 +255,7 @@ public interface PageService {
}
/** Publishes a given PageEvent to the current page tree
* This goes through the page-tree and collects all listeners the are listen to
* This goes through the page-tree and collects all listeners they are listen to
* the specified page event type.
*
* @param event the concrete PageEvent instance */
@ -277,23 +278,23 @@ public interface PageService {
/** Publishes a PageAction to the current page. This uses the firePageEvent form
* PageContext of the given PageAction and fires a ActionPublishEvent for the given PageAction
*
* <p>
* All ActionPublishEventListeners that are registered within the current page will
* receive the ActionPublishEvent sent by this.
*
* @param pageAction the PageAction to publish
* @param active indicates whether the action is active or not */
void publishAction(final PageAction pageAction, boolean active);
void publishAction(PageAction pageAction, boolean active);
/** Publishes a PageAction to the current page. This uses the firePageEvent form
* PageContext of the given PageAction and fires a ActionPublishEvent for the given PageAction
*
* <p>
* All ActionPublishEventListeners that are registered within the current page will
* receive the ActionPublishEvent sent by this.
*
* @param pageAction the PageAction to publish
* @param actionConsumer An consumer that gets the actions TreeItem after creation */
void publishAction(final PageAction pageAction, Consumer<TreeItem> actionConsumer);
* @param actionConsumer A consumer that gets the actions TreeItem after creation */
void publishAction(PageAction pageAction, Consumer<TreeItem> actionConsumer);
/** Get a new FormBuilder for the given PageContext
* This FormBuilder uses the standard form grid which has 8 rows (2 title, 5 input and 1 right-space)
@ -307,11 +308,11 @@ public interface PageService {
/** Get a new FormBuilder for the given PageContext and with number of rows.
*
* @param pageContext the PageContext on that the FormBuilder should work
* @param rows the number of rows of the from
* @param rows the number of rows of the form
* @return a FormBuilder instance for the given PageContext and with number of rows */
FormBuilder formBuilder(PageContext pageContext, int rows);
/** Get an new TableBuilder for specified page based RestCall.
/** Get a new TableBuilder for specified page based RestCall.
*
* @param apiCall the SEB Server API RestCall that feeds the table with data
* @param <T> the type of the Entity of the table
@ -320,7 +321,7 @@ public interface PageService {
return entityTableBuilder(this.getRestService().getRestCall(apiCall));
}
/** Get an new TableBuilder for specified page based RestCall.
/** Get a new TableBuilder for specified page based RestCall.
*
* @param apiCall the SEB Server API RestCall that feeds the table with data
* @param <T> the type of the Entity of the table
@ -329,7 +330,7 @@ public interface PageService {
return entityTableBuilder(apiCall.getClass().getSimpleName(), apiCall);
}
/** Get an new TableBuilder for specified page based RestCall.
/** Get a new TableBuilder for specified page based RestCall.
*
* @param name The name of the table to build
* @param apiCall the SEB Server API RestCall that feeds the table with data
@ -339,7 +340,15 @@ public interface PageService {
String name,
RestCall<Page<T>> apiCall);
<T extends ModelIdAware> TableBuilder<T> staticListTableBuilder(final List<T> staticList, EntityType entityType);
default <T extends ModelIdAware> TableBuilder<T> staticListTableBuilder(
final List<T> staticList,
final EntityType entityType) {
return staticListTableBuilder(UUID.randomUUID().toString(), staticList, entityType);
}
<T extends ModelIdAware> TableBuilder<T> staticListTableBuilder(
String name,
List<T> staticList,
EntityType entityType);
<T extends ModelIdAware> TableBuilder<T> remoteListTableBuilder(
RestCall<Collection<T>> apiCall,
@ -353,7 +362,7 @@ public interface PageService {
return new PageActionBuilder(this, pageContext);
}
/** This triggers a logout on the current authorization context to logout the current user
/** This triggers a logout on the current authorization context to log out the current user
* and forward to the login page with showing a successful logout message to the user. */
boolean logout(PageContext pageContext);

View file

@ -370,14 +370,11 @@ public class PageServiceImpl implements PageService {
@Override
public <T extends ModelIdAware> TableBuilder<T> staticListTableBuilder(
final String name,
final List<T> staticList,
final EntityType entityType) {
return new TableBuilder<>(
(entityType != null)
? entityType.name()
: "",
this, staticList, entityType);
return new TableBuilder<>(name,this, staticList, entityType);
}
@Override

View file

@ -117,6 +117,11 @@ public class StaticListPageSupplier<T> implements PageSupplier<T> {
if (to >= this.list.size()) {
to = this.list.size();
}
if (from >= to) {
from = 0;
to = Math.min(this.pageSize, this.list.size());
this.pageNumber = 1;
}
final List<T> subList = this.list.subList(from, to);
return new Page<>(numOfPages, this.pageNumber, this.pageSize, this.column, subList);

View file

@ -35,7 +35,7 @@ public interface ClientConnectionDAO extends
/** Get a list of all connection tokens of all connections (no matter what state)
* of an exam.
*
* <p>
* Caches the resulting collection of tokens with the given examId as key
* unless the result has no error.
*
@ -77,7 +77,7 @@ public interface ClientConnectionDAO extends
*
* @param connectionTokens The set of connection tokens to filter
* @return Result refer to all inactive connection tokens from the given set */
Result<Collection<String>> getInactiveConnctionTokens(Set<String> connectionTokens);
Result<Collection<String>> getInactiveConnectionTokens(Set<String> connectionTokens);
/** Get a collection of all client connections records that needs a proctoring room update
* and that are in the status ACTIVE.
@ -151,7 +151,7 @@ public interface ClientConnectionDAO extends
@CacheEvict(
cacheNames = ExamSessionCacheService.CACHE_NAME_ACTIVE_CLIENT_CONNECTION,
key = "#connectionToken")
Result<Void> markScreenProcotringApplied(Long connectionId, String connectionToken);
Result<Void> markScreenProctoringApplied(Long connectionId, String connectionToken);
/** Get a ClientConnection by connection token.
*
@ -159,10 +159,10 @@ public interface ClientConnectionDAO extends
* @return Result refer to the ClientConnection for the specified connection token or to an error if happened */
Result<ClientConnection> byConnectionToken(String connectionToken);
/** Use this to check whether a single ClientConnection is up to date or needs a refresh.
/** Use this to check whether a single ClientConnection is up-to-date or needs a refresh.
*
* @param clientConnection the actual ClientConnection (from the internal cache)
* @return Result refer to true if the given ClientConnection is up to date */
* @return Result refer to true if the given ClientConnection is up-to-date */
Result<Boolean> isUpToDate(ClientConnection clientConnection);
Result<Set<String>> getClientConnectionsOutOfSyc(Long examId, Set<Long> timestamps);
@ -183,7 +183,7 @@ public interface ClientConnectionDAO extends
* @return Result refer to filtered Set of connection tokens or to an error when happened */
Result<Set<String>> filterForInstructionStatus(Long examId, Set<String> connectionToken);
/** Used to get the VDI paired connection if it already exsits.
/** Used to get the VDI paired connection if it already exists.
*
* @param clientName the VDI connection identifier sent by the SEB client on connect
* @return Result refer to the relevant VDI pair connection if exists or to an error if not */
@ -200,27 +200,27 @@ public interface ClientConnectionDAO extends
*
* @param examId the exam identifier
* @return Result refer to a collection of client connection records or to an error when happened */
Result<Collection<ClientConnectionRecord>> getsecurityKeyConnectionRecords(Long examId);
Result<Collection<ClientConnectionRecord>> getSecurityKeyConnectionRecords(Long examId);
/** Get all client connection records that don't have a security access grant yet
* and for specific exam.
*
* @param examId The exam identifier
* @return Result refer to client connection records to the an error when happened */
* @return Result refer to client connection records to an error when happened */
Result<Collection<ClientConnectionRecord>> getAllActiveNotGranted(Long examId);
/** Count all known and matching ASK hashes for a given exam.
*
* @param examId The exam identifier
* @param signatureHash The signature hash the count
* @return Result refer to the signature hash count or to an result when happened */
* @return Result refer to the signature hash count or to result when happened */
Result<Long> countSignatureHashes(Long examId, String signatureHash);
/** Get all client connection records that don't have a SEB client version check yet
* and for specific exam.
*
* @param examId The exam identifier
* @return Result refer to client connection records to the an error when happened */
* @return Result refer to client connection records to the error when happened */
Result<Collection<ClientConnectionRecord>> getAllActiveNoSEBVersionCheck(Long examId);
/** Get all client connection identifiers for an exam.

View file

@ -273,7 +273,7 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO {
@Override
@Transactional(readOnly = true)
public Result<Collection<String>> getInactiveConnctionTokens(final Set<String> connectionTokens) {
public Result<Collection<String>> getInactiveConnectionTokens(final Set<String> connectionTokens) {
return Result.tryCatch(() -> {
if (connectionTokens == null || connectionTokens.isEmpty()) {
return Collections.emptyList();
@ -597,7 +597,7 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO {
}
@Override
public Result<Void> markScreenProcotringApplied(final Long connectionId, final String connectionToken) {
public Result<Void> markScreenProctoringApplied(final Long connectionId, final String connectionToken) {
return Result.tryCatch(() -> {
UpdateDSL.updateWithMapper(
this.clientConnectionRecordMapper::update,
@ -843,15 +843,15 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO {
@Override
@Transactional(readOnly = true)
public Result<Collection<ClientConnectionRecord>> getsecurityKeyConnectionRecords(final Long examId) {
public Result<Collection<ClientConnectionRecord>> getSecurityKeyConnectionRecords(final Long examId) {
return Result.tryCatch(() -> this.clientConnectionRecordMapper
.selectByExample()
.where(
ClientConnectionRecordDynamicSqlSupport.examId,
SqlBuilder.isEqualTo(examId))
.and(
ClientConnectionRecordDynamicSqlSupport.status,
SqlBuilder.isIn(ClientConnection.SECURE_STATES))
// .and(
// ClientConnectionRecordDynamicSqlSupport.status,
// SqlBuilder.isIn(ClientConnection.SECURE_STATES))
.build()
.execute());
}

View file

@ -60,7 +60,7 @@ public interface SecurityKeyService {
* @return Result refer to the newly created security key entry or to an error when happened */
Result<SecurityKey> grantAppSignatureKey(Long institutionId, Long examId, Long connectionId, String tag);
/** Get the hashed App Signature Key value from a encrypted App Signature Key sent by a SEB client.
/** Get the hashed App Signature Key value from an encrypted App Signature Key sent by a SEB client.
* The App Signature Key hash is used for security checks. The plain App Signature Key will never be used nor stored
*
* @param appSignatureKey The encrypted App Signature Key sent by a SEB client
@ -72,8 +72,8 @@ public interface SecurityKeyService {
CharSequence salt);
/** Use this to update an App Signature Key grant for a particular SEB connection. This will
* apply the security check again and mark the connection regarding to the security check.
*
* apply the security check again and mark the connection regarding the security check.
* <p>
* This is used by the internal monitoring update task
*
* @param record The ClientConnectionRecord of the specific SEB client connection */

View file

@ -85,7 +85,7 @@ public class SecurityKeyServiceImpl implements SecurityKeyService {
return Result.tryCatch(() -> {
return this.clientConnectionDAO
.getsecurityKeyConnectionRecords(examId)
.getSecurityKeyConnectionRecords(examId)
.getOrThrow()
.stream()
.reduce(
@ -94,6 +94,7 @@ public class SecurityKeyServiceImpl implements SecurityKeyService {
Utils::<String, Map<Long, String>> mergeMap)
.entrySet()
.stream()
.filter(m -> m.getValue() != null && !m.getValue().isEmpty())
.map(m -> new AppSignatureKeyInfo(institutionId, examId, m.getKey(), m.getValue()))
.collect(Collectors.toList());
});
@ -179,7 +180,7 @@ public class SecurityKeyServiceImpl implements SecurityKeyService {
return Cryptor
.decryptASK(appSignatureKey, connectionToken, salt)
.map(signature -> createSignatureHash(signature));
.map(this::createSignatureHash);
}
@ -218,7 +219,7 @@ public class SecurityKeyServiceImpl implements SecurityKeyService {
final String grantedKeyHash = String.valueOf(key);
this.securityKeyRegistryDAO.delete(keyId).getOrThrow();
this.clientConnectionDAO.getsecurityKeyConnectionRecords(key.examId)
this.clientConnectionDAO.getSecurityKeyConnectionRecords(key.examId)
.getOrThrow()
.stream()
.filter(rec -> ConnectionStatus.ACTIVE.name().equals(rec.getStatus()))
@ -271,18 +272,14 @@ public class SecurityKeyServiceImpl implements SecurityKeyService {
.filter(key -> Utils.isEqualsWithEmptyCheck(String.valueOf(key.key), hashedSignatureKey))
.collect(Collectors.toList());
if (matches == null || matches.isEmpty()) {
if (matches.isEmpty()) {
return numericalCheck(examId, hashedSignatureKey);
} else {
return new SecurityCheckResult(
matches.stream()
.filter(key -> key.examId != null)
.findFirst()
.isPresent(),
.anyMatch(key -> key.examId != null),
matches.stream()
.filter(key -> key.examId == null)
.findFirst()
.isPresent(),
.anyMatch(key -> key.examId == null),
false);
}
});
@ -350,8 +347,7 @@ public class SecurityKeyServiceImpl implements SecurityKeyService {
try {
final MessageDigest hasher = MessageDigest.getInstance(Constants.SHA_256);
hasher.update(Utils.toByteArray(signature));
final String signatureHash = Hex.toHexString(hasher.digest());
return signatureHash;
return Hex.toHexString(hasher.digest());
} catch (final Exception e) {
throw new RuntimeException(e);
}

View file

@ -306,7 +306,7 @@ public class SEBClientInstructionServiceImpl implements SEBClientInstructionServ
synchronized (this.instructions) {
final Result<Collection<String>> result = this.clientConnectionDAO
.getInactiveConnctionTokens(this.instructions.keySet());
.getInactiveConnectionTokens(this.instructions.keySet());
if (result.hasValue()) {
result.get().stream().forEach(token -> this.instructions.remove(token));

View file

@ -317,7 +317,7 @@ public class ScreenProctoringServiceImpl implements ScreenProctoringService {
registerJoinInstruction(ccRecord, spsSessionToken, group, runningExam);
this.clientConnectionDAO
.markScreenProcotringApplied(ccRecord.getId(), ccRecord.getConnectionToken())
.markScreenProctoringApplied(ccRecord.getId(), ccRecord.getConnectionToken())
.getOrThrow();
} catch (final Exception e) {

View file

@ -9,7 +9,6 @@
package ch.ethz.seb.sebserver.webservice.weblayer.api;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@ -30,7 +29,6 @@ import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.API.BulkActionType;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.EntityDependency;
import ch.ethz.seb.sebserver.gbl.model.Page;
import ch.ethz.seb.sebserver.gbl.model.PageSortOrder;
@ -58,8 +56,6 @@ public class ClientConnectionController extends ReadonlyEntityController<ClientC
private final SEBClientSessionService sebClientSessionService;
private static final Set<String> EXT_FILTER = new HashSet<>(Arrays.asList(ClientConnection.FILTER_ATTR_INFO));
protected ClientConnectionController(
final AuthorizationService authorization,
final BulkActionService bulkActionService,