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/09/02 09:17:19 UTC

[incubator-dlab] branch dlab_refactored updated: DLAB-1022 add possibility to cache keycloak token on DLab side

This is an automated email from the ASF dual-hosted git repository.

bhliva pushed a commit to branch dlab_refactored
in repository https://gitbox.apache.org/repos/asf/incubator-dlab.git


The following commit(s) were added to refs/heads/dlab_refactored by this push:
     new 29d61e5  DLAB-1022 add possibility to cache keycloak token on DLab side
29d61e5 is described below

commit 29d61e5b23c737bd35808bd52e023bb55d4cedf3
Author: bhliva <bo...@epam.com>
AuthorDate: Mon Sep 2 12:13:14 2019 +0300

    DLAB-1022 add possibility to cache keycloak token on DLab side
---
 .../dlab/backendapi/SelfServiceApplication.java    | 58 ++--------------------
 .../backendapi/auth/KeycloakAuthenticator.java     | 49 ++++++++++++++++++
 .../filters}/DropwizardBearerTokenFilterImpl.java  |  2 +-
 .../com/epam/dlab/backendapi/dao/SecurityDAO.java  | 31 +++++++++---
 .../dropwizard/bundles/DlabKeycloakBundle.java     | 40 +++++++++++++++
 .../listeners/MongoStartupListener.java            |  2 +-
 .../listeners/RestoreHandlerStartupListener.java   |  2 +-
 7 files changed, 120 insertions(+), 64 deletions(-)

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 78ae616..1ef045c 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
@@ -19,15 +19,14 @@
 
 package com.epam.dlab.backendapi;
 
-import com.epam.dlab.auth.UserInfo;
-import com.epam.dlab.backendapi.auth.SelfServiceSecurityAuthorizer;
 import com.epam.dlab.backendapi.conf.SelfServiceApplicationConfiguration;
 import com.epam.dlab.backendapi.dao.IndexCreator;
 import com.epam.dlab.backendapi.domain.EnvStatusListener;
 import com.epam.dlab.backendapi.domain.ExploratoryLibCache;
+import com.epam.dlab.backendapi.dropwizard.bundles.DlabKeycloakBundle;
+import com.epam.dlab.backendapi.dropwizard.listeners.MongoStartupListener;
+import com.epam.dlab.backendapi.dropwizard.listeners.RestoreHandlerStartupListener;
 import com.epam.dlab.backendapi.healthcheck.MongoHealthCheck;
-import com.epam.dlab.backendapi.listeners.MongoStartupListener;
-import com.epam.dlab.backendapi.listeners.RestoreHandlerStartupListener;
 import com.epam.dlab.backendapi.modules.ModuleFactory;
 import com.epam.dlab.backendapi.resources.*;
 import com.epam.dlab.backendapi.resources.callback.*;
@@ -45,15 +44,10 @@ import com.google.inject.Guice;
 import com.google.inject.Injector;
 import com.google.inject.Key;
 import com.google.inject.name.Names;
-import de.ahus1.keycloak.dropwizard.AbstractKeycloakAuthenticator;
-import de.ahus1.keycloak.dropwizard.KeycloakBundle;
-import de.ahus1.keycloak.dropwizard.KeycloakConfiguration;
 import de.thomaskrille.dropwizard_template_config.TemplateConfigBundle;
 import de.thomaskrille.dropwizard_template_config.TemplateConfigBundleConfiguration;
 import io.dropwizard.Application;
 import io.dropwizard.assets.AssetsBundle;
-import io.dropwizard.auth.Authenticator;
-import io.dropwizard.auth.Authorizer;
 import io.dropwizard.forms.MultiPartBundle;
 import io.dropwizard.jersey.setup.JerseyEnvironment;
 import io.dropwizard.jetty.BiDiGzipHandler;
@@ -68,14 +62,9 @@ import lombok.extern.slf4j.Slf4j;
 import org.eclipse.jetty.server.Handler;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.handler.HandlerWrapper;
-import org.keycloak.KeycloakSecurityContext;
-import org.keycloak.representations.AccessToken;
 
 import javax.servlet.DispatcherType;
-import javax.servlet.http.HttpServletRequest;
-import java.security.Principal;
 import java.util.EnumSet;
-import java.util.Optional;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -111,46 +100,7 @@ public class SelfServiceApplication extends Application<SelfServiceApplicationCo
 				new TemplateConfigBundleConfiguration().fileIncludePath(ServiceUtils.getConfPath())
 		));
 
-		bootstrap.addBundle(new KeycloakBundle<SelfServiceApplicationConfiguration>() {
-			@Override
-			protected KeycloakConfiguration getKeycloakConfiguration(SelfServiceApplicationConfiguration configuration) {
-				return configuration.getKeycloakConfiguration();
-			}
-
-			@Override
-			protected Class<? extends Principal> getUserClass() {
-				return UserInfo.class;
-			}
-
-			@Override
-			protected Authorizer createAuthorizer() {
-				return new SelfServiceSecurityAuthorizer();
-			}
-
-			@Override
-			protected Authenticator createAuthenticator(KeycloakConfiguration configuration) {
-				class KeycloakAuthenticator extends AbstractKeycloakAuthenticator<UserInfo> {
-
-					private KeycloakAuthenticator(KeycloakConfiguration keycloakConfiguration) {
-						super(keycloakConfiguration);
-					}
-
-					@Override
-					protected UserInfo prepareAuthentication(KeycloakSecurityContext keycloakSecurityContext,
-															 HttpServletRequest httpServletRequest,
-															 KeycloakConfiguration keycloakConfiguration) {
-						final AccessToken token = keycloakSecurityContext.getToken();
-						final UserInfo userInfo = new UserInfo(token.getPreferredUsername(),
-								keycloakSecurityContext.getTokenString());
-						final AccessToken.Access resourceAccess =
-								token.getResourceAccess(keycloakConfiguration.getResource());
-						Optional.ofNullable(resourceAccess).ifPresent(ra -> userInfo.addRoles(ra.getRoles()));
-						return userInfo;
-					}
-				}
-				return new KeycloakAuthenticator(configuration);
-			}
-		});
+		bootstrap.addBundle(new DlabKeycloakBundle());
 	}
 
 	@Override
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/auth/KeycloakAuthenticator.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/auth/KeycloakAuthenticator.java
new file mode 100644
index 0000000..8c42f22
--- /dev/null
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/auth/KeycloakAuthenticator.java
@@ -0,0 +1,49 @@
+package com.epam.dlab.backendapi.auth;
+
+import com.epam.dlab.auth.UserInfo;
+import com.epam.dlab.backendapi.SelfServiceApplication;
+import com.epam.dlab.backendapi.dao.SecurityDAO;
+import de.ahus1.keycloak.dropwizard.AbstractKeycloakAuthenticator;
+import de.ahus1.keycloak.dropwizard.KeycloakConfiguration;
+import io.dropwizard.auth.AuthenticationException;
+import org.apache.commons.lang3.StringUtils;
+import org.keycloak.KeycloakSecurityContext;
+import org.keycloak.representations.AccessToken;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.core.HttpHeaders;
+import java.util.Optional;
+
+public class KeycloakAuthenticator extends AbstractKeycloakAuthenticator<UserInfo> {
+
+	private static final String TOKEN_PREFIX = "Bearer ";
+
+	public KeycloakAuthenticator(KeycloakConfiguration keycloakConfiguration) {
+		super(keycloakConfiguration);
+	}
+
+	@Override
+	public Optional<UserInfo> authenticate(HttpServletRequest request) throws AuthenticationException {
+		final String token = StringUtils.substringAfter(request.getHeader(HttpHeaders.AUTHORIZATION), TOKEN_PREFIX);
+		final Optional<UserInfo> cachedUser =
+				SelfServiceApplication.getInjector().getInstance(SecurityDAO.class).getUser(token);
+		if (!cachedUser.isPresent()) {
+			return super.authenticate(request);
+		} else {
+			return cachedUser;
+		}
+	}
+
+	@Override
+	protected UserInfo prepareAuthentication(KeycloakSecurityContext keycloakSecurityContext,
+											 HttpServletRequest httpServletRequest,
+											 KeycloakConfiguration keycloakConfiguration) {
+		final AccessToken token = keycloakSecurityContext.getToken();
+		final UserInfo userInfo = new UserInfo(token.getPreferredUsername(),
+				keycloakSecurityContext.getTokenString());
+		final AccessToken.Access resourceAccess =
+				token.getResourceAccess(keycloakConfiguration.getResource());
+		Optional.ofNullable(resourceAccess).ifPresent(ra -> userInfo.addRoles(ra.getRoles()));
+		return userInfo;
+	}
+}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/DropwizardBearerTokenFilterImpl.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/auth/filters/DropwizardBearerTokenFilterImpl.java
similarity index 93%
rename from services/self-service/src/main/java/com/epam/dlab/backendapi/DropwizardBearerTokenFilterImpl.java
rename to services/self-service/src/main/java/com/epam/dlab/backendapi/auth/filters/DropwizardBearerTokenFilterImpl.java
index 9be4251..8c72669 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/DropwizardBearerTokenFilterImpl.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/auth/filters/DropwizardBearerTokenFilterImpl.java
@@ -1,4 +1,4 @@
-package com.epam.dlab.backendapi;
+package com.epam.dlab.backendapi.auth.filters;
 
 import org.keycloak.adapters.AdapterDeploymentContext;
 import org.keycloak.adapters.KeycloakDeployment;
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/SecurityDAO.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/SecurityDAO.java
index 7fb59a4..a0bcfc0 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/SecurityDAO.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/SecurityDAO.java
@@ -19,7 +19,9 @@
 
 package com.epam.dlab.backendapi.dao;
 
+import com.epam.dlab.auth.UserInfo;
 import com.epam.dlab.auth.dto.UserCredentialDTO;
+import com.epam.dlab.backendapi.conf.SelfServiceApplicationConfiguration;
 import com.epam.dlab.exceptions.DlabException;
 import com.epam.dlab.util.UsernameUtils;
 import com.google.inject.Singleton;
@@ -33,8 +35,7 @@ import java.util.stream.Collectors;
 
 import static com.epam.dlab.backendapi.dao.MongoCollections.LOGIN_ATTEMPTS;
 import static com.epam.dlab.backendapi.dao.MongoCollections.ROLES;
-import static com.mongodb.client.model.Filters.eq;
-import static com.mongodb.client.model.Filters.ne;
+import static com.mongodb.client.model.Filters.*;
 import static com.mongodb.client.model.Projections.*;
 
 /**
@@ -43,6 +44,10 @@ import static com.mongodb.client.model.Projections.*;
 @Singleton
 public class SecurityDAO extends BaseDAO {
 
+	private SelfServiceApplicationConfiguration conf;
+	private static final String SECURITY_COLLECTION = "security";
+	private static final String TOKEN_RESPONSE = "tokenResponse";
+
 	/**
 	 * Write the attempt of user login into Mongo database.
 	 *
@@ -71,16 +76,28 @@ public class SecurityDAO extends BaseDAO {
 	}
 
 	public void saveUser(String userName, AccessTokenResponse accessTokenResponse) {
-		updateOne("security", eq(ID, userName),
+		updateOne(SECURITY_COLLECTION, eq(ID, userName),
 				new Document(SET,
-						new Document().append(ID, userName).append("created", new Date()).append("tokenResponse",
-								convertToBson(accessTokenResponse))),
+						new Document()
+								.append(ID, userName)
+								.append("created", new Date())
+								.append("last_access", new Date())
+								.append(TOKEN_RESPONSE, convertToBson(accessTokenResponse))),
 				true);
 	}
 
+	public Optional<UserInfo> getUser(String token) {
+		return Optional.ofNullable(mongoService.getCollection(SECURITY_COLLECTION)
+				.findOneAndUpdate(and(eq(TOKEN_RESPONSE + ".access_token", token), gte("last_access",
+						new Date(new Date().getTime() - conf.getInactiveUserTimeoutMillSec()))), new Document("$set",
+						new Document("last_access", new Date()))))
+				.map(d -> new UserInfo(d.getString(ID), token));
+	}
+
+
 	public Optional<AccessTokenResponse> getTokenResponse(String user) {
-		return findOne("security", eq(ID, user), Projections.fields(include("tokenResponse")))
-				.map(d -> convertFromDocument((Document) d.get("tokenResponse"), AccessTokenResponse.class));
+		return findOne(SECURITY_COLLECTION, eq(ID, user), Projections.fields(include(TOKEN_RESPONSE)))
+				.map(d -> convertFromDocument((Document) d.get(TOKEN_RESPONSE), AccessTokenResponse.class));
 	}
 
 	@SuppressWarnings("unchecked")
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/dropwizard/bundles/DlabKeycloakBundle.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/dropwizard/bundles/DlabKeycloakBundle.java
new file mode 100644
index 0000000..5089c1c
--- /dev/null
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/dropwizard/bundles/DlabKeycloakBundle.java
@@ -0,0 +1,40 @@
+package com.epam.dlab.backendapi.dropwizard.bundles;
+
+import com.epam.dlab.auth.UserInfo;
+import com.epam.dlab.backendapi.auth.KeycloakAuthenticator;
+import com.epam.dlab.backendapi.auth.SelfServiceSecurityAuthorizer;
+import com.epam.dlab.backendapi.conf.SelfServiceApplicationConfiguration;
+import com.google.inject.Inject;
+import de.ahus1.keycloak.dropwizard.KeycloakBundle;
+import de.ahus1.keycloak.dropwizard.KeycloakConfiguration;
+import io.dropwizard.auth.Authenticator;
+import io.dropwizard.auth.Authorizer;
+import io.dropwizard.setup.Environment;
+
+import java.security.Principal;
+
+public class DlabKeycloakBundle extends KeycloakBundle<SelfServiceApplicationConfiguration> {
+
+	@Inject
+	private KeycloakAuthenticator authenticator;
+
+	@Override
+	protected KeycloakConfiguration getKeycloakConfiguration(SelfServiceApplicationConfiguration configuration) {
+		return configuration.getKeycloakConfiguration();
+	}
+
+	@Override
+	protected Class<? extends Principal> getUserClass() {
+		return UserInfo.class;
+	}
+
+	@Override
+	protected Authorizer createAuthorizer() {
+		return new SelfServiceSecurityAuthorizer();
+	}
+
+	@Override
+	protected Authenticator createAuthenticator(KeycloakConfiguration configuration) {
+		return new KeycloakAuthenticator(configuration);
+	}
+}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/listeners/MongoStartupListener.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/dropwizard/listeners/MongoStartupListener.java
similarity index 98%
rename from services/self-service/src/main/java/com/epam/dlab/backendapi/listeners/MongoStartupListener.java
rename to services/self-service/src/main/java/com/epam/dlab/backendapi/dropwizard/listeners/MongoStartupListener.java
index d9e7abd..f94028a 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/listeners/MongoStartupListener.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/dropwizard/listeners/MongoStartupListener.java
@@ -1,4 +1,4 @@
-package com.epam.dlab.backendapi.listeners;
+package com.epam.dlab.backendapi.dropwizard.listeners;
 
 import com.epam.dlab.backendapi.conf.CloudConfiguration;
 import com.epam.dlab.backendapi.conf.SelfServiceApplicationConfiguration;
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/listeners/RestoreHandlerStartupListener.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/dropwizard/listeners/RestoreHandlerStartupListener.java
similarity index 96%
rename from services/self-service/src/main/java/com/epam/dlab/backendapi/listeners/RestoreHandlerStartupListener.java
rename to services/self-service/src/main/java/com/epam/dlab/backendapi/dropwizard/listeners/RestoreHandlerStartupListener.java
index 7d94b77..73f5961 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/listeners/RestoreHandlerStartupListener.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/dropwizard/listeners/RestoreHandlerStartupListener.java
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-package com.epam.dlab.backendapi.listeners;
+package com.epam.dlab.backendapi.dropwizard.listeners;
 
 import com.epam.dlab.rest.client.RESTService;
 import io.dropwizard.lifecycle.ServerLifecycleListener;


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@dlab.apache.org
For additional commands, e-mail: commits-help@dlab.apache.org