You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dlab.apache.org by bh...@apache.org on 2019/07/25 11:31:30 UTC
[incubator-dlab] 08/12: DLAB-2 added backend for web terminal
This is an automated email from the ASF dual-hosted git repository.
bhliva pushed a commit to branch v2.1.1
in repository https://gitbox.apache.org/repos/asf/incubator-dlab.git
commit 84ee64251853fe78e0f7339fcd661e8a09194d91
Author: bhliva <bo...@epam.com>
AuthorDate: Thu May 2 15:21:23 2019 +0300
DLAB-2 added backend for web terminal
---
services/self-service/pom.xml | 6 +++
services/self-service/self-service.yml | 22 +++++++--
.../dlab/backendapi/SelfServiceApplication.java | 13 +++++
.../SelfServiceApplicationConfiguration.java | 16 +++++++
.../epam/dlab/backendapi/modules/DevModule.java | 17 +++++--
.../dlab/backendapi/modules/ProductionModule.java | 1 +
.../dlab/backendapi/service/GuacamoleService.java | 10 ++++
.../service/impl/GuacamoleServiceImpl.java | 56 ++++++++++++++++++++++
.../servlet/guacamole/GuacamoleSecurityFilter.java | 56 ++++++++++++++++++++++
.../servlet/guacamole/GuacamoleServlet.java | 32 +++++++++++++
10 files changed, 219 insertions(+), 10 deletions(-)
diff --git a/services/self-service/pom.xml b/services/self-service/pom.xml
index e1190a6..5521958 100644
--- a/services/self-service/pom.xml
+++ b/services/self-service/pom.xml
@@ -161,6 +161,12 @@
</exclusions>
<scope>test</scope>
</dependency>
+
+ <dependency>
+ <groupId>org.apache.guacamole</groupId>
+ <artifactId>guacamole-common</artifactId>
+ <version>1.0.0</version>
+ </dependency>
</dependencies>
<build>
diff --git a/services/self-service/self-service.yml b/services/self-service/self-service.yml
index 8a2fc60..8ae3c02 100644
--- a/services/self-service/self-service.yml
+++ b/services/self-service/self-service.yml
@@ -67,6 +67,8 @@ maxSessionDurabilityMilliseconds: 288000000
</#if>
server:
+ gzip:
+ enabled: false
requestLog:
appenders:
- type: file
@@ -76,8 +78,8 @@ server:
archivedFileCount: 10
rootPath: "/api"
applicationConnectors:
-# - type: http
-# port: 8080
+ # - type: http
+ # port: 8080
- type: https
port: 8443
certAlias: dlab
@@ -87,8 +89,8 @@ server:
trustStorePath: ${TRUST_STORE_PATH}
trustStorePassword: ${TRUST_STORE_PASSWORD}
adminConnectors:
-# - type: http
-# port: 8081
+ # - type: http
+ # port: 8081
- type: https
port: 8444
certAlias: dlab
@@ -105,6 +107,7 @@ logging:
loggers:
com.epam: TRACE
com.novemberain: ERROR
+ org.apache.guacamole: TRACE
appenders:
<#if DEV_MODE == "true">
- type: console
@@ -158,4 +161,13 @@ schedulers:
cron: "0 0 * ? * * *"
checkUserQuoteScheduler:
enabled: true
- cron: "0 0 * ? * * *"
\ No newline at end of file
+ cron: "0 0 * ? * * *"
+
+guacamole:
+ connectionProtocol: ssh
+ serverHost: localhost
+ serverPort: 4822
+ port: 22
+ username: dlab-user
+
+
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/SelfServiceApplication.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/SelfServiceApplication.java
index 7df4e47..1a8f59b 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/SelfServiceApplication.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/SelfServiceApplication.java
@@ -29,6 +29,8 @@ import com.epam.dlab.backendapi.modules.ModuleFactory;
import com.epam.dlab.backendapi.resources.*;
import com.epam.dlab.backendapi.resources.callback.*;
import com.epam.dlab.backendapi.schedulers.internal.ManagedScheduler;
+import com.epam.dlab.backendapi.servlet.guacamole.GuacamoleSecurityFilter;
+import com.epam.dlab.backendapi.servlet.guacamole.GuacamoleServlet;
import com.epam.dlab.cloud.CloudModule;
import com.epam.dlab.constants.ServiceConsts;
import com.epam.dlab.migration.mongo.DlabMongoMigration;
@@ -52,6 +54,9 @@ import io.federecio.dropwizard.swagger.SwaggerBundle;
import io.federecio.dropwizard.swagger.SwaggerBundleConfiguration;
import lombok.extern.slf4j.Slf4j;
+import javax.servlet.DispatcherType;
+import java.util.EnumSet;
+
/**
* Self Service based on Dropwizard application.
*/
@@ -113,6 +118,14 @@ public class SelfServiceApplication extends Application<SelfServiceApplicationCo
environment.healthChecks().register(
ServiceConsts.PROVISIONING_SERVICE_NAME, injector.getInstance(ProvisioningServiceHealthCheck.class));
+ final String guacamoleServletName = "GuacamoleServlet";
+ environment.servlets().addServlet(guacamoleServletName, injector.getInstance(GuacamoleServlet.class))
+ .addMapping("/api/tunnel");
+ environment.servlets().addFilter("GuacamoleSecurityFilter",
+ injector.getInstance(GuacamoleSecurityFilter.class))
+ .addMappingForServletNames(EnumSet.allOf(DispatcherType.class), true, guacamoleServletName);
+
+
JerseyEnvironment jersey = environment.jersey();
jersey.register(new RuntimeExceptionMapper());
jersey.register(new JsonProcessingExceptionMapper());
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/SelfServiceApplicationConfiguration.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/SelfServiceApplicationConfiguration.java
index 78a074b..6499b67 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/SelfServiceApplicationConfiguration.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/SelfServiceApplicationConfiguration.java
@@ -121,6 +121,22 @@ public class SelfServiceApplicationConfiguration extends ServiceConfiguration {
@JsonProperty(ServiceConsts.MAVEN_SEARCH_API)
private RESTServiceFactory mavenApiFactory;
+ @Valid
+ @NotNull
+ private Map<String, String> guacamole;
+
+ public Map<String, String> getGuacamole() {
+ return guacamole;
+ }
+
+ public String getGuacamoleHost() {
+ return guacamole.get("serverHost");
+ }
+
+ public Integer getGuacamolePort() {
+ return Integer.valueOf(guacamole.get("serverPort"));
+ }
+
@JsonProperty("jerseyClient")
public JerseyClientConfiguration getJerseyClientConfiguration() {
return jerseyClient;
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/modules/DevModule.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/modules/DevModule.java
index 27c38e4..bd4a5ab 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/modules/DevModule.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/modules/DevModule.java
@@ -109,6 +109,7 @@ public class DevModule extends ModuleBase<SelfServiceApplicationConfiguration> i
bind(UserGroupDao.class).to(UserGroupDaoImpl.class);
bind(ApplicationSettingService.class).to(ApplicationSettingServiceImpl.class);
bind(UserSettingService.class).to(UserSettingServiceImpl.class);
+ bind(GuacamoleService.class).to(GuacamoleServiceImpl.class);
}
/**
@@ -133,6 +134,8 @@ public class DevModule extends ModuleBase<SelfServiceApplicationConfiguration> i
return authorize((UserCredentialDTO) parameter);
} else if (GET_USER_INFO.equals(path) && TOKEN.equals(parameter) && clazz.equals(UserInfo.class)) {
return (T) getUserInfo();
+ } else if (GET_USER_INFO.equals(path) && !TOKEN.equals(parameter) && clazz.equals(UserInfo.class)) {
+ return null;
} else if (LOGOUT.equals(path)) {
return (T) Response.ok().build();
}
@@ -144,14 +147,18 @@ public class DevModule extends ModuleBase<SelfServiceApplicationConfiguration> i
if (LOGIN_NAME.equals(credential.getUsername())) {
return (T) Response.ok(TOKEN).build();
} else {
- return (T) Response.status(Response.Status.UNAUTHORIZED)
- .entity(new ErrorDTO(Response.Status.UNAUTHORIZED.getStatusCode(), "Username or password" +
- " is invalid"))
- .type(MediaType.APPLICATION_JSON_TYPE)
- .build();
+ return (T) unauthorized();
}
}
+ private Response unauthorized() {
+ return Response.status(Response.Status.UNAUTHORIZED)
+ .entity(new ErrorDTO(Response.Status.UNAUTHORIZED.getStatusCode(), "Username or password" +
+ " is invalid"))
+ .type(MediaType.APPLICATION_JSON_TYPE)
+ .build();
+ }
+
@Override
public <T> T get(String path, Class<T> clazz) {
throw new UnsupportedOperationException(OPERATION_IS_NOT_SUPPORTED);
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/modules/ProductionModule.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/modules/ProductionModule.java
index e3ed4dc..7db5b26 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/modules/ProductionModule.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/modules/ProductionModule.java
@@ -86,5 +86,6 @@ public class ProductionModule extends ModuleBase<SelfServiceApplicationConfigura
bind(InactivityService.class).to(InactivityServiceImpl.class);
bind(ApplicationSettingService.class).to(ApplicationSettingServiceImpl.class);
bind(UserSettingService.class).to(UserSettingServiceImpl.class);
+ bind(GuacamoleService.class).to(GuacamoleServiceImpl.class);
}
}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/GuacamoleService.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/GuacamoleService.java
new file mode 100644
index 0000000..760e701
--- /dev/null
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/GuacamoleService.java
@@ -0,0 +1,10 @@
+package com.epam.dlab.backendapi.service;
+
+import com.epam.dlab.auth.UserInfo;
+import org.apache.guacamole.net.GuacamoleTunnel;
+
+public interface GuacamoleService {
+
+ GuacamoleTunnel getTunnel(UserInfo userInfo, String host);
+
+}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/GuacamoleServiceImpl.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/GuacamoleServiceImpl.java
new file mode 100644
index 0000000..7292646
--- /dev/null
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/GuacamoleServiceImpl.java
@@ -0,0 +1,56 @@
+package com.epam.dlab.backendapi.service.impl;
+
+import com.epam.dlab.auth.UserInfo;
+import com.epam.dlab.backendapi.SelfServiceApplicationConfiguration;
+import com.epam.dlab.backendapi.service.GuacamoleService;
+import com.epam.dlab.exceptions.DlabException;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.guacamole.net.GuacamoleTunnel;
+import org.apache.guacamole.net.InetGuacamoleSocket;
+import org.apache.guacamole.net.SimpleGuacamoleTunnel;
+import org.apache.guacamole.protocol.ConfiguredGuacamoleSocket;
+import org.apache.guacamole.protocol.GuacamoleConfiguration;
+
+import java.util.Map;
+
+@Slf4j
+@Singleton
+public class GuacamoleServiceImpl implements GuacamoleService {
+
+ private static final String PRIVATE_KEY_PARAM_NAME = "private-key";
+ private static final String HOSTNAME_PARAM = "hostname";
+ private static final String CONNECTION_PROTOCOL_PARAM = "connectionProtocol";
+ private final SelfServiceApplicationConfiguration conf;
+
+ @Inject
+ public GuacamoleServiceImpl(SelfServiceApplicationConfiguration conf) {
+ this.conf = conf;
+ }
+
+ @Override
+ public GuacamoleTunnel getTunnel(UserInfo userInfo, String host) {
+ try {
+ final String privateKeyContent = "";// TODO figure out from which place private key should be taken
+ final InetGuacamoleSocket socket = new InetGuacamoleSocket(conf.getGuacamoleHost(),
+ conf.getGuacamolePort());
+ final GuacamoleConfiguration guacamoleConfig = getGuacamoleConfig(privateKeyContent, conf.getGuacamole(),
+ host);
+ return new SimpleGuacamoleTunnel(new ConfiguredGuacamoleSocket(socket, guacamoleConfig));
+ } catch (Exception e) {
+ log.error("Can not create guacamole tunnel due to: " + e.getMessage());
+ throw new DlabException("Can not create guacamole tunnel due to: " + e.getMessage(), e);
+ }
+ }
+
+ private GuacamoleConfiguration getGuacamoleConfig(String privateKeyContent, Map<String, String> guacamoleParams,
+ String host) {
+ GuacamoleConfiguration guacamoleConfiguration = new GuacamoleConfiguration();
+ guacamoleConfiguration.setProtocol(guacamoleParams.get(CONNECTION_PROTOCOL_PARAM));
+ guacamoleConfiguration.setParameters(guacamoleParams);
+ guacamoleConfiguration.setParameter(HOSTNAME_PARAM, host);
+ guacamoleConfiguration.setParameter(PRIVATE_KEY_PARAM_NAME, privateKeyContent);
+ return guacamoleConfiguration;
+ }
+}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/servlet/guacamole/GuacamoleSecurityFilter.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/servlet/guacamole/GuacamoleSecurityFilter.java
new file mode 100644
index 0000000..4545ed7
--- /dev/null
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/servlet/guacamole/GuacamoleSecurityFilter.java
@@ -0,0 +1,56 @@
+package com.epam.dlab.backendapi.servlet.guacamole;
+
+import com.epam.dlab.auth.UserInfo;
+import com.epam.dlab.backendapi.auth.SelfServiceSecurityAuthenticator;
+import com.google.inject.Inject;
+import io.dropwizard.auth.AuthenticationException;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.core.HttpHeaders;
+import java.io.IOException;
+import java.util.Optional;
+
+@Slf4j
+public class GuacamoleSecurityFilter implements Filter {
+ private static final String AUTH_HEADER_PREFIX = "Bearer ";
+ private final SelfServiceSecurityAuthenticator authenticator;
+
+ @Inject
+ public GuacamoleSecurityFilter(SelfServiceSecurityAuthenticator authenticator) {
+ this.authenticator = authenticator;
+ }
+
+ @Override
+ public void init(FilterConfig filterConfig) {
+
+ }
+
+ @Override
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+ HttpServletRequest request = (HttpServletRequest) servletRequest;
+ HttpServletResponse response = (HttpServletResponse) servletResponse;
+ final String authorization = request.getHeader(HttpHeaders.AUTHORIZATION);
+ try {
+ final String credentials = StringUtils.substringAfter(authorization, AUTH_HEADER_PREFIX);
+ final Optional<UserInfo> user = authenticator.authenticate(credentials);
+ if (user.isPresent()) {
+ request.setAttribute(GuacamoleServlet.USER_ATTRIBUTE, user.get());
+ filterChain.doFilter(servletRequest, servletResponse);
+ } else {
+ response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
+ }
+ } catch (AuthenticationException e) {
+ log.error("Authentication error occurred: {}", e.getMessage());
+ }
+
+ }
+
+ @Override
+ public void destroy() {
+
+ }
+}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/servlet/guacamole/GuacamoleServlet.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/servlet/guacamole/GuacamoleServlet.java
new file mode 100644
index 0000000..d0025ba
--- /dev/null
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/servlet/guacamole/GuacamoleServlet.java
@@ -0,0 +1,32 @@
+package com.epam.dlab.backendapi.servlet.guacamole;
+
+import com.epam.dlab.auth.UserInfo;
+import com.epam.dlab.backendapi.service.GuacamoleService;
+import com.epam.dlab.exceptions.DlabException;
+import com.google.inject.Inject;
+import org.apache.guacamole.net.GuacamoleTunnel;
+import org.apache.guacamole.servlet.GuacamoleHTTPTunnelServlet;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+
+public class GuacamoleServlet extends GuacamoleHTTPTunnelServlet {
+ static final String USER_ATTRIBUTE = "user";
+ private final GuacamoleService guacamoleService;
+
+ @Inject
+ public GuacamoleServlet(GuacamoleService guacamoleService) {
+ this.guacamoleService = guacamoleService;
+ }
+
+ @Override
+ protected GuacamoleTunnel doConnect(HttpServletRequest request) {
+ try {
+ final UserInfo userInfo = (UserInfo) request.getAttribute(USER_ATTRIBUTE);
+ final String host = request.getReader().readLine();
+ return guacamoleService.getTunnel(userInfo, host);
+ } catch (IOException e) {
+ throw new DlabException("Can not read request body: " + e.getMessage(), e);
+ }
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@dlab.apache.org
For additional commands, e-mail: commits-help@dlab.apache.org