SEBSERV-90 fornt-end implementation and fixes on back-end

This commit is contained in:
anhefti 2019-10-07 13:46:15 +02:00
parent 5d481d8933
commit 307177f426
9 changed files with 92 additions and 123 deletions

View file

@ -44,8 +44,8 @@ import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.springframework.util.ResourceUtils; import org.springframework.util.ResourceUtils;
import ch.ethz.seb.sebserver.gbl.api.Proxy; import ch.ethz.seb.sebserver.gbl.api.ProxyData;
import ch.ethz.seb.sebserver.gbl.api.Proxy.ProxyAuthType; import ch.ethz.seb.sebserver.gbl.api.ProxyData.ProxyAuthType;
import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Result;
@Service @Service
@ -66,7 +66,7 @@ public class ClientHttpRequestFactoryService {
return getClientHttpRequestFactory(null); return getClientHttpRequestFactory(null);
} }
public Result<ClientHttpRequestFactory> getClientHttpRequestFactory(final Proxy proxy) { public Result<ClientHttpRequestFactory> getClientHttpRequestFactory(final ProxyData proxy) {
return Result.tryCatch(() -> { return Result.tryCatch(() -> {
final List<String> activeProfiles = Arrays.asList(this.environment.getActiveProfiles()); final List<String> activeProfiles = Arrays.asList(this.environment.getActiveProfiles());
if (CollectionUtils.containsAny(activeProfiles, DEV_PROFILES)) { if (CollectionUtils.containsAny(activeProfiles, DEV_PROFILES)) {
@ -83,7 +83,7 @@ public class ClientHttpRequestFactoryService {
* not following redirects on redirect responses. * not following redirects on redirect responses.
* *
* @return ClientHttpRequestFactory bean for development profiles */ * @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"); log.info("Initialize ClientHttpRequestFactory with insecure ClientHttpRequestFactory for development");
@ -113,7 +113,7 @@ public class ClientHttpRequestFactoryService {
* @throws KeyStoreException * @throws KeyStoreException
* @throws NoSuchAlgorithmException * @throws NoSuchAlgorithmException
* @throws KeyManagementException */ * @throws KeyManagementException */
private ClientHttpRequestFactory clientHttpRequestFactoryTLS(final Proxy proxy) throws KeyManagementException, private ClientHttpRequestFactory clientHttpRequestFactoryTLS(final ProxyData proxy) throws KeyManagementException,
NoSuchAlgorithmException, KeyStoreException, CertificateException, FileNotFoundException, IOException { NoSuchAlgorithmException, KeyStoreException, CertificateException, FileNotFoundException, IOException {
log.info("Initialize with secure ClientHttpRequestFactory for production"); log.info("Initialize with secure ClientHttpRequestFactory for production");
@ -157,9 +157,6 @@ public class ClientHttpRequestFactoryService {
.build(); .build();
} }
final HttpClientBuilder clientBuilder = HttpClients.custom()
.setSSLContext(sslContext);
if (proxy.proxyAuthType != null && proxy.proxyAuthType != ProxyAuthType.NONE) { if (proxy.proxyAuthType != null && proxy.proxyAuthType != ProxyAuthType.NONE) {
log.info("Initialize ClientHttpRequestFactory with proxy auth: {} : {}", log.info("Initialize ClientHttpRequestFactory with proxy auth: {} : {}",
@ -178,14 +175,14 @@ public class ClientHttpRequestFactoryService {
} }
// TODO set connection and read timeout!? configurable!? // 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(); final CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials( credsProvider.setCredentials(
AuthScope.ANY, AuthScope.ANY,
new UsernamePasswordCredentials( new UsernamePasswordCredentials(
proxy.proxyAuthUsername, proxy.getProxyAuthUsernameAsString(),
proxy.proxyAuthSecret)); proxy.getProxyAuthSecretAsString()));
final HttpClientBuilder clientBuilder = HttpClients final HttpClientBuilder clientBuilder = HttpClients
.custom() .custom()

View file

@ -12,8 +12,6 @@ import java.io.IOException;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.error.ErrorController; import org.springframework.boot.web.servlet.error.ErrorController;
@ -42,8 +40,6 @@ import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
@Order(7) @Order(7)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements ErrorController { public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements ErrorController {
private static final Logger log = LoggerFactory.getLogger(WebSecurityConfig.class);
@Value("${sebserver.webservice.http.redirect.gui}") @Value("${sebserver.webservice.http.redirect.gui}")
private String guiRedirect; private String guiRedirect;
@Value("${sebserver.webservice.api.exam.endpoint.discovery}") @Value("${sebserver.webservice.api.exam.endpoint.discovery}")
@ -98,99 +94,4 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements E
public String getErrorPath() { public String getErrorPath() {
return "/error"; 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);
// }
// }
} }

View file

@ -8,7 +8,9 @@
package ch.ethz.seb.sebserver.gbl.api; package ch.ethz.seb.sebserver.gbl.api;
public class Proxy { import ch.ethz.seb.sebserver.gbl.util.Utils;
public class ProxyData {
public enum ProxyAuthType { public enum ProxyAuthType {
NONE, NONE,
@ -18,15 +20,15 @@ public class Proxy {
public final ProxyAuthType proxyAuthType; public final ProxyAuthType proxyAuthType;
public final String proxyName; public final String proxyName;
public final int proxyPort; public final int proxyPort;
public final String proxyAuthUsername; public final CharSequence proxyAuthUsername;
public final String proxyAuthSecret; public final CharSequence proxyAuthSecret;
protected Proxy( public ProxyData(
final ProxyAuthType proxyAuthType, final ProxyAuthType proxyAuthType,
final String proxyName, final String proxyName,
final int proxyPort, final int proxyPort,
final String proxyAuthUsername, final CharSequence proxyAuthUsername,
final String proxyAuthSecret) { final CharSequence proxyAuthSecret) {
; ;
this.proxyAuthType = proxyAuthType; this.proxyAuthType = proxyAuthType;
this.proxyName = proxyName; this.proxyName = proxyName;
@ -47,12 +49,20 @@ public class Proxy {
return this.proxyAuthType; return this.proxyAuthType;
} }
public String getProxyAuthUsername() { public CharSequence getProxyAuthUsername() {
return this.proxyAuthUsername; return this.proxyAuthUsername;
} }
public String getProxyAuthSecret() { public CharSequence getProxyAuthSecret() {
return this.proxyAuthSecret; return this.proxyAuthSecret;
} }
public String getProxyAuthUsernameAsString() {
return Utils.toString(this.proxyAuthUsername);
}
public String getProxyAuthSecretAsString() {
return Utils.toString(this.proxyAuthSecret);
}
} }

View file

@ -21,7 +21,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.api.POSTMapper; 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.Activatable;
import ch.ethz.seb.sebserver.gbl.model.Domain.INSTITUTION; import ch.ethz.seb.sebserver.gbl.model.Domain.INSTITUTION;
import ch.ethz.seb.sebserver.gbl.model.Domain.LMS_SETUP; import ch.ethz.seb.sebserver.gbl.model.Domain.LMS_SETUP;

View file

@ -19,6 +19,7 @@ import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.api.API; 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.Domain;
import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup;
@ -73,6 +74,12 @@ public class LmsSetupForm implements TemplateComposer {
new LocTextKey("sebserver.lmssetup.form.name"); new LocTextKey("sebserver.lmssetup.form.name");
private static final LocTextKey FORM_INSTITUTION_TEXT_KEY = private static final LocTextKey FORM_INSTITUTION_TEXT_KEY =
new LocTextKey("sebserver.lmssetup.form.institution"); 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 PageService pageService;
private final ResourceService resourceService; private final ResourceService resourceService;
@ -141,6 +148,7 @@ public class LmsSetupForm implements TemplateComposer {
// The LMS Setup form // The LMS Setup form
final LmsType lmsType = lmsSetup.getLmsType(); final LmsType lmsType = lmsSetup.getLmsType();
final ProxyAuthType proxyAuthType = lmsSetup.getProxyAuthType();
final FormHandle<LmsSetup> formHandle = this.pageService.formBuilder( final FormHandle<LmsSetup> formHandle = this.pageService.formBuilder(
formContext.copyOf(content), 4) formContext.copyOf(content), 4)
.readonly(readonly) .readonly(readonly)
@ -184,6 +192,20 @@ public class LmsSetupForm implements TemplateComposer {
FORM_SECRET_LMS_TEXT_KEY) FORM_SECRET_LMS_TEXT_KEY)
.asPasswordField()) .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) .buildFor((entityKey == null)
? restService.getRestCall(NewLmsSetup.class) ? restService.getRestCall(NewLmsSetup.class)
: restService.getRestCall(SaveLmsSetup.class)); : restService.getRestCall(SaveLmsSetup.class));

View file

@ -28,6 +28,7 @@ import org.springframework.stereotype.Service;
import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.EntityType; 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.Entity;
import ch.ethz.seb.sebserver.gbl.model.EntityName; import ch.ethz.seb.sebserver.gbl.model.EntityName;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam; 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 USERACCOUNT_ROLE_PREFIX = "sebserver.useraccount.role.";
public static final String EXAM_INDICATOR_TYPE_PREFIX = "sebserver.exam.indicator.type."; 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_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 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 USER_ACTIVITY_TYPE_PREFIX = "sebserver.overall.types.activityType.";
public static final String ENTITY_TYPE_PREFIX = "sebserver.overall.types.entityType."; public static final String ENTITY_TYPE_PREFIX = "sebserver.overall.types.entityType.";
@ -149,6 +152,16 @@ public class ResourceService {
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
public List<Tuple<String>> 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<Tuple<String>> clientEventTypeResources() { public List<Tuple<String>> clientEventTypeResources() {
return Arrays.asList(EventType.values()) return Arrays.asList(EventType.values())
.stream() .stream()

View file

@ -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;
import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException; import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException;
import ch.ethz.seb.sebserver.gbl.api.EntityType; 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.Domain;
import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup;

View file

@ -50,6 +50,8 @@ import org.springframework.util.MultiValueMap;
import ch.ethz.seb.sebserver.ClientHttpRequestFactoryService; import ch.ethz.seb.sebserver.ClientHttpRequestFactoryService;
import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.APIMessage; 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.AsyncService;
import ch.ethz.seb.sebserver.gbl.async.MemoizingCircuitBreaker; import ch.ethz.seb.sebserver.gbl.async.MemoizingCircuitBreaker;
import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; import ch.ethz.seb.sebserver.gbl.model.exam.QuizData;
@ -226,9 +228,28 @@ final class OpenEdxLmsAPITemplate implements LmsAPITemplate {
details.setClientSecret(plainClientSecret.toString()); details.setClientSecret(plainClientSecret.toString());
// TODO get with proxy configuration if applied in LMSSetup // TODO get with proxy configuration if applied in LMSSetup
final ClientHttpRequestFactory clientHttpRequestFactory = this.clientHttpRequestFactoryService ClientHttpRequestFactory clientHttpRequestFactory = null;
.getClientHttpRequestFactory() if (lmsSetup.proxyAuthType != ProxyAuthType.NONE) {
.getOrThrow(); 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); final OAuth2RestTemplate template = new OAuth2RestTemplate(details);
template.setRequestFactory(clientHttpRequestFactory); template.setRequestFactory(clientHttpRequestFactory);

View file

@ -237,6 +237,11 @@ sebserver.lmssetup.form.secret.seb=SEB Auth. Password
sebserver.lmssetup.form.url=LMS Server Address sebserver.lmssetup.form.url=LMS Server Address
sebserver.lmssetup.form.clientname.lms=LMS Server Username sebserver.lmssetup.form.clientname.lms=LMS Server Username
sebserver.lmssetup.form.secret.lms=LMS Server Password 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 # Quiz Discovery