diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/Configuration.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/Configuration.java index a0c089c7..41f8cc52 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/Configuration.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/Configuration.java @@ -16,7 +16,6 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; -import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.api.POSTMapper; import ch.ethz.seb.sebserver.gbl.model.Domain; @@ -26,6 +25,8 @@ import ch.ethz.seb.sebserver.gbl.model.GrantEntity; @JsonIgnoreProperties(ignoreUnknown = true) public final class Configuration implements GrantEntity { + public static final String FOLLOW_UP_VERSION_NAME = "[TIP]"; + public static final String FILTER_ATTR_CONFIGURATION_NODE_ID = "configurationNodeId"; public static final String FILTER_ATTR_FROM_DATE = "fromDate"; public static final String FILTER_ATTR_FOLLOWUP = "followup"; @@ -87,7 +88,9 @@ public final class Configuration implements GrantEntity { public String getName() { return (this.version != null) ? this.version - : this.versionDate.toString(Constants.DEFAULT_DISPLAY_DATE_FORMAT); + : (this.followup) + ? FOLLOW_UP_VERSION_NAME + : null; } @Override diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountForm.java index cd5a2d17..a7385340 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountForm.java @@ -39,7 +39,6 @@ import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; 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.TemplateComposer; -import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction; import ch.ethz.seb.sebserver.gui.service.page.impl.PageUtils; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitution; @@ -249,12 +248,15 @@ public class UserAccountForm implements TemplateComposer { .newAction(ActionDefinition.USER_ACCOUNT_SAVE) .withEntityKey(entityKey) .withExec(action -> { - final PageAction saveAction = formHandle.processFormSave(action); - if (ownAccount) { - currentUser.refresh(); - pageContext.forwardToMainPage(); - } - return saveAction; + return formHandle.handleFormPost(formHandle.doAPIPost() + .map(userInfo -> { + if (ownAccount) { + currentUser.refresh(userInfo); + pageContext.forwardToMainPage(); + } + return userInfo; + }), + action); }) .ignoreMoveAwayFromEdit() .publishIf(() -> !readonly) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/auth/CurrentUser.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/auth/CurrentUser.java index 094e2362..61bc8c8f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/auth/CurrentUser.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/auth/CurrentUser.java @@ -28,8 +28,8 @@ import org.springframework.web.context.WebApplicationContext; import ch.ethz.seb.sebserver.gbl.api.API; import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.api.authorization.Privilege; -import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType; import ch.ethz.seb.sebserver.gbl.api.authorization.Privilege.RoleTypeKey; +import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType; import ch.ethz.seb.sebserver.gbl.model.GrantEntity; import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; import ch.ethz.seb.sebserver.gbl.model.user.UserRole; @@ -99,7 +99,8 @@ public class CurrentUser { if (loadPrivileges()) { try { final UserInfo userInfo = get(); - return userInfo.getRoles() + return userInfo + .getRoles() .stream() .map(roleName -> UserRole.valueOf(roleName)) .map(role -> new RoleTypeKey(entityType, role)) @@ -156,8 +157,8 @@ public class CurrentUser { return this.authContext != null && this.authContext.isLoggedIn(); } - public void refresh() { - this.authContext.refreshUser(); + public void refresh(final UserInfo userInfo) { + this.authContext.refreshUser(userInfo); } private void updateContext() { diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/auth/OAuth2AuthorizationContextHolder.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/auth/OAuth2AuthorizationContextHolder.java index 45285c8a..d2c1d54b 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/auth/OAuth2AuthorizationContextHolder.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/auth/OAuth2AuthorizationContextHolder.java @@ -227,16 +227,23 @@ public class OAuth2AuthorizationContextHolder implements AuthorizationContextHol } @Override - public void refreshUser() { + public void refreshUser(final UserInfo userInfo) { // delete the access-token (and refresh-token) on authentication server side this.restTemplate.delete(this.revokeTokenURI); // delete the access-token within the RestTemplate this.restTemplate.getOAuth2ClientContext().setAccessToken(null); + // check if username has changed + if (!userInfo.username.equals(getLoggedInUser().get().username)) { + // Set new username to be able to request new access token + this.resource.setUsername(userInfo.username); + } + // and request new access token this.restTemplate.getAccessToken(); // and reset logged in user by getting actual one from webservice this.loggedInUser = null; - getLoggedInUser(); + getLoggedInUser() + .getOrThrow(); } @Override diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/auth/SEBServerAuthorizationContext.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/auth/SEBServerAuthorizationContext.java index 1efa2c7a..55c60abf 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/auth/SEBServerAuthorizationContext.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/auth/SEBServerAuthorizationContext.java @@ -50,7 +50,7 @@ public interface SEBServerAuthorizationContext { * @return Result of logged in user data or of an error on fail */ Result getLoggedInUser(); - void refreshUser(); + void refreshUser(UserInfo userInfo); /** Returns true if a current logged in user has the specified role. * diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ClientConnectionTable.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ClientConnectionTable.java index 0ec309b3..f15f3559 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ClientConnectionTable.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ClientConnectionTable.java @@ -13,11 +13,14 @@ import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.EnumMap; +import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map.Entry; +import java.util.Set; import java.util.stream.Collectors; +import org.apache.commons.lang3.StringUtils; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.GridData; @@ -74,6 +77,7 @@ public final class ClientConnectionTable { private int tableWidth; private boolean needsSort = false; private LinkedHashMap tableMapping; + private final Set sessionIds; public ClientConnectionTable( final WidgetFactory widgetFactory, @@ -121,6 +125,7 @@ public final class ClientConnectionTable { } this.tableMapping = new LinkedHashMap<>(); + this.sessionIds = new HashSet<>(); this.table.layout(); } @@ -177,7 +182,7 @@ public final class ClientConnectionTable { .forEach(data -> { final UpdatableTableItem tableItem = this.tableMapping.computeIfAbsent( data.getConnectionId(), - userIdentifier -> new UpdatableTableItem(data.getConnectionId())); + connectionId -> new UpdatableTableItem(connectionId)); tableItem.push(data); }); } @@ -243,6 +248,9 @@ public final class ClientConnectionTable { private boolean changed = false; private ClientConnectionData connectionData; private int[] thresholdColorIndices; + private boolean duplicateChecked = false; + private final boolean duplicateMarked = false; + private boolean isDuplicate = false; UpdatableTableItem(final Long connectionId) { this.connectionId = connectionId; @@ -260,6 +268,7 @@ public final class ClientConnectionTable { if (this.connectionData != null) { updateConnectionStatusColor(tableItem); updateIndicatorValues(tableItem); + updateDuplicateColor(tableItem); } } @@ -275,6 +284,14 @@ public final class ClientConnectionTable { ClientConnectionTable.this.statusData.getStatusColor(this.connectionData)); } + void updateDuplicateColor(final TableItem tableItem) { + if (this.isDuplicate && this.duplicateChecked && !this.duplicateMarked) { + tableItem.setBackground(0, ClientConnectionTable.this.statusData.color3); + } else { + tableItem.setBackground(0, null); + } + } + void updateIndicatorValues(final TableItem tableItem) { if (this.connectionData == null) { return; @@ -381,6 +398,15 @@ public final class ClientConnectionTable { } this.connectionData = connectionData; + + if (!this.duplicateChecked && StringUtils.isNotBlank(connectionData.clientConnection.userSessionId)) { + if (ClientConnectionTable.this.sessionIds.contains(connectionData.clientConnection.userSessionId)) { + this.isDuplicate = true; + } else { + ClientConnectionTable.this.sessionIds.add(connectionData.clientConnection.userSessionId); + } + this.duplicateChecked = true; + } } } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/UserActivityLogDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/UserActivityLogDAO.java index 30a2a44a..8521b38b 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/UserActivityLogDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/UserActivityLogDAO.java @@ -29,8 +29,16 @@ public interface UserActivityLogDAO extends * @return Result of the Entity or referring to an Error id happened */ public Result logCreate(E entity); + /** Creates a user activity log entry for SEB Exam Configuration save in history action + * + * @param entity the Entity + * @return Result of the Entity or referring to an Error id happened */ public Result logSaveToHistory(E entity); + /** Creates a user activity log entry for SEB Exam Configuration undoy action + * + * @param entity the Entity + * @return Result of the Entity or referring to an Error id happened */ public Result logUndo(E entity); /** Create a user activity log entry for the current user of activity type IMPORT diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationDAOBatchService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationDAOBatchService.java index 8a82c830..5ddfd06b 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationDAOBatchService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationDAOBatchService.java @@ -212,7 +212,8 @@ class ConfigurationDAOBatchService { oldValRec.getValue())) .forEach(newValRec -> this.batchConfigurationValueRecordMapper.insert(newValRec)); - return newFollowup; + return this.batchConfigurationRecordMapper + .selectByPrimaryKey(configUpdate.getId()); }) .flatMap(ConfigurationDAOImpl::toDomainModel); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserActivityLogDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserActivityLogDAOImpl.java index 4fe8e9a8..6ee8989f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserActivityLogDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserActivityLogDAOImpl.java @@ -90,13 +90,19 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO { @Override @Transactional public Result logSaveToHistory(final E entity) { - return log(UserLogActivityType.MODIFY, entity, "SEB Exam Configuration : Save To History"); + return log( + UserLogActivityType.MODIFY, + entity, + "SEB Exam Configuration : Save To History : " + toMessage(entity)); } @Override @Transactional public Result logUndo(final E entity) { - return log(UserLogActivityType.MODIFY, entity, "SEB Exam Configuration : Undo"); + return log( + UserLogActivityType.MODIFY, + entity, + "SEB Exam Configuration : Undo : " + toMessage(entity)); } @Override diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index ca97e301..0d1b95be 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -41,7 +41,7 @@ sebserver.overall.types.entityType.CONFIGURATION_ATTRIBUTE=Configuration Attribu sebserver.overall.types.entityType.CONFIGURATION_VALUE=Configuration Value sebserver.overall.types.entityType.VIEW=Configuration View sebserver.overall.types.entityType.ORIENTATION=Configuration Orientation -sebserver.overall.types.entityType.CONFIGURATION=Configuration History Point +sebserver.overall.types.entityType.CONFIGURATION=Exam Configuration History sebserver.overall.types.entityType.CONFIGURATION_NODE=Exam Configuration sebserver.overall.types.entityType.EXAM_CONFIGURATION_MAP=Exam Configuration Mapping sebserver.overall.types.entityType.EXAM=Exam diff --git a/src/test/java/ch/ethz/seb/sebserver/HTTPClientBot.java b/src/test/java/ch/ethz/seb/sebserver/HTTPClientBot.java index fa342b6e..e328f6ff 100644 --- a/src/test/java/ch/ethz/seb/sebserver/HTTPClientBot.java +++ b/src/test/java/ch/ethz/seb/sebserver/HTTPClientBot.java @@ -15,6 +15,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Random; import java.util.UUID; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -73,6 +74,8 @@ public class HTTPClientBot { private final long runtime; private final int connectionAttempts; + private final Random random = new Random(); + public HTTPClientBot(final Map args) { this.webserviceAddress = args.getOrDefault("webserviceAddress", "http://localhost:8080"); @@ -90,12 +93,20 @@ public class HTTPClientBot { this.connectionAttempts = Integer.parseInt(args.getOrDefault("connectionAttempts", "1")); for (int i = 0; i < this.numberOfConnections; i++) { - this.executorService.execute(new ConnectionBot("connection_" + i)); + this.executorService.execute(new ConnectionBot("connection_" + getRandomName())); } this.executorService.shutdown(); } + private String getRandomName() { + final StringBuilder sb = new StringBuilder(String.valueOf(this.random.nextInt(100))); + while (sb.length() < 3) { + sb.insert(0, "0"); + } + return sb.toString(); + } + public static void main(final String[] args) { final Map argsMap = new HashMap<>(); if (args.length > 0) {