diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsAPITemplate.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsAPITemplate.java index 35be346a..b97c4d14 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsAPITemplate.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsAPITemplate.java @@ -8,6 +8,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.olat; +import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; @@ -20,10 +21,16 @@ import org.apache.commons.lang3.StringUtils; import org.joda.time.DateTime; import org.springframework.cache.CacheManager; import org.springframework.core.env.Environment; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.security.oauth2.client.OAuth2RestTemplate; +import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails; +import ch.ethz.seb.sebserver.ClientHttpRequestFactoryService; import ch.ethz.seb.sebserver.gbl.api.APIMessage; import ch.ethz.seb.sebserver.gbl.async.AsyncService; +import ch.ethz.seb.sebserver.gbl.client.ClientCredentialService; import ch.ethz.seb.sebserver.gbl.client.ClientCredentials; +import ch.ethz.seb.sebserver.gbl.client.ProxyData; import ch.ethz.seb.sebserver.gbl.model.Domain.LMS_SETUP; import ch.ethz.seb.sebserver.gbl.model.exam.Chapters; import ch.ethz.seb.sebserver.gbl.model.exam.Exam; @@ -45,6 +52,8 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.AbstractCachedCour public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements LmsAPITemplate { // TODO add needed dependencies here + private final ClientHttpRequestFactoryService clientHttpRequestFactoryService; + private final ClientCredentialService clientCredentialService; private final APITemplateDataSupplier apiTemplateDataSupplier; private final Long lmsSetupId; @@ -52,6 +61,8 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm // TODO if you need more dependencies inject them here and set the reference + final ClientHttpRequestFactoryService clientHttpRequestFactoryService, + final ClientCredentialService clientCredentialService, final APITemplateDataSupplier apiTemplateDataSupplier, final AsyncService asyncService, final Environment environment, @@ -59,6 +70,8 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm super(asyncService, environment, cacheManager); + this.clientHttpRequestFactoryService = clientHttpRequestFactoryService; + this.clientCredentialService = clientCredentialService; this.apiTemplateDataSupplier = apiTemplateDataSupplier; this.lmsSetupId = apiTemplateDataSupplier.getLmsSetup().id; } @@ -288,4 +301,34 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm return Result.ofRuntimeError("TODO"); } + // TODO: This is an example of how to create a RestTemplate for the service to access the LMS API + // The example deals with a Http based API that is secured by an OAuth2 client-credential flow. + // You might need some different template, then you have to adapt this code + // To your needs. + private OAuth2RestTemplate createRestTemplate(final String accessTokenRequestPath) throws URISyntaxException { + + final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup(); + final ClientCredentials credentials = this.apiTemplateDataSupplier.getLmsClientCredentials(); + final ProxyData proxyData = this.apiTemplateDataSupplier.getProxyData(); + + final CharSequence plainClientId = credentials.clientId; + final CharSequence plainClientSecret = this.clientCredentialService + .getPlainClientSecret(credentials) + .getOrThrow(); + + final ClientCredentialsResourceDetails details = new ClientCredentialsResourceDetails(); + details.setAccessTokenUri(lmsSetup.lmsApiUrl + accessTokenRequestPath); + details.setClientId(plainClientId.toString()); + details.setClientSecret(plainClientSecret.toString()); + + final ClientHttpRequestFactory clientHttpRequestFactory = this.clientHttpRequestFactoryService + .getClientHttpRequestFactory(proxyData) + .getOrThrow(); + + final OAuth2RestTemplate template = new OAuth2RestTemplate(details); + template.setRequestFactory(clientHttpRequestFactory); + + return template; + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsAPITemplateFactory.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsAPITemplateFactory.java index 05cce45b..cb4b71a9 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsAPITemplateFactory.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsAPITemplateFactory.java @@ -13,7 +13,9 @@ import org.springframework.context.annotation.Lazy; import org.springframework.core.env.Environment; import org.springframework.stereotype.Service; +import ch.ethz.seb.sebserver.ClientHttpRequestFactoryService; import ch.ethz.seb.sebserver.gbl.async.AsyncService; +import ch.ethz.seb.sebserver.gbl.client.ClientCredentialService; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.util.Result; @@ -33,15 +35,21 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplateFactory; * as usual. Just add the additionally needed dependencies used to build a OlatLmsAPITemplate. */ public class OlatLmsAPITemplateFactory implements LmsAPITemplateFactory { + private final ClientHttpRequestFactoryService clientHttpRequestFactoryService; + private final ClientCredentialService clientCredentialService; private final AsyncService asyncService; private final Environment environment; private final CacheManager cacheManager; public OlatLmsAPITemplateFactory( + final ClientHttpRequestFactoryService clientHttpRequestFactoryService, + final ClientCredentialService clientCredentialService, final AsyncService asyncService, final Environment environment, final CacheManager cacheManager) { + this.clientHttpRequestFactoryService = clientHttpRequestFactoryService; + this.clientCredentialService = clientCredentialService; this.asyncService = asyncService; this.environment = environment; this.cacheManager = cacheManager; @@ -56,6 +64,8 @@ public class OlatLmsAPITemplateFactory implements LmsAPITemplateFactory { public Result create(final APITemplateDataSupplier apiTemplateDataSupplier) { return Result.tryCatch(() -> { return new OlatLmsAPITemplate( + this.clientHttpRequestFactoryService, + this.clientCredentialService, apiTemplateDataSupplier, this.asyncService, this.environment, diff --git a/src/main/resources/config/application-dev-ws.properties b/src/main/resources/config/application-dev-ws.properties index f1a12d83..81ac2bc0 100644 --- a/src/main/resources/config/application-dev-ws.properties +++ b/src/main/resources/config/application-dev-ws.properties @@ -51,21 +51,6 @@ 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