From 36509985f643124eb439b9d1e98433b10b69a4c3 Mon Sep 17 00:00:00 2001 From: Jan Zyka Date: Sun, 9 Apr 2017 21:00:22 +0200 Subject: [PATCH] Allow forward URL to choose HTTP method in SPNEGO Closes gh-108 --- .../web/authentication/SpnegoEntryPoint.java | 39 +++++++++++++++++-- .../kerberos/web/SpnegoEntryPointTest.java | 34 ++++++++++++++++ 2 files changed, 69 insertions(+), 4 deletions(-) diff --git a/spring-security-kerberos-web/src/main/java/org/springframework/security/kerberos/web/authentication/SpnegoEntryPoint.java b/spring-security-kerberos-web/src/main/java/org/springframework/security/kerberos/web/authentication/SpnegoEntryPoint.java index d7cd6fc..4001984 100644 --- a/spring-security-kerberos-web/src/main/java/org/springframework/security/kerberos/web/authentication/SpnegoEntryPoint.java +++ b/spring-security-kerberos-web/src/main/java/org/springframework/security/kerberos/web/authentication/SpnegoEntryPoint.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2015 the original author or authors. + * Copyright 2009-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package org.springframework.security.kerberos.web.authentication; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.http.HttpMethod; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.util.UrlUtils; @@ -26,6 +27,7 @@ import org.springframework.util.StringUtils; import jakarta.servlet.RequestDispatcher; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequestWrapper; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; @@ -57,6 +59,8 @@ public class SpnegoEntryPoint implements AuthenticationEntryPoint { private final String forwardUrl; + private final HttpMethod forwardMethod; + private final boolean forward; /** @@ -71,8 +75,8 @@ public class SpnegoEntryPoint implements AuthenticationEntryPoint { /** * Instantiates a new spnego entry point. This constructor enables security - * configuration to use SPNEGO in combination with login form as fallback - * for clients that do not support this kind of authentication. + * configuration to use SPNEGO in combination with a fallback page (login form, + * custom 401 page ...). The forward method will be the same as the original request. * * @param forwardUrl * URL where the login page can be found. Should be @@ -80,13 +84,34 @@ public class SpnegoEntryPoint implements AuthenticationEntryPoint { * {@code /}) and can't be absolute URL. */ public SpnegoEntryPoint(String forwardUrl) { + this(forwardUrl, null); + } + + /** + * Instantiates a new spnego entry point. This constructor enables security + * configuration to use SPNEGO in combination a fallback page (login form, + * custom 401 page ...). The forward URL will be accessed via provided HTTP + * method. + * + * @param forwardUrl + * URL where the login page can be found. Should be + * relative to the web-app context path (include a leading + * {@code /}) and can't be absolute URL. + * + * @param forwardMethod + * HTTP method to use when accessing the forward URL + */ + public SpnegoEntryPoint(String forwardUrl, HttpMethod forwardMethod) { if (StringUtils.hasText(forwardUrl)) { Assert.isTrue(UrlUtils.isValidRedirectUrl(forwardUrl), "Forward url specified must be a valid forward URL"); Assert.isTrue(!UrlUtils.isAbsoluteUrl(forwardUrl), "Forward url specified must not be absolute"); + this.forwardUrl = forwardUrl; + this.forwardMethod = forwardMethod; this.forward = true; } else { this.forwardUrl = null; + this.forwardMethod = null; this.forward = false; } } @@ -102,7 +127,13 @@ public class SpnegoEntryPoint implements AuthenticationEntryPoint { if (forward) { RequestDispatcher dispatcher = request.getRequestDispatcher(forwardUrl); - dispatcher.forward(request, response); + HttpServletRequest fwdRequest = forwardMethod != null ? new HttpServletRequestWrapper(request) { + @Override + public String getMethod() { + return forwardMethod.name(); + } + } : request; + dispatcher.forward(fwdRequest, response); } else { response.flushBuffer(); } diff --git a/spring-security-kerberos-web/src/test/java/org/springframework/security/kerberos/web/SpnegoEntryPointTest.java b/spring-security-kerberos-web/src/test/java/org/springframework/security/kerberos/web/SpnegoEntryPointTest.java index 4c5b953..5f3420c 100644 --- a/spring-security-kerberos-web/src/test/java/org/springframework/security/kerberos/web/SpnegoEntryPointTest.java +++ b/spring-security-kerberos-web/src/test/java/org/springframework/security/kerberos/web/SpnegoEntryPointTest.java @@ -17,10 +17,14 @@ package org.springframework.security.kerberos.web; import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.springframework.http.HttpMethod; import org.springframework.security.kerberos.web.authentication.SpnegoEntryPoint; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -28,6 +32,7 @@ import static org.mockito.Mockito.when; import jakarta.servlet.RequestDispatcher; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import org.springframework.web.bind.annotation.RequestMethod; /** * Test class for {@link SpnegoEntryPoint} @@ -79,6 +84,35 @@ public class SpnegoEntryPointTest { verify(requestDispatcher).forward(request, response); } + @Test + public void testForwardUsesDefaultHttpMethod() throws Exception { + ArgumentCaptor servletRequestCaptor = ArgumentCaptor.forClass(HttpServletRequest.class); + String forwardUrl = "/login"; + SpnegoEntryPoint entryPoint = new SpnegoEntryPoint(forwardUrl); + HttpServletResponse response = mock(HttpServletResponse.class); + HttpServletRequest request = mock(HttpServletRequest.class); + when(request.getMethod()).thenReturn(RequestMethod.POST.name()); + RequestDispatcher requestDispatcher = mock(RequestDispatcher.class); + when(request.getRequestDispatcher(anyString())).thenReturn(requestDispatcher); + entryPoint.commence(request, response, null); + verify(requestDispatcher).forward(servletRequestCaptor.capture(), eq(response)); + assertThat(servletRequestCaptor.getValue().getMethod()).isEqualTo(HttpMethod.POST.name()); + } + + @Test + public void testForwardUsesCustomHttpMethod() throws Exception { + ArgumentCaptor servletRequestCaptor = ArgumentCaptor.forClass(HttpServletRequest.class); + String forwardUrl = "/login"; + SpnegoEntryPoint entryPoint = new SpnegoEntryPoint(forwardUrl, HttpMethod.DELETE); + HttpServletResponse response = mock(HttpServletResponse.class); + HttpServletRequest request = mock(HttpServletRequest.class); + RequestDispatcher requestDispatcher = mock(RequestDispatcher.class); + when(request.getRequestDispatcher(anyString())).thenReturn(requestDispatcher); + entryPoint.commence(request, response, null); + verify(requestDispatcher).forward(servletRequestCaptor.capture(), eq(response)); + assertThat(servletRequestCaptor.getValue().getMethod()).isEqualTo(HttpMethod.DELETE.name()); + } + @Test public void testEntryPointForwardAbsolute() throws Exception { assertThatThrownBy(() -> {