fixed missing ping indicator and try to create initial access token for SEB Client Config

This commit is contained in:
anhefti 2020-03-02 14:01:12 +01:00
parent 66b77f5737
commit a37ab31ff1
9 changed files with 425 additions and 377 deletions

View file

@ -460,7 +460,7 @@ public final class ClientConnectionTable {
}
void updateData(final TableItem tableItem) {
tableItem.setText(0, getConnectionIdentifer());
tableItem.setText(0, getConnectionIdentifier());
tableItem.setText(1, getConnectionAddress());
tableItem.setText(2, getStatusName());
}
@ -533,7 +533,7 @@ public final class ClientConnectionTable {
public int compareTo(final UpdatableTableItem other) {
return Comparator.comparingInt(UpdatableTableItem::statusWeight)
.thenComparingInt(UpdatableTableItem::thresholdsWeight)
.thenComparing(UpdatableTableItem::getConnectionIdentifer)
.thenComparing(UpdatableTableItem::getConnectionIdentifier)
.compare(this, other);
}
@ -580,7 +580,7 @@ public final class ClientConnectionTable {
return Constants.EMPTY_NOTE;
}
String getConnectionIdentifer() {
String getConnectionIdentifier() {
if (this.connectionData != null && this.connectionData.clientConnection.userSessionId != null) {
return this.connectionData.clientConnection.userSessionId;
}
@ -608,10 +608,7 @@ public final class ClientConnectionTable {
final IndicatorData indicatorData =
ClientConnectionTable.this.indicatorMapping.get(indicatorValue.getType());
if (indicatorData == null) {
log.error("No IndicatorData of type: {} found", indicatorValue.getType());
} else {
if (indicatorData != null) {
final double value = indicatorValue.getValue();
final int indicatorWeight = IndicatorData.getWeight(indicatorData, value);
if (this.indicatorWeights[indicatorData.index] != indicatorWeight) {

View file

@ -57,13 +57,6 @@ public class WebserviceInit implements ApplicationListener<ApplicationReadyEvent
SEBServerInit.INIT_LOGGER.info("----> **** Webservice starting up... ****");
SEBServerInit.INIT_LOGGER.info("----> ");
SEBServerInit.INIT_LOGGER.info("----> Init Database with flyway...");
SEBServerInit.INIT_LOGGER.info("----> TODO ");
// TODO integration of Flyway for database initialization and migration: https://flywaydb.org
// see also https://flywaydb.org/getstarted/firststeps/api
SEBServerInit.INIT_LOGGER.info("----> ");
SEBServerInit.INIT_LOGGER.info("----> Intitialize Services...");
SEBServerInit.INIT_LOGGER.info("----> ");

View file

@ -60,10 +60,10 @@ public interface ClientConfigService {
unless = "#result.hasError()")
Result<ClientDetails> getClientConfigDetails(String clientName);
@CacheEvict(
cacheNames = EXAM_CLIENT_DETAILS_CACHE,
allEntries = true)
@EventListener(BulkActionEvent.class)
void flushClientConfigData(BulkActionEvent event);
/** Internally used to check OAuth2 access for a active SebClientConfig.
*
* @param config the SebClientConfig to check access
* @return true if the system was able to gain an access token for the client. False otherwise
*/
boolean checkAccess(SebClientConfig config);
}

View file

@ -10,8 +10,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl;
import ch.ethz.seb.sebserver.WebSecurityConfig;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.API.BulkActionType;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.institution.Institution;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.SebClientConfig;
@ -19,8 +18,6 @@ import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.webservice.WebserviceInfo;
import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.impl.BulkAction;
import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.impl.BulkActionEvent;
import ch.ethz.seb.sebserver.webservice.servicelayer.client.ClientCredentialService;
import ch.ethz.seb.sebserver.webservice.servicelayer.client.ClientCredentials;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.InstitutionDAO;
@ -38,12 +35,21 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import java.io.IOException;
import java.io.InputStream;
@ -51,6 +57,7 @@ import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Collection;
import java.util.Collections;
import java.util.UUID;
@ -316,19 +323,48 @@ public class ClientConfigServiceImpl implements ClientConfigService {
}
@Override
public void flushClientConfigData(final BulkActionEvent event) {
try {
final BulkAction bulkAction = event.getBulkAction();
if (bulkAction.type == BulkActionType.DEACTIVATE ||
bulkAction.type == BulkActionType.HARD_DELETE) {
bulkAction.extractKeys(EntityType.SEB_CLIENT_CONFIGURATION)
.forEach(this::flushClientConfigData);
public boolean checkAccess(SebClientConfig config) {
if(!config.isActive()) {
return false;
}
} catch (final Exception e) {
log.error("Unexpected error while trying to flush ClientConfig data ", e);
try {
RestTemplate restTemplate = new RestTemplate();
String externalServerURL = webserviceInfo.getExternalServerURL() +
API.OAUTH_TOKEN_ENDPOINT;
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
ClientCredentials credentials = sebClientConfigDAO
.getSebClientCredentials(config.getModelId())
.getOrThrow();
CharSequence plainClientSecret = clientCredentialService.getPlainClientSecret(credentials);
String basicAuth = credentials.clientId +
String.valueOf(Constants.COLON) +
plainClientSecret;
String encoded = Base64.getEncoder()
.encodeToString(basicAuth.getBytes());
headers.add(HttpHeaders.AUTHORIZATION, "Basic " + encoded);
HttpEntity<String> entity = new HttpEntity<>(
"grant_type=client_credentials&scope=read write",
headers);
ResponseEntity<String> exchange = restTemplate.exchange(
externalServerURL,
HttpMethod.POST,
entity,
String.class);
if (exchange.getStatusCode().value() == HttpStatus.OK.value()) {
return true;
} else {
log.warn("Failed to check access SebClientConfig {} response: {}", config, exchange.getStatusCode());
return false;
}
} catch (Exception e) {
log.warn("Failed to check access for SebClientConfig: {} cause: {}", config, e.getMessage());
return false;
}
}

View file

@ -9,6 +9,7 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@ -93,6 +94,17 @@ public class ClientIndicatorFactory {
final PingIntervalClientIndicator pingIndicator = this.applicationContext
.getBean(PingIntervalClientIndicator.class);
pingIndicator.hidden = true;
final Indicator indicator = new Indicator(
null,
clientConnection.examId,
"hidden_ping_indicator",
IndicatorType.LAST_PING,
"",
Arrays.asList(new Indicator.Threshold(5000d, "")));
pingIndicator.init(
indicator,
clientConnection.id,
this.enableCaching);
result.add(pingIndicator);
}

View file

@ -38,7 +38,6 @@ public final class PingIntervalClientIndicator extends AbstractPingIndicator {
long pingErrorThreshold;
boolean missingPing = false;
boolean hidden = false;
public PingIntervalClientIndicator(final ClientEventExtensionMapper clientEventExtensionMapper) {

View file

@ -39,6 +39,7 @@ 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.RestController;
import org.springframework.web.client.RestTemplate;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
@ -144,6 +145,15 @@ public class SebClientConfigController extends ActivatableEntityController<SebCl
.map(this::checkPasswordMatch);
}
@Override
protected Result<SebClientConfig> notifySaved(SebClientConfig entity) {
if (entity.isActive()) {
// try to get access token for SEB client
sebClientConfigService.checkAccess(entity);
}
return super.notifySaved(entity);
}
private SebClientConfig checkPasswordMatch(final SebClientConfig entity) {
Collection<APIMessage> errors = new ArrayList<>();
if (entity.hasEncryptionSecret() && !entity.encryptSecret.equals(entity.encryptSecretConfirm)) {

View file

@ -17,6 +17,7 @@ import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
// TODO check if we can apply some caching here to get better performance for SEB client connection attempts
public class DefaultTokenServicesFallback extends DefaultTokenServices {
private static final Logger log = LoggerFactory.getLogger(DefaultTokenServicesFallback.class);
@ -29,22 +30,22 @@ public class DefaultTokenServicesFallback extends DefaultTokenServices {
return super.createAccessToken(authentication);
} catch (final DuplicateKeyException e) {
log.info(
"Catched DuplicateKeyException, try to handle it by trying to get the already stored access token after waited some time");
log.warn(
"Caught DuplicateKeyException, try to handle it by trying to get the already stored access token after waited some time");
final String clientName = authentication.getName();
if (StringUtils.isNotBlank(clientName)) {
// wait a second...
// wait some time...
try {
Thread.sleep(1000);
Thread.sleep(500);
} catch (final InterruptedException e1) {
log.warn("Failed to sleep: {}", e1.getMessage());
}
final OAuth2AccessToken accessToken = this.getAccessToken(authentication);
if (accessToken != null) {
log.info("Found original accees token for client: {} token: {}", clientName, accessToken);
log.debug("Found original access token for client: {} ", clientName);
return accessToken;
}
}

View file

@ -25,7 +25,7 @@ sebserver.init.adminaccount.gen-on-init=false
sebserver.webservice.distributed=false
sebserver.webservice.http.scheme=http
sebserver.webservice.http.external.servername=
sebserver.webservice.http.external.port=
sebserver.webservice.http.external.port=${server.port}
sebserver.webservice.http.redirect.gui=/gui