diff --git a/pom.xml b/pom.xml index bce89616..ddca3e5c 100644 --- a/pom.xml +++ b/pom.xml @@ -262,10 +262,10 @@ - - - - + + + + @@ -303,6 +303,11 @@ jncryptor 1.2.0 + + org.apache.commons + commons-text + 1.8 + diff --git a/src/main/java/ch/ethz/seb/sebserver/ClientHttpRequestFactoryService.java b/src/main/java/ch/ethz/seb/sebserver/ClientHttpRequestFactoryService.java index 8a0d11e2..8cd3071c 100644 --- a/src/main/java/ch/ethz/seb/sebserver/ClientHttpRequestFactoryService.java +++ b/src/main/java/ch/ethz/seb/sebserver/ClientHttpRequestFactoryService.java @@ -183,7 +183,7 @@ public class ClientHttpRequestFactoryService { .toCharArray(); if (password.length < 3) { - log.error("Missing or incorrect trust-store password: " + String.valueOf(password)); + log.error("Missing or incorrect trust-store password"); throw new IllegalArgumentException("Missing or incorrect trust-store password"); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/api/JSONMapper.java b/src/main/java/ch/ethz/seb/sebserver/gbl/api/JSONMapper.java index 3bf31141..3f9d03ba 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/api/JSONMapper.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/api/JSONMapper.java @@ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.gbl.api; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; +import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.joda.JodaModule; @@ -29,6 +30,7 @@ public class JSONMapper extends ObjectMapper { super.configure( com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_WITH_ZONE_ID, false); + super.setSerializationInclusion(Include.NON_NULL); } } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/ClientConnectionData.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/ClientConnectionData.java index 6eae4633..981e6be1 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/ClientConnectionData.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/ClientConnectionData.java @@ -16,8 +16,6 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; -import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType; -import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus; import ch.ethz.seb.sebserver.gbl.util.Utils; public class ClientConnectionData { @@ -26,31 +24,32 @@ public class ClientConnectionData { public final ClientConnection clientConnection; @JsonProperty("indicatorValues") public final List indicatorValues; - @JsonIgnore - public final boolean missingPing; + + public final Boolean missingPing; @JsonCreator - protected ClientConnectionData( + public ClientConnectionData( + @JsonProperty("missingPing") final Boolean missingPing, @JsonProperty("clientConnection") final ClientConnection clientConnection, @JsonProperty("indicatorValues") final Collection indicatorValues) { + this.missingPing = missingPing; this.clientConnection = clientConnection; this.indicatorValues = Utils.immutableListOf(indicatorValues); - this.missingPing = clientConnection.status == ConnectionStatus.ACTIVE && - this.indicatorValues.stream() - .filter(ind -> ind.getType() == IndicatorType.LAST_PING) - .findFirst() - .map(ind -> (long) ind.getValue()) - .orElse(0L) > 5000; } protected ClientConnectionData( - @JsonProperty("clientConnection") final ClientConnection clientConnection, - @JsonProperty("indicatorValues") final List indicatorValues) { + final ClientConnection clientConnection, + final List indicatorValues) { + this.missingPing = null; this.clientConnection = clientConnection; this.indicatorValues = Utils.immutableListOf(indicatorValues); - this.missingPing = false; + } + + @JsonProperty("missingPing") + public Boolean getMissingPing() { + return this.missingPing; } @JsonIgnore diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java b/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java index b8fd9b18..085b16f4 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java @@ -28,6 +28,7 @@ import java.util.stream.Collector; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.text.StringEscapeUtils; import org.eclipse.swt.graphics.RGB; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; @@ -375,6 +376,13 @@ public final class Utils { return builder.toString(); } + public static String escapeHTML_XML_EcmaScript(final String string) { + return StringEscapeUtils.escapeXml11( + StringEscapeUtils.escapeHtml4( + StringEscapeUtils.escapeEcmaScript(string))); + } + + // https://www.owasp.org/index.php/HTTP_Response_Splitting public static String preventResponseSplittingAttack(final String string) { final int xni = string.indexOf('\n'); final int xri = string.indexOf('\r'); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/download/AbstractDownloadServiceHandler.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/download/AbstractDownloadServiceHandler.java index c4c14a8f..593a4029 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/download/AbstractDownloadServiceHandler.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/download/AbstractDownloadServiceHandler.java @@ -61,8 +61,9 @@ public abstract class AbstractDownloadServiceHandler implements DownloadServiceH downloadFileName); } - final String header = - "attachment; filename=\"" + Utils.preventResponseSplittingAttack(downloadFileName) + "\""; + final String header = "attachment; filename=\"" + + Utils.escapeHTML_XML_EcmaScript(Utils.preventResponseSplittingAttack(downloadFileName)) + + "\""; response.setHeader(HttpHeaders.CONTENT_DISPOSITION, header); response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ClientConnectionDataInternal.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ClientConnectionDataInternal.java index e7cf96a4..21540c6d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ClientConnectionDataInternal.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ClientConnectionDataInternal.java @@ -14,6 +14,8 @@ import java.util.Collections; import java.util.EnumMap; import java.util.List; +import com.fasterxml.jackson.annotation.JsonProperty; + import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection; import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData; import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.EventType; @@ -61,4 +63,10 @@ public class ClientConnectionDataInternal extends ClientConnectionData { return this.indicatorMapping.get(eventType); } + @Override + @JsonProperty("missingPing") + public Boolean getMissingPing() { + return this.pingIndicator.missingPing; + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/PingIntervalClientIndicator.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/PingIntervalClientIndicator.java index 3dea0f09..5ca3bbe2 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/PingIntervalClientIndicator.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/PingIntervalClientIndicator.java @@ -36,8 +36,8 @@ public final class PingIntervalClientIndicator extends AbstractPingIndicator { private static final Logger log = LoggerFactory.getLogger(PingIntervalClientIndicator.class); - private long pingErrorThreshold; - private boolean isOnError = false; + long pingErrorThreshold; + boolean missingPing = false; boolean hidden = false; @@ -84,9 +84,9 @@ public final class PingIntervalClientIndicator extends AbstractPingIndicator { public ClientEventRecord updateLogEvent() { final long now = DateTime.now(DateTimeZone.UTC).getMillis(); final long value = now - (long) super.currentValue; - if (this.isOnError) { + if (this.missingPing) { if (this.pingErrorThreshold > value) { - this.isOnError = false; + this.missingPing = false; return new ClientEventRecord( null, this.connectionId, @@ -98,7 +98,7 @@ public final class PingIntervalClientIndicator extends AbstractPingIndicator { } } else { if (this.pingErrorThreshold < value) { - this.isOnError = true; + this.missingPing = true; return new ClientEventRecord( null, this.connectionId, diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SebClientConnectionServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SebClientConnectionServiceImpl.java index bdb98590..61ed446d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SebClientConnectionServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SebClientConnectionServiceImpl.java @@ -13,6 +13,7 @@ import java.util.Objects; import java.util.UUID; import java.util.function.Predicate; +import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -25,6 +26,7 @@ import ch.ethz.seb.sebserver.gbl.model.exam.Exam; import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamType; import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection; import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus; +import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData; import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.util.Result; @@ -385,6 +387,17 @@ public class SebClientConnectionServiceImpl implements SebClientConnectionServic @Override public Result disableConnection(final String connectionToken, final Long institutionId) { return Result.tryCatch(() -> { + final ClientConnectionData connectionData = getExamSessionService() + .getConnectionData(connectionToken) + .getOrThrow(); + + // An active connection can only be disabled if we have a missing ping + if (connectionData.clientConnection.status == ConnectionStatus.ACTIVE && + !BooleanUtils.isTrue(connectionData.getMissingPing())) { + + return connectionData.clientConnection; + } + if (log.isDebugEnabled()) { log.debug("SEB client connection: SEB Server disable attempt for " + "instituion {} " diff --git a/src/test/java/ch/ethz/seb/sebserver/gbl/model/institution/InstitutionTest.java b/src/test/java/ch/ethz/seb/sebserver/gbl/model/institution/InstitutionTest.java index 72444c08..8928cf6e 100644 --- a/src/test/java/ch/ethz/seb/sebserver/gbl/model/institution/InstitutionTest.java +++ b/src/test/java/ch/ethz/seb/sebserver/gbl/model/institution/InstitutionTest.java @@ -62,4 +62,13 @@ public class InstitutionTest { json); } + @Test + public void testNullValues() throws Exception { + final JSONMapper jsonMapper = new JSONMapper(); + + final Institution inst = new Institution(1L, null, "suffix", "logo", "theme", null); + final String jsonString = jsonMapper.writeValueAsString(inst); + assertEquals("{\"id\":1,\"urlSuffix\":\"suffix\",\"logoImage\":\"logo\",\"themeName\":\"theme\"}", jsonString); + } + }