Skip to content

Commit 2944be0

Browse files
committed
UY-1502: fix auto proxy login functionality
1 parent 0a052f5 commit 2944be0

10 files changed

Lines changed: 318 additions & 61 deletions

File tree

oauth/src/main/java/pl/edu/icm/unity/oauth/as/webauthz/ASConsentDeciderServlet.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,12 @@
77
import static io.imunity.vaadin.endpoint.common.consent_utils.LoginInProgressService.noSignInContextException;
88

99
import java.io.IOException;
10+
import java.util.Arrays;
1011
import java.util.Collection;
12+
import java.util.List;
13+
import java.util.Map;
1114
import java.util.Optional;
15+
import java.util.stream.Collectors;
1216

1317
import org.apache.logging.log4j.Logger;
1418
import org.eclipse.jetty.ee10.servlet.ServletApiRequest;
@@ -22,6 +26,7 @@
2226
import com.nimbusds.openid.connect.sdk.OIDCError;
2327

2428
import io.imunity.vaadin.endpoint.common.EopException;
29+
import io.imunity.vaadin.endpoint.common.QueryBuilder;
2530
import io.imunity.vaadin.endpoint.common.consent_utils.LoginInProgressService;
2631
import jakarta.servlet.ServletException;
2732
import jakarta.servlet.http.HttpServlet;
@@ -151,10 +156,12 @@ private void sendRedirect(HttpServletRequest req, HttpServletResponse resp) thro
151156

152157
private String getQueryToAppend(HttpServletRequest req)
153158
{
154-
String signInContextKey = req.getParameter(OAuthSessionService.URL_PARAM_CONTEXT_KEY);
155-
return signInContextKey == null
156-
? ""
157-
: "?" + OAuthSessionService.URL_PARAM_CONTEXT_KEY + "=" + signInContextKey;
159+
Map<String, List<String>> urlParameters = req.getParameterMap().entrySet().stream()
160+
.collect(Collectors.toMap(
161+
Map.Entry::getKey,
162+
entry -> Arrays.asList(entry.getValue())
163+
));
164+
return QueryBuilder.buildQuery(urlParameters);
158165
}
159166

160167
private void sendNonePromptError(OAuthAuthzContext oauthCtx, HttpServletRequest req, HttpServletResponse resp)

oauth/src/main/java/pl/edu/icm/unity/oauth/as/webauthz/OAuthAuthzWebEndpoint.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222

2323
import com.nimbusds.oauth2.sdk.AuthorizationErrorResponse;
2424
import com.nimbusds.oauth2.sdk.SerializeException;
25-
import com.nimbusds.oauth2.sdk.id.State;
2625
import com.nimbusds.openid.connect.sdk.OIDCError;
2726

2827
import eu.unicore.util.configuration.ConfigurationException;
@@ -164,8 +163,8 @@ protected ServletContextHandler getServletContextHandlerOverridable(WebAppContex
164163

165164
Servlet oauthParseServlet = new OAuthParseServlet(oauthProperties, getServletUrl(OAUTH_CONSENT_DECIDER_SERVLET_PATH),
166165
new ErrorHandler(freemarkerHandler), identitiesManagement, attributesManagement, scopeService, serverConfig);
167-
ServletHolder samlParseHolder = createServletHolder(oauthParseServlet);
168-
servletContextHandler.addServlet(samlParseHolder, OAUTH_CONSUMER_SERVLET_PATH + "/*");
166+
ServletHolder oauthParseHolder = createServletHolder(oauthParseServlet);
167+
servletContextHandler.addServlet(oauthParseHolder, OAUTH_CONSUMER_SERVLET_PATH + "/*");
169168

170169
SessionManagement sessionMan = applicationContext.getBean(SessionManagement.class);
171170
LoginToHttpSessionBinder sessionBinder = applicationContext.getBean(LoginToHttpSessionBinder.class);
@@ -190,7 +189,7 @@ protected ServletContextHandler getServletContextHandlerOverridable(WebAppContex
190189
proxyAuthnFilter = new ProxyAuthenticationFilter(authenticationFlows,
191190
description.getEndpoint().getContextAddress(),
192191
genericEndpointProperties.getBooleanValue(VaadinEndpointProperties.AUTO_LOGIN), description.getRealm());
193-
servletContextHandler.addFilter(new FilterHolder(proxyAuthnFilter), AUTHENTICATION_PATH + "/*",
192+
servletContextHandler.addFilter(new FilterHolder(proxyAuthnFilter), OAUTH_UI_SERVLET_PATH + "/*",
194193
EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD));
195194

196195
contextSetupFilter = new InvocationContextSetupFilter(config, description.getRealm(), null,

oauth/src/main/java/pl/edu/icm/unity/oauth/as/webauthz/OAuthParseServlet.java

Lines changed: 6 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,17 @@
55
package pl.edu.icm.unity.oauth.as.webauthz;
66

77
import java.io.IOException;
8-
import java.net.URISyntaxException;
98
import java.nio.charset.StandardCharsets;
109
import java.util.LinkedList;
1110
import java.util.List;
1211
import java.util.Locale;
1312
import java.util.Map;
14-
import java.util.Map.Entry;
1513
import java.util.Optional;
1614
import java.util.Set;
1715
import java.util.StringTokenizer;
1816
import java.util.stream.Collectors;
1917

2018
import org.apache.commons.codec.binary.Base64;
21-
import org.apache.hc.core5.net.URIBuilder;
2219
import org.apache.logging.log4j.Logger;
2320

2421
import com.google.common.collect.Sets;
@@ -37,6 +34,7 @@
3734

3835
import io.imunity.vaadin.endpoint.common.EopException;
3936
import io.imunity.vaadin.endpoint.common.LanguageCookie;
37+
import io.imunity.vaadin.endpoint.common.QueryBuilder;
4038
import io.imunity.vaadin.endpoint.common.consent_utils.LoginInProgressService;
4139
import jakarta.servlet.ServletException;
4240
import jakarta.servlet.http.HttpServlet;
@@ -279,37 +277,15 @@ private AuthenticationPolicy mapPromptToAuthenticationPolicy(Set<Prompt> prompts
279277

280278
return AuthenticationPolicy.DEFAULT;
281279
}
282-
283280
/**
284-
* We are passing all unknown to OAuth query parameters to downstream servlet.
285-
* This may help to build extended UIs, which can interpret those parameters.
286-
*/
281+
* We are passing all unknown to OAuth query parameters to downstream servlet.
282+
* This may help to build extended UIs, which can interpret those parameters.
283+
*/
287284
private String getQueryToAppend(AuthorizationRequest authzRequest, LoginInProgressService.SignInContextKey contextKey)
288285
{
289-
Map<String, List<String>> customParameters = authzRequest.getCustomParameters();
290-
URIBuilder b = new URIBuilder();
291-
for (Entry<String, List<String>> entry : customParameters.entrySet())
292-
{
293-
for (String value : entry.getValue())
294-
{
295-
b.addParameter(entry.getKey(), value);
296-
}
297-
}
298-
if (!LoginInProgressService.UrlParamSignInContextKey.DEFAULT.equals(contextKey))
299-
{
300-
b.addParameter(OAuthSessionService.URL_PARAM_CONTEXT_KEY, contextKey.getKey());
301-
}
302-
String query = null;
303-
try
304-
{
305-
query = b.build().getRawQuery();
306-
} catch (URISyntaxException e)
307-
{
308-
log.error("Can't re-encode URL query params, shouldn't happen", e);
309-
}
310-
return query == null ? "" : "?" + query;
286+
return QueryBuilder.buildQuery(authzRequest.getCustomParameters(), contextKey.getKey());
311287
}
312-
288+
313289
private class ParsedRequestParametersWithUILocales
314290
{
315291
final Map<String, List<String>> parsedRequestParameters;

saml/src/main/java/pl/edu/icm/unity/saml/idp/web/SamlAuthVaadinEndpoint.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ protected ServletContextHandler getServletContextHandlerOverridable(WebAppContex
278278
description.getEndpoint().getContextAddress(),
279279
genericEndpointProperties.getBooleanValue(VaadinEndpointProperties.AUTO_LOGIN),
280280
description.getRealm());
281-
servletContextHandler.addFilter(new FilterHolder(proxyAuthnFilter), AUTHENTICATION_PATH + "/*",
281+
servletContextHandler.addFilter(new FilterHolder(proxyAuthnFilter), SAML_UI_SERVLET_PATH + "/*",
282282
EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD));
283283

284284
contextSetupFilter = new InvocationContextSetupFilter(config, description.getRealm(),

saml/src/main/java/pl/edu/icm/unity/saml/idp/web/filter/IdpConsentDeciderServlet.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,15 @@
66

77
import java.io.IOException;
88
import java.util.ArrayList;
9+
import java.util.Arrays;
910
import java.util.Calendar;
1011
import java.util.Collection;
1112
import java.util.List;
13+
import java.util.Map;
1214
import java.util.Optional;
1315
import java.util.Set;
1416
import java.util.TimeZone;
17+
import java.util.stream.Collectors;
1518

1619
import org.apache.logging.log4j.Logger;
1720
import org.eclipse.jetty.ee10.servlet.ServletApiRequest;
@@ -27,6 +30,7 @@
2730
import eu.unicore.security.dsig.DSigException;
2831
import io.imunity.idp.LastIdPClinetAccessAttributeManagement;
2932
import io.imunity.vaadin.endpoint.common.EopException;
33+
import io.imunity.vaadin.endpoint.common.QueryBuilder;
3034
import io.imunity.vaadin.endpoint.common.consent_utils.LoginInProgressService;
3135
import jakarta.servlet.ServletException;
3236
import jakarta.servlet.http.HttpServlet;
@@ -197,10 +201,12 @@ private void sendRedirect(HttpServletRequest req, HttpServletResponse resp) thro
197201

198202
private String getQueryToAppend(HttpServletRequest req)
199203
{
200-
String signInContextKey = req.getParameter(SamlSessionService.URL_PARAM_CONTEXT_KEY);
201-
return signInContextKey == null
202-
? ""
203-
: "?" + SamlSessionService.URL_PARAM_CONTEXT_KEY + "=" + signInContextKey;
204+
Map<String, List<String>> urlParameters = req.getParameterMap().entrySet().stream()
205+
.collect(Collectors.toMap(
206+
Map.Entry::getKey,
207+
entry -> Arrays.asList(entry.getValue())
208+
));
209+
return QueryBuilder.buildQuery(urlParameters);
204210
}
205211

206212
protected SPSettings loadPreferences(SAMLAuthnContext samlCtx) throws EngineException

vaadin-authentication/src/main/java/io/imunity/vaadin/auth/server/ProxyAuthenticationFilter.java

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,29 @@
44
*/
55
package io.imunity.vaadin.auth.server;
66

7-
import io.imunity.vaadin.auth.PreferredAuthenticationHelper;
8-
import io.imunity.vaadin.auth.ProxyAuthenticationCapable;
7+
import static io.imunity.vaadin.auth.server.AuthenticationFilter.isVaadinBackgroundRequest;
8+
9+
import java.io.IOException;
10+
import java.util.HashMap;
11+
import java.util.List;
12+
import java.util.Map;
13+
914
import org.apache.hc.core5.net.URIBuilder;
1015
import org.apache.logging.log4j.Logger;
16+
17+
import io.imunity.vaadin.auth.PreferredAuthenticationHelper;
18+
import io.imunity.vaadin.auth.ProxyAuthenticationCapable;
19+
import jakarta.servlet.DispatcherType;
20+
import jakarta.servlet.Filter;
21+
import jakarta.servlet.FilterChain;
22+
import jakarta.servlet.FilterConfig;
23+
import jakarta.servlet.RequestDispatcher;
24+
import jakarta.servlet.ServletException;
25+
import jakarta.servlet.ServletRequest;
26+
import jakarta.servlet.ServletResponse;
27+
import jakarta.servlet.http.HttpServletRequest;
28+
import jakarta.servlet.http.HttpServletResponse;
29+
import jakarta.servlet.http.HttpSession;
1130
import pl.edu.icm.unity.base.authn.AuthenticationOptionKeyUtils;
1231
import pl.edu.icm.unity.base.authn.AuthenticationRealm;
1332
import pl.edu.icm.unity.base.utils.Log;
@@ -17,17 +36,6 @@
1736
import pl.edu.icm.unity.engine.api.authn.AuthenticatorStepContext.FactorOrder;
1837
import pl.edu.icm.unity.engine.api.endpoint.BindingAuthn;
1938

20-
import jakarta.servlet.*;
21-
import jakarta.servlet.http.HttpServletRequest;
22-
import jakarta.servlet.http.HttpServletResponse;
23-
import jakarta.servlet.http.HttpSession;
24-
import java.io.IOException;
25-
import java.util.HashMap;
26-
import java.util.List;
27-
import java.util.Map;
28-
29-
import static io.imunity.vaadin.auth.server.AuthenticationFilter.isVaadinBackgroundRequest;
30-
3139
/**
3240
* Non UI code which is invoked as a part of authentication pipeline for unauthenticated clients.
3341
* This filter checks whether automated proxy authentication is enabled for the protected endpoint
@@ -94,14 +102,33 @@ private static String filteredQuery(HttpServletRequest httpRequest)
94102
}
95103

96104
public static String getCurrentRelativeURL(HttpServletRequest httpRequest)
105+
{
106+
DispatcherType dispatcherType = httpRequest.getDispatcherType();
107+
return switch (dispatcherType)
108+
{
109+
case FORWARD -> forwardBasedRelativeURL(httpRequest);
110+
case REQUEST -> requestBasedRelativeURL(httpRequest);
111+
default -> throw new IllegalStateException("Unexpected dispatcher type: " + dispatcherType);
112+
};
113+
}
114+
115+
private static String requestBasedRelativeURL(HttpServletRequest httpRequest)
116+
{
117+
String relativeUrl = httpRequest.getRequestURI().substring(httpRequest.getContextPath().length());
118+
String query = httpRequest.getQueryString() == null ? "" :
119+
ProxyAuthenticationFilter.filteredQuery(httpRequest);
120+
return relativeUrl + query;
121+
}
122+
123+
private static String forwardBasedRelativeURL(HttpServletRequest httpRequest)
97124
{
98125
String origReqUri = (String)httpRequest.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI);
99126
String servletPath = origReqUri == null ? "/" : origReqUri;
100-
String query = httpRequest.getQueryString() == null ? "" :
127+
String query = httpRequest.getQueryString() == null ? "" :
101128
ProxyAuthenticationFilter.filteredQuery(httpRequest);
102129
return servletPath + query;
103130
}
104-
131+
105132
@Override
106133
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
107134
throws IOException, ServletException

vaadin-authentication/src/main/java/io/imunity/vaadin/auth/server/SecureVaadin2XEndpoint.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,8 @@ protected ServletContextHandler getServletContextHandlerOverridable(WebAppContex
8585
description.getEndpoint().getContextAddress(),
8686
genericEndpointProperties.getBooleanValue(VaadinEndpointProperties.AUTO_LOGIN),
8787
description.getRealm());
88-
servletContextHandler.addFilter(new FilterHolder(proxyAuthnFilter), "/",
88+
servletContextHandler.addFilter(new FilterHolder(proxyAuthnFilter), "/*",
8989
EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD));
90-
// servletContextHandler.addFilter(new FilterHolder(proxyAuthnFilter), AUTHENTICATION_PATH + "/*",
91-
// EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD));
9290

9391
contextSetupFilter = new InvocationContextSetupFilter(serverConfig, description.getRealm(),
9492
getServletUrl(uiServletPath), getAuthenticationFlows());
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* Copyright (c) 2025 Bixbit - Krzysztof Benedyczak. All rights reserved.
3+
* See LICENCE.txt file for licensing information.
4+
*/
5+
6+
package io.imunity.vaadin.auth.server;
7+
8+
import static org.junit.jupiter.api.Assertions.assertEquals;
9+
import static org.junit.jupiter.api.Assertions.assertThrows;
10+
import static org.mockito.Mockito.when;
11+
12+
import java.util.Map;
13+
14+
import org.junit.jupiter.api.Test;
15+
import org.junit.jupiter.api.extension.ExtendWith;
16+
import org.mockito.Mock;
17+
import org.mockito.junit.jupiter.MockitoExtension;
18+
19+
import jakarta.servlet.DispatcherType;
20+
import jakarta.servlet.RequestDispatcher;
21+
import jakarta.servlet.http.HttpServletRequest;
22+
23+
@ExtendWith(MockitoExtension.class)
24+
class ProxyAuthenticationFilterTest
25+
{
26+
@Mock
27+
private HttpServletRequest request;
28+
29+
@Test
30+
void shouldHandleForwardDispatcherType()
31+
{
32+
when(request.getDispatcherType()).thenReturn(DispatcherType.FORWARD);
33+
when(request.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI)).thenReturn("/test/path");
34+
when(request.getQueryString()).thenReturn("param1=value1");
35+
when(request.getParameterMap()).thenReturn(Map.of("param1", new String[]{"value1"}));
36+
37+
String result = ProxyAuthenticationFilter.getCurrentRelativeURL(request);
38+
39+
assertEquals("/test/path?param1=value1", result);
40+
}
41+
42+
@Test
43+
void shouldHandleRequestDispatcherType()
44+
{
45+
when(request.getDispatcherType()).thenReturn(DispatcherType.REQUEST);
46+
when(request.getRequestURI()).thenReturn("/context/test/path");
47+
when(request.getContextPath()).thenReturn("/context");
48+
when(request.getQueryString()).thenReturn("param1=value1");
49+
when(request.getParameterMap()).thenReturn(Map.of("param1", new String[]{"value1"}));
50+
51+
String result = ProxyAuthenticationFilter.getCurrentRelativeURL(request);
52+
53+
assertEquals("/test/path?param1=value1", result);
54+
}
55+
56+
@Test
57+
void shouldHandleNullQueryString()
58+
{
59+
when(request.getDispatcherType()).thenReturn(DispatcherType.REQUEST);
60+
when(request.getRequestURI()).thenReturn("/context/test/path");
61+
when(request.getContextPath()).thenReturn("/context");
62+
when(request.getQueryString()).thenReturn(null);
63+
64+
String result = ProxyAuthenticationFilter.getCurrentRelativeURL(request);
65+
66+
assertEquals("/test/path", result);
67+
}
68+
69+
@Test
70+
void shouldThrowExceptionForUnsupportedDispatcherType()
71+
{
72+
when(request.getDispatcherType()).thenReturn(DispatcherType.INCLUDE);
73+
74+
assertThrows(IllegalStateException.class,
75+
() -> ProxyAuthenticationFilter.getCurrentRelativeURL(request));
76+
}
77+
78+
@Test
79+
void shouldHandleNullForwardRequestUri()
80+
{
81+
when(request.getDispatcherType()).thenReturn(DispatcherType.FORWARD);
82+
when(request.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI)).thenReturn(null);
83+
when(request.getQueryString()).thenReturn(null);
84+
85+
String result = ProxyAuthenticationFilter.getCurrentRelativeURL(request);
86+
87+
assertEquals("/", result);
88+
}
89+
}

0 commit comments

Comments
 (0)