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) { |         void updateData(final TableItem tableItem) { | ||||||
|             tableItem.setText(0, getConnectionIdentifer()); |             tableItem.setText(0, getConnectionIdentifier()); | ||||||
|             tableItem.setText(1, getConnectionAddress()); |             tableItem.setText(1, getConnectionAddress()); | ||||||
|             tableItem.setText(2, getStatusName()); |             tableItem.setText(2, getStatusName()); | ||||||
|         } |         } | ||||||
|  | @ -533,7 +533,7 @@ public final class ClientConnectionTable { | ||||||
|         public int compareTo(final UpdatableTableItem other) { |         public int compareTo(final UpdatableTableItem other) { | ||||||
|             return Comparator.comparingInt(UpdatableTableItem::statusWeight) |             return Comparator.comparingInt(UpdatableTableItem::statusWeight) | ||||||
|                     .thenComparingInt(UpdatableTableItem::thresholdsWeight) |                     .thenComparingInt(UpdatableTableItem::thresholdsWeight) | ||||||
|                     .thenComparing(UpdatableTableItem::getConnectionIdentifer) |                     .thenComparing(UpdatableTableItem::getConnectionIdentifier) | ||||||
|                     .compare(this, other); |                     .compare(this, other); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -580,7 +580,7 @@ public final class ClientConnectionTable { | ||||||
|             return Constants.EMPTY_NOTE; |             return Constants.EMPTY_NOTE; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         String getConnectionIdentifer() { |         String getConnectionIdentifier() { | ||||||
|             if (this.connectionData != null && this.connectionData.clientConnection.userSessionId != null) { |             if (this.connectionData != null && this.connectionData.clientConnection.userSessionId != null) { | ||||||
|                 return this.connectionData.clientConnection.userSessionId; |                 return this.connectionData.clientConnection.userSessionId; | ||||||
|             } |             } | ||||||
|  | @ -608,10 +608,7 @@ public final class ClientConnectionTable { | ||||||
|                 final IndicatorData indicatorData = |                 final IndicatorData indicatorData = | ||||||
|                         ClientConnectionTable.this.indicatorMapping.get(indicatorValue.getType()); |                         ClientConnectionTable.this.indicatorMapping.get(indicatorValue.getType()); | ||||||
| 
 | 
 | ||||||
|                 if (indicatorData == null) { |                 if (indicatorData != null) { | ||||||
|                     log.error("No IndicatorData of type: {} found", indicatorValue.getType()); |  | ||||||
|                 } else { |  | ||||||
| 
 |  | ||||||
|                     final double value = indicatorValue.getValue(); |                     final double value = indicatorValue.getValue(); | ||||||
|                     final int indicatorWeight = IndicatorData.getWeight(indicatorData, value); |                     final int indicatorWeight = IndicatorData.getWeight(indicatorData, value); | ||||||
|                     if (this.indicatorWeights[indicatorData.index] != indicatorWeight) { |                     if (this.indicatorWeights[indicatorData.index] != indicatorWeight) { | ||||||
|  |  | ||||||
|  | @ -57,13 +57,6 @@ public class WebserviceInit implements ApplicationListener<ApplicationReadyEvent | ||||||
| 
 | 
 | ||||||
|         SEBServerInit.INIT_LOGGER.info("---->  **** Webservice starting up... ****"); |         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("----> "); | ||||||
|         SEBServerInit.INIT_LOGGER.info("----> Intitialize Services..."); |         SEBServerInit.INIT_LOGGER.info("----> Intitialize Services..."); | ||||||
|         SEBServerInit.INIT_LOGGER.info("----> "); |         SEBServerInit.INIT_LOGGER.info("----> "); | ||||||
|  |  | ||||||
|  | @ -60,10 +60,10 @@ public interface ClientConfigService { | ||||||
|             unless = "#result.hasError()") |             unless = "#result.hasError()") | ||||||
|     Result<ClientDetails> getClientConfigDetails(String clientName); |     Result<ClientDetails> getClientConfigDetails(String clientName); | ||||||
| 
 | 
 | ||||||
|     @CacheEvict( |     /** Internally used to check OAuth2 access for a active SebClientConfig. | ||||||
|             cacheNames = EXAM_CLIENT_DETAILS_CACHE, |      * | ||||||
|             allEntries = true) |      * @param config the SebClientConfig to check access | ||||||
|     @EventListener(BulkActionEvent.class) |      * @return true if the system was able to gain an access token for the client. False otherwise | ||||||
|     void flushClientConfigData(BulkActionEvent event); |      */ | ||||||
| 
 |     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.WebSecurityConfig; | ||||||
| import ch.ethz.seb.sebserver.gbl.Constants; | import ch.ethz.seb.sebserver.gbl.Constants; | ||||||
| import ch.ethz.seb.sebserver.gbl.api.API.BulkActionType; | import ch.ethz.seb.sebserver.gbl.api.API; | ||||||
| import ch.ethz.seb.sebserver.gbl.api.EntityType; |  | ||||||
| import ch.ethz.seb.sebserver.gbl.model.EntityKey; | import ch.ethz.seb.sebserver.gbl.model.EntityKey; | ||||||
| import ch.ethz.seb.sebserver.gbl.model.institution.Institution; | import ch.ethz.seb.sebserver.gbl.model.institution.Institution; | ||||||
| import ch.ethz.seb.sebserver.gbl.model.sebconfig.SebClientConfig; | 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.Result; | ||||||
| import ch.ethz.seb.sebserver.gbl.util.Utils; | import ch.ethz.seb.sebserver.gbl.util.Utils; | ||||||
| import ch.ethz.seb.sebserver.webservice.WebserviceInfo; | 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.ClientCredentialService; | ||||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.client.ClientCredentials; | import ch.ethz.seb.sebserver.webservice.servicelayer.client.ClientCredentials; | ||||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.dao.InstitutionDAO; | import ch.ethz.seb.sebserver.webservice.servicelayer.dao.InstitutionDAO; | ||||||
|  | @ -38,12 +35,21 @@ import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| import org.springframework.beans.factory.annotation.Qualifier; | import org.springframework.beans.factory.annotation.Qualifier; | ||||||
| import org.springframework.context.annotation.Lazy; | 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.crypto.password.PasswordEncoder; | ||||||
| import org.springframework.security.oauth2.common.OAuth2AccessToken; | import org.springframework.security.oauth2.common.OAuth2AccessToken; | ||||||
| import org.springframework.security.oauth2.provider.ClientDetails; | import org.springframework.security.oauth2.provider.ClientDetails; | ||||||
| import org.springframework.security.oauth2.provider.client.BaseClientDetails; | import org.springframework.security.oauth2.provider.client.BaseClientDetails; | ||||||
| import org.springframework.security.oauth2.provider.token.TokenStore; | import org.springframework.security.oauth2.provider.token.TokenStore; | ||||||
| import org.springframework.stereotype.Service; | 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.IOException; | ||||||
| import java.io.InputStream; | import java.io.InputStream; | ||||||
|  | @ -51,6 +57,7 @@ import java.io.OutputStream; | ||||||
| import java.io.PipedInputStream; | import java.io.PipedInputStream; | ||||||
| import java.io.PipedOutputStream; | import java.io.PipedOutputStream; | ||||||
| import java.nio.charset.StandardCharsets; | import java.nio.charset.StandardCharsets; | ||||||
|  | import java.util.Base64; | ||||||
| import java.util.Collection; | import java.util.Collection; | ||||||
| import java.util.Collections; | import java.util.Collections; | ||||||
| import java.util.UUID; | import java.util.UUID; | ||||||
|  | @ -316,19 +323,48 @@ public class ClientConfigServiceImpl implements ClientConfigService { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     public void flushClientConfigData(final BulkActionEvent event) { |     public boolean checkAccess(SebClientConfig config) { | ||||||
|         try { |         if(!config.isActive()) { | ||||||
|             final BulkAction bulkAction = event.getBulkAction(); |             return false; | ||||||
| 
 |  | ||||||
|             if (bulkAction.type == BulkActionType.DEACTIVATE || |  | ||||||
|                     bulkAction.type == BulkActionType.HARD_DELETE) { |  | ||||||
| 
 |  | ||||||
|                 bulkAction.extractKeys(EntityType.SEB_CLIENT_CONFIGURATION) |  | ||||||
|                         .forEach(this::flushClientConfigData); |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         } catch (final Exception e) { |         try { | ||||||
|             log.error("Unexpected error while trying to flush ClientConfig data ", e); |             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; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -9,6 +9,7 @@ | ||||||
| package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl; | package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl; | ||||||
| 
 | 
 | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
|  | import java.util.Arrays; | ||||||
| import java.util.Collection; | import java.util.Collection; | ||||||
| import java.util.Collections; | import java.util.Collections; | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  | @ -93,6 +94,17 @@ public class ClientIndicatorFactory { | ||||||
|                 final PingIntervalClientIndicator pingIndicator = this.applicationContext |                 final PingIntervalClientIndicator pingIndicator = this.applicationContext | ||||||
|                         .getBean(PingIntervalClientIndicator.class); |                         .getBean(PingIntervalClientIndicator.class); | ||||||
|                 pingIndicator.hidden = true; |                 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); |                 result.add(pingIndicator); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -38,7 +38,6 @@ public final class PingIntervalClientIndicator extends AbstractPingIndicator { | ||||||
| 
 | 
 | ||||||
|     long pingErrorThreshold; |     long pingErrorThreshold; | ||||||
|     boolean missingPing = false; |     boolean missingPing = false; | ||||||
| 
 |  | ||||||
|     boolean hidden = false; |     boolean hidden = false; | ||||||
| 
 | 
 | ||||||
|     public PingIntervalClientIndicator(final ClientEventExtensionMapper clientEventExtensionMapper) { |     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.RequestMapping; | ||||||
| import org.springframework.web.bind.annotation.RequestMethod; | import org.springframework.web.bind.annotation.RequestMethod; | ||||||
| import org.springframework.web.bind.annotation.RestController; | import org.springframework.web.bind.annotation.RestController; | ||||||
|  | import org.springframework.web.client.RestTemplate; | ||||||
| 
 | 
 | ||||||
| import javax.servlet.ServletOutputStream; | import javax.servlet.ServletOutputStream; | ||||||
| import javax.servlet.http.HttpServletResponse; | import javax.servlet.http.HttpServletResponse; | ||||||
|  | @ -144,6 +145,15 @@ public class SebClientConfigController extends ActivatableEntityController<SebCl | ||||||
|                 .map(this::checkPasswordMatch); |                 .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) { |     private SebClientConfig checkPasswordMatch(final SebClientConfig entity) { | ||||||
|         Collection<APIMessage> errors = new ArrayList<>(); |         Collection<APIMessage> errors = new ArrayList<>(); | ||||||
|         if (entity.hasEncryptionSecret() && !entity.encryptSecret.equals(entity.encryptSecretConfirm)) { |         if (entity.hasEncryptionSecret() && !entity.encryptSecret.equals(entity.encryptSecretConfirm)) { | ||||||
|  |  | ||||||
|  | @ -17,6 +17,7 @@ import org.springframework.security.oauth2.common.OAuth2AccessToken; | ||||||
| import org.springframework.security.oauth2.provider.OAuth2Authentication; | import org.springframework.security.oauth2.provider.OAuth2Authentication; | ||||||
| import org.springframework.security.oauth2.provider.token.DefaultTokenServices; | 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 { | public class DefaultTokenServicesFallback extends DefaultTokenServices { | ||||||
| 
 | 
 | ||||||
|     private static final Logger log = LoggerFactory.getLogger(DefaultTokenServicesFallback.class); |     private static final Logger log = LoggerFactory.getLogger(DefaultTokenServicesFallback.class); | ||||||
|  | @ -29,22 +30,22 @@ public class DefaultTokenServicesFallback extends DefaultTokenServices { | ||||||
|             return super.createAccessToken(authentication); |             return super.createAccessToken(authentication); | ||||||
|         } catch (final DuplicateKeyException e) { |         } catch (final DuplicateKeyException e) { | ||||||
| 
 | 
 | ||||||
|             log.info( |             log.warn( | ||||||
|                     "Catched DuplicateKeyException, try to handle it by trying to get the already stored access token after waited some time"); |                     "Caught DuplicateKeyException, try to handle it by trying to get the already stored access token after waited some time"); | ||||||
| 
 | 
 | ||||||
|             final String clientName = authentication.getName(); |             final String clientName = authentication.getName(); | ||||||
|             if (StringUtils.isNotBlank(clientName)) { |             if (StringUtils.isNotBlank(clientName)) { | ||||||
| 
 | 
 | ||||||
|                 // wait a second... |                 // wait some time... | ||||||
|                 try { |                 try { | ||||||
|                     Thread.sleep(1000); |                     Thread.sleep(500); | ||||||
|                 } catch (final InterruptedException e1) { |                 } catch (final InterruptedException e1) { | ||||||
|                     log.warn("Failed to sleep: {}", e1.getMessage()); |                     log.warn("Failed to sleep: {}", e1.getMessage()); | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 final OAuth2AccessToken accessToken = this.getAccessToken(authentication); |                 final OAuth2AccessToken accessToken = this.getAccessToken(authentication); | ||||||
|                 if (accessToken != null) { |                 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; |                     return accessToken; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  | @ -25,7 +25,7 @@ sebserver.init.adminaccount.gen-on-init=false | ||||||
| sebserver.webservice.distributed=false | sebserver.webservice.distributed=false | ||||||
| sebserver.webservice.http.scheme=http | sebserver.webservice.http.scheme=http | ||||||
| sebserver.webservice.http.external.servername= | sebserver.webservice.http.external.servername= | ||||||
| sebserver.webservice.http.external.port= | sebserver.webservice.http.external.port=${server.port} | ||||||
| sebserver.webservice.http.redirect.gui=/gui | sebserver.webservice.http.redirect.gui=/gui | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 anhefti
						anhefti