SEBSERV-417 access token for Moodle

This commit is contained in:
anhefti 2024-04-18 16:12:09 +02:00
parent 0f27ef0f38
commit 6e2feafc5a
23 changed files with 241 additions and 86 deletions

View file

@ -286,7 +286,7 @@ public class WebserviceInfo {
}
public String getOAuthTokenURI() {
return getExternalServerURL() + API.OAUTH_ENDPOINT + API.OAUTH_TOKEN_ENDPOINT;
return getExternalServerURL() + API.OAUTH_TOKEN_ENDPOINT;
}
public boolean isLightSetup() {

View file

@ -53,4 +53,6 @@ public interface LmsSetupDAO extends ActivatableEntityDAO<LmsSetup, LmsSetup>, B
Result<LmsSetup> setIntegrationActive(Long lmsSetupId, boolean active);
Result<Collection<Long>> idsOfActiveWithFullIntegration(Long institutionId);
Result<Collection<Long>> allIdsFullIntegration();
}

View file

@ -133,6 +133,27 @@ public class LmsSetupDAOImpl implements LmsSetupDAO {
.execute());
}
@Override
@Transactional(readOnly = true)
public Result<Collection<Long>> allIdsFullIntegration() {
return Result.tryCatch(() -> {
final List<String> types = Arrays.stream(LmsType.values())
.filter(type -> type.features.contains(LmsSetup.Features.LMS_FULL_INTEGRATION))
.map(LmsType::name)
.collect(Collectors.toList());
return this.lmsSetupRecordMapper.selectIdsByExample()
.where(
LmsSetupRecordDynamicSqlSupport.active,
isEqualTo(1))
.and(
lmsType,
isIn(types))
.build()
.execute();
});
}
@Override
@Transactional(readOnly = true)
public Result<Collection<LmsSetup>> allMatching(

View file

@ -8,11 +8,20 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.lms;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.FullLmsIntegrationService.IntegrationData;
public interface FullLmsIntegrationAPI {
/** Performs a test for the underling {@link LmsSetup } configuration and checks if the
* LMS and the full LMS integration API of the LMS can be accessed or if there are some difficulties,
* missing API functions
*
* @return {@link LmsSetupTestResult } instance with the test result report */
LmsSetupTestResult testFullIntegrationAPI();
Result<IntegrationData> applyConnectionDetails(IntegrationData data);
Result<Void> deleteConnectionDetails();

View file

@ -10,6 +10,8 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.function.Function;
import java.util.stream.Collectors;
@ -61,7 +63,7 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService
private final ExamTemplateDAO examTemplateDAO;
private final WebserviceInfo webserviceInfo;
//private final ClientCredentialsResourceDetails resource;
private final ClientCredentialsResourceDetails resource;
private final OAuth2RestTemplate restTemplate;
@ -83,7 +85,7 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService
this.examTemplateDAO = examTemplateDAO;
this.webserviceInfo = webserviceInfo;
final ClientCredentialsResourceDetails resource = new ClientCredentialsResourceDetails();
resource = new ClientCredentialsResourceDetails();
resource.setAccessTokenUri(webserviceInfo.getOAuthTokenURI());
resource.setClientId(clientId);
resource.setClientSecret(clientSecret);
@ -104,10 +106,18 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService
@Override
public void notifyLmsSetupChange(final LmsSetupChangeEvent event) {
final LmsSetup lmsSetup = event.getLmsSetup();
if (lmsSetup.integrationActive) {
if (!lmsSetup.getLmsType().features.contains(LmsSetup.Features.LMS_FULL_INTEGRATION)) {
return;
}
if (lmsSetup.active) {
applyFullLmsIntegration(lmsSetup.id)
.onError(error -> log.warn("Failed to update LMS integration for: {}", lmsSetup, error))
.onSuccess( data -> log.debug("Successfully updated LMS integration for: {} data: {}", lmsSetup, data));
.onSuccess(data -> log.debug("Successfully updated LMS integration for: {} data: {}", lmsSetup, data));
} else if (lmsSetup.integrationActive) {
deleteFullLmsIntegration(lmsSetup.id)
.onError(error -> log.warn("Failed to delete LMS integration for: {}", lmsSetup, error))
.onSuccess(data -> log.debug("Successfully deleted LMS integration for: {} data: {}", lmsSetup, data));
}
}
@ -144,11 +154,16 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService
throw new IllegalStateException("No LMS Setup connectionId available for: " + lmsSetup);
}
// reset old token to get actual one
resource.setScope(Arrays.asList(String.valueOf(lmsSetupId)));
restTemplate.getOAuth2ClientContext().setAccessToken(null);
final String accessToken = restTemplate.getAccessToken().getValue();
final IntegrationData data = new IntegrationData(
connectionId,
lmsSetup.name,
webserviceInfo.getExternalServerURL(),
restTemplate.getAccessToken().getValue(),
accessToken,
this.getIntegrationTemplates(lmsSetup.institutionId)
);

View file

@ -156,8 +156,17 @@ public class LmsAPIServiceImpl implements LmsAPIService {
final LmsSetupTestResult lmsSetupTestResult = template.testCourseRestrictionAPI();
if (!lmsSetupTestResult.isOk()) {
this.cache.remove(new CacheKey(template.lmsSetup().getModelId(), 0));
return lmsSetupTestResult;
}
}
if (template.lmsSetup().getLmsType().features.contains(LmsSetup.Features.LMS_FULL_INTEGRATION)) {
final LmsSetupTestResult lmsSetupTestResult = template.testFullIntegrationAPI();
if (!lmsSetupTestResult.isOk()) {
this.cache.remove(new CacheKey(template.lmsSetup().getModelId(), 0));
return lmsSetupTestResult;
}
return lmsSetupTestResult;
}
return LmsSetupTestResult.ofOkay(template.lmsSetup().getLmsType());
@ -184,8 +193,19 @@ public class LmsAPIServiceImpl implements LmsAPIService {
return testCourseAccessAPI;
}
if (lmsSetupTemplate.lmsSetup().getLmsType().features.contains(LmsSetup.Features.SEB_RESTRICTION)) {
return lmsSetupTemplate.testCourseRestrictionAPI();
final LmsType lmsType = lmsSetupTemplate.lmsSetup().getLmsType();
if (lmsType.features.contains(LmsSetup.Features.SEB_RESTRICTION)) {
final LmsSetupTestResult lmsSetupTestResult = lmsSetupTemplate.testCourseRestrictionAPI();
if (!lmsSetupTestResult.isOk()) {
return lmsSetupTestResult;
}
}
if (lmsType.features.contains(LmsSetup.Features.LMS_FULL_INTEGRATION)) {
final LmsSetupTestResult lmsSetupTestResult = lmsSetupTemplate.testFullIntegrationAPI();
if (!lmsSetupTestResult.isOk()) {
return lmsSetupTestResult;
}
}
return LmsSetupTestResult.ofOkay(lmsSetupTemplate.lmsSetup().getLmsType());

View file

@ -397,10 +397,6 @@ public class LmsAPITemplateAdapter implements LmsAPITemplate {
return this.sebRestrictionAPI.testCourseRestrictionAPI();
}
if (log.isDebugEnabled()) {
log.debug("Test course restriction API for LMSSetup: {}", lmsSetup());
}
return LmsSetupTestResult.ofAPINotSupported(getType());
}
@ -476,6 +472,16 @@ public class LmsAPITemplateAdapter implements LmsAPITemplate {
return protectedRun;
}
@Override
public LmsSetupTestResult testFullIntegrationAPI() {
if (this.lmsIntegrationAPI != null) {
return this.lmsIntegrationAPI.testFullIntegrationAPI();
}
return LmsSetupTestResult.ofAPINotSupported(getType());
}
@Override
public Result<IntegrationData> applyConnectionDetails(final IntegrationData data) {
if (this.lmsIntegrationAPI == null) {

View file

@ -421,6 +421,11 @@ public class AnsLmsAPITemplate extends AbstractCachedCourseAccess implements Lms
.map(x -> exam);
}
@Override
public LmsSetupTestResult testFullIntegrationAPI() {
return LmsSetupTestResult.ofAPINotSupported(LmsType.ANS_DELFT);
}
@Override
public Result<IntegrationData> applyConnectionDetails(final IntegrationData data) {
return Result.ofRuntimeError("Not Supported");

View file

@ -8,12 +8,18 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.mockup;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.FullLmsIntegrationAPI;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.FullLmsIntegrationService.IntegrationData;
public class MockupFullIntegration implements FullLmsIntegrationAPI {
@Override
public LmsSetupTestResult testFullIntegrationAPI() {
return LmsSetupTestResult.ofAPINotSupported(LmsSetup.LmsType.MOODLE_PLUGIN);
}
@Override
public Result<IntegrationData> applyConnectionDetails(final IntegrationData data) {

View file

@ -32,6 +32,8 @@ public interface MoodleRestTemplateFactory {
* @return Set of known and configured API access token paths */
Set<String> getKnownTokenAccessPaths();
Result<MoodleAPIRestTemplate> getRestTemplate();
/** Creates a MoodleAPIRestTemplate for the bundled LMSSetup of this factory.
*
* @param service The moodle web service name to within requesting an access token for

View file

@ -63,6 +63,8 @@ public class MoodleRestTemplateFactoryImpl implements MoodleRestTemplateFactory
public final ClientCredentialService clientCredentialService;
public final Set<String> knownTokenAccessPaths;
private Result<MoodleAPIRestTemplate> activeRestTemplate = Result.ofRuntimeError("Not Initialized");
public MoodleRestTemplateFactoryImpl(
final JSONMapper jsonMapper,
final APITemplateDataSupplier apiTemplateDataSupplier,
@ -88,6 +90,11 @@ public class MoodleRestTemplateFactoryImpl implements MoodleRestTemplateFactory
return this.knownTokenAccessPaths;
}
@Override
public Result<MoodleAPIRestTemplate> getRestTemplate() {
return activeRestTemplate;
}
@Override
public APITemplateDataSupplier getApiTemplateDataSupplier() {
return this.apiTemplateDataSupplier;
@ -139,13 +146,12 @@ public class MoodleRestTemplateFactoryImpl implements MoodleRestTemplateFactory
public Result<MoodleAPIRestTemplate> createRestTemplate(final String service) {
final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup();
return this.knownTokenAccessPaths
this. activeRestTemplate = this.knownTokenAccessPaths
.stream()
.map(path -> this.createRestTemplate(service, path))
.map(result -> {
if (result.hasError()) {
log.warn("Failed to get access token for LMS: {}({})",
log.warn("Failed to get access token for LMS: {}({}), error {}",
lmsSetup.name,
lmsSetup.id,
result.getError().getMessage());
@ -158,6 +164,10 @@ public class MoodleRestTemplateFactoryImpl implements MoodleRestTemplateFactory
"Failed to gain any access for LMS " +
lmsSetup.name + "(" + lmsSetup.id +
") on paths: " + this.knownTokenAccessPaths));
log.info("Created new MoodleAPIRestTemplate for service: {} factory: {}", service, this.hashCode());
return activeRestTemplate;
}
@Override
@ -199,6 +209,7 @@ public class MoodleRestTemplateFactoryImpl implements MoodleRestTemplateFactory
") on path: " + accessTokenPath);
}
activeRestTemplate = Result.of(restTemplate);
return restTemplate;
});
}
@ -266,7 +277,7 @@ public class MoodleRestTemplateFactoryImpl implements MoodleRestTemplateFactory
WebserviceInfo.class);
if (StringUtils.isBlank(webserviceInfo.username) || StringUtils.isBlank(webserviceInfo.userid)) {
throw new RuntimeException("Invalid WebserviceInfo: " + webserviceInfo);
throw new RuntimeException("Invalid WebserviceInfo Response");
}
if (functions != null) {
@ -281,8 +292,10 @@ public class MoodleRestTemplateFactoryImpl implements MoodleRestTemplateFactory
}
} catch (final RuntimeException re) {
log.warn("Failed to Moodle API access: {}", re.getMessage());
throw re;
} catch (final Exception e) {
log.warn("Failed to Moodle API access: {}", e.getMessage());
throw new RuntimeException("Failed to test Moodle rest API: ", e);
}
}

View file

@ -118,7 +118,7 @@ public abstract class MoodleUtils {
return idNumber.equals(Constants.EMPTY_NOTE) ? null : idNumber;
}
public static final void logMoodleWarning(
public static void logMoodleWarning(
final Collection<Warning> warnings,
final String lmsSetupName,
final String function) {
@ -148,7 +148,7 @@ public abstract class MoodleUtils {
private static final Pattern ACCESS_DENIED_PATTERN_2 =
Pattern.compile(Pattern.quote("access denied"), Pattern.CASE_INSENSITIVE);
public static final boolean checkAccessDeniedError(final String courseKeyPageJSON) {
public static boolean checkAccessDeniedError(final String courseKeyPageJSON) {
return ACCESS_DENIED_PATTERN_1
.matcher(courseKeyPageJSON)
.find() ||

View file

@ -20,6 +20,7 @@ import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.plugin.MooldePluginLmsAPITemplateFactory;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
@ -103,7 +104,6 @@ public class MoodleCourseAccess implements CourseAccessAPI {
private final int pageSize;
private final int maxSize;
private MoodleAPIRestTemplate restTemplate;
public MoodleCourseAccess(
final JSONMapper jsonMapper,
@ -583,17 +583,12 @@ public class MoodleCourseAccess implements CourseAccessAPI {
}
private Result<MoodleAPIRestTemplate> getRestTemplate() {
if (this.restTemplate == null) {
final Result<MoodleAPIRestTemplate> templateRequest = this.restTemplateFactory
.createRestTemplate(MoodleLmsAPITemplateFactory.MOODLE_MOBILE_APP_SERVICE);
if (templateRequest.hasError()) {
return templateRequest;
} else {
this.restTemplate = templateRequest.get();
}
final Result<MoodleAPIRestTemplate> result = this.restTemplateFactory.getRestTemplate();
if (!result.hasError()) {
return result;
}
return Result.of(this.restTemplate);
return this.restTemplateFactory.createRestTemplate(MoodleLmsAPITemplateFactory.MOODLE_MOBILE_APP_SERVICE);
}
private Collection<CourseData> getCoursesPage(

View file

@ -98,8 +98,6 @@ public class MoodlePluginCourseAccess extends AbstractCachedCourseAccess impleme
private final int cutoffTimeOffset;
private final boolean applyNameCriteria;
private MoodleAPIRestTemplate restTemplate;
public MoodlePluginCourseAccess(
final JSONMapper jsonMapper,
final AsyncService asyncService,
@ -595,19 +593,12 @@ public class MoodlePluginCourseAccess extends AbstractCachedCourseAccess impleme
}
private Result<MoodleAPIRestTemplate> getRestTemplate() {
if (this.restTemplate == null) {
final Result<MoodleAPIRestTemplate> templateRequest = this.restTemplateFactory
.createRestTemplate(MooldePluginLmsAPITemplateFactory.SEB_SERVER_SERVICE_NAME);
if (templateRequest.hasError()) {
return templateRequest;
} else {
this.restTemplate = templateRequest.get();
}
final Result<MoodleAPIRestTemplate> result = this.restTemplateFactory.getRestTemplate();
if (!result.hasError()) {
return result;
}
return Result.of(this.restTemplate);
return this.restTemplateFactory.createRestTemplate(MooldePluginLmsAPITemplateFactory.SEB_SERVER_SERVICE_NAME);
}
protected String toTestString() {
@ -619,7 +610,7 @@ public class MoodlePluginCourseAccess extends AbstractCachedCourseAccess impleme
builder.append(", cutoffTimeOffset=");
builder.append(this.cutoffTimeOffset);
builder.append(", restTemplate=");
builder.append(this.restTemplate);
builder.append(this.getRestTemplate().getOr(null));
builder.append("]");
return builder.toString();
}

View file

@ -55,8 +55,6 @@ public class MoodlePluginCourseRestriction implements SEBRestrictionAPI {
private final MoodleRestTemplateFactory restTemplateFactory;
private final ExamConfigurationValueService examConfigurationValueService;
private MoodleAPIRestTemplate restTemplate;
public MoodlePluginCourseRestriction(
final JSONMapper jsonMapper,
final MoodleRestTemplateFactory restTemplateFactory,
@ -90,7 +88,6 @@ public class MoodlePluginCourseRestriction implements SEBRestrictionAPI {
RESTRICTION_SET_FUNCTION_NAME);
} catch (final RuntimeException e) {
log.error("Failed to access Moodle course API: ", e);
return LmsSetupTestResult.ofQuizAccessAPIError(LmsType.MOODLE_PLUGIN, e.getMessage());
}
@ -285,24 +282,18 @@ public class MoodlePluginCourseRestriction implements SEBRestrictionAPI {
}
private Result<MoodleAPIRestTemplate> getRestTemplate() {
if (this.restTemplate == null) {
final Result<MoodleAPIRestTemplate> templateRequest = this.restTemplateFactory
.createRestTemplate(MooldePluginLmsAPITemplateFactory.SEB_SERVER_SERVICE_NAME);
if (templateRequest.hasError()) {
return templateRequest;
} else {
this.restTemplate = templateRequest.get();
}
final Result<MoodleAPIRestTemplate> result = this.restTemplateFactory.getRestTemplate();
if (!result.hasError()) {
return result;
}
return Result.of(this.restTemplate);
return this.restTemplateFactory.createRestTemplate(MooldePluginLmsAPITemplateFactory.SEB_SERVER_SERVICE_NAME);
}
public String toTestString() {
final StringBuilder builder = new StringBuilder();
builder.append("MoodlePluginCourseRestriction [restTemplate=");
builder.append(this.restTemplate);
builder.append(this.getRestTemplate().getOr(null));
builder.append("]");
return builder.toString();
}

View file

@ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.plugin;
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.FullLmsIntegrationService.IntegrationData;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.FullLmsIntegrationAPI;
@ -32,8 +33,6 @@ public class MoodlePluginFullIntegration implements FullLmsIntegrationAPI {
private final JSONMapper jsonMapper;
private final MoodleRestTemplateFactory restTemplateFactory;
private MoodleAPIRestTemplate restTemplate;
public MoodlePluginFullIntegration(
final JSONMapper jsonMapper,
final MoodleRestTemplateFactory restTemplateFactory) {
@ -42,6 +41,34 @@ public class MoodlePluginFullIntegration implements FullLmsIntegrationAPI {
this.restTemplateFactory = restTemplateFactory;
}
@Override
public LmsSetupTestResult testFullIntegrationAPI() {
final LmsSetupTestResult attributesCheck = this.restTemplateFactory.test();
if (!attributesCheck.isOk()) {
return attributesCheck;
}
final Result<MoodleAPIRestTemplate> restTemplateRequest = getRestTemplate();
if (restTemplateRequest.hasError()) {
final String message = "Failed to gain access token from Moodle Rest API:\n tried token endpoints: " +
this.restTemplateFactory.getKnownTokenAccessPaths();
log.error(message + " cause: {}", restTemplateRequest.getError().getMessage());
return LmsSetupTestResult.ofTokenRequestError(LmsSetup.LmsType.MOODLE_PLUGIN, message);
}
try {
final MoodleAPIRestTemplate restTemplate = restTemplateRequest.get();
restTemplate.testAPIConnection(
FUNCTION_NAME_SEBSERVER_CONNECTION,
FUNCTION_NAME_SEBSERVER_CONNECTION_DELETE);
} catch (final RuntimeException e) {
return LmsSetupTestResult.ofQuizAccessAPIError(LmsSetup.LmsType.MOODLE_PLUGIN, e.getMessage());
}
return LmsSetupTestResult.ofOkay(LmsSetup.LmsType.MOODLE_PLUGIN);
}
@Override
public Result<IntegrationData> applyConnectionDetails(final IntegrationData data) {
return Result.tryCatch(() -> {
@ -116,17 +143,11 @@ public class MoodlePluginFullIntegration implements FullLmsIntegrationAPI {
private Result<MoodleAPIRestTemplate> getRestTemplate() {
if (this.restTemplate == null) {
final Result<MoodleAPIRestTemplate> templateRequest = this.restTemplateFactory
.createRestTemplate(MooldePluginLmsAPITemplateFactory.SEB_SERVER_SERVICE_NAME);
if (templateRequest.hasError()) {
return templateRequest;
} else {
this.restTemplate = templateRequest.get();
}
final Result<MoodleAPIRestTemplate> result = this.restTemplateFactory.getRestTemplate();
if (!result.hasError()) {
return result;
}
return Result.of(this.restTemplate);
return this.restTemplateFactory.createRestTemplate(MooldePluginLmsAPITemplateFactory.SEB_SERVER_SERVICE_NAME);
}
}

View file

@ -408,6 +408,11 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
.map(x -> exam);
}
@Override
public LmsSetupTestResult testFullIntegrationAPI() {
return LmsSetupTestResult.ofAPINotSupported(LmsType.OPEN_OLAT);
}
@Override
public Result<IntegrationData> applyConnectionDetails(final IntegrationData data) {
return Result.ofRuntimeError("Not Supported");

View file

@ -10,35 +10,65 @@ package ch.ethz.seb.sebserver.webservice.weblayer.oauth;
import ch.ethz.seb.sebserver.WebSecurityConfig;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.LmsSetupDAO;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import org.springframework.stereotype.Component;
@Lazy
@Component
public class LmsAPIClientDetails extends BaseClientDetails {
public class LmsAPIClientDetails {
private final PasswordEncoder clientPasswordEncoder;
private final String clientId;
private final String clientSecret;
private final Integer accessTokenValiditySeconds;
private final LmsSetupDAO lmsSetupDAO;
public LmsAPIClientDetails(
@Qualifier(WebSecurityConfig.CLIENT_PASSWORD_ENCODER_BEAN_NAME) final PasswordEncoder clientPasswordEncoder,
final LmsSetupDAO lmsSetupDAO,
@Value("${sebserver.webservice.lms.api.clientId}") final String clientId,
@Value("${sebserver.webservice.api.admin.clientSecret}") final String clientSecret,
@Value("${sebserver.webservice.lms.api.accessTokenValiditySeconds:-1}") final Integer accessTokenValiditySeconds
) {
super(
this.clientPasswordEncoder = clientPasswordEncoder;
this.lmsSetupDAO = lmsSetupDAO;
this.clientId = clientId;
this.clientSecret = clientSecret;
this.accessTokenValiditySeconds = accessTokenValiditySeconds;
}
public String getClientId() {
return clientId;
}
// It seems this get called multiple times per token request
// TODO do we need very short time caching here?
public ClientDetails getClientDetails() {
final String joinIds = StringUtils.join(
lmsSetupDAO.allIdsFullIntegration().getOrThrow(),
Constants.LIST_SEPARATOR
);
final BaseClientDetails clientDetails = new BaseClientDetails(
clientId,
WebserviceResourceConfiguration.LMS_API_RESOURCE_ID,
StringUtils.joinWith(
Constants.LIST_SEPARATOR,
Constants.OAUTH2_SCOPE_READ,
Constants.OAUTH2_SCOPE_WRITE),
joinIds,
Constants.OAUTH2_GRANT_TYPE_CLIENT_CREDENTIALS,
null
);
super.setClientSecret(clientPasswordEncoder.encode(clientSecret));
super.setAccessTokenValiditySeconds(accessTokenValiditySeconds);
clientDetails.setClientSecret(clientPasswordEncoder.encode(clientSecret));
clientDetails.setAccessTokenValiditySeconds(accessTokenValiditySeconds);
return clientDetails;
}
}

View file

@ -68,7 +68,7 @@ public class WebClientDetailsService implements ClientDetailsService {
}
if (clientId.equals(this.lmsAPIClientDetails.getClientId())) {
return this.lmsAPIClientDetails;
return this.lmsAPIClientDetails.getClientDetails();
}
return getForExamClientAPI(clientId)

View file

@ -46,6 +46,8 @@ public class MoodleMockupRestTemplateFactory implements MoodleRestTemplateFactor
private final APITemplateDataSupplier apiTemplateDataSupplier;
private Result<MoodleAPIRestTemplate> template = null;
public MoodleMockupRestTemplateFactory(final APITemplateDataSupplier apiTemplateDataSupplier) {
this.apiTemplateDataSupplier = apiTemplateDataSupplier;
}
@ -67,14 +69,25 @@ public class MoodleMockupRestTemplateFactory implements MoodleRestTemplateFactor
return paths;
}
@Override
public Result<MoodleAPIRestTemplate> getRestTemplate() {
return createRestTemplate("");
}
@Override
public Result<MoodleAPIRestTemplate> createRestTemplate(final String service) {
return Result.of(new MockupMoodleRestTemplate(this.apiTemplateDataSupplier.getLmsSetup().lmsApiUrl));
if (template == null) {
template = Result.of(new MockupMoodleRestTemplate(this.apiTemplateDataSupplier.getLmsSetup().lmsApiUrl));
}
return template;
}
@Override
public Result<MoodleAPIRestTemplate> createRestTemplate(final String service, final String accessTokenPath) {
return Result.of(new MockupMoodleRestTemplate(this.apiTemplateDataSupplier.getLmsSetup().lmsApiUrl));
if (template == null) {
template = Result.of(new MockupMoodleRestTemplate(this.apiTemplateDataSupplier.getLmsSetup().lmsApiUrl));
}
return template;
}
public static final class MockupMoodleRestTemplate implements MoodleAPIRestTemplate {

View file

@ -47,6 +47,8 @@ public class MoodleCourseAccessTest {
final MoodleAPIRestTemplateImpl moodleAPIRestTemplate = mock(MoodleAPIRestTemplateImpl.class);
when(moodleRestTemplateFactory.createRestTemplate(Mockito.anyString()))
.thenReturn(Result.of(moodleAPIRestTemplate));
when(moodleRestTemplateFactory.getRestTemplate())
.thenReturn(Result.of(moodleAPIRestTemplate));
when(moodleAPIRestTemplate.callMoodleAPIFunction(
anyString(),
any())).thenReturn("[\r\n" +
@ -122,6 +124,8 @@ public class MoodleCourseAccessTest {
final MoodleRestTemplateFactoryImpl moodleRestTemplateFactory = mock(MoodleRestTemplateFactoryImpl.class);
when(moodleRestTemplateFactory.createRestTemplate(Mockito.anyString()))
.thenReturn(Result.ofRuntimeError("Error1"));
when(moodleRestTemplateFactory.getRestTemplate())
.thenReturn(Result.ofRuntimeError("Error1"));
when(moodleRestTemplateFactory.test()).thenReturn(LmsSetupTestResult.ofOkay(LmsType.MOODLE));
final MoodleCourseAccess moodleCourseAccess = new MoodleCourseAccess(
@ -143,6 +147,8 @@ public class MoodleCourseAccessTest {
final MoodleAPIRestTemplateImpl moodleAPIRestTemplate = mock(MoodleAPIRestTemplateImpl.class);
when(moodleRestTemplateFactory.createRestTemplate(Mockito.anyString()))
.thenReturn(Result.of(moodleAPIRestTemplate));
when(moodleRestTemplateFactory.getRestTemplate())
.thenReturn(Result.of(moodleAPIRestTemplate));
doThrow(RuntimeException.class).when(moodleAPIRestTemplate).testAPIConnection(any());
when(moodleRestTemplateFactory.test()).thenReturn(LmsSetupTestResult.ofOkay(LmsType.MOODLE));
@ -165,6 +171,8 @@ public class MoodleCourseAccessTest {
final MoodleAPIRestTemplateImpl moodleAPIRestTemplate = mock(MoodleAPIRestTemplateImpl.class);
when(moodleRestTemplateFactory.createRestTemplate(Mockito.anyString()))
.thenReturn(Result.of(moodleAPIRestTemplate));
when(moodleRestTemplateFactory.getRestTemplate())
.thenReturn(Result.of(moodleAPIRestTemplate));
when(moodleRestTemplateFactory.test()).thenReturn(LmsSetupTestResult.ofOkay(LmsType.MOODLE));
final MoodleCourseAccess moodleCourseAccess = new MoodleCourseAccess(

View file

@ -47,11 +47,13 @@ public class MoodlePluginCourseAccessTest {
public void testSetup() {
final MoodlePluginCourseAccess candidate = crateMockup();
assertEquals("MoodlePluginCourseAccess ["
+ "pageSize=500, "
+ "maxSize=10000, "
+ "cutoffTimeOffset=3, "
+ "restTemplate=null]", candidate.toTestString());
assertEquals(
"MoodlePluginCourseAccess [" +
"pageSize=500, " +
"maxSize=10000, " +
"cutoffTimeOffset=3, " +
"restTemplate=MockupMoodleRestTemplate [accessToken=MockupMoodleRestTemplate-Test-Token, url=https://test.org/, testLog=[], callLog=[]]]",
candidate.toTestString());
final LmsSetupTestResult testCourseAccessAPI = candidate.testCourseAccessAPI();

View file

@ -36,7 +36,7 @@ public class MoodlePluginCourseRestrictionTest {
final MoodlePluginCourseRestriction candidate = crateMockup();
assertEquals("MoodlePluginCourseRestriction [restTemplate=null]", candidate.toTestString());
assertEquals("MoodlePluginCourseRestriction [restTemplate=MockupMoodleRestTemplate [accessToken=MockupMoodleRestTemplate-Test-Token, url=https://test.org/, testLog=[], callLog=[]]]", candidate.toTestString());
final LmsSetupTestResult testCourseRestrictionAPI = candidate.testCourseRestrictionAPI();