From 307177f426d9eb3e557ed43c2a08c726974bb17a Mon Sep 17 00:00:00 2001 From: anhefti Date: Mon, 7 Oct 2019 13:46:15 +0200 Subject: [PATCH] SEBSERV-90 fornt-end implementation and fixes on back-end --- .../ClientHttpRequestFactoryService.java | 19 ++-- .../ethz/seb/sebserver/WebSecurityConfig.java | 99 ------------------- .../gbl/api/{Proxy.java => ProxyData.java} | 26 +++-- .../gbl/model/institution/LmsSetup.java | 2 +- .../sebserver/gui/content/LmsSetupForm.java | 22 +++++ .../gui/service/ResourceService.java | 13 +++ .../dao/impl/LmsSetupDAOImpl.java | 2 +- .../lms/impl/OpenEdxLmsAPITemplate.java | 27 ++++- src/main/resources/messages.properties | 5 + 9 files changed, 92 insertions(+), 123 deletions(-) rename src/main/java/ch/ethz/seb/sebserver/gbl/api/{Proxy.java => ProxyData.java} (63%) diff --git a/src/main/java/ch/ethz/seb/sebserver/ClientHttpRequestFactoryService.java b/src/main/java/ch/ethz/seb/sebserver/ClientHttpRequestFactoryService.java index 143f6786..e444ce81 100644 --- a/src/main/java/ch/ethz/seb/sebserver/ClientHttpRequestFactoryService.java +++ b/src/main/java/ch/ethz/seb/sebserver/ClientHttpRequestFactoryService.java @@ -44,8 +44,8 @@ import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import org.springframework.util.ResourceUtils; -import ch.ethz.seb.sebserver.gbl.api.Proxy; -import ch.ethz.seb.sebserver.gbl.api.Proxy.ProxyAuthType; +import ch.ethz.seb.sebserver.gbl.api.ProxyData; +import ch.ethz.seb.sebserver.gbl.api.ProxyData.ProxyAuthType; import ch.ethz.seb.sebserver.gbl.util.Result; @Service @@ -66,7 +66,7 @@ public class ClientHttpRequestFactoryService { return getClientHttpRequestFactory(null); } - public Result getClientHttpRequestFactory(final Proxy proxy) { + public Result getClientHttpRequestFactory(final ProxyData proxy) { return Result.tryCatch(() -> { final List activeProfiles = Arrays.asList(this.environment.getActiveProfiles()); if (CollectionUtils.containsAny(activeProfiles, DEV_PROFILES)) { @@ -83,7 +83,7 @@ public class ClientHttpRequestFactoryService { * not following redirects on redirect responses. * * @return ClientHttpRequestFactory bean for development profiles */ - private ClientHttpRequestFactory clientHttpRequestFactory(final Proxy proxy) { + private ClientHttpRequestFactory clientHttpRequestFactory(final ProxyData proxy) { log.info("Initialize ClientHttpRequestFactory with insecure ClientHttpRequestFactory for development"); @@ -113,7 +113,7 @@ public class ClientHttpRequestFactoryService { * @throws KeyStoreException * @throws NoSuchAlgorithmException * @throws KeyManagementException */ - private ClientHttpRequestFactory clientHttpRequestFactoryTLS(final Proxy proxy) throws KeyManagementException, + private ClientHttpRequestFactory clientHttpRequestFactoryTLS(final ProxyData proxy) throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException, CertificateException, FileNotFoundException, IOException { log.info("Initialize with secure ClientHttpRequestFactory for production"); @@ -157,9 +157,6 @@ public class ClientHttpRequestFactoryService { .build(); } - final HttpClientBuilder clientBuilder = HttpClients.custom() - .setSSLContext(sslContext); - if (proxy.proxyAuthType != null && proxy.proxyAuthType != ProxyAuthType.NONE) { log.info("Initialize ClientHttpRequestFactory with proxy auth: {} : {}", @@ -178,14 +175,14 @@ public class ClientHttpRequestFactoryService { } // TODO set connection and read timeout!? configurable!? - private HttpClient createProxiedClient(final Proxy proxy, final SSLContext sslContext) { + private HttpClient createProxiedClient(final ProxyData proxy, final SSLContext sslContext) { final CredentialsProvider credsProvider = new BasicCredentialsProvider(); credsProvider.setCredentials( AuthScope.ANY, new UsernamePasswordCredentials( - proxy.proxyAuthUsername, - proxy.proxyAuthSecret)); + proxy.getProxyAuthUsernameAsString(), + proxy.getProxyAuthSecretAsString())); final HttpClientBuilder clientBuilder = HttpClients .custom() diff --git a/src/main/java/ch/ethz/seb/sebserver/WebSecurityConfig.java b/src/main/java/ch/ethz/seb/sebserver/WebSecurityConfig.java index 22ce597c..7912beba 100644 --- a/src/main/java/ch/ethz/seb/sebserver/WebSecurityConfig.java +++ b/src/main/java/ch/ethz/seb/sebserver/WebSecurityConfig.java @@ -12,8 +12,6 @@ import java.io.IOException; import javax.servlet.http.HttpServletResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.error.ErrorController; @@ -42,8 +40,6 @@ import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; @Order(7) public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements ErrorController { - private static final Logger log = LoggerFactory.getLogger(WebSecurityConfig.class); - @Value("${sebserver.webservice.http.redirect.gui}") private String guiRedirect; @Value("${sebserver.webservice.api.exam.endpoint.discovery}") @@ -98,99 +94,4 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements E public String getErrorPath() { return "/error"; } - -// /** A ClientHttpRequestFactory for development profile with no TSL SSL protocol and -// * not following redirects on redirect responses. -// * -// * @return ClientHttpRequestFactory bean for development profiles */ -// @Bean -// @DevGuiProfile -// @DevWebServiceProfile -// public ClientHttpRequestFactory clientHttpRequestFactory() { -// -// log.info("Initialize with insecure ClientHttpRequestFactory for development"); -// -// final DevClientHttpRequestFactory devClientHttpRequestFactory = new DevClientHttpRequestFactory(); -// devClientHttpRequestFactory.setOutputStreaming(false); -// return devClientHttpRequestFactory; -// } -// -// /** A ClientHttpRequestFactory used in production with TSL SSL configuration. -// * -// * @return ClientHttpRequestFactory with TLS / SSL configuration -// * @throws IOException -// * @throws FileNotFoundException -// * @throws CertificateException -// * @throws KeyStoreException -// * @throws NoSuchAlgorithmException -// * @throws KeyManagementException */ -// @Bean -// @ProdGuiProfile -// @ProdWebServiceProfile -// public ClientHttpRequestFactory clientHttpRequestFactoryTLS(final Environment env) throws KeyManagementException, -// NoSuchAlgorithmException, KeyStoreException, CertificateException, FileNotFoundException, IOException { -// -// log.info("Initialize with secure ClientHttpRequestFactory for production"); -// -// final String truststoreFilePath = env -// .getProperty("server.ssl.trust-store", ""); -// -// SSLContext sslContext = null; -// if (StringUtils.isBlank(truststoreFilePath)) { -// -// log.info("Securing outgoing calls without trust-store by trusting all certificates"); -// -// sslContext = org.apache.http.ssl.SSLContexts -// .custom() -// .loadTrustMaterial(null, new TrustAllStrategy()) -// .build(); -// -// } else { -// -// log.info("Securing with defined trust-store"); -// -// final File trustStoreFile = ResourceUtils.getFile("file:" + truststoreFilePath); -// -// final char[] password = env -// .getProperty("server.ssl.trust-store-password", "") -// .toCharArray(); -// -// if (password.length < 3) { -// log.error("Missing or incorrect trust-store password: " + String.valueOf(password)); -// throw new IllegalArgumentException("Missing or incorrect trust-store password"); -// } -// -// // Set the specified trust-store also on javax.net.ssl level -// System.setProperty("javax.net.ssl.trustStore", truststoreFilePath); -// System.setProperty("javax.net.ssl.trustStorePassword", String.valueOf(password)); -// -// sslContext = SSLContextBuilder -// .create() -// .loadTrustMaterial(trustStoreFile, password) -// .setKeyStoreType("pkcs12") -// .build(); -// } -// -// final HttpClient client = HttpClients.custom() -// .setSSLContext(sslContext) -// .build(); -// -// // TODO set connection and read timeout!? configurable!? -// return new HttpComponentsClientHttpRequestFactory(client); -// } -// -// // TODO set connection and read timeout!? configurable!? -// private static class DevClientHttpRequestFactory extends SimpleClientHttpRequestFactory { -// -// @Override -// protected void prepareConnection( -// final HttpURLConnection connection, -// final String httpMethod) throws IOException { -// -// super.prepareConnection(connection, httpMethod); -// super.setBufferRequestBody(false); -// connection.setInstanceFollowRedirects(false); -// } -// } - } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/api/Proxy.java b/src/main/java/ch/ethz/seb/sebserver/gbl/api/ProxyData.java similarity index 63% rename from src/main/java/ch/ethz/seb/sebserver/gbl/api/Proxy.java rename to src/main/java/ch/ethz/seb/sebserver/gbl/api/ProxyData.java index 7d3649eb..c935bf66 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/api/Proxy.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/api/ProxyData.java @@ -8,7 +8,9 @@ package ch.ethz.seb.sebserver.gbl.api; -public class Proxy { +import ch.ethz.seb.sebserver.gbl.util.Utils; + +public class ProxyData { public enum ProxyAuthType { NONE, @@ -18,15 +20,15 @@ public class Proxy { public final ProxyAuthType proxyAuthType; public final String proxyName; public final int proxyPort; - public final String proxyAuthUsername; - public final String proxyAuthSecret; + public final CharSequence proxyAuthUsername; + public final CharSequence proxyAuthSecret; - protected Proxy( + public ProxyData( final ProxyAuthType proxyAuthType, final String proxyName, final int proxyPort, - final String proxyAuthUsername, - final String proxyAuthSecret) { + final CharSequence proxyAuthUsername, + final CharSequence proxyAuthSecret) { ; this.proxyAuthType = proxyAuthType; this.proxyName = proxyName; @@ -47,12 +49,20 @@ public class Proxy { return this.proxyAuthType; } - public String getProxyAuthUsername() { + public CharSequence getProxyAuthUsername() { return this.proxyAuthUsername; } - public String getProxyAuthSecret() { + public CharSequence getProxyAuthSecret() { return this.proxyAuthSecret; } + public String getProxyAuthUsernameAsString() { + return Utils.toString(this.proxyAuthUsername); + } + + public String getProxyAuthSecretAsString() { + return Utils.toString(this.proxyAuthSecret); + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/institution/LmsSetup.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/institution/LmsSetup.java index e4a77176..4ce94703 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/institution/LmsSetup.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/institution/LmsSetup.java @@ -21,7 +21,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.api.POSTMapper; -import ch.ethz.seb.sebserver.gbl.api.Proxy.ProxyAuthType; +import ch.ethz.seb.sebserver.gbl.api.ProxyData.ProxyAuthType; import ch.ethz.seb.sebserver.gbl.model.Activatable; import ch.ethz.seb.sebserver.gbl.model.Domain.INSTITUTION; import ch.ethz.seb.sebserver.gbl.model.Domain.LMS_SETUP; diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/LmsSetupForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/LmsSetupForm.java index 1a92c70a..da545927 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/LmsSetupForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/LmsSetupForm.java @@ -19,6 +19,7 @@ import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import ch.ethz.seb.sebserver.gbl.api.API; +import ch.ethz.seb.sebserver.gbl.api.ProxyData.ProxyAuthType; import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup; @@ -73,6 +74,12 @@ public class LmsSetupForm implements TemplateComposer { new LocTextKey("sebserver.lmssetup.form.name"); private static final LocTextKey FORM_INSTITUTION_TEXT_KEY = new LocTextKey("sebserver.lmssetup.form.institution"); + private static final LocTextKey FORM_PROXY_AUTH_TYPE_KEY = + new LocTextKey("sebserver.lmssetup.form.proxy.auth-type"); + private static final LocTextKey FORM_PROXY_AUTH_NAME_KEY = + new LocTextKey("sebserver.lmssetup.form.proxy.auth-name"); + private static final LocTextKey FORM_PROXY_AUTH_PASS_KEY = + new LocTextKey("sebserver.lmssetup.form.proxy.auth-secret"); private final PageService pageService; private final ResourceService resourceService; @@ -141,6 +148,7 @@ public class LmsSetupForm implements TemplateComposer { // The LMS Setup form final LmsType lmsType = lmsSetup.getLmsType(); + final ProxyAuthType proxyAuthType = lmsSetup.getProxyAuthType(); final FormHandle formHandle = this.pageService.formBuilder( formContext.copyOf(content), 4) .readonly(readonly) @@ -184,6 +192,20 @@ public class LmsSetupForm implements TemplateComposer { FORM_SECRET_LMS_TEXT_KEY) .asPasswordField()) + .addField(FormBuilder.singleSelection( + Domain.LMS_SETUP.ATTR_LMS_PROXY_AUTH_TYPE, + FORM_PROXY_AUTH_TYPE_KEY, + (proxyAuthType != null) ? proxyAuthType.name() : null, + this.resourceService::lmsProxyAuthTypeResources)) + .addField(FormBuilder.text( + Domain.LMS_SETUP.ATTR_LMS_PROXY_AUTH_USERNAME, + FORM_PROXY_AUTH_NAME_KEY, + (lmsSetup.getProxyAuthUsername() != null) ? lmsSetup.getProxyAuthUsername() : null)) + .addField(FormBuilder.text( + Domain.LMS_SETUP.ATTR_LMS_PROXY_AUTH_SECRET, + FORM_PROXY_AUTH_PASS_KEY) + .asPasswordField()) + .buildFor((entityKey == null) ? restService.getRestCall(NewLmsSetup.class) : restService.getRestCall(SaveLmsSetup.class)); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/ResourceService.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/ResourceService.java index 757853bf..42428f77 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/ResourceService.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/ResourceService.java @@ -28,6 +28,7 @@ import org.springframework.stereotype.Service; import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.api.ProxyData.ProxyAuthType; import ch.ethz.seb.sebserver.gbl.model.Entity; import ch.ethz.seb.sebserver.gbl.model.EntityName; import ch.ethz.seb.sebserver.gbl.model.exam.Exam; @@ -89,6 +90,8 @@ public class ResourceService { public static final String USERACCOUNT_ROLE_PREFIX = "sebserver.useraccount.role."; public static final String EXAM_INDICATOR_TYPE_PREFIX = "sebserver.exam.indicator.type."; public static final String LMSSETUP_TYPE_PREFIX = "sebserver.lmssetup.type."; + public static final String LMSSETUP_PROXY_AUTH_TYPE_PREFIX = "sebserver.lmssetup.form.proxy.auth-type."; + public static final String CLIENT_EVENT_TYPE_PREFIX = "sebserver.monitoring.exam.connection.event.type."; public static final String USER_ACTIVITY_TYPE_PREFIX = "sebserver.overall.types.activityType."; public static final String ENTITY_TYPE_PREFIX = "sebserver.overall.types.entityType."; @@ -149,6 +152,16 @@ public class ResourceService { .collect(Collectors.toList()); } + public List> lmsProxyAuthTypeResources() { + return Arrays.asList(ProxyAuthType.values()) + .stream() + .map(authType -> new Tuple<>( + authType.name(), + this.i18nSupport.getText(LMSSETUP_PROXY_AUTH_TYPE_PREFIX + authType.name(), authType.name()))) + .sorted(RESOURCE_COMPARATOR) + .collect(Collectors.toList()); + } + public List> clientEventTypeResources() { return Arrays.asList(EventType.values()) .stream() diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/LmsSetupDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/LmsSetupDAOImpl.java index 3b2e4ce5..7e8bddf9 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/LmsSetupDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/LmsSetupDAOImpl.java @@ -27,7 +27,7 @@ import org.springframework.transaction.annotation.Transactional; import ch.ethz.seb.sebserver.gbl.api.APIMessage; import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException; import ch.ethz.seb.sebserver.gbl.api.EntityType; -import ch.ethz.seb.sebserver.gbl.api.Proxy.ProxyAuthType; +import ch.ethz.seb.sebserver.gbl.api.ProxyData.ProxyAuthType; import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup; diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/OpenEdxLmsAPITemplate.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/OpenEdxLmsAPITemplate.java index bc13d3fb..c299287d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/OpenEdxLmsAPITemplate.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/OpenEdxLmsAPITemplate.java @@ -50,6 +50,8 @@ import org.springframework.util.MultiValueMap; import ch.ethz.seb.sebserver.ClientHttpRequestFactoryService; import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.api.APIMessage; +import ch.ethz.seb.sebserver.gbl.api.ProxyData; +import ch.ethz.seb.sebserver.gbl.api.ProxyData.ProxyAuthType; import ch.ethz.seb.sebserver.gbl.async.AsyncService; import ch.ethz.seb.sebserver.gbl.async.MemoizingCircuitBreaker; import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; @@ -226,9 +228,28 @@ final class OpenEdxLmsAPITemplate implements LmsAPITemplate { details.setClientSecret(plainClientSecret.toString()); // TODO get with proxy configuration if applied in LMSSetup - final ClientHttpRequestFactory clientHttpRequestFactory = this.clientHttpRequestFactoryService - .getClientHttpRequestFactory() - .getOrThrow(); + ClientHttpRequestFactory clientHttpRequestFactory = null; + if (lmsSetup.proxyAuthType != ProxyAuthType.NONE) { + final ClientCredentials proxyCredentials = new ClientCredentials( + lmsSetup.proxyAuthUsername, + lmsSetup.proxyAuthSecret); + + final CharSequence proxySecretPlain = this.clientCredentialService.getPlainClientSecret(proxyCredentials); + final ProxyData proxyData = new ProxyData( + lmsSetup.proxyAuthType, + null, + -1, + proxyCredentials.clientId, + proxySecretPlain); + + clientHttpRequestFactory = this.clientHttpRequestFactoryService + .getClientHttpRequestFactory(proxyData) + .getOrThrow(); + } else { + clientHttpRequestFactory = this.clientHttpRequestFactoryService + .getClientHttpRequestFactory() + .getOrThrow(); + } final OAuth2RestTemplate template = new OAuth2RestTemplate(details); template.setRequestFactory(clientHttpRequestFactory); diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index 991aea8a..45f5a7f2 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -237,6 +237,11 @@ sebserver.lmssetup.form.secret.seb=SEB Auth. Password sebserver.lmssetup.form.url=LMS Server Address sebserver.lmssetup.form.clientname.lms=LMS Server Username sebserver.lmssetup.form.secret.lms=LMS Server Password +sebserver.lmssetup.form.proxy.auth-type=Proxy Authentication +sebserver.lmssetup.form.proxy.auth-type.NONE=None +sebserver.lmssetup.form.proxy.auth-type.BASIC_AUTH=Basic Authentication +sebserver.lmssetup.form.proxy.auth-name=Proxy Username +sebserver.lmssetup.form.proxy.auth-secret=Proxy Password ################################ # Quiz Discovery