fixed missing ping indicator and try to create initial access token for SEB Client Config
This commit is contained in:
parent
66b77f5737
commit
a37ab31ff1
9 changed files with 425 additions and 377 deletions
|
@ -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) {
|
||||
|
|
|
@ -1,113 +1,106 @@
|
|||
/*
|
||||
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package ch.ethz.seb.sebserver.webservice;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import javax.annotation.PreDestroy;
|
||||
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import ch.ethz.seb.sebserver.SEBServerInit;
|
||||
import ch.ethz.seb.sebserver.SEBServerInitEvent;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
|
||||
@Component
|
||||
@WebServiceProfile
|
||||
@Import(DataSourceAutoConfiguration.class)
|
||||
public class WebserviceInit implements ApplicationListener<ApplicationReadyEvent> {
|
||||
|
||||
private final SEBServerInit sebServerInit;
|
||||
private final Environment environment;
|
||||
private final WebserviceInfo webserviceInfo;
|
||||
private final AdminUserInitializer adminUserInitializer;
|
||||
private final ApplicationEventPublisher applicationEventPublisher;
|
||||
|
||||
protected WebserviceInit(
|
||||
final SEBServerInit sebServerInit,
|
||||
final Environment environment,
|
||||
final WebserviceInfo webserviceInfo,
|
||||
final AdminUserInitializer adminUserInitializer,
|
||||
final ApplicationEventPublisher applicationEventPublisher) {
|
||||
|
||||
this.sebServerInit = sebServerInit;
|
||||
this.environment = environment;
|
||||
this.webserviceInfo = webserviceInfo;
|
||||
this.adminUserInitializer = adminUserInitializer;
|
||||
this.applicationEventPublisher = applicationEventPublisher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(final ApplicationReadyEvent event) {
|
||||
|
||||
this.sebServerInit.init();
|
||||
|
||||
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("----> ");
|
||||
|
||||
this.applicationEventPublisher.publishEvent(new SEBServerInitEvent(this));
|
||||
|
||||
SEBServerInit.INIT_LOGGER.info("----> ");
|
||||
SEBServerInit.INIT_LOGGER.info("----> **** Webservice successfully started up! **** ");
|
||||
SEBServerInit.INIT_LOGGER.info("---->");
|
||||
SEBServerInit.INIT_LOGGER.info("----> *** Info:");
|
||||
|
||||
try {
|
||||
SEBServerInit.INIT_LOGGER.info("----> Server address: {}", this.environment.getProperty("server.address"));
|
||||
SEBServerInit.INIT_LOGGER.info("----> Server port: {}", this.environment.getProperty("server.port"));
|
||||
SEBServerInit.INIT_LOGGER.info("---->");
|
||||
SEBServerInit.INIT_LOGGER.info("----> Local-Host address: {}", InetAddress.getLocalHost().getHostAddress());
|
||||
SEBServerInit.INIT_LOGGER.info("----> Local-Host name: {}", InetAddress.getLocalHost().getHostName());
|
||||
SEBServerInit.INIT_LOGGER.info("---->");
|
||||
SEBServerInit.INIT_LOGGER.info("----> Remote-Host address: {}",
|
||||
InetAddress.getLoopbackAddress().getHostAddress());
|
||||
SEBServerInit.INIT_LOGGER.info("----> Remote-Host name: {}",
|
||||
InetAddress.getLoopbackAddress().getHostName());
|
||||
} catch (final UnknownHostException e) {
|
||||
SEBServerInit.INIT_LOGGER.error("Unknown Host: ", e);
|
||||
}
|
||||
|
||||
SEBServerInit.INIT_LOGGER.info("---->");
|
||||
SEBServerInit.INIT_LOGGER.info("----> External-Host URL: {}", this.webserviceInfo.getExternalServerURL());
|
||||
SEBServerInit.INIT_LOGGER.info("----> LMS-External-Address-Alias: {}",
|
||||
this.webserviceInfo.getLmsExternalAddressAlias());
|
||||
SEBServerInit.INIT_LOGGER.info("---->");
|
||||
SEBServerInit.INIT_LOGGER.info("----> HTTP Scheme {}", this.webserviceInfo.getHttpScheme());
|
||||
SEBServerInit.INIT_LOGGER.info("---->");
|
||||
SEBServerInit.INIT_LOGGER.info("----> Property Override Test: {}", this.webserviceInfo.getTestProperty());
|
||||
|
||||
// Create an initial admin account if requested and not already in the data-base
|
||||
this.adminUserInitializer.initAdminAccount();
|
||||
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void gracefulShutdown() {
|
||||
SEBServerInit.INIT_LOGGER.info("**** Gracefully Shutdown of SEB Server instance {} ****",
|
||||
this.webserviceInfo.getHostAddress());
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package ch.ethz.seb.sebserver.webservice;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import javax.annotation.PreDestroy;
|
||||
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import ch.ethz.seb.sebserver.SEBServerInit;
|
||||
import ch.ethz.seb.sebserver.SEBServerInitEvent;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
|
||||
@Component
|
||||
@WebServiceProfile
|
||||
@Import(DataSourceAutoConfiguration.class)
|
||||
public class WebserviceInit implements ApplicationListener<ApplicationReadyEvent> {
|
||||
|
||||
private final SEBServerInit sebServerInit;
|
||||
private final Environment environment;
|
||||
private final WebserviceInfo webserviceInfo;
|
||||
private final AdminUserInitializer adminUserInitializer;
|
||||
private final ApplicationEventPublisher applicationEventPublisher;
|
||||
|
||||
protected WebserviceInit(
|
||||
final SEBServerInit sebServerInit,
|
||||
final Environment environment,
|
||||
final WebserviceInfo webserviceInfo,
|
||||
final AdminUserInitializer adminUserInitializer,
|
||||
final ApplicationEventPublisher applicationEventPublisher) {
|
||||
|
||||
this.sebServerInit = sebServerInit;
|
||||
this.environment = environment;
|
||||
this.webserviceInfo = webserviceInfo;
|
||||
this.adminUserInitializer = adminUserInitializer;
|
||||
this.applicationEventPublisher = applicationEventPublisher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(final ApplicationReadyEvent event) {
|
||||
|
||||
this.sebServerInit.init();
|
||||
|
||||
SEBServerInit.INIT_LOGGER.info("----> **** Webservice starting up... ****");
|
||||
|
||||
SEBServerInit.INIT_LOGGER.info("----> ");
|
||||
SEBServerInit.INIT_LOGGER.info("----> Intitialize Services...");
|
||||
SEBServerInit.INIT_LOGGER.info("----> ");
|
||||
|
||||
this.applicationEventPublisher.publishEvent(new SEBServerInitEvent(this));
|
||||
|
||||
SEBServerInit.INIT_LOGGER.info("----> ");
|
||||
SEBServerInit.INIT_LOGGER.info("----> **** Webservice successfully started up! **** ");
|
||||
SEBServerInit.INIT_LOGGER.info("---->");
|
||||
SEBServerInit.INIT_LOGGER.info("----> *** Info:");
|
||||
|
||||
try {
|
||||
SEBServerInit.INIT_LOGGER.info("----> Server address: {}", this.environment.getProperty("server.address"));
|
||||
SEBServerInit.INIT_LOGGER.info("----> Server port: {}", this.environment.getProperty("server.port"));
|
||||
SEBServerInit.INIT_LOGGER.info("---->");
|
||||
SEBServerInit.INIT_LOGGER.info("----> Local-Host address: {}", InetAddress.getLocalHost().getHostAddress());
|
||||
SEBServerInit.INIT_LOGGER.info("----> Local-Host name: {}", InetAddress.getLocalHost().getHostName());
|
||||
SEBServerInit.INIT_LOGGER.info("---->");
|
||||
SEBServerInit.INIT_LOGGER.info("----> Remote-Host address: {}",
|
||||
InetAddress.getLoopbackAddress().getHostAddress());
|
||||
SEBServerInit.INIT_LOGGER.info("----> Remote-Host name: {}",
|
||||
InetAddress.getLoopbackAddress().getHostName());
|
||||
} catch (final UnknownHostException e) {
|
||||
SEBServerInit.INIT_LOGGER.error("Unknown Host: ", e);
|
||||
}
|
||||
|
||||
SEBServerInit.INIT_LOGGER.info("---->");
|
||||
SEBServerInit.INIT_LOGGER.info("----> External-Host URL: {}", this.webserviceInfo.getExternalServerURL());
|
||||
SEBServerInit.INIT_LOGGER.info("----> LMS-External-Address-Alias: {}",
|
||||
this.webserviceInfo.getLmsExternalAddressAlias());
|
||||
SEBServerInit.INIT_LOGGER.info("---->");
|
||||
SEBServerInit.INIT_LOGGER.info("----> HTTP Scheme {}", this.webserviceInfo.getHttpScheme());
|
||||
SEBServerInit.INIT_LOGGER.info("---->");
|
||||
SEBServerInit.INIT_LOGGER.info("----> Property Override Test: {}", this.webserviceInfo.getTestProperty());
|
||||
|
||||
// Create an initial admin account if requested and not already in the data-base
|
||||
this.adminUserInitializer.initAdminAccount();
|
||||
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void gracefulShutdown() {
|
||||
SEBServerInit.INIT_LOGGER.info("**** Gracefully Shutdown of SEB Server instance {} ****",
|
||||
this.webserviceInfo.getHostAddress());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
public boolean checkAccess(SebClientConfig config) {
|
||||
if(!config.isActive()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
final BulkAction bulkAction = event.getBulkAction();
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
String externalServerURL = webserviceInfo.getExternalServerURL() +
|
||||
API.OAUTH_TOKEN_ENDPOINT;
|
||||
|
||||
if (bulkAction.type == BulkActionType.DEACTIVATE ||
|
||||
bulkAction.type == BulkActionType.HARD_DELETE) {
|
||||
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());
|
||||
|
||||
bulkAction.extractKeys(EntityType.SEB_CLIENT_CONFIGURATION)
|
||||
.forEach(this::flushClientConfigData);
|
||||
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 (final Exception e) {
|
||||
log.error("Unexpected error while trying to flush ClientConfig data ", e);
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to check access for SebClientConfig: {} cause: {}", config, e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,109 +1,121 @@
|
|||
/*
|
||||
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.IndicatorDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ClientIndicator;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@WebServiceProfile
|
||||
public class ClientIndicatorFactory {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ClientIndicatorFactory.class);
|
||||
|
||||
private final ApplicationContext applicationContext;
|
||||
private final IndicatorDAO indicatorDAO;
|
||||
private final boolean enableCaching;
|
||||
|
||||
@Autowired
|
||||
public ClientIndicatorFactory(
|
||||
final ApplicationContext applicationContext,
|
||||
final IndicatorDAO indicatorDAO,
|
||||
@Value("${sebserver.webservice.api.exam.enable-indicator-cache:true}") final boolean enableCaching) {
|
||||
|
||||
this.applicationContext = applicationContext;
|
||||
this.indicatorDAO = indicatorDAO;
|
||||
this.enableCaching = enableCaching;
|
||||
}
|
||||
|
||||
public List<ClientIndicator> createFor(final ClientConnection clientConnection) {
|
||||
final List<ClientIndicator> result = new ArrayList<>();
|
||||
|
||||
if (clientConnection.examId == null) {
|
||||
return result;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
final Collection<Indicator> examIndicators = this.indicatorDAO
|
||||
.allForExam(clientConnection.examId)
|
||||
.getOrThrow();
|
||||
|
||||
boolean pingIndicatorAvailable = false;
|
||||
|
||||
for (final Indicator indicatorDef : examIndicators) {
|
||||
try {
|
||||
|
||||
final ClientIndicator indicator = this.applicationContext
|
||||
.getBean(indicatorDef.type.name(), ClientIndicator.class);
|
||||
|
||||
if (!pingIndicatorAvailable) {
|
||||
pingIndicatorAvailable = indicatorDef.type == IndicatorType.LAST_PING;
|
||||
}
|
||||
|
||||
indicator.init(
|
||||
indicatorDef,
|
||||
clientConnection.id,
|
||||
this.enableCaching);
|
||||
|
||||
result.add(indicator);
|
||||
} catch (final Exception e) {
|
||||
log.warn("No Indicator with type: {} found as registered bean. Ignore this one.", indicatorDef.type,
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
// If there is no ping interval indicator set from the exam, we add a hidden one
|
||||
// to at least create missing ping events and track missing state
|
||||
if (!pingIndicatorAvailable) {
|
||||
final PingIntervalClientIndicator pingIndicator = this.applicationContext
|
||||
.getBean(PingIntervalClientIndicator.class);
|
||||
pingIndicator.hidden = true;
|
||||
result.add(pingIndicator);
|
||||
}
|
||||
|
||||
} catch (final RuntimeException e) {
|
||||
log.error("Failed to create ClientIndicator for ClientConnection: {}", clientConnection);
|
||||
throw e;
|
||||
} catch (final Exception e) {
|
||||
log.error("Failed to create ClientIndicator for ClientConnection: {}", clientConnection);
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(result);
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
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;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.IndicatorDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ClientIndicator;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@WebServiceProfile
|
||||
public class ClientIndicatorFactory {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ClientIndicatorFactory.class);
|
||||
|
||||
private final ApplicationContext applicationContext;
|
||||
private final IndicatorDAO indicatorDAO;
|
||||
private final boolean enableCaching;
|
||||
|
||||
@Autowired
|
||||
public ClientIndicatorFactory(
|
||||
final ApplicationContext applicationContext,
|
||||
final IndicatorDAO indicatorDAO,
|
||||
@Value("${sebserver.webservice.api.exam.enable-indicator-cache:true}") final boolean enableCaching) {
|
||||
|
||||
this.applicationContext = applicationContext;
|
||||
this.indicatorDAO = indicatorDAO;
|
||||
this.enableCaching = enableCaching;
|
||||
}
|
||||
|
||||
public List<ClientIndicator> createFor(final ClientConnection clientConnection) {
|
||||
final List<ClientIndicator> result = new ArrayList<>();
|
||||
|
||||
if (clientConnection.examId == null) {
|
||||
return result;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
final Collection<Indicator> examIndicators = this.indicatorDAO
|
||||
.allForExam(clientConnection.examId)
|
||||
.getOrThrow();
|
||||
|
||||
boolean pingIndicatorAvailable = false;
|
||||
|
||||
for (final Indicator indicatorDef : examIndicators) {
|
||||
try {
|
||||
|
||||
final ClientIndicator indicator = this.applicationContext
|
||||
.getBean(indicatorDef.type.name(), ClientIndicator.class);
|
||||
|
||||
if (!pingIndicatorAvailable) {
|
||||
pingIndicatorAvailable = indicatorDef.type == IndicatorType.LAST_PING;
|
||||
}
|
||||
|
||||
indicator.init(
|
||||
indicatorDef,
|
||||
clientConnection.id,
|
||||
this.enableCaching);
|
||||
|
||||
result.add(indicator);
|
||||
} catch (final Exception e) {
|
||||
log.warn("No Indicator with type: {} found as registered bean. Ignore this one.", indicatorDef.type,
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
// If there is no ping interval indicator set from the exam, we add a hidden one
|
||||
// to at least create missing ping events and track missing state
|
||||
if (!pingIndicatorAvailable) {
|
||||
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);
|
||||
}
|
||||
|
||||
} catch (final RuntimeException e) {
|
||||
log.error("Failed to create ClientIndicator for ClientConnection: {}", clientConnection);
|
||||
throw e;
|
||||
} catch (final Exception e) {
|
||||
log.error("Failed to create ClientIndicator for ClientConnection: {}", clientConnection);
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(result);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -38,7 +38,6 @@ public final class PingIntervalClientIndicator extends AbstractPingIndicator {
|
|||
|
||||
long pingErrorThreshold;
|
||||
boolean missingPing = false;
|
||||
|
||||
boolean hidden = false;
|
||||
|
||||
public PingIntervalClientIndicator(final ClientEventExtensionMapper clientEventExtensionMapper) {
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -1,58 +1,59 @@
|
|||
/*
|
||||
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package ch.ethz.seb.sebserver.webservice.weblayer.oauth;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.provider.OAuth2Authentication;
|
||||
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
|
||||
|
||||
public class DefaultTokenServicesFallback extends DefaultTokenServices {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(DefaultTokenServicesFallback.class);
|
||||
|
||||
@Override
|
||||
public OAuth2AccessToken createAccessToken(final OAuth2Authentication authentication)
|
||||
throws AuthenticationException {
|
||||
|
||||
try {
|
||||
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");
|
||||
|
||||
final String clientName = authentication.getName();
|
||||
if (StringUtils.isNotBlank(clientName)) {
|
||||
|
||||
// wait a second...
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} 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);
|
||||
return accessToken;
|
||||
}
|
||||
}
|
||||
|
||||
// If no access token is available, propagate the original exception
|
||||
log.error("Unable the handle DuplicateKeyException properly", e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package ch.ethz.seb.sebserver.webservice.weblayer.oauth;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
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);
|
||||
|
||||
@Override
|
||||
public OAuth2AccessToken createAccessToken(final OAuth2Authentication authentication)
|
||||
throws AuthenticationException {
|
||||
|
||||
try {
|
||||
return super.createAccessToken(authentication);
|
||||
} catch (final DuplicateKeyException e) {
|
||||
|
||||
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 some time...
|
||||
try {
|
||||
Thread.sleep(500);
|
||||
} catch (final InterruptedException e1) {
|
||||
log.warn("Failed to sleep: {}", e1.getMessage());
|
||||
}
|
||||
|
||||
final OAuth2AccessToken accessToken = this.getAccessToken(authentication);
|
||||
if (accessToken != null) {
|
||||
log.debug("Found original access token for client: {} ", clientName);
|
||||
return accessToken;
|
||||
}
|
||||
}
|
||||
|
||||
// If no access token is available, propagate the original exception
|
||||
log.error("Unable the handle DuplicateKeyException properly", e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,71 +1,71 @@
|
|||
server.address=localhost
|
||||
server.port=8090
|
||||
|
||||
logging.file=log/sebserver.log
|
||||
|
||||
# data source configuration
|
||||
spring.datasource.initialize=true
|
||||
spring.datasource.initialization-mode=always
|
||||
spring.datasource.url=jdbc:mariadb://localhost:3306/SEBServer?createDatabaseIfNotExist=true&verifyServerCertificate=false&useSSL=false&requireSSL=false
|
||||
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
|
||||
spring.flyway.enabled=true
|
||||
spring.flyway.locations=classpath:config/sql/base,classpath:config/sql/dev
|
||||
spring.flyway.baselineOnMigrate=true
|
||||
spring.datasource.hikari.initializationFailTimeout=30000
|
||||
spring.datasource.hikari.connectionTimeout=30000
|
||||
spring.datasource.hikari.idleTimeout=600000
|
||||
spring.datasource.hikari.maxLifetime=1800000
|
||||
|
||||
sebserver.http.client.connect-timeout=15000
|
||||
sebserver.http.client.connection-request-timeout=10000
|
||||
sebserver.http.client.read-timeout=20000
|
||||
|
||||
# webservice configuration
|
||||
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.redirect.gui=/gui
|
||||
|
||||
|
||||
sebserver.webservice.api.admin.endpoint=/admin-api/v1
|
||||
sebserver.webservice.api.admin.accessTokenValiditySeconds=3600
|
||||
sebserver.webservice.api.admin.refreshTokenValiditySeconds=-1
|
||||
sebserver.webservice.api.exam.config.init.permittedProcesses=config/initialPermittedProcesses.xml
|
||||
sebserver.webservice.api.exam.config.init.prohibitedProcesses=config/initialProhibitedProcesses.xml
|
||||
sebserver.webservice.api.exam.update-interval=1 * * * * *
|
||||
sebserver.webservice.api.exam.time-prefix=0
|
||||
sebserver.webservice.api.exam.time-suffix=0
|
||||
sebserver.webservice.api.exam.endpoint=/exam-api
|
||||
sebserver.webservice.api.exam.endpoint.discovery=${sebserver.webservice.api.exam.endpoint}/discovery
|
||||
sebserver.webservice.api.exam.endpoint.v1=${sebserver.webservice.api.exam.endpoint}/v1
|
||||
sebserver.webservice.api.exam.accessTokenValiditySeconds=3600
|
||||
sebserver.webservice.api.exam.event-handling-strategy=ASYNC_BATCH_STORE_STRATEGY
|
||||
sebserver.webservice.api.exam.enable-indicator-cache=true
|
||||
sebserver.webservice.api.pagination.maxPageSize=500
|
||||
# comma separated list of known possible OpenEdX API access token request endpoints
|
||||
sebserver.webservice.lms.openedx.api.token.request.paths=/oauth2/access_token
|
||||
sebserver.webservice.lms.moodle.api.token.request.paths=
|
||||
sebserver.webservice.lms.address.alias=lms.mockup.com=lms.address.alias
|
||||
|
||||
# NOTE: This is a temporary work-around for SEB Restriction API within Open edX SEB integration plugin to
|
||||
# apply on load-balanced infrastructure or infrastructure that has several layers of cache.
|
||||
# The reason for this is that the API (Open edX system) internally don't apply a resource-change that is
|
||||
# done within HTTP API call immediately from an outside perspective.
|
||||
# After a resource-change on the API is done, the system toggles between the old and the new resource
|
||||
# while constantly calling GET. This usually happens for about a minute or two then it stabilizes on the new resource
|
||||
#
|
||||
# This may source on load-balancing or internally caching on Open edX side.
|
||||
# To mitigate this effect the SEB Server can be configured to apply a resource-change on the
|
||||
# API several times in a row to flush as match caches and reach as match as possible server instances.
|
||||
#
|
||||
# Since this is a brute-force method to mitigate the problem, this should only be a temporary
|
||||
# work-around until a better solution on Open edX SEB integration side has been found and applied.
|
||||
#sebserver.webservice.lms.openedx.seb.restriction.push-count=10
|
||||
|
||||
# actuator configuration
|
||||
management.server.port=${server.port}
|
||||
management.endpoints.web.base-path=/management
|
||||
management.endpoints.web.exposure.include=logfile,loggers,jolokia
|
||||
server.address=localhost
|
||||
server.port=8090
|
||||
|
||||
logging.file=log/sebserver.log
|
||||
|
||||
# data source configuration
|
||||
spring.datasource.initialize=true
|
||||
spring.datasource.initialization-mode=always
|
||||
spring.datasource.url=jdbc:mariadb://localhost:3306/SEBServer?createDatabaseIfNotExist=true&verifyServerCertificate=false&useSSL=false&requireSSL=false
|
||||
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
|
||||
spring.flyway.enabled=true
|
||||
spring.flyway.locations=classpath:config/sql/base,classpath:config/sql/dev
|
||||
spring.flyway.baselineOnMigrate=true
|
||||
spring.datasource.hikari.initializationFailTimeout=30000
|
||||
spring.datasource.hikari.connectionTimeout=30000
|
||||
spring.datasource.hikari.idleTimeout=600000
|
||||
spring.datasource.hikari.maxLifetime=1800000
|
||||
|
||||
sebserver.http.client.connect-timeout=15000
|
||||
sebserver.http.client.connection-request-timeout=10000
|
||||
sebserver.http.client.read-timeout=20000
|
||||
|
||||
# webservice configuration
|
||||
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=${server.port}
|
||||
sebserver.webservice.http.redirect.gui=/gui
|
||||
|
||||
|
||||
sebserver.webservice.api.admin.endpoint=/admin-api/v1
|
||||
sebserver.webservice.api.admin.accessTokenValiditySeconds=3600
|
||||
sebserver.webservice.api.admin.refreshTokenValiditySeconds=-1
|
||||
sebserver.webservice.api.exam.config.init.permittedProcesses=config/initialPermittedProcesses.xml
|
||||
sebserver.webservice.api.exam.config.init.prohibitedProcesses=config/initialProhibitedProcesses.xml
|
||||
sebserver.webservice.api.exam.update-interval=1 * * * * *
|
||||
sebserver.webservice.api.exam.time-prefix=0
|
||||
sebserver.webservice.api.exam.time-suffix=0
|
||||
sebserver.webservice.api.exam.endpoint=/exam-api
|
||||
sebserver.webservice.api.exam.endpoint.discovery=${sebserver.webservice.api.exam.endpoint}/discovery
|
||||
sebserver.webservice.api.exam.endpoint.v1=${sebserver.webservice.api.exam.endpoint}/v1
|
||||
sebserver.webservice.api.exam.accessTokenValiditySeconds=3600
|
||||
sebserver.webservice.api.exam.event-handling-strategy=ASYNC_BATCH_STORE_STRATEGY
|
||||
sebserver.webservice.api.exam.enable-indicator-cache=true
|
||||
sebserver.webservice.api.pagination.maxPageSize=500
|
||||
# comma separated list of known possible OpenEdX API access token request endpoints
|
||||
sebserver.webservice.lms.openedx.api.token.request.paths=/oauth2/access_token
|
||||
sebserver.webservice.lms.moodle.api.token.request.paths=
|
||||
sebserver.webservice.lms.address.alias=lms.mockup.com=lms.address.alias
|
||||
|
||||
# NOTE: This is a temporary work-around for SEB Restriction API within Open edX SEB integration plugin to
|
||||
# apply on load-balanced infrastructure or infrastructure that has several layers of cache.
|
||||
# The reason for this is that the API (Open edX system) internally don't apply a resource-change that is
|
||||
# done within HTTP API call immediately from an outside perspective.
|
||||
# After a resource-change on the API is done, the system toggles between the old and the new resource
|
||||
# while constantly calling GET. This usually happens for about a minute or two then it stabilizes on the new resource
|
||||
#
|
||||
# This may source on load-balancing or internally caching on Open edX side.
|
||||
# To mitigate this effect the SEB Server can be configured to apply a resource-change on the
|
||||
# API several times in a row to flush as match caches and reach as match as possible server instances.
|
||||
#
|
||||
# Since this is a brute-force method to mitigate the problem, this should only be a temporary
|
||||
# work-around until a better solution on Open edX SEB integration side has been found and applied.
|
||||
#sebserver.webservice.lms.openedx.seb.restriction.push-count=10
|
||||
|
||||
# actuator configuration
|
||||
management.server.port=${server.port}
|
||||
management.endpoints.web.base-path=/management
|
||||
management.endpoints.web.exposure.include=logfile,loggers,jolokia
|
||||
management.endpoints.web.path-mapping.jolokia=jmx
|
Loading…
Reference in a new issue