SEBSERV-417 access token for Moodle
This commit is contained in:
parent
0f27ef0f38
commit
6e2feafc5a
23 changed files with 241 additions and 86 deletions
|
@ -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() {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)
|
||||
);
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() ||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
Loading…
Reference in a new issue