diff --git a/core/src/main/java/org/apache/struts2/interceptor/I18nInterceptor.java b/core/src/main/java/org/apache/struts2/interceptor/I18nInterceptor.java index 6bce042446..e2aedba1d1 100644 --- a/core/src/main/java/org/apache/struts2/interceptor/I18nInterceptor.java +++ b/core/src/main/java/org/apache/struts2/interceptor/I18nInterceptor.java @@ -65,7 +65,7 @@ public class I18nInterceptor extends AbstractInterceptor { private Set supportedLocale = Collections.emptySet(); - protected enum Storage { COOKIE, SESSION, REQUEST, ACCEPT_LANGUAGE } + protected enum Storage {COOKIE, SESSION, REQUEST, ACCEPT_LANGUAGE} public void setParameterName(String parameterName) { this.parameterName = parameterName; @@ -103,10 +103,14 @@ public void setLocaleStorage(String storageName) { */ public void setSupportedLocale(String supportedLocale) { this.supportedLocale = TextParseUtil - .commaDelimitedStringToSet(supportedLocale) - .stream() - .map(Locale::new) - .collect(Collectors.toSet()); + .commaDelimitedStringToSet(supportedLocale) + .stream() + .map(Locale::new) + .collect(Collectors.toSet()); + } + + protected boolean isLocaleSupported(Locale locale) { + return supportedLocale.isEmpty() || supportedLocale.contains(locale); } @Inject @@ -222,8 +226,11 @@ protected void useLocale(ActionInvocation invocation, Locale locale) { */ protected interface LocaleHandler { Locale find(); + Locale read(ActionInvocation invocation); + Locale store(ActionInvocation invocation, Locale locale); + boolean shouldStore(); } @@ -241,7 +248,10 @@ public Locale find() { Parameter requestedLocale = findLocaleParameter(actionInvocation, requestOnlyParameterName); if (requestedLocale.isDefined()) { - return getLocaleFromParam(requestedLocale.getValue()); + Locale locale = getLocaleFromParam(requestedLocale.getValue()); + if (locale != null && isLocaleSupported(locale)) { + return locale; + } } return null; @@ -278,6 +288,11 @@ protected AcceptLanguageLocaleHandler(ActionInvocation invocation) { @Override @SuppressWarnings("rawtypes") public Locale find() { + Locale requestOnlyLocale = super.find(); + if (requestOnlyLocale != null) { + return requestOnlyLocale; + } + if (!supportedLocale.isEmpty()) { Enumeration locales = actionInvocation.getInvocationContext().getServletRequest().getLocales(); while (locales.hasMoreElements()) { @@ -287,7 +302,7 @@ public Locale find() { } } } - return super.find(); + return null; } } @@ -300,20 +315,20 @@ protected SessionLocaleHandler(ActionInvocation invocation) { @Override public Locale find() { - Locale requestOnlyLocale = super.find(); + Parameter requestedLocale = findLocaleParameter(actionInvocation, parameterName); + if (requestedLocale.isDefined()) { + Locale locale = getLocaleFromParam(requestedLocale.getValue()); + if (locale != null && isLocaleSupported(locale)) { + return locale; + } + } + Locale requestOnlyLocale = super.find(); if (requestOnlyLocale != null) { - LOG.debug("Found locale under request only param, it won't be stored in session!"); shouldStore = false; return requestOnlyLocale; } - LOG.debug("Searching locale in request under parameter {}", parameterName); - Parameter requestedLocale = findLocaleParameter(actionInvocation, parameterName); - if (requestedLocale.isDefined()) { - return getLocaleFromParam(requestedLocale.getValue()); - } - return null; } @@ -344,7 +359,12 @@ public Locale read(ActionInvocation invocation) { Object sessionLocale = invocation.getInvocationContext().getSession().get(attributeName); if (sessionLocale instanceof Locale) { locale = (Locale) sessionLocale; - LOG.debug("Applied session locale: {}", locale); + if (!isLocaleSupported(locale)) { + LOG.debug("Stored session locale {} is not supported, discarding", locale); + locale = null; + } else { + LOG.debug("Applied session locale: {}", locale); + } } } } @@ -368,17 +388,18 @@ protected CookieLocaleHandler(ActionInvocation invocation) { @Override public Locale find() { - Locale requestOnlySessionLocale = super.find(); - - if (requestOnlySessionLocale != null) { - shouldStore = false; - return requestOnlySessionLocale; - } - - LOG.debug("Searching locale in request under parameter {}", requestCookieParameterName); Parameter requestedLocale = findLocaleParameter(actionInvocation, requestCookieParameterName); if (requestedLocale.isDefined()) { - return getLocaleFromParam(requestedLocale.getValue()); + Locale locale = getLocaleFromParam(requestedLocale.getValue()); + if (locale != null && isLocaleSupported(locale)) { + return locale; + } + } + + Locale requestOnlyLocale = super.find(); + if (requestOnlyLocale != null) { + shouldStore = false; + return requestOnlyLocale; } return null; @@ -404,6 +425,10 @@ public Locale read(ActionInvocation invocation) { for (Cookie cookie : cookies) { if (attributeName.equals(cookie.getName())) { locale = getLocaleFromParam(cookie.getValue()); + if (locale != null && !isLocaleSupported(locale)) { + LOG.debug("Stored cookie locale {} is not supported, discarding", locale); + locale = null; + } } } } diff --git a/core/src/test/java/org/apache/struts2/interceptor/I18nInterceptorTest.java b/core/src/test/java/org/apache/struts2/interceptor/I18nInterceptorTest.java index 604d61be69..3a592574df 100644 --- a/core/src/test/java/org/apache/struts2/interceptor/I18nInterceptorTest.java +++ b/core/src/test/java/org/apache/struts2/interceptor/I18nInterceptorTest.java @@ -205,7 +205,7 @@ public void testRealLocaleObjectInParams() throws Exception { } public void testRealLocalesInParams() throws Exception { - Locale[] locales = new Locale[] { Locale.CANADA_FRENCH }; + Locale[] locales = new Locale[]{Locale.CANADA_FRENCH}; assertTrue(locales.getClass().isArray()); prepare(I18nInterceptor.DEFAULT_PARAMETER, locales); interceptor.intercept(mai); @@ -294,6 +294,66 @@ public void testAcceptLanguageBasedLocaleWithFallbackToDefault() throws Exceptio assertEquals(Locale.US, mai.getInvocationContext().getLocale()); } + public void testRequestLocaleWithSupportedLocale() throws Exception { + // given + interceptor.setSupportedLocale("en,de"); + prepare(I18nInterceptor.DEFAULT_PARAMETER, "de"); + + // when + interceptor.intercept(mai); + + // then + Locale german = new Locale("de"); + assertEquals(german, session.get(I18nInterceptor.DEFAULT_SESSION_ATTRIBUTE)); + assertEquals(german, mai.getInvocationContext().getLocale()); + } + + public void testUnsupportedRequestLocaleRejected() throws Exception { + // given + interceptor.setSupportedLocale("en,de"); + prepare(I18nInterceptor.DEFAULT_PARAMETER, "fr"); + + // when + interceptor.intercept(mai); + + // then - fr is not supported, should fall back to default + assertNull("unsupported locale should not be stored", session.get(I18nInterceptor.DEFAULT_SESSION_ATTRIBUTE)); + } + + public void testStaleSessionLocaleRejected() throws Exception { + // given - session has a stored locale that is no longer supported + session.put(I18nInterceptor.DEFAULT_SESSION_ATTRIBUTE, Locale.FRENCH); + interceptor.setSupportedLocale("en,de"); + + // when + interceptor.intercept(mai); + + // then - stored fr locale should be discarded since it's not in supportedLocale + assertFalse("stale session locale should be discarded", + Locale.FRENCH.equals(mai.getInvocationContext().getLocale())); + } + + public void testCookieRequestLocaleWithSupportedLocale() throws Exception { + // given + interceptor.setSupportedLocale("en,de"); + interceptor.setLocaleStorage(I18nInterceptor.Storage.COOKIE.name()); + prepare(I18nInterceptor.DEFAULT_COOKIE_PARAMETER, "de"); + + final Cookie cookie = new Cookie(I18nInterceptor.DEFAULT_COOKIE_ATTRIBUTE, "de"); + HttpServletResponse response = EasyMock.createMock(HttpServletResponse.class); + response.addCookie(CookieMatcher.eqCookie(cookie)); + EasyMock.replay(response); + ac.put(StrutsStatics.HTTP_RESPONSE, response); + + // when + interceptor.intercept(mai); + + // then + EasyMock.verify(response); + Locale german = new Locale("de"); + assertEquals(german, mai.getInvocationContext().getLocale()); + } + private void prepare(String key, Serializable value) { Map params = new HashMap<>(); params.put(key, value); @@ -308,9 +368,9 @@ public void setUp() throws Exception { session = new HashMap<>(); ac = ActionContext.of() - .bind() - .withSession(session) - .withParameters(HttpParameters.create().build()); + .bind() + .withSession(session) + .withParameters(HttpParameters.create().build()); request = new MockHttpServletRequest(); request.setSession(new MockHttpSession()); @@ -348,8 +408,8 @@ static class CookieMatcher implements IArgumentMatcher { public boolean matches(Object argument) { Cookie cookie = ((Cookie) argument); return - (cookie.getName().equals(expected.getName()) && - cookie.getValue().equals(expected.getValue())); + (cookie.getName().equals(expected.getName()) && + cookie.getValue().equals(expected.getValue())); } public static Cookie eqCookie(Cookie ck) { @@ -359,10 +419,10 @@ public static Cookie eqCookie(Cookie ck) { public void appendTo(StringBuffer buffer) { buffer - .append("Received") - .append(expected.getName()) - .append("/") - .append(expected.getValue()); + .append("Received") + .append(expected.getName()) + .append("/") + .append(expected.getValue()); } }