summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/AbstractAuthenticationManager.java7
-rw-r--r--eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/AbstractController.java19
-rw-r--r--eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/AbstractProcessEngineSignalController.java12
-rw-r--r--eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/ProtocolFinalizationController.java3
-rw-r--r--eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/validation/CookieBasedRequestValidator.java77
-rw-r--r--eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/validation/IHttpRequestValidator.java31
-rw-r--r--eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/validation/NothingHttpRequestValidatior.java28
-rw-r--r--eaaf_core/src/main/resources/messages/eaaf_core_messages.properties3
-rw-r--r--eaaf_core/src/test/java/at/gv/egiz/eaaf/core/test/impl/idp/validation/CookieBasedRequestValidatorTest.java115
-rw-r--r--eaaf_core_api/src/main/java/at/gv/egiz/eaaf/core/exceptions/EaafSecurityException.java43
10 files changed, 334 insertions, 4 deletions
diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/AbstractAuthenticationManager.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/AbstractAuthenticationManager.java
index 78653cf8..c93f872a 100644
--- a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/AbstractAuthenticationManager.java
+++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/AbstractAuthenticationManager.java
@@ -50,6 +50,8 @@ import at.gv.egiz.eaaf.core.exceptions.TaskExecutionException;
import at.gv.egiz.eaaf.core.impl.idp.auth.modules.ModuleRegistration;
import at.gv.egiz.eaaf.core.impl.idp.controller.protocols.RequestImpl;
import at.gv.egiz.eaaf.core.impl.idp.process.ExecutionContextImpl;
+import at.gv.egiz.eaaf.core.impl.idp.validation.IHttpRequestValidator;
+import at.gv.egiz.eaaf.core.impl.idp.validation.NothingHttpRequestValidatior;
import at.gv.egiz.eaaf.core.impl.utils.TransactionIdUtils;
import jakarta.annotation.PostConstruct;
import jakarta.servlet.ServletException;
@@ -80,6 +82,8 @@ public abstract class AbstractAuthenticationManager implements IAuthenticationMa
protected IRevisionLogger revisionsLogger;
@Autowired(required = false)
protected ISsoManager ssoManager;
+ @Autowired(required = false)
+ protected IHttpRequestValidator httpRequestValidator = new NothingHttpRequestValidatior();
ModuleRegistration moduleRegistration;
@@ -146,6 +150,9 @@ public abstract class AbstractAuthenticationManager implements IAuthenticationMa
throw new NoPassivAuthenticationException();
}
+ // inject additional security information
+ httpRequestValidator.setValidationInfos(httpResp, pendingReq);
+
// check Single Sign-On functionality if SSOManager is available
boolean isValidSsoSession = false;
if (ssoManager != null) {
diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/AbstractController.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/AbstractController.java
index 41d15743..49aa2b35 100644
--- a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/AbstractController.java
+++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/AbstractController.java
@@ -40,11 +40,14 @@ import at.gv.egiz.eaaf.core.api.logging.IRevisionLogger;
import at.gv.egiz.eaaf.core.api.storage.ITransactionStorage;
import at.gv.egiz.eaaf.core.api.utils.IPendingRequestIdGenerationStrategy;
import at.gv.egiz.eaaf.core.exceptions.EaafException;
+import at.gv.egiz.eaaf.core.exceptions.EaafSecurityException;
import at.gv.egiz.eaaf.core.exceptions.PendingReqIdValidationException;
import at.gv.egiz.eaaf.core.exceptions.ProcessExecutionException;
import at.gv.egiz.eaaf.core.exceptions.TaskExecutionException;
import at.gv.egiz.eaaf.core.impl.data.ExceptionContainer;
import at.gv.egiz.eaaf.core.impl.data.Pair;
+import at.gv.egiz.eaaf.core.impl.idp.validation.IHttpRequestValidator;
+import at.gv.egiz.eaaf.core.impl.idp.validation.NothingHttpRequestValidatior;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@@ -75,6 +78,9 @@ public abstract class AbstractController {
@Autowired
protected IPendingRequestIdGenerationStrategy reqIdGenerationStrategy;
+ @Autowired(required = false)
+ protected IHttpRequestValidator httpRequestValidator = new NothingHttpRequestValidatior();
+
/**
* EAAF framework exception handler.
*
@@ -145,6 +151,19 @@ public abstract class AbstractController {
}
+ /**
+ * Extension point to implement additional request validation.
+ *
+ * @param httpReq Current HTTP request
+ * @param pendingReq Current pending-request selected by pendingRequestId
+ * @throws EaafSecurityException In case of a validation error
+ */
+ protected void extendedRequestValidation(@Nonnull final HttpServletRequest httpReq,
+ @Nonnull final IRequest pendingReq) throws EaafSecurityException {
+ httpRequestValidator.validate(httpReq, pendingReq);
+
+ }
+
protected void handleError(final String errorMessage, final Throwable exceptionThrown,
final HttpServletRequest req, final HttpServletResponse resp, IRequest pendingReq)
throws IOException, EaafException {
diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/AbstractProcessEngineSignalController.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/AbstractProcessEngineSignalController.java
index 46de6167..17d240cb 100644
--- a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/AbstractProcessEngineSignalController.java
+++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/AbstractProcessEngineSignalController.java
@@ -22,8 +22,6 @@ package at.gv.egiz.eaaf.core.impl.idp.controller;
import java.io.IOException;
import org.apache.commons.text.StringEscapeUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import at.gv.egiz.eaaf.core.api.IRequest;
@@ -37,16 +35,17 @@ import at.gv.egiz.eaaf.core.exceptions.PendingReqIdValidationException;
import at.gv.egiz.eaaf.core.impl.utils.TransactionIdUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
+import lombok.extern.slf4j.Slf4j;
/**
* Servlet that resumes a suspended process (in case of asynchronous tasks).
*
* @author tknall
+ * @author tlenz
*
*/
+@Slf4j
public abstract class AbstractProcessEngineSignalController extends AbstractController {
- private static final Logger log =
- LoggerFactory.getLogger(AbstractProcessEngineSignalController.class);
@Autowired(required = true)
protected ProcessEngine processEngine;
@@ -75,6 +74,9 @@ public abstract class AbstractProcessEngineSignalController extends AbstractCont
// change pending-request ID
requestStorage.changePendingRequestID(pendingReq);
+ // extended validation of in-comming HTTP requests
+ extendedRequestValidation(req, pendingReq);
+
// process instance is mandatory
if (pendingReq.getProcessInstanceId() == null) {
throw new EaafIllegalStateException(
@@ -99,6 +101,8 @@ public abstract class AbstractProcessEngineSignalController extends AbstractCont
}
+
+
/**
* Retrieves the current pending-request id from the HttpServletRequest
* parameter
diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/ProtocolFinalizationController.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/ProtocolFinalizationController.java
index a52d2fda..6f8790b2 100644
--- a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/ProtocolFinalizationController.java
+++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/ProtocolFinalizationController.java
@@ -237,6 +237,9 @@ public class ProtocolFinalizationController extends AbstractController {
//set MDC variables
TransactionIdUtils.setAllLoggingVariables(pendingReq);
+ // extended validation of in-comming HTTP requests
+ extendedRequestValidation(req, pendingReq);
+
//perform protocol finalization steps
protAuthService.finalizeAuthentication(req, resp, pendingReq);
diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/validation/CookieBasedRequestValidator.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/validation/CookieBasedRequestValidator.java
new file mode 100644
index 00000000..98da0c46
--- /dev/null
+++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/validation/CookieBasedRequestValidator.java
@@ -0,0 +1,77 @@
+package at.gv.egiz.eaaf.core.impl.idp.validation;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.UUID;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.web.util.WebUtils;
+
+import at.gv.egiz.eaaf.core.api.IRequest;
+import at.gv.egiz.eaaf.core.exceptions.EaafSecurityException;
+import at.gv.egiz.eaaf.core.exceptions.EaafStorageException;
+import jakarta.annotation.Nonnull;
+import jakarta.servlet.http.Cookie;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Validate incoming requests based in HTTP cookies.
+ */
+@Slf4j
+public class CookieBasedRequestValidator implements IHttpRequestValidator {
+
+ public static final String HTTP_COOKIE_SEC = "eaafSession";
+
+ @Override
+ public void setValidationInfos(@Nonnull final HttpServletResponse httpResponse,
+ @Nonnull final IRequest pendingReq) throws EaafSecurityException {
+ try {
+ log.debug("Injecting authentication-process HTTP cookie ... ");
+ String authProcessIdentifier = UUID.randomUUID().toString();
+ httpResponse.addCookie(generatePendingRequestIdCookie(authProcessIdentifier, pendingReq));
+ pendingReq.setRawDataToTransaction(HTTP_COOKIE_SEC, authProcessIdentifier);
+
+ } catch (MalformedURLException | EaafStorageException e) {
+ throw new EaafSecurityException("process.81", e);
+ }
+
+ }
+
+ @Override
+ public void validate(@Nonnull final HttpServletRequest httpReq,
+ @Nonnull final IRequest pendingReq) throws EaafSecurityException {
+ String storedAuthProcessIdentifier = pendingReq.getRawData(HTTP_COOKIE_SEC, String.class);
+
+ if (StringUtils.isNotEmpty(storedAuthProcessIdentifier)) {
+ Cookie authProcessIdentifier = WebUtils.getCookie(httpReq, HTTP_COOKIE_SEC);
+ if (storedAuthProcessIdentifier.equals(authProcessIdentifier.getValue())) {
+ log.trace("Stored authentication-process HTTP cookie matches. Resume process ... ");
+
+ } else {
+ log.info("Stored authentication-process-Id:{} does not match to Id from HTTP cookie:{}",
+ storedAuthProcessIdentifier, authProcessIdentifier);
+ throw new EaafSecurityException("process.80");
+
+ }
+
+ } else {
+ log.debug("No stored authentication-process HTTP cookie. Skipping validation ... ");
+
+ }
+ }
+
+ private Cookie generatePendingRequestIdCookie(final String authProcessIdentifier, final IRequest pendingReq)
+ throws MalformedURLException {
+ Cookie cookie = new Cookie(
+ HTTP_COOKIE_SEC, authProcessIdentifier);
+ cookie.setHttpOnly(true);
+ cookie.setSecure(true);
+ URL url = new URL(pendingReq.getAuthUrlWithOutSlash());
+ cookie.setPath(url.getPath());
+ return cookie;
+
+ }
+
+}
diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/validation/IHttpRequestValidator.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/validation/IHttpRequestValidator.java
new file mode 100644
index 00000000..5c5e0ba1
--- /dev/null
+++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/validation/IHttpRequestValidator.java
@@ -0,0 +1,31 @@
+package at.gv.egiz.eaaf.core.impl.idp.validation;
+
+import at.gv.egiz.eaaf.core.api.IRequest;
+import at.gv.egiz.eaaf.core.exceptions.EaafSecurityException;
+import jakarta.annotation.Nonnull;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+public interface IHttpRequestValidator {
+
+ /**
+ * Set validation information to current process.
+ *
+ * @param httpResponse Current HTTP response
+ * @param pendingReq Current pendingRequest
+ * @throws EaafSecurityException In case of an internal processing error
+ */
+ void setValidationInfos(@Nonnull final HttpServletResponse httpResponse,
+ @Nonnull final IRequest pendingReq) throws EaafSecurityException;
+
+ /**
+ * Validate incoming HTTP requests.
+ *
+ * @param httpReq Current HTTP request
+ * @param pendingReq Current pending-request selected by pendingRequestId
+ * @throws EaafSecurityException In case of a validation error
+ */
+ void validate(HttpServletRequest httpReq,
+ IRequest pendingReq) throws EaafSecurityException;
+
+} \ No newline at end of file
diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/validation/NothingHttpRequestValidatior.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/validation/NothingHttpRequestValidatior.java
new file mode 100644
index 00000000..073dbbd0
--- /dev/null
+++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/validation/NothingHttpRequestValidatior.java
@@ -0,0 +1,28 @@
+package at.gv.egiz.eaaf.core.impl.idp.validation;
+
+import at.gv.egiz.eaaf.core.api.IRequest;
+import at.gv.egiz.eaaf.core.exceptions.EaafSecurityException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Blank HTTP request validation with no validation.
+ */
+@Slf4j
+public class NothingHttpRequestValidatior implements IHttpRequestValidator {
+
+ @Override
+ public void validate(HttpServletRequest httpReq, IRequest pendingReq)
+ throws EaafSecurityException {
+ log.trace("Extended HTTP request validation is not implemented ... ");
+
+ }
+
+ @Override
+ public void setValidationInfos(HttpServletResponse httpResponse, IRequest pendingReq)
+ throws EaafSecurityException {
+
+ }
+
+}
diff --git a/eaaf_core/src/main/resources/messages/eaaf_core_messages.properties b/eaaf_core/src/main/resources/messages/eaaf_core_messages.properties
index c5cb1bb1..a88523f2 100644
--- a/eaaf_core/src/main/resources/messages/eaaf_core_messages.properties
+++ b/eaaf_core/src/main/resources/messages/eaaf_core_messages.properties
@@ -13,6 +13,9 @@ process.02=Find no applicable authentication process for current state or user-s
process.03=Can not resume the authentication process. Reason: {0}
process.04=Can not execute authentication process. Problem with an internal state
+process.80=Can not continue authentication process, because HTTP security validation failed.
+process.81=Can not continue authentication process, because can initialize security context.
+
process.90=Forward to service-provider not possible, because it's not supported.
process.98=Not supported internal state. Reason: {0}
diff --git a/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/test/impl/idp/validation/CookieBasedRequestValidatorTest.java b/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/test/impl/idp/validation/CookieBasedRequestValidatorTest.java
new file mode 100644
index 00000000..9e02fc91
--- /dev/null
+++ b/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/test/impl/idp/validation/CookieBasedRequestValidatorTest.java
@@ -0,0 +1,115 @@
+package at.gv.egiz.eaaf.core.test.impl.idp.validation;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.util.UUID;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpServletResponse;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import at.gv.egiz.eaaf.core.api.IRequest;
+import at.gv.egiz.eaaf.core.exceptions.EaafSecurityException;
+import at.gv.egiz.eaaf.core.impl.idp.auth.dummy.DummyPendingRequest;
+import at.gv.egiz.eaaf.core.impl.idp.validation.CookieBasedRequestValidator;
+import at.gv.egiz.eaaf.core.test.dummy.DummyAuthConfigMap;
+import jakarta.servlet.http.Cookie;
+import lombok.SneakyThrows;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration("/SpringTest-context_eaaf_core.xml")
+public class CookieBasedRequestValidatorTest {
+
+ DummyAuthConfigMap config = new DummyAuthConfigMap();
+
+ CookieBasedRequestValidator toCheck = new CookieBasedRequestValidator();
+ MockHttpServletRequest httpReq;
+ IRequest pendingReq;
+
+ /**
+ * jUnit test initializer.
+ */
+ @Before
+ @SneakyThrows
+ public void initialize() {
+ pendingReq = new DummyPendingRequest();
+
+ httpReq = new MockHttpServletRequest("POST", "https://localhost/authhandler");
+ ((DummyPendingRequest) pendingReq).initialize(httpReq, config);
+
+ }
+
+ @Test
+ @SneakyThrows
+ public void setHttpCookie() {
+ MockHttpServletResponse httpResp = new MockHttpServletResponse();
+ toCheck.setValidationInfos(httpResp, pendingReq);
+
+ // validate state
+ String storedCookie = pendingReq.getRawData(CookieBasedRequestValidator.HTTP_COOKIE_SEC, String.class);
+ assertNotNull("stored http cookie", storedCookie);
+
+ Cookie cookie = httpResp.getCookie(CookieBasedRequestValidator.HTTP_COOKIE_SEC);
+ assertNotNull("response http cookie", cookie);
+
+ assertEquals(storedCookie, cookie.getValue(), "cookie value not match");
+
+ assertTrue("httpOnly", cookie.isHttpOnly());
+ assertTrue("secured", cookie.getSecure());
+
+ assertEquals("", cookie.getPath(), "wrong Context Path");
+
+ }
+
+ @Test
+ @SneakyThrows
+ public void success() {
+ MockHttpServletResponse httpResp = new MockHttpServletResponse();
+ toCheck.setValidationInfos(httpResp, pendingReq);
+
+ // validate state
+ httpReq.setCookies(httpResp.getCookies());
+ toCheck.validate(httpReq, pendingReq);
+
+ }
+
+ @Test
+ @SneakyThrows
+ public void notCookieInSession() {
+ MockHttpServletResponse httpResp = new MockHttpServletResponse();
+ toCheck.setValidationInfos(httpResp, pendingReq);
+
+ // validate state
+ pendingReq.removeRawDataFromTransaction(CookieBasedRequestValidator.HTTP_COOKIE_SEC);
+
+ httpReq.setCookies(httpResp.getCookies());
+ toCheck.validate(httpReq, pendingReq);
+
+ }
+
+ @Test
+ @SneakyThrows
+ public void wrongCookie() {
+ MockHttpServletResponse httpResp = new MockHttpServletResponse();
+ toCheck.setValidationInfos(httpResp, pendingReq);
+
+ // validate state
+
+ Cookie cookie = httpResp.getCookie(CookieBasedRequestValidator.HTTP_COOKIE_SEC);
+ cookie.setValue(UUID.randomUUID().toString());
+ httpReq.setCookies(cookie);
+
+ EaafSecurityException error = assertThrows(EaafSecurityException.class,
+ () -> toCheck.validate(httpReq, pendingReq));
+ assertEquals("process.80", error.getErrorId(), "wrong ErrorCode");
+
+ }
+
+}
diff --git a/eaaf_core_api/src/main/java/at/gv/egiz/eaaf/core/exceptions/EaafSecurityException.java b/eaaf_core_api/src/main/java/at/gv/egiz/eaaf/core/exceptions/EaafSecurityException.java
new file mode 100644
index 00000000..1a3dd67e
--- /dev/null
+++ b/eaaf_core_api/src/main/java/at/gv/egiz/eaaf/core/exceptions/EaafSecurityException.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2017 Graz University of Technology EAAF-Core Components has been developed in a
+ * cooperation between EGIZ, A-SIT Plus, A-SIT, and Graz University of Technology.
+ *
+ * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European
+ * Commission - subsequent versions of the EUPL (the "Licence"); You may not use this work except in
+ * compliance with the Licence. You may obtain a copy of the Licence at:
+ * https://joinup.ec.europa.eu/news/understanding-eupl-v12
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the Licence
+ * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the Licence for the specific language governing permissions and limitations under
+ * the Licence.
+ *
+ * This product combines work with different licenses. See the "NOTICE" text file for details on the
+ * various modules and licenses. The "NOTICE" text file is part of the distribution. Any derivative
+ * works that you distribute must include a readable copy of the "NOTICE" text file.
+*/
+
+package at.gv.egiz.eaaf.core.exceptions;
+
+/**
+ * In case of a security-validation error.
+ */
+public class EaafSecurityException extends EaafException {
+
+ private static final long serialVersionUID = 1L;
+
+ public EaafSecurityException(final String msg) {
+ super(msg, null);
+
+ }
+
+ public EaafSecurityException(final String msg, final Object[] params) {
+ super(msg, params);
+
+ }
+
+ public EaafSecurityException(String msg, Exception e) {
+ super(msg, null, e);
+ }
+
+}