Skip to content

Commit

Permalink
[FIXES #2971] MapStore - SSO keycloak kerberos (#362)
Browse files Browse the repository at this point in the history
Co-authored-by: Alessio Fabiani <[email protected]>
  • Loading branch information
tdipisa and afabiani authored Jul 11, 2024
1 parent 80fbfa3 commit ba23e39
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ public GeoStoreKeycloakAuthProvider(KeyCloakConfiguration configuration) {
}

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {

public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
KeycloakAuthenticationToken token = (KeycloakAuthenticationToken) authentication;
OidcKeycloakAccount account = token.getAccount();
KeycloakSecurityContext context = account.getKeycloakSecurityContext();
Expand Down Expand Up @@ -130,7 +130,7 @@ public Authentication authenticate(Authentication authentication) throws Authent
User user = retrieveUser(username, "", grantedAuthoritiesMapper,keycloakGroups);
addEveryOne(user.getGroups());
if (user.getRole() == null) {
// no role get the one configured to be default for authenticated users.
// no role gets the one configured to be default for authenticated users.
Role defRole = configuration.getAuthenticatedDefaultRole();
user.setRole(defRole);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,40 +27,45 @@
*/
package it.geosolutions.geostore.services.rest.security.keycloak;

import static it.geosolutions.geostore.services.rest.SessionServiceDelegate.PROVIDER_KEY;
import static it.geosolutions.geostore.services.rest.security.keycloak.KeyCloakLoginService.KEYCLOAK_REDIRECT;
import static it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Utils.*;
import static it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Utils.getResponse;

import it.geosolutions.geostore.services.UserService;
import it.geosolutions.geostore.services.rest.security.TokenAuthenticationCache;
import it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Utils;
import it.geosolutions.geostore.services.rest.utils.GeoStoreContext;
import java.io.IOException;
import java.util.Date;
import java.util.Objects;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.RequestAuthenticator;
import org.keycloak.adapters.spi.AuthOutcome;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.adapters.springsecurity.facade.SimpleHttpFacade;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.filter.GenericFilterBean;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;


import static it.geosolutions.geostore.services.rest.SessionServiceDelegate.PROVIDER_KEY;
import static it.geosolutions.geostore.services.rest.security.keycloak.KeyCloakLoginService.KEYCLOAK_REDIRECT;
import static it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Utils.ACCESS_TOKEN_PARAM;
import static it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Utils.REFRESH_TOKEN_PARAM;

/**
* Keycloak Authentication Filter. Manage the logic to authenticate a user against a keycloak server.
*/
@SuppressWarnings("PMD.UnusedLocalVariable")
public class KeyCloakFilter extends GenericFilterBean {


Expand Down Expand Up @@ -140,7 +145,9 @@ protected Authentication authenticateAndUpdateCache(HttpServletRequest request,
} else {
entryPoint = new KeycloakAuthenticationEntryPoint(authenticator.getChallenge());
}
RequestContextHolder.getRequestAttributes().setAttribute(KEYCLOAK_REDIRECT,entryPoint,0);

Objects.requireNonNull(RequestContextHolder.getRequestAttributes())
.setAttribute(KEYCLOAK_REDIRECT, entryPoint, RequestAttributes.SCOPE_REQUEST);
} else {
LOGGER.warn("Failed to authentication and to redirect the user.");
}
Expand All @@ -151,13 +158,19 @@ protected Authentication authenticateAndUpdateCache(HttpServletRequest request,
* Updates the cache with the new Authentication entry.
* @param authentication the new Authentication entry.
*/
protected void updateCache(Authentication authentication){
Object details=authentication.getDetails();
if (details instanceof KeycloakTokenDetails){
KeycloakTokenDetails keycloakDetails=(KeycloakTokenDetails) details;
String accessToken=keycloakDetails.getAccessToken();
if (accessToken!=null){
cache.putCacheEntry(accessToken,authentication);
protected void updateCache(Authentication authentication) {
Object details = authentication.getDetails();
if (details instanceof KeycloakTokenDetails) {
KeyCloakHelper helper = GeoStoreContext.bean(KeyCloakHelper.class);
KeycloakTokenDetails keycloakDetails = (KeycloakTokenDetails) details;
String accessToken = keycloakDetails.getAccessToken();
if (accessToken != null) {
cache.putCacheEntry(accessToken, authentication);
if (helper != null) {
HttpFacade facade = new SimpleHttpFacade(getRequest(), getResponse());
KeycloakDeployment deployment = helper.getDeployment(facade);
KeycloakCookieUtils.setTokenCookie(deployment, facade, keycloakDetails);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,26 +30,26 @@
import it.geosolutions.geostore.services.rest.IdPLoginRest;
import it.geosolutions.geostore.services.rest.security.oauth2.Oauth2LoginService;

import java.io.IOException;
import java.util.Objects;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.core.Response;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.core.Response;
import java.io.IOException;


import static it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Utils.getAccessToken;
import static it.geosolutions.geostore.services.rest.security.oauth2.OAuth2Utils.getRefreshAccessToken;

/**
* Keycloak implementation for a LoginService.
* Since keycloak redirects to the url from which the call to the authorization page was issued
* no internal redirect is really performed here.
* Keycloak implementation for a LoginService. Since keycloak redirects to the url from which the
* call to the authorization page was issued, no internal redirect is really performed here.
*/
public class KeyCloakLoginService extends Oauth2LoginService {

Expand All @@ -64,16 +64,25 @@ public KeyCloakLoginService(IdPLoginRest loginRest) {

@Override
public void doLogin(HttpServletRequest request, HttpServletResponse response, String provider) {
AuthenticationEntryPoint challenge = (AuthenticationEntryPoint) RequestContextHolder.getRequestAttributes()
.getAttribute(KEYCLOAK_REDIRECT, 0);
if (challenge != null) {
KeycloakTokenDetails details = getDetails();
boolean attempInternalRedirect = (details != null && details.getAccessToken() != null);

AuthenticationEntryPoint challenge =
(AuthenticationEntryPoint)
Objects.requireNonNull(RequestContextHolder.getRequestAttributes())
.getAttribute(KEYCLOAK_REDIRECT, RequestAttributes.SCOPE_REQUEST);
if (challenge == null) attempInternalRedirect = true;
else if (!attempInternalRedirect) {
try {
challenge.commence(request, response, null);
attempInternalRedirect = false;
} catch (Exception e) {
LOGGER.error("Error while redirecting to Keycloak authorization.", e);
throw new RuntimeException(e);
}
} else {
}

if (attempInternalRedirect) {
try {
response.sendRedirect(configuration(provider).getInternalRedirectUri());
} catch (IOException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,18 @@ static void setTokenCookie(KeycloakDeployment deployment, HttpFacade facade, Key
.append(refreshToken).toString();

String cookiePath = getCookiePath(deployment, facade);
// forces the expiration of the old keycloak cookie after refresh token. Keycloak doesn't do it for us.
facade.getResponse().setCookie(AdapterConstants.KEYCLOAK_ADAPTER_STATE_COOKIE, cookie, cookiePath, null, 0, deployment.getSslRequired().isRequired(facade.getRequest().getRemoteAddr()), true);

// Forces the expiration of the old keycloak cookie after refresh token. Keycloak doesn't do
// it for us.
facade.getResponse()
.setCookie(
AdapterConstants.KEYCLOAK_ADAPTER_STATE_COOKIE,
cookie,
cookiePath,
null,
0,
deployment.getSslRequired().isRequired(facade.getRequest().getRemoteAddr()),
true);
}

static String getCookiePath(KeycloakDeployment deployment, HttpFacade facade) {
Expand Down

0 comments on commit ba23e39

Please sign in to comment.