OpenEdX API Setup plus tests
This commit is contained in:
parent
44dbe1e714
commit
cda50a15e7
9 changed files with 260 additions and 131 deletions
|
@ -24,7 +24,8 @@ public class InternalEncryptionService {
|
|||
|
||||
private static final Logger log = LoggerFactory.getLogger(InternalEncryptionService.class);
|
||||
|
||||
private static final String NO_SALT = "NO_SALT";
|
||||
static final String SEBSERVER_WEBSERVICE_INTERNAL_SECRET_KEY = "sebserver.webservice.internalSecret";
|
||||
private static final String DEFAULT_SALT = "b7dbe99bbfa3e21e";
|
||||
|
||||
private final Environment environment;
|
||||
|
||||
|
@ -35,8 +36,8 @@ public class InternalEncryptionService {
|
|||
public String encrypt(final String text) {
|
||||
try {
|
||||
return Encryptors.text(
|
||||
this.environment.getRequiredProperty("sebserver.webservice.internalSecret"),
|
||||
NO_SALT).encrypt(text);
|
||||
this.environment.getRequiredProperty(SEBSERVER_WEBSERVICE_INTERNAL_SECRET_KEY),
|
||||
DEFAULT_SALT).encrypt(text);
|
||||
} catch (final Exception e) {
|
||||
log.error("Failed to encrypt text: ", e);
|
||||
return text;
|
||||
|
@ -46,8 +47,8 @@ public class InternalEncryptionService {
|
|||
public String decrypt(final String text) {
|
||||
try {
|
||||
return Encryptors.text(
|
||||
this.environment.getRequiredProperty("sebserver.webservice.internalSecret"),
|
||||
NO_SALT).decrypt(text);
|
||||
this.environment.getRequiredProperty(SEBSERVER_WEBSERVICE_INTERNAL_SECRET_KEY),
|
||||
DEFAULT_SALT).decrypt(text);
|
||||
} catch (final Exception e) {
|
||||
log.error("Failed to decrypt text: ", e);
|
||||
return text;
|
||||
|
@ -57,7 +58,7 @@ public class InternalEncryptionService {
|
|||
public String encrypt(final String text, final CharSequence salt) {
|
||||
try {
|
||||
return Encryptors.text(
|
||||
this.environment.getRequiredProperty("sebserver.webservice.internalSecret"),
|
||||
this.environment.getRequiredProperty(SEBSERVER_WEBSERVICE_INTERNAL_SECRET_KEY),
|
||||
salt).encrypt(text);
|
||||
} catch (final Exception e) {
|
||||
log.error("Failed to encrypt text: ", e);
|
||||
|
@ -68,7 +69,7 @@ public class InternalEncryptionService {
|
|||
public String decrypt(final String text, final CharSequence salt) {
|
||||
try {
|
||||
return Encryptors.text(
|
||||
this.environment.getRequiredProperty("sebserver.webservice.internalSecret"),
|
||||
this.environment.getRequiredProperty(SEBSERVER_WEBSERVICE_INTERNAL_SECRET_KEY),
|
||||
salt).decrypt(text);
|
||||
} catch (final Exception e) {
|
||||
log.error("Failed to decrypt text: ", e);
|
||||
|
|
|
@ -9,8 +9,25 @@
|
|||
package ch.ethz.seb.sebserver.webservice.servicelayer.dao;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionSupportDAO;
|
||||
|
||||
public interface LmsSetupDAO extends ActivatableEntityDAO<LmsSetup, LmsSetup>, BulkActionSupportDAO<LmsSetup> {
|
||||
|
||||
Result<Credentials> getLmsAPIAccessCredentials(String lmsSetupId);
|
||||
|
||||
Result<Credentials> getSEBClientCredentials(String lmsSetupId);
|
||||
|
||||
final class Credentials {
|
||||
public final String clientId;
|
||||
public final String secret;
|
||||
public final String accessToken;
|
||||
|
||||
public Credentials(final String clientId, final String secret, final String accessToken) {
|
||||
super();
|
||||
this.clientId = clientId;
|
||||
this.secret = secret;
|
||||
this.accessToken = accessToken;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -254,6 +254,34 @@ public class LmsSetupDAOImpl implements LmsSetupDAO {
|
|||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public Result<Credentials> getLmsAPIAccessCredentials(final String lmsSetupId) {
|
||||
return Result.tryCatch(() -> {
|
||||
final LmsSetupRecord record = this.lmsSetupRecordMapper
|
||||
.selectByPrimaryKey(Long.parseLong(lmsSetupId));
|
||||
|
||||
return new Credentials(
|
||||
record.getLmsClientname(),
|
||||
record.getLmsClientsecret(),
|
||||
record.getLmsRestApiToken());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public Result<Credentials> getSEBClientCredentials(final String lmsSetupId) {
|
||||
return Result.tryCatch(() -> {
|
||||
final LmsSetupRecord record = this.lmsSetupRecordMapper
|
||||
.selectByPrimaryKey(Long.parseLong(lmsSetupId));
|
||||
|
||||
return new Credentials(
|
||||
record.getSebClientname(),
|
||||
record.getSebClientsecret(),
|
||||
null);
|
||||
});
|
||||
}
|
||||
|
||||
private Result<Collection<EntityKey>> allIdsOfInstitution(final EntityKey institutionKey) {
|
||||
return Result.tryCatch(() -> {
|
||||
return this.lmsSetupRecordMapper.selectIdsByExample()
|
||||
|
|
|
@ -20,7 +20,7 @@ import ch.ethz.seb.sebserver.gbl.util.Result;
|
|||
|
||||
public interface LmsAPITemplate {
|
||||
|
||||
LmsSetup lmsSetup();
|
||||
Result<LmsSetup> lmsSetup();
|
||||
|
||||
LmsSetupTestResult testLmsSetup();
|
||||
|
||||
|
@ -35,4 +35,6 @@ public interface LmsAPITemplate {
|
|||
|
||||
Result<ExamineeAccountDetails> getExamineeAccountDetails(String examineeUserId);
|
||||
|
||||
void reset();
|
||||
|
||||
}
|
||||
|
|
|
@ -33,6 +33,8 @@ public class LmsAPIServiceImpl implements LmsAPIService {
|
|||
private final ClientHttpRequestFactory clientHttpRequestFactory;
|
||||
private final String[] openEdxAlternativeTokenRequestPaths;
|
||||
|
||||
// TODO internal caching of LmsAPITemplate per LmsSetup (Id)
|
||||
|
||||
public LmsAPIServiceImpl(
|
||||
final LmsSetupDAO lmsSetupDAO,
|
||||
final InternalEncryptionService internalEncryptionService,
|
||||
|
@ -59,10 +61,14 @@ public class LmsAPIServiceImpl implements LmsAPIService {
|
|||
public Result<LmsAPITemplate> createLmsAPITemplate(final LmsSetup lmsSetup) {
|
||||
switch (lmsSetup.lmsType) {
|
||||
case MOCKUP:
|
||||
return Result.of(new MockupLmsAPITemplate(lmsSetup));
|
||||
return Result.of(new MockupLmsAPITemplate(
|
||||
this.lmsSetupDAO,
|
||||
lmsSetup,
|
||||
this.internalEncryptionService));
|
||||
case OPEN_EDX:
|
||||
return Result.of(new OpenEdxLmsAPITemplate(
|
||||
lmsSetup,
|
||||
lmsSetup.getModelId(),
|
||||
this.lmsSetupDAO,
|
||||
this.internalEncryptionService,
|
||||
this.clientHttpRequestFactory,
|
||||
this.openEdxAlternativeTokenRequestPaths));
|
||||
|
|
|
@ -24,16 +24,30 @@ 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.user.ExamineeAccountDetails;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.InternalEncryptionService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService.SortOrder;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.LmsSetupDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate;
|
||||
|
||||
final class MockupLmsAPITemplate implements LmsAPITemplate {
|
||||
|
||||
public static final String MOCKUP_LMS_CLIENT_NAME = "mockupLmsClientName";
|
||||
public static final String MOCKUP_LMS_CLIENT_SECRET = "mockupLmsClientSecret";
|
||||
|
||||
private final InternalEncryptionService internalEncryptionService;
|
||||
private final LmsSetupDAO lmsSetupDao;
|
||||
private final LmsSetup setup;
|
||||
|
||||
private LmsSetupDAO.Credentials credentials = null;
|
||||
private final Collection<QuizData> mockups;
|
||||
|
||||
MockupLmsAPITemplate(final LmsSetup setup) {
|
||||
MockupLmsAPITemplate(
|
||||
final LmsSetupDAO lmsSetupDao,
|
||||
final LmsSetup setup,
|
||||
final InternalEncryptionService internalEncryptionService) {
|
||||
|
||||
this.lmsSetupDao = lmsSetupDao;
|
||||
this.internalEncryptionService = internalEncryptionService;
|
||||
if (!setup.isActive() || setup.lmsType != LmsType.MOCKUP) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
@ -64,8 +78,8 @@ final class MockupLmsAPITemplate implements LmsAPITemplate {
|
|||
}
|
||||
|
||||
@Override
|
||||
public LmsSetup lmsSetup() {
|
||||
return this.setup;
|
||||
public Result<LmsSetup> lmsSetup() {
|
||||
return Result.of(this.setup);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -73,10 +87,8 @@ final class MockupLmsAPITemplate implements LmsAPITemplate {
|
|||
if (this.setup.lmsType != LmsType.MOCKUP) {
|
||||
return LmsSetupTestResult.ofMissingAttributes(LMS_SETUP.ATTR_LMS_TYPE);
|
||||
}
|
||||
if (this.setup.lmsApiUrl.equals("mockup") &&
|
||||
this.setup.lmsAuthName.equals("mockup") &&
|
||||
this.setup.lmsAuthSecret.equals("mockup")) {
|
||||
|
||||
initCredentials();
|
||||
if (this.credentials != null) {
|
||||
return LmsSetupTestResult.ofOkay();
|
||||
} else {
|
||||
return LmsSetupTestResult.ofMissingAttributes(
|
||||
|
@ -121,6 +133,11 @@ final class MockupLmsAPITemplate implements LmsAPITemplate {
|
|||
final int pageSize) {
|
||||
|
||||
return Result.tryCatch(() -> {
|
||||
initCredentials();
|
||||
if (this.credentials == null) {
|
||||
throw new IllegalArgumentException("Wrong clientId or secret");
|
||||
}
|
||||
|
||||
final int startIndex = pageNumber * pageSize;
|
||||
final int endIndex = startIndex + pageSize;
|
||||
int index = 0;
|
||||
|
@ -142,6 +159,11 @@ final class MockupLmsAPITemplate implements LmsAPITemplate {
|
|||
|
||||
@Override
|
||||
public Collection<Result<QuizData>> getQuizzes(final Set<String> ids) {
|
||||
initCredentials();
|
||||
if (this.credentials == null) {
|
||||
throw new IllegalArgumentException("Wrong clientId or secret");
|
||||
}
|
||||
|
||||
return this.mockups.stream()
|
||||
.filter(mockup -> ids.contains(mockup.id))
|
||||
.map(mockup -> Result.of(mockup))
|
||||
|
@ -150,7 +172,31 @@ final class MockupLmsAPITemplate implements LmsAPITemplate {
|
|||
|
||||
@Override
|
||||
public Result<ExamineeAccountDetails> getExamineeAccountDetails(final String examineeUserId) {
|
||||
initCredentials();
|
||||
if (this.credentials == null) {
|
||||
throw new IllegalArgumentException("Wrong clientId or secret");
|
||||
}
|
||||
|
||||
return Result.of(new ExamineeAccountDetails(examineeUserId, "mockup", "mockup", "mockup"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
this.credentials = null;
|
||||
}
|
||||
|
||||
private void initCredentials() {
|
||||
try {
|
||||
this.credentials = this.lmsSetupDao
|
||||
.getLmsAPIAccessCredentials(this.setup.getModelId())
|
||||
.getOrThrow();
|
||||
|
||||
assert (this.credentials.clientId.equals("lmsMockupClientId")) : "wrong clientId";
|
||||
assert (this.internalEncryptionService.decrypt(this.credentials.clientId)
|
||||
.equals("lmsMockupSecret")) : "wrong clientId";
|
||||
} catch (final Exception e) {
|
||||
this.credentials = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -24,11 +24,8 @@ import org.springframework.http.HttpHeaders;
|
|||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.security.crypto.encrypt.Encryptors;
|
||||
import org.springframework.security.crypto.encrypt.TextEncryptor;
|
||||
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
|
||||
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails;
|
||||
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.model.Domain.LMS_SETUP;
|
||||
|
@ -40,6 +37,8 @@ import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult;
|
|||
import ch.ethz.seb.sebserver.gbl.model.user.ExamineeAccountDetails;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.InternalEncryptionService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.LmsSetupDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.LmsSetupDAO.Credentials;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate;
|
||||
|
||||
final class OpenEdxLmsAPITemplate implements LmsAPITemplate {
|
||||
|
@ -50,7 +49,8 @@ final class OpenEdxLmsAPITemplate implements LmsAPITemplate {
|
|||
private static final String OPEN_EDX_DEFAULT_COURSE_ENDPOINT = "/api/courses/v1/courses/";
|
||||
private static final String OPEN_EDX_DEFAULT_COURSE_START_URL_PREFIX = "/courses/";
|
||||
|
||||
private final LmsSetup lmsSetup;
|
||||
private final String lmsSetupId;
|
||||
private final LmsSetupDAO lmsSetupDAO;
|
||||
private final ClientHttpRequestFactory clientHttpRequestFactory;
|
||||
private final InternalEncryptionService internalEncryptionService;
|
||||
private final Set<String> knownTokenAccessPaths;
|
||||
|
@ -58,12 +58,14 @@ final class OpenEdxLmsAPITemplate implements LmsAPITemplate {
|
|||
private OAuth2RestTemplate restTemplate = null;
|
||||
|
||||
OpenEdxLmsAPITemplate(
|
||||
final LmsSetup lmsSetup,
|
||||
final String lmsSetupId,
|
||||
final LmsSetupDAO lmsSetupDAO,
|
||||
final InternalEncryptionService internalEncryptionService,
|
||||
final ClientHttpRequestFactory clientHttpRequestFactory,
|
||||
final String[] alternativeTokenRequestPaths) {
|
||||
|
||||
this.lmsSetup = lmsSetup;
|
||||
this.lmsSetupId = lmsSetupId;
|
||||
this.lmsSetupDAO = lmsSetupDAO;
|
||||
this.clientHttpRequestFactory = clientHttpRequestFactory;
|
||||
this.internalEncryptionService = internalEncryptionService;
|
||||
|
||||
|
@ -75,27 +77,30 @@ final class OpenEdxLmsAPITemplate implements LmsAPITemplate {
|
|||
}
|
||||
|
||||
@Override
|
||||
public LmsSetup lmsSetup() {
|
||||
return this.lmsSetup;
|
||||
public Result<LmsSetup> lmsSetup() {
|
||||
return this.lmsSetupDAO
|
||||
.byModelId(this.lmsSetupId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LmsSetupTestResult testLmsSetup() {
|
||||
|
||||
log.info("Test Lms Binding for OpenEdX and LmsSetup: {}", this.lmsSetup);
|
||||
final LmsSetup lmsSetup = lmsSetup().getOrThrow();
|
||||
|
||||
log.info("Test Lms Binding for OpenEdX and LmsSetup: {}", lmsSetup);
|
||||
|
||||
// validation of LmsSetup
|
||||
if (this.lmsSetup.lmsType != LmsType.MOCKUP) {
|
||||
if (lmsSetup.lmsType != LmsType.MOCKUP) {
|
||||
return LmsSetupTestResult.ofMissingAttributes(LMS_SETUP.ATTR_LMS_TYPE);
|
||||
}
|
||||
final List<String> missingAttrs = new ArrayList<>();
|
||||
if (StringUtils.isBlank(this.lmsSetup.lmsApiUrl)) {
|
||||
if (StringUtils.isBlank(lmsSetup.lmsApiUrl)) {
|
||||
missingAttrs.add(LMS_SETUP.ATTR_LMS_TYPE);
|
||||
}
|
||||
if (StringUtils.isBlank(this.lmsSetup.getLmsAuthName())) {
|
||||
if (StringUtils.isBlank(lmsSetup.getLmsAuthName())) {
|
||||
missingAttrs.add(LMS_SETUP.ATTR_LMS_CLIENTNAME);
|
||||
}
|
||||
if (StringUtils.isBlank(this.lmsSetup.getLmsAuthSecret())) {
|
||||
if (StringUtils.isBlank(lmsSetup.getLmsAuthSecret())) {
|
||||
missingAttrs.add(LMS_SETUP.ATTR_LMS_CLIENTSECRET);
|
||||
}
|
||||
|
||||
|
@ -104,7 +109,7 @@ final class OpenEdxLmsAPITemplate implements LmsAPITemplate {
|
|||
}
|
||||
|
||||
// request OAuth2 access token on OpenEdx API
|
||||
initRestTemplateAndRequestAccessToken();
|
||||
initRestTemplateAndRequestAccessToken(lmsSetup);
|
||||
if (this.restTemplate == null) {
|
||||
return LmsSetupTestResult.ofTokenRequestError(
|
||||
"Failed to gain access token form OpenEdX Rest API: tried token endpoints: " +
|
||||
|
@ -124,34 +129,35 @@ final class OpenEdxLmsAPITemplate implements LmsAPITemplate {
|
|||
final int pageNumber,
|
||||
final int pageSize) {
|
||||
|
||||
return Result.tryCatch(() -> {
|
||||
initRestTemplateAndRequestAccessToken();
|
||||
return this.lmsSetup()
|
||||
.flatMap(this::initRestTemplateAndRequestAccessToken)
|
||||
.map(lmsSetup -> {
|
||||
|
||||
// TODO sort and pagination
|
||||
final HttpHeaders httpHeaders = new HttpHeaders();
|
||||
// TODO sort and pagination
|
||||
final HttpHeaders httpHeaders = new HttpHeaders();
|
||||
|
||||
final ResponseEntity<EdXPage> response = this.restTemplate.exchange(
|
||||
this.lmsSetup.lmsApiUrl + OPEN_EDX_DEFAULT_COURSE_ENDPOINT,
|
||||
HttpMethod.GET,
|
||||
new HttpEntity<>(httpHeaders),
|
||||
EdXPage.class);
|
||||
final EdXPage edxpage = response.getBody();
|
||||
final ResponseEntity<EdXPage> response = this.restTemplate.exchange(
|
||||
lmsSetup.lmsApiUrl + OPEN_EDX_DEFAULT_COURSE_ENDPOINT,
|
||||
HttpMethod.GET,
|
||||
new HttpEntity<>(httpHeaders),
|
||||
EdXPage.class);
|
||||
final EdXPage edxpage = response.getBody();
|
||||
|
||||
final List<QuizData> content = edxpage.results
|
||||
.stream()
|
||||
.reduce(
|
||||
new ArrayList<QuizData>(),
|
||||
(list, courseData) -> {
|
||||
list.add(quizDataOf(courseData));
|
||||
return list;
|
||||
},
|
||||
(list1, list2) -> {
|
||||
list1.addAll(list2);
|
||||
return list1;
|
||||
});
|
||||
final List<QuizData> content = edxpage.results
|
||||
.stream()
|
||||
.reduce(
|
||||
new ArrayList<QuizData>(),
|
||||
(list, courseData) -> {
|
||||
list.add(quizDataOf(lmsSetup, courseData));
|
||||
return list;
|
||||
},
|
||||
(list1, list2) -> {
|
||||
list1.addAll(list2);
|
||||
return list1;
|
||||
});
|
||||
|
||||
return new Page<>(edxpage.num_pages, pageNumber, sort, content);
|
||||
});
|
||||
return new Page<>(edxpage.num_pages, pageNumber, sort, content);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -166,45 +172,67 @@ final class OpenEdxLmsAPITemplate implements LmsAPITemplate {
|
|||
return null;
|
||||
}
|
||||
|
||||
private void initRestTemplateAndRequestAccessToken() {
|
||||
|
||||
log.info("Initialize Rest Template for OpenEdX API access. LmsSetup: {}", this.lmsSetup);
|
||||
|
||||
if (this.restTemplate != null) {
|
||||
try {
|
||||
this.restTemplate.getAccessToken();
|
||||
return;
|
||||
} catch (final Exception e) {
|
||||
log.warn(
|
||||
"Error while trying to get access token within already existing OAuth2RestTemplate instance. Try to create new one.",
|
||||
e);
|
||||
this.restTemplate = null;
|
||||
}
|
||||
}
|
||||
|
||||
final Iterator<String> tokenAccessPaths = this.knownTokenAccessPaths.iterator();
|
||||
while (this.restTemplate == null && tokenAccessPaths.hasNext()) {
|
||||
final String accessTokenRequestPath = tokenAccessPaths.next();
|
||||
try {
|
||||
final OAuth2RestTemplate template = createRestTemplate(accessTokenRequestPath);
|
||||
final OAuth2AccessToken accessToken = template.getAccessToken();
|
||||
if (accessToken != null) {
|
||||
this.restTemplate = template;
|
||||
storeAccessToken(accessToken);
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
log.info("Failed to request access token on access token request path: {}", accessTokenRequestPath, e);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void reset() {
|
||||
this.restTemplate = null;
|
||||
}
|
||||
|
||||
private OAuth2RestTemplate createRestTemplate(final String accessTokenRequestPath) {
|
||||
private Result<LmsSetup> initRestTemplateAndRequestAccessToken(final LmsSetup lmsSetup) {
|
||||
|
||||
final String lmsAuthSecret = this.internalEncryptionService.decrypt(this.lmsSetup.lmsAuthSecret);
|
||||
log.info("Initialize Rest Template for OpenEdX API access. LmsSetup: {}", lmsSetup);
|
||||
|
||||
return Result.tryCatch(() -> {
|
||||
if (this.restTemplate != null) {
|
||||
try {
|
||||
this.restTemplate.getAccessToken();
|
||||
return lmsSetup;
|
||||
} catch (final Exception e) {
|
||||
log.warn(
|
||||
"Error while trying to get access token within already existing OAuth2RestTemplate instance. Try to create new one.",
|
||||
e);
|
||||
this.restTemplate = null;
|
||||
}
|
||||
}
|
||||
|
||||
final Credentials credentials = this.lmsSetupDAO
|
||||
.getLmsAPIAccessCredentials(this.lmsSetupId)
|
||||
.getOrThrow();
|
||||
|
||||
final Iterator<String> tokenAccessPaths = this.knownTokenAccessPaths.iterator();
|
||||
while (tokenAccessPaths.hasNext()) {
|
||||
final String accessTokenRequestPath = tokenAccessPaths.next();
|
||||
try {
|
||||
|
||||
final OAuth2RestTemplate template = createRestTemplate(
|
||||
lmsSetup,
|
||||
credentials,
|
||||
accessTokenRequestPath);
|
||||
|
||||
final OAuth2AccessToken accessToken = template.getAccessToken();
|
||||
if (accessToken != null) {
|
||||
this.restTemplate = template;
|
||||
return lmsSetup;
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
log.info("Failed to request access token on access token request path: {}", accessTokenRequestPath,
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unable to establish OpenEdX API connection for lmsSetup: " + lmsSetup);
|
||||
});
|
||||
}
|
||||
|
||||
private OAuth2RestTemplate createRestTemplate(
|
||||
final LmsSetup lmsSetup,
|
||||
final LmsSetupDAO.Credentials credentials,
|
||||
final String accessTokenRequestPath) {
|
||||
|
||||
final String lmsAuthSecret = this.internalEncryptionService.decrypt(credentials.secret);
|
||||
|
||||
final ClientCredentialsResourceDetails details = new ClientCredentialsResourceDetails();
|
||||
details.setAccessTokenUri(this.lmsSetup.lmsApiUrl + accessTokenRequestPath);
|
||||
details.setClientId(this.lmsSetup.lmsAuthName);
|
||||
details.setAccessTokenUri(lmsSetup.lmsApiUrl + accessTokenRequestPath);
|
||||
details.setClientId(credentials.clientId);
|
||||
details.setClientSecret(lmsAuthSecret);
|
||||
details.setGrantType("client_credentials");
|
||||
|
||||
|
@ -214,51 +242,14 @@ final class OpenEdxLmsAPITemplate implements LmsAPITemplate {
|
|||
|
||||
final OAuth2RestTemplate template = new OAuth2RestTemplate(details);
|
||||
template.setRequestFactory(this.clientHttpRequestFactory);
|
||||
|
||||
final OAuth2AccessToken previousAccessToken = loadPreviousAccessToken();
|
||||
if (previousAccessToken != null) {
|
||||
template.getOAuth2ClientContext().setAccessToken(previousAccessToken);
|
||||
}
|
||||
return template;
|
||||
}
|
||||
|
||||
private void storeAccessToken(final OAuth2AccessToken accessToken) {
|
||||
try {
|
||||
private QuizData quizDataOf(
|
||||
final LmsSetup lmsSetup,
|
||||
final CourseData courseData) {
|
||||
|
||||
final String accessTokenString = accessToken.getValue();
|
||||
final TextEncryptor textEncryptor = Encryptors.text(this.lmsSetup.lmsAuthSecret, this.lmsSetup.lmsAuthName);
|
||||
final String accessTokenEncrypted = textEncryptor.encrypt(accessTokenString);
|
||||
|
||||
// TODO store the accessTokenEncrypted to additional attributes of LmsSetup
|
||||
|
||||
} catch (final Exception e) {
|
||||
log.warn("Failed to store access token for later use.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private OAuth2AccessToken loadPreviousAccessToken() {
|
||||
|
||||
// TODO get the previous token from additional attributes of LmsSetup
|
||||
final String prevTokenEncrypted = null;
|
||||
|
||||
if (StringUtils.isBlank(prevTokenEncrypted)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
final TextEncryptor textEncryptor = Encryptors.text(this.lmsSetup.lmsAuthSecret, this.lmsSetup.lmsAuthName);
|
||||
final String prevTokenDecrypt = textEncryptor.decrypt(prevTokenEncrypted);
|
||||
return new DefaultOAuth2AccessToken(prevTokenDecrypt);
|
||||
|
||||
} catch (final Exception e) {
|
||||
log.warn("Failed to decrypt previous access-token.", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private QuizData quizDataOf(final CourseData courseData) {
|
||||
final String startURI = this.lmsSetup.lmsApiUrl + OPEN_EDX_DEFAULT_COURSE_START_URL_PREFIX + courseData.id;
|
||||
final String startURI = lmsSetup.lmsApiUrl + OPEN_EDX_DEFAULT_COURSE_START_URL_PREFIX + courseData.id;
|
||||
return new QuizData(
|
||||
courseData.id,
|
||||
courseData.name,
|
||||
|
|
|
@ -16,6 +16,7 @@ import org.springframework.web.bind.annotation.RestController;
|
|||
|
||||
import ch.ethz.seb.sebserver.gbl.api.SEBServerRestEndpoints;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Domain.LMS_SETUP;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Entity;
|
||||
import ch.ethz.seb.sebserver.gbl.model.EntityType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Page;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.QuizData;
|
||||
|
@ -23,6 +24,7 @@ import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
|||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationGrantService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.PrivilegeType;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.UserService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate;
|
||||
|
||||
|
@ -51,6 +53,10 @@ public class QuizImportController {
|
|||
|
||||
@RequestMapping(method = RequestMethod.GET)
|
||||
public Page<QuizData> search(
|
||||
@RequestParam(
|
||||
name = Entity.FILTER_ATTR_INSTITUTION,
|
||||
required = true,
|
||||
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
|
||||
@RequestParam(name = LMS_SETUP.ATTR_ID, required = true) final Long lmsSetupId,
|
||||
@RequestParam(name = QuizData.FILTER_ATTR_NAME, required = false) final String nameLike,
|
||||
@RequestParam(name = QuizData.FILTER_ATTR_START_TIME, required = false) final String startTime,
|
||||
|
@ -65,7 +71,7 @@ public class QuizImportController {
|
|||
this.authorizationGrantService.checkPrivilege(
|
||||
EntityType.EXAM,
|
||||
PrivilegeType.READ_ONLY,
|
||||
lmsAPITemplate.lmsSetup().institutionId);
|
||||
institutionId);
|
||||
|
||||
return lmsAPITemplate.getQuizzes(
|
||||
nameLike,
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright (c) 2019 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;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
public class InternalEncryptionServiceTest {
|
||||
|
||||
@Test
|
||||
public void testEncryptSimpleSecret() {
|
||||
final Environment envMock = mock(Environment.class);
|
||||
when(envMock.getRequiredProperty(InternalEncryptionService.SEBSERVER_WEBSERVICE_INTERNAL_SECRET_KEY))
|
||||
.thenReturn("secret1");
|
||||
|
||||
final InternalEncryptionService service = new InternalEncryptionService(envMock);
|
||||
final String encrypt = service.encrypt("text1");
|
||||
final String decrypt = service.decrypt(encrypt);
|
||||
assertEquals("text1", decrypt);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue