From 1ab2a4fbcbe4df449bbfe5389e1f46fe7eb00aa3 Mon Sep 17 00:00:00 2001 From: Tommaso D'Alessandro Date: Wed, 20 Dec 2023 12:48:52 +0100 Subject: [PATCH] AAE-19071 disable CSRF protection for public URLs (#1298) * AAE-19071 disable CSRF for public URLs, unit test. * AAE-19071 fix other tests * AAE-19071 sonarqube issues --- .../AuthorizationConfigurer.java | 13 ++++++ .../authorization/CsrfIgnoreMatcher.java | 39 ++++++++++++++++++ .../authorization/CsrfIgnoreMatcherTest.java | 41 +++++++++++++++++++ 3 files changed, 93 insertions(+) create mode 100644 activiti-cloud-service-common/activiti-cloud-services-common-security/src/main/java/org/activiti/cloud/security/authorization/CsrfIgnoreMatcher.java create mode 100644 activiti-cloud-service-common/activiti-cloud-services-common-security/src/test/java/org/activiti/cloud/security/authorization/CsrfIgnoreMatcherTest.java diff --git a/activiti-cloud-service-common/activiti-cloud-services-common-security/src/main/java/org/activiti/cloud/security/authorization/AuthorizationConfigurer.java b/activiti-cloud-service-common/activiti-cloud-services-common-security/src/main/java/org/activiti/cloud/security/authorization/AuthorizationConfigurer.java index c638960bab4..cf1d578a93b 100644 --- a/activiti-cloud-service-common/activiti-cloud-services-common-security/src/main/java/org/activiti/cloud/security/authorization/AuthorizationConfigurer.java +++ b/activiti-cloud-service-common/activiti-cloud-services-common-security/src/main/java/org/activiti/cloud/security/authorization/AuthorizationConfigurer.java @@ -20,6 +20,7 @@ import jakarta.annotation.PostConstruct; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.function.Consumer; @@ -75,10 +76,22 @@ public void configure(HttpSecurity http) throws Exception { List orderedSecurityConstraints = getOrderedList( authorizationProperties.getSecurityConstraints() ); + List publicUrls = new ArrayList<>(); for (SecurityConstraint securityConstraint : orderedSecurityConstraints) { String[] roles = securityConstraint.getAuthRoles(); + if (roles.length == 0) { + List patterns = Arrays + .stream(securityConstraint.getSecurityCollections()) + .flatMap(s -> Arrays.stream(getPatterns(s.getPatterns()))) + .toList(); + publicUrls.addAll(patterns); + } configureAuthorization(http, roles, securityConstraint.getSecurityCollections()); } + if (!publicUrls.isEmpty()) { + LOGGER.debug("Disabling CSRF protection for public URLs: {}", publicUrls); + http.csrf(csrf -> csrf.ignoringRequestMatchers(new CsrfIgnoreMatcher(publicUrls))); + } http.anonymous(withDefaults()); } diff --git a/activiti-cloud-service-common/activiti-cloud-services-common-security/src/main/java/org/activiti/cloud/security/authorization/CsrfIgnoreMatcher.java b/activiti-cloud-service-common/activiti-cloud-services-common-security/src/main/java/org/activiti/cloud/security/authorization/CsrfIgnoreMatcher.java new file mode 100644 index 00000000000..defb7290825 --- /dev/null +++ b/activiti-cloud-service-common/activiti-cloud-services-common-security/src/main/java/org/activiti/cloud/security/authorization/CsrfIgnoreMatcher.java @@ -0,0 +1,39 @@ +/* + * Copyright 2017-2020 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.activiti.cloud.security.authorization; + +import jakarta.servlet.http.HttpServletRequest; +import java.util.List; +import org.springframework.security.web.util.matcher.RequestMatcher; +import org.springframework.util.AntPathMatcher; +import org.springframework.util.PathMatcher; + +public class CsrfIgnoreMatcher implements RequestMatcher { + + private final List publicUrls; + + private final PathMatcher matcher; + + public CsrfIgnoreMatcher(List publicUrls) { + this.publicUrls = publicUrls; + this.matcher = new AntPathMatcher(); + } + + @Override + public boolean matches(HttpServletRequest request) { + return publicUrls.stream().anyMatch(url -> matcher.match(url, request.getRequestURI())); + } +} diff --git a/activiti-cloud-service-common/activiti-cloud-services-common-security/src/test/java/org/activiti/cloud/security/authorization/CsrfIgnoreMatcherTest.java b/activiti-cloud-service-common/activiti-cloud-services-common-security/src/test/java/org/activiti/cloud/security/authorization/CsrfIgnoreMatcherTest.java new file mode 100644 index 00000000000..4d684aaaa01 --- /dev/null +++ b/activiti-cloud-service-common/activiti-cloud-services-common-security/src/test/java/org/activiti/cloud/security/authorization/CsrfIgnoreMatcherTest.java @@ -0,0 +1,41 @@ +/* + * Copyright 2017-2020 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.activiti.cloud.security.authorization; + +import static java.util.Arrays.asList; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; +import org.junit.jupiter.api.Test; +import org.springframework.mock.web.MockHttpServletRequest; + +class CsrfIgnoreMatcherTest { + + private final List publicUrlsPatterns = asList("/public", "/public/**"); + private final List nonPublicUrlsPatterns = asList("/non-public", "/non-public/**"); + private final CsrfIgnoreMatcher matcher = new CsrfIgnoreMatcher(publicUrlsPatterns); + + @Test + void should_matchPublicURLsPatterns() { + nonPublicUrlsPatterns.forEach(url -> assertThat(matcher.matches(new MockHttpServletRequest("", url))).isFalse() + ); + } + + @Test + void should_not_matchNonPublicURLsPatterns() { + publicUrlsPatterns.forEach(url -> assertThat(matcher.matches(new MockHttpServletRequest("", url))).isTrue()); + } +}