Merge remote-tracking branch 'origin/dev-lms-open-olat' into dev-1.2

This commit is contained in:
anhefti 2021-08-12 11:09:13 +02:00
commit 1df182fae6
3 changed files with 380 additions and 123 deletions

View file

@ -10,19 +10,28 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.olat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.CacheManager; import org.springframework.cache.CacheManager;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails; import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails;
import org.springframework.web.client.RestTemplate;
import ch.ethz.seb.sebserver.ClientHttpRequestFactoryService; import ch.ethz.seb.sebserver.ClientHttpRequestFactoryService;
import ch.ethz.seb.sebserver.gbl.api.APIMessage; import ch.ethz.seb.sebserver.gbl.api.APIMessage;
@ -36,7 +45,6 @@ import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; import ch.ethz.seb.sebserver.gbl.model.exam.QuizData;
import ch.ethz.seb.sebserver.gbl.model.exam.SEBRestriction; import ch.ethz.seb.sebserver.gbl.model.exam.SEBRestriction;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.Features;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult;
import ch.ethz.seb.sebserver.gbl.model.user.ExamineeAccountDetails; import ch.ethz.seb.sebserver.gbl.model.user.ExamineeAccountDetails;
@ -47,19 +55,23 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.lms.APITemplateDataSupplier
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.AbstractCachedCourseAccess; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.AbstractCachedCourseAccess;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.olat.OlatLmsData.AssessmentData;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.olat.OlatLmsData.RestrictionData;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.olat.OlatLmsData.RestrictionDataPost;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.olat.OlatLmsData.UserData;
public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements LmsAPITemplate { public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements LmsAPITemplate {
// TODO add needed dependencies here private static final Logger log = LoggerFactory.getLogger(OlatLmsAPITemplate.class);
private final ClientHttpRequestFactoryService clientHttpRequestFactoryService; private final ClientHttpRequestFactoryService clientHttpRequestFactoryService;
private final ClientCredentialService clientCredentialService; private final ClientCredentialService clientCredentialService;
private final APITemplateDataSupplier apiTemplateDataSupplier; private final APITemplateDataSupplier apiTemplateDataSupplier;
private final Long lmsSetupId; private final Long lmsSetupId;
private OlatLmsRestTemplate cachedRestTemplate;
protected OlatLmsAPITemplate( protected OlatLmsAPITemplate(
// TODO if you need more dependencies inject them here and set the reference
final ClientHttpRequestFactoryService clientHttpRequestFactoryService, final ClientHttpRequestFactoryService clientHttpRequestFactoryService,
final ClientCredentialService clientCredentialService, final ClientCredentialService clientCredentialService,
final APITemplateDataSupplier apiTemplateDataSupplier, final APITemplateDataSupplier apiTemplateDataSupplier,
@ -95,32 +107,19 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
final LmsSetupTestResult testLmsSetupSettings = testLmsSetupSettings(); final LmsSetupTestResult testLmsSetupSettings = testLmsSetupSettings();
if (testLmsSetupSettings.hasAnyError()) { if (testLmsSetupSettings.hasAnyError()) {
return testLmsSetupSettings; return testLmsSetupSettings;
} else {
} }
try {
// TODO check if the course API of the remote LMS is available this.getRestTemplate().get();
// if not, create corresponding LmsSetupTestResult error } catch (final Exception e) {
return LmsSetupTestResult.ofQuizAccessAPIError(LmsType.OPEN_OLAT, "TODO: implement LMS access check"); log.error("Failed to access OLAT course API: ", e);
return LmsSetupTestResult.ofQuizAccessAPIError(LmsType.OPEN_OLAT, e.getMessage());
//return LmsSetupTestResult.ofOkay(LmsType.OPEN_OLAT); }
return LmsSetupTestResult.ofOkay(LmsType.OPEN_OLAT);
} }
@Override @Override
public LmsSetupTestResult testCourseRestrictionAPI() { public LmsSetupTestResult testCourseRestrictionAPI() {
final LmsSetupTestResult testLmsSetupSettings = testLmsSetupSettings(); return testCourseAccessAPI();
if (testLmsSetupSettings.hasAnyError()) {
return testLmsSetupSettings;
}
if (LmsType.OPEN_OLAT.features.contains(Features.SEB_RESTRICTION)) {
// TODO check if the course API of the remote LMS is available
// if not, create corresponding LmsSetupTestResult error
}
return LmsSetupTestResult.ofOkay(LmsType.OPEN_OLAT);
} }
private LmsSetupTestResult testLmsSetupSettings() { private LmsSetupTestResult testLmsSetupSettings() {
@ -207,62 +206,100 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
@Override @Override
protected Supplier<List<QuizData>> allQuizzesSupplier(final FilterMap filterMap) { protected Supplier<List<QuizData>> allQuizzesSupplier(final FilterMap filterMap) {
@SuppressWarnings("unused")
final String quizName = filterMap.getString(QuizData.FILTER_ATTR_QUIZ_NAME);
@SuppressWarnings("unused")
final DateTime quizFromTime = (filterMap != null) ? filterMap.getQuizFromTime() : null;
return () -> { return () -> {
final List<QuizData> res = getRestTemplate()
// TODO Get all course / quiz data from remote LMS that matches the filter criteria. .map(t -> this.collectAllQuizzes(t, filterMap))
// If the LMS API uses paging, go through all pages using the filter criteria .getOrThrow();
// and collect the course data. super.putToCache(res);
// Transform the data from courses / quizzes from LMS into QuizData objects return res;
// Put loaded QuizData objects to the cache: super.putToCache(quizDataCollection);
// before returning it.
throw new RuntimeException("TODO");
}; };
} }
private String examUrl(final long olatRepositoryId) {
final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup();
return lmsSetup.lmsApiUrl + "/auth/RepositoryEntry/" + olatRepositoryId;
}
private List<QuizData> collectAllQuizzes(final OlatLmsRestTemplate restTemplate, final FilterMap filterMap) {
final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup();
final String quizName = filterMap.getString(QuizData.FILTER_ATTR_QUIZ_NAME);
final DateTime quizFromTime = (filterMap != null) ? filterMap.getQuizFromTime() : null;
final long fromCutTime = (quizFromTime != null) ? Utils.toUnixTimeInSeconds(quizFromTime) : -1;
String url = "/restapi/assessment_modes/seb?";
if (fromCutTime != -1) {
url = String.format("%sdateFrom=%s&", url, fromCutTime);
}
if (quizName != null) {
url = String.format("%sname=%s&", url, quizName);
}
final List<AssessmentData> as =
this.apiGetList(restTemplate, url, new ParameterizedTypeReference<List<AssessmentData>>() {
});
return as.stream()
.map(a -> {
return new QuizData(
String.format("%d", a.key),
lmsSetup.getInstitutionId(),
lmsSetup.id,
lmsSetup.getLmsType(),
a.name,
a.description,
Utils.toDateTimeUTC(a.dateFrom),
Utils.toDateTimeUTC(a.dateTo),
examUrl(a.repositoryEntryKey),
new HashMap<String, String>());
})
.collect(Collectors.toList());
}
@Override @Override
protected Supplier<Collection<QuizData>> quizzesSupplier(final Set<String> ids) { protected Supplier<Collection<QuizData>> quizzesSupplier(final Set<String> ids) {
return () -> ids.stream().map(id -> quizSupplier(id).get()).collect(Collectors.toList());
return () -> {
// TODO get all quiz / course data for specified identifiers from remote LMS
// Transform the data from courses / quizzes from LMS into QuizData objects
// and put it to the cache: super.putToCache(quizDataCollection);
// before returning it.
throw new RuntimeException("TODO");
};
} }
@Override @Override
protected Supplier<QuizData> quizSupplier(final String id) { protected Supplier<QuizData> quizSupplier(final String id) {
return () -> getRestTemplate()
.map(t -> this.quizById(t, id))
.getOrThrow();
}
return () -> { private QuizData quizById(final OlatLmsRestTemplate restTemplate, final String id) {
final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup();
final String url = String.format("/restapi/assessment_modes/%s", id);
final AssessmentData a = this.apiGet(restTemplate, url, AssessmentData.class);
return new QuizData(
String.format("%d", a.key),
lmsSetup.getInstitutionId(),
lmsSetup.id,
lmsSetup.getLmsType(),
a.name,
a.description,
Utils.toDateTimeUTC(a.dateFrom),
Utils.toDateTimeUTC(a.dateTo),
examUrl(a.repositoryEntryKey),
new HashMap<String, String>());
}
// TODO get the specified quiz / course data for specified identifier from remote LMS private ExamineeAccountDetails getExamineeById(final RestTemplate restTemplate, final String id) {
// and put it to the cache: super.putToCache(quizDataCollection); final String url = String.format("/restapi/users/%s/name_username", id);
// before returning it. final UserData u = this.apiGet(restTemplate, url, UserData.class);
final Map<String, String> attrs = new HashMap<>();
throw new RuntimeException("TODO"); return new ExamineeAccountDetails(
}; String.valueOf(u.key),
u.lastName + ", " + u.firstName,
u.username,
"OLAT API does not provide email addresses",
attrs);
} }
@Override @Override
protected Supplier<ExamineeAccountDetails> accountDetailsSupplier(final String examineeSessionId) { protected Supplier<ExamineeAccountDetails> accountDetailsSupplier(final String id) {
return () -> getRestTemplate()
return () -> { .map(t -> this.getExamineeById(t, id))
.getOrThrow();
// TODO get the examinee's account details by the given examineeSessionId from remote LMS.
// Currently only the name is needed to display on monitoring view.
throw new RuntimeException("TODO");
};
} }
@Override @Override
@ -272,55 +309,105 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
}; };
} }
private SEBRestriction getRestrictionForAssignmentId(final RestTemplate restTemplate, final String id) {
final String url = String.format("/restapi/assessment_modes/%s/seb_restriction", id);
final RestrictionData r = this.apiGet(restTemplate, url, RestrictionData.class);
return new SEBRestriction(Long.valueOf(id), r.configKeys, r.browserExamKeys, new HashMap<String, String>());
}
private SEBRestriction setRestrictionForAssignmentId(
final RestTemplate restTemplate,
final String id,
final SEBRestriction restriction) {
final String url = String.format("/restapi/assessment_modes/%s/seb_restriction", id);
final RestrictionDataPost post = new RestrictionDataPost();
post.browserExamKeys = new ArrayList<>(restriction.browserExamKeys);
post.configKeys = new ArrayList<>(restriction.configKeys);
final RestrictionData r =
this.apiPost(restTemplate, url, post, RestrictionDataPost.class, RestrictionData.class);
return new SEBRestriction(Long.valueOf(id), r.configKeys, r.browserExamKeys, new HashMap<String, String>());
}
private SEBRestriction deleteRestrictionForAssignmentId(final RestTemplate restTemplate, final String id) {
final String url = String.format("/restapi/assessment_modes/%s/seb_restriction", id);
final RestrictionData r = this.apiDelete(restTemplate, url, RestrictionData.class);
// OLAT returns RestrictionData with null values upon deletion.
// We return it here for consistency, even though SEB server does not need it
return new SEBRestriction(Long.valueOf(id), r.configKeys, r.browserExamKeys, new HashMap<String, String>());
}
@Override @Override
public Result<SEBRestriction> getSEBClientRestriction(final Exam exam) { public Result<SEBRestriction> getSEBClientRestriction(final Exam exam) {
@SuppressWarnings("unused") return getRestTemplate()
final String quizId = exam.externalId; .map(t -> this.getRestrictionForAssignmentId(t, exam.externalId));
return Result.tryCatch(() -> {
// TODO get the SEB client restrictions that are currently set on the remote LMS for
// the given quiz / course derived from the given exam
throw new RuntimeException("TODO");
});
} }
@Override @Override
public Result<SEBRestriction> applySEBClientRestriction( public Result<SEBRestriction> applySEBClientRestriction(
final String externalExamId, final String externalExamId,
final SEBRestriction sebRestrictionData) { final SEBRestriction sebRestrictionData) {
return getRestTemplate()
return Result.tryCatch(() -> { .map(t -> this.setRestrictionForAssignmentId(t, externalExamId, sebRestrictionData));
// TODO apply the given sebRestrictionData settings as current SEB client restriction setting
// to the remote LMS for the given quiz / course.
// Mainly SEBRestriction.configKeys and SEBRestriction.browserExamKeys
throw new RuntimeException("TODO");
});
} }
@Override @Override
public Result<Exam> releaseSEBClientRestriction(final Exam exam) { public Result<Exam> releaseSEBClientRestriction(final Exam exam) {
@SuppressWarnings("unused") return getRestTemplate()
final String quizId = exam.externalId; .map(t -> this.deleteRestrictionForAssignmentId(t, exam.externalId))
.map(x -> exam);
return Result.tryCatch(() -> {
// TODO Release respectively delete all SEB client restrictions for the given
// course / quize on the remote LMS.
throw new RuntimeException("TODO");
});
} }
// TODO: This is an example of how to create a RestTemplate for the service to access the LMS API private <T> T apiGet(final RestTemplate restTemplate, final String url, final Class<T> type) {
// The example deals with a Http based API that is secured by an OAuth2 client-credential flow. final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup();
// You might need some different template, then you have to adapt this code final ResponseEntity<T> res = restTemplate.exchange(
// To your needs. lmsSetup.lmsApiUrl + url,
@SuppressWarnings("unused") HttpMethod.GET,
private OAuth2RestTemplate createRestTemplate(final String accessTokenRequestPath) { HttpEntity.EMPTY,
type);
return res.getBody();
}
private <T> List<T> apiGetList(final RestTemplate restTemplate, final String url,
final ParameterizedTypeReference<List<T>> type) {
final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup();
final ResponseEntity<List<T>> res = restTemplate.exchange(
lmsSetup.lmsApiUrl + url,
HttpMethod.GET,
HttpEntity.EMPTY,
type);
return res.getBody();
}
private <P, R> R apiPost(final RestTemplate restTemplate, final String url, final P post, final Class<P> postType,
final Class<R> responseType) {
final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup();
final HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.set("content-type", "application/json");
final HttpEntity<P> requestEntity = new HttpEntity<>(post, httpHeaders);
final ResponseEntity<R> res = restTemplate.exchange(
lmsSetup.lmsApiUrl + url,
HttpMethod.POST,
requestEntity,
responseType);
return res.getBody();
}
private <T> T apiDelete(final RestTemplate restTemplate, final String url, final Class<T> type) {
final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup();
final ResponseEntity<T> res = restTemplate.exchange(
lmsSetup.lmsApiUrl + url,
HttpMethod.DELETE,
HttpEntity.EMPTY,
type);
return res.getBody();
}
private Result<OlatLmsRestTemplate> getRestTemplate() {
return Result.tryCatch(() -> {
if (this.cachedRestTemplate != null) {
return this.cachedRestTemplate;
}
final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup(); final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup();
final ClientCredentials credentials = this.apiTemplateDataSupplier.getLmsClientCredentials(); final ClientCredentials credentials = this.apiTemplateDataSupplier.getLmsClientCredentials();
@ -332,7 +419,7 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
.getOrThrow(); .getOrThrow();
final ClientCredentialsResourceDetails details = new ClientCredentialsResourceDetails(); final ClientCredentialsResourceDetails details = new ClientCredentialsResourceDetails();
details.setAccessTokenUri(lmsSetup.lmsApiUrl + accessTokenRequestPath); details.setAccessTokenUri(lmsSetup.lmsApiUrl + "/restapi/auth/");
details.setClientId(plainClientId.toString()); details.setClientId(plainClientId.toString());
details.setClientSecret(plainClientSecret.toString()); details.setClientSecret(plainClientSecret.toString());
@ -340,10 +427,12 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
.getClientHttpRequestFactory(proxyData) .getClientHttpRequestFactory(proxyData)
.getOrThrow(); .getOrThrow();
final OAuth2RestTemplate template = new OAuth2RestTemplate(details); final OlatLmsRestTemplate template = new OlatLmsRestTemplate(details);
template.setRequestFactory(clientHttpRequestFactory); template.setRequestFactory(clientHttpRequestFactory);
return template; this.cachedRestTemplate = template;
return this.cachedRestTemplate;
});
} }
} }

View file

@ -0,0 +1,84 @@
/*
* Copyright (c) 2021 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.lms.impl.olat;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
public final class OlatLmsData {
@JsonIgnoreProperties(ignoreUnknown = true)
static public final class AssessmentData {
/* OLAT API example:
{
"courseName": "course 1",
"dateFrom": 1624420800000,
"dateTo": 1624658400000,
"description": "",
"key": 6356992,
repositoryEntryKey: 462324,
"name": "SEB test"
}
*/
public long key;
public long repositoryEntryKey;
public String name;
public String description;
public String courseName;
public long dateFrom;
public long dateTo;
}
@JsonIgnoreProperties(ignoreUnknown = true)
static final class UserData {
/* OLAT API example:
{
"firstName": "OpenOLAT",
"key": 360448,
"lastName": "Administrator",
"username": "administrator"
}
*/
public long key;
public String firstName;
public String lastName;
public String username;
}
@JsonIgnoreProperties(ignoreUnknown = true)
static final class RestrictionData {
/* OLAT API example:
{
"browserExamKeys": [ "1" ],
"configKeys": null,
"key": 8028160
}
*/
public long key;
public List<String> browserExamKeys;
public List<String> configKeys;
}
@JsonIgnoreProperties(ignoreUnknown = true)
static final class RestrictionDataPost {
/* OLAT API example:
{
"configKeys": ["a", "b"],
"browserExamKeys": ["1", "2"]
}
*/
public List<String> browserExamKeys;
public List<String> configKeys;
}
}

View file

@ -0,0 +1,84 @@
/*
* Copyright (c) 2021 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.lms.impl.olat;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.client.RestTemplate;
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpRequest;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
public class OlatLmsRestTemplate extends RestTemplate {
private static final Logger log = LoggerFactory.getLogger(OlatLmsRestTemplate.class);
private String token;
private ClientCredentialsResourceDetails details;
public OlatLmsRestTemplate(ClientCredentialsResourceDetails details) {
super();
this.details = details;
// Add X-OLAT-TOKEN request header to every request done using this RestTemplate
this.getInterceptors().add(new ClientHttpRequestInterceptor(){
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
// if there's no token, authenticate first
if (token == null) { authenticate(); }
// when authenticating, just do a normal call
else if (token.equals("authenticating")) { return execution.execute(request, body); }
// otherwise, add the X-OLAT-TOKEN
request.getHeaders().set("accept", "application/json");
request.getHeaders().set("X-OLAT-TOKEN", token);
ClientHttpResponse response = execution.execute(request, body);
log.debug("OLAT [regular API call] {} Headers: {}", response.getStatusCode(), response.getHeaders());
// If we get a 401, re-authenticate and try once more
if (response.getStatusCode() == HttpStatus.UNAUTHORIZED) {
authenticate();
request.getHeaders().set("X-OLAT-TOKEN", token);
response = execution.execute(request, body);
log.debug("OLAT [retry API call] {} Headers: {}", response.getStatusCode(), response.getHeaders());
}
return response;
}
});
}
private void authenticate() {
// Authenticate with OLAT and store the received X-OLAT-TOKEN
token = "authenticating";
final String authUrl = String.format("%s%s?password=%s",
details.getAccessTokenUri(),
details.getClientId(),
details.getClientSecret());
try {
final ResponseEntity<String> response = this.getForEntity(authUrl, String.class);
final HttpHeaders responseHeaders = response.getHeaders();
log.debug("OLAT [authenticate] {} Headers: {}", response.getStatusCode(), responseHeaders);
token = responseHeaders.getFirst("X-OLAT-TOKEN");
}
catch (Exception e) {
token = null;
throw e;
}
}
}