Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions integration-tests/jakarta-ee/src/main/webapp/WEB-INF/web.xml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@
<param-name>org.apache.shiro.form-resubmit.secure-cookies</param-name>
<param-value>false</param-value>
</context-param>
<context-param>
<param-name>org.apache.shiro.form-resubmit.whitelist.disabled</param-name>
<param-value>true</param-value>
</context-param>

<!-- Apache Shiro Security -->
<context-param>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,15 @@
import static org.apache.shiro.ee.filters.FormAuthenticationFilter.LOGIN_URL_ATTR_NAME;
import static org.apache.shiro.ee.filters.FormAuthenticationFilter.LOGIN_WAITTIME_ATTR_NAME;
import static org.apache.shiro.ee.filters.FormAuthenticationFilter.NO_PREDICATE;
import static org.apache.shiro.ee.filters.FormAuthenticationFilter.getPathWithinApplication;
import static org.apache.shiro.ee.filters.FormResubmitSupport.isPostRequest;
import static org.apache.shiro.ee.filters.FormResubmitSupport.savePostDataForResubmit;
import static org.apache.shiro.ee.filters.FormResubmitSupport.saveRequestReferer;
import static org.apache.shiro.ee.filters.LogoutFilter.LOGOUT_PREDICATE_ATTR_NAME;
import static org.apache.shiro.ee.filters.LogoutFilter.YES_PREDICATE;
import static org.apache.shiro.ee.listeners.EnvironmentLoaderListener.isFormResubmitDisabled;
import static org.apache.shiro.ee.listeners.EnvironmentLoaderListener.isServletNoPrincipal;
import static org.apache.shiro.web.filter.authc.NoAccessFilter.FORM_RESUBMIT_CHECK_SERVLET_PATH;
import static org.apache.shiro.web.jaxrs.SubjectPrincipalRequestFilter.SHIRO_WEB_JAXRS_DISABLE_PRINCIPAL_PARAM;

/**
Expand Down Expand Up @@ -101,7 +104,10 @@ public boolean preHandle(ServletRequest request, ServletResponse response) throw
public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
Subject subject = methods.getSubject(request, response);
boolean isAuthenticated = subject.isAuthenticated() && subject.getPrincipal() != null;
return isAuthenticated || (useRemembered && subject.isRemembered());
return isAuthenticated || (useRemembered && subject.isRemembered())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For logic like this, it would be nice to have a unit test targeting this code and triggering the NPE when the fix is not in place.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(or: if the NPE wasn't here, a test that replicates the NPE stack trace. I usually make such tests as simple as possible with as much mocking as possible, targeting own code)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch Steinar!
I have updated the integration tests to cover the failure path.

|| (isPostRequest(request)
&& FORM_RESUBMIT_CHECK_SERVLET_PATH.equals(getPathWithinApplication(request,
() -> WebUtils.getPathWithinApplication(WebUtils.toHttp(request)))));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,12 @@
import java.net.URISyntaxException;
import java.time.Duration;
import java.util.Collections;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.crypto.CryptoException;
import org.apache.shiro.ee.filters.Forms.FallbackPredicate;
import static org.apache.shiro.ee.filters.FormResubmitSupportCookies.initializeCookies;
import static org.apache.shiro.ee.filters.FormResubmitSupportCookies.transformCookieHeader;
import static org.apache.shiro.ee.listeners.EnvironmentLoaderListener.isFormResubmitBlacklistEnabled;
import static org.apache.shiro.ee.listeners.EnvironmentLoaderListener.isFormResubmitDisabled;
import java.io.IOException;
import java.net.CookieManager;
Expand All @@ -57,6 +59,7 @@
import java.util.Set;
import java.util.UUID;
import static java.util.function.Predicate.not;
import static org.apache.shiro.ee.listeners.EnvironmentLoaderListener.isFormResubmitWhitelistEnabled;
import static org.apache.shiro.ee.listeners.IniEnvironment.hasFacesContext;
import static org.apache.shiro.web.filter.authc.NoAccessFilter.FORM_RESUBMIT_CHECK_SERVLET_PATH;
import static org.apache.shiro.web.filter.authz.PortFilter.DEFAULT_HTTP_PORT;
Expand Down Expand Up @@ -646,13 +649,18 @@ private static boolean checkWhitelist(ServletContext servletContext, URI savedRe
} else if (isBlacklisted(blacklist, authority)) {
log.debug("Form resubmit blacklist cache hit for {}", savedRequestURI);
return false;
} else if (checkWhitelistClient(savedRequestURI, servletContext.getContextPath(), client, savedFormDataKey)) {
putWhitelistEntry(whitelist, authority);
} else if (checkWhitelistClient(savedRequestURI, servletContext.getContextPath(), client,
savedFormDataKey, dsm.getCacheManager())) {
if (isFormResubmitWhitelistEnabled(servletContext)) {
putWhitelistEntry(whitelist, authority);
}
blacklist.remove(authority);
return true;
}

putBlacklistEntry(blacklist, authority);
if (isFormResubmitBlacklistEnabled(servletContext)) {
putBlacklistEntry(blacklist, authority);
}
return false;
}

Expand Down Expand Up @@ -710,7 +718,10 @@ static boolean isBlacklisted(Cache<String, Long> blacklist, String authority,
}

private static boolean checkWhitelistClient(URI savedRequestURI, String contextPath, HttpClient client,
String savedFormDataKey) {
String savedFormDataKey, @NonNull CacheManager cacheManager) {
Cache<UUID, String> cache = null;
UUID savedFormDataUUID = null;

try {
var rememberMeManager = getRememberMeManager();
if (rememberMeManager == null || rememberMeManager.getCipherService() == null
Expand All @@ -719,6 +730,13 @@ private static boolean checkWhitelistClient(URI savedRequestURI, String contextP
return false;
}

if (savedFormDataKey == null) {
savedFormDataUUID = UUID.randomUUID();
savedFormDataKey = savedFormDataUUID.toString();
cache = cacheManager.getCache(FORM_DATA_CACHE);
cache.put(savedFormDataUUID, "__DUMMY_FOR_CLIENT_WHITELIST_CHECK__");
}

var request = HttpRequest.newBuilder()
.uri(URI.create("%s://%s%s%s".formatted(savedRequestURI.getScheme(), savedRequestURI.getAuthority(),
contextPath, FORM_RESUBMIT_CHECK_SERVLET_PATH)))
Expand All @@ -738,6 +756,10 @@ private static boolean checkWhitelistClient(URI savedRequestURI, String contextP
} catch (IOException | InterruptedException e) {
log.debug("Form resubmit whitelist check failed for {} with exception: {}",
savedRequestURI, e);
} finally {
if (cache != null) {
cache.remove(savedFormDataUUID);
}
}
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ public class EnvironmentLoaderListener extends EnvironmentLoader implements Serv
private static final String SHIRO_EE_CHAR_ENCODING_PARAM = "org.apache.shiro.ee.character-encoding";
private static final String FORM_RESUBMIT_DISABLED_PARAM = "org.apache.shiro.form-resubmit.disabled";
private static final String FORM_RESUBMIT_SECURE_COOKIES = "org.apache.shiro.form-resubmit.secure-cookies";
private static final String FORM_RESUBMIT_WHITE_LIST_DISABLED = "org.apache.shiro.form-resubmit.whitelist.disabled";
private static final String FORM_RESUBMIT_BLACK_LIST_DISABLED = "org.apache.shiro.form-resubmit.blacklist.disabled";
private static final String SHIRO_WEB_DISABLE_PRINCIPAL_PARAM = "org.apache.shiro.web.disable-principal";

public static boolean isShiroEEDisabled(ServletContext ctx) {
Expand All @@ -64,6 +66,14 @@ public static boolean isFormResubmitSecureCookies(ServletContext ctx) {
return Boolean.TRUE.equals(ctx.getAttribute(FORM_RESUBMIT_SECURE_COOKIES));
}

public static boolean isFormResubmitWhitelistEnabled(ServletContext ctx) {
return !Boolean.TRUE.equals(ctx.getAttribute(FORM_RESUBMIT_WHITE_LIST_DISABLED));
}

public static boolean isFormResubmitBlacklistEnabled(ServletContext ctx) {
return !Boolean.TRUE.equals(ctx.getAttribute(FORM_RESUBMIT_BLACK_LIST_DISABLED));
}

public static boolean isServletNoPrincipal(ServletContext ctx) {
return Boolean.TRUE.equals(ctx.getAttribute(SHIRO_WEB_DISABLE_PRINCIPAL_PARAM));
}
Expand Down Expand Up @@ -96,6 +106,12 @@ public void contextInitialized(ServletContextEvent sce) {
} else {
sce.getServletContext().setAttribute(FORM_RESUBMIT_SECURE_COOKIES, Boolean.FALSE);
}
if (Boolean.parseBoolean(sce.getServletContext().getInitParameter(FORM_RESUBMIT_WHITE_LIST_DISABLED))) {
sce.getServletContext().setAttribute(FORM_RESUBMIT_WHITE_LIST_DISABLED, Boolean.TRUE);
}
if (Boolean.parseBoolean(sce.getServletContext().getInitParameter(FORM_RESUBMIT_BLACK_LIST_DISABLED))) {
sce.getServletContext().setAttribute(FORM_RESUBMIT_BLACK_LIST_DISABLED, Boolean.TRUE);
}
if (Boolean.parseBoolean(sce.getServletContext().getInitParameter(SHIRO_WEB_DISABLE_PRINCIPAL_PARAM))) {
sce.getServletContext().setAttribute(SHIRO_WEB_DISABLE_PRINCIPAL_PARAM, Boolean.TRUE);
}
Expand Down
Loading