From f68f477b66608a7ad928806f733b7aa0bd844c6c Mon Sep 17 00:00:00 2001 From: Luca Bassi <luca@argoware.com> Date: Fri, 8 Nov 2024 18:58:18 +0100 Subject: [PATCH] Upgrade to Spring Security 5.8 --- pom.xml | 1 + .../StormMethodSecurityExpressionHandler.java | 10 +- .../authz/managers/ConsensusBasedManager.java | 75 ++++++++++ .../FineGrainedAuthzManager.java} | 26 ++-- .../FineGrainedCopyMoveAuthzManager.java} | 45 +++--- .../LocalAuthzManager.java} | 47 +++--- .../authz/managers/MacaroonAuthzManager.java | 41 +++++ .../PathAuthzPdpManagerSupport.java} | 36 ++--- .../managers/UnanimousDelegatedManager.java | 81 ++++++++++ .../WlcgScopeAuthzCopyMoveManager.java} | 48 +++--- .../WlcgScopeAuthzManager.java} | 32 ++-- .../authz/voters/MacaroonAuthzVoter.java | 51 ------- .../authz/voters/UnanimousDelegatedVoter.java | 104 ------------- .../webdav/spring/web/SecurityConfig.java | 141 ++++++++++-------- ...ecurityExpressionHandlerConfiguration.java | 33 ---- 15 files changed, 404 insertions(+), 367 deletions(-) create mode 100644 src/main/java/org/italiangrid/storm/webdav/authz/managers/ConsensusBasedManager.java rename src/main/java/org/italiangrid/storm/webdav/authz/{voters/FineGrainedAuthzVoter.java => managers/FineGrainedAuthzManager.java} (64%) rename src/main/java/org/italiangrid/storm/webdav/authz/{voters/FineGrainedCopyMoveAuthzVoter.java => managers/FineGrainedCopyMoveAuthzManager.java} (59%) rename src/main/java/org/italiangrid/storm/webdav/authz/{voters/LocalAuthzVoter.java => managers/LocalAuthzManager.java} (61%) create mode 100644 src/main/java/org/italiangrid/storm/webdav/authz/managers/MacaroonAuthzManager.java rename src/main/java/org/italiangrid/storm/webdav/authz/{voters/PathAuthzPdpVoterSupport.java => managers/PathAuthzPdpManagerSupport.java} (75%) create mode 100644 src/main/java/org/italiangrid/storm/webdav/authz/managers/UnanimousDelegatedManager.java rename src/main/java/org/italiangrid/storm/webdav/authz/{voters/WlcgScopeAuthzCopyMoveVoter.java => managers/WlcgScopeAuthzCopyMoveManager.java} (61%) rename src/main/java/org/italiangrid/storm/webdav/authz/{voters/WlcgScopeAuthzVoter.java => managers/WlcgScopeAuthzManager.java} (63%) delete mode 100644 src/main/java/org/italiangrid/storm/webdav/authz/voters/MacaroonAuthzVoter.java delete mode 100644 src/main/java/org/italiangrid/storm/webdav/authz/voters/UnanimousDelegatedVoter.java delete mode 100644 src/main/java/org/italiangrid/storm/webdav/spring/web/SecurityExpressionHandlerConfiguration.java diff --git a/pom.xml b/pom.xml index 2a9d9925..7216044f 100644 --- a/pom.xml +++ b/pom.xml @@ -32,6 +32,7 @@ <!-- Keep this aligned with the parent project version! --> <spring-boot.version>2.7.18</spring-boot.version> + <spring-security.version>5.8.15</spring-security.version> <!-- Sonarcloud.io properties --> <sonar.projectKey>italiangrid_storm-webdav</sonar.projectKey> diff --git a/src/main/java/org/italiangrid/storm/webdav/authz/expression/StormMethodSecurityExpressionHandler.java b/src/main/java/org/italiangrid/storm/webdav/authz/expression/StormMethodSecurityExpressionHandler.java index 44997ee2..b5196ee0 100644 --- a/src/main/java/org/italiangrid/storm/webdav/authz/expression/StormMethodSecurityExpressionHandler.java +++ b/src/main/java/org/italiangrid/storm/webdav/authz/expression/StormMethodSecurityExpressionHandler.java @@ -15,8 +15,10 @@ */ package org.italiangrid.storm.webdav.authz.expression; +import java.util.function.Supplier; + import org.aopalliance.intercept.MethodInvocation; -import org.springframework.expression.spel.support.StandardEvaluationContext; +import org.springframework.expression.EvaluationContext; import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; import org.springframework.security.core.Authentication; import org.springframework.stereotype.Component; @@ -26,11 +28,11 @@ public class StormMethodSecurityExpressionHandler extends DefaultMethodSecurityE @Override - public StandardEvaluationContext createEvaluationContextInternal(Authentication authentication, + public EvaluationContext createEvaluationContext(Supplier<Authentication> authentication, MethodInvocation mi) { - StandardEvaluationContext ec = super.createEvaluationContextInternal(authentication, mi); - ec.setVariable("storm", new StormSecurityExpressionMethods(authentication)); + EvaluationContext ec = super.createEvaluationContext(authentication, mi); + ec.setVariable("storm", new StormSecurityExpressionMethods(authentication.get())); return ec; } diff --git a/src/main/java/org/italiangrid/storm/webdav/authz/managers/ConsensusBasedManager.java b/src/main/java/org/italiangrid/storm/webdav/authz/managers/ConsensusBasedManager.java new file mode 100644 index 00000000..4533da30 --- /dev/null +++ b/src/main/java/org/italiangrid/storm/webdav/authz/managers/ConsensusBasedManager.java @@ -0,0 +1,75 @@ +/** + * Copyright (c) Istituto Nazionale di Fisica Nucleare, 2014-2023. + * + * 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.italiangrid.storm.webdav.authz.managers; + +import java.util.List; +import java.util.function.Supplier; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.authorization.AuthorizationDecision; +import org.springframework.security.authorization.AuthorizationManager; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.access.intercept.RequestAuthorizationContext; + +public class ConsensusBasedManager implements AuthorizationManager<RequestAuthorizationContext> { + + public static final Logger LOG = LoggerFactory.getLogger(ConsensusBasedManager.class); + + private final List<AuthorizationManager<RequestAuthorizationContext>> managers; + + private final String name; + + public ConsensusBasedManager(String name, + List<AuthorizationManager<RequestAuthorizationContext>> managers) { + this.name = name; + this.managers = managers; + } + + @Override + public AuthorizationDecision check(Supplier<Authentication> authentication, + RequestAuthorizationContext requestAuthorizationContext) { + int grant = 0; + int notGrant = 0; + + for (AuthorizationManager<RequestAuthorizationContext> manager : managers) { + AuthorizationDecision result = manager.check(authentication, requestAuthorizationContext); + + if (LOG.isDebugEnabled()) { + LOG.debug("Voter: {}, returned: {}", manager, result); + } + + if (result != null) { + if (result.isGranted()) { + grant++; + } else { + notGrant++; + } + } + } + + if (grant == 0 && notGrant == 0) { + return new AuthorizationDecision(true); + } else { + return new AuthorizationDecision(grant >= notGrant); + } + } + + @Override + public String toString() { + return name; + } +} diff --git a/src/main/java/org/italiangrid/storm/webdav/authz/voters/FineGrainedAuthzVoter.java b/src/main/java/org/italiangrid/storm/webdav/authz/managers/FineGrainedAuthzManager.java similarity index 64% rename from src/main/java/org/italiangrid/storm/webdav/authz/voters/FineGrainedAuthzVoter.java rename to src/main/java/org/italiangrid/storm/webdav/authz/managers/FineGrainedAuthzManager.java index 3b66ce2a..68039afa 100644 --- a/src/main/java/org/italiangrid/storm/webdav/authz/voters/FineGrainedAuthzVoter.java +++ b/src/main/java/org/italiangrid/storm/webdav/authz/managers/FineGrainedAuthzManager.java @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.italiangrid.storm.webdav.authz.voters; +package org.italiangrid.storm.webdav.authz.managers; import static org.italiangrid.storm.webdav.authz.pdp.PathAuthorizationRequest.newAuthorizationRequest; -import java.util.Collection; +import java.util.function.Supplier; import org.italiangrid.storm.webdav.authz.pdp.PathAuthorizationPdp; import org.italiangrid.storm.webdav.config.ServiceConfigurationProperties; @@ -26,32 +26,34 @@ import org.italiangrid.storm.webdav.tpc.LocalURLService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.security.access.ConfigAttribute; +import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.security.core.Authentication; -import org.springframework.security.web.FilterInvocation; +import org.springframework.security.web.access.intercept.RequestAuthorizationContext; -public class FineGrainedAuthzVoter extends PathAuthzPdpVoterSupport { +public class FineGrainedAuthzManager extends PathAuthzPdpManagerSupport { - public static final Logger LOG = LoggerFactory.getLogger(FineGrainedAuthzVoter.class); + public static final Logger LOG = LoggerFactory.getLogger(FineGrainedAuthzManager.class); - public FineGrainedAuthzVoter(ServiceConfigurationProperties config, PathResolver resolver, + public FineGrainedAuthzManager(ServiceConfigurationProperties config, PathResolver resolver, PathAuthorizationPdp pdp, LocalURLService localUrlService) { super(config, resolver, pdp, localUrlService, true); } @Override - public int vote(Authentication authentication, FilterInvocation filter, - Collection<ConfigAttribute> attributes) { + public AuthorizationDecision check(Supplier<Authentication> authentication, + RequestAuthorizationContext requestAuthorizationContext) { - final String requestPath = getRequestPath(filter.getHttpRequest()); + final String requestPath = getRequestPath(requestAuthorizationContext.getRequest()); StorageAreaInfo sa = resolver.resolveStorageArea(requestPath); if (sa == null || !sa.fineGrainedAuthzEnabled()) { - return ACCESS_ABSTAIN; + return null; } - return renderDecision(newAuthorizationRequest(filter.getHttpRequest(), authentication), LOG); + return renderDecision( + newAuthorizationRequest(requestAuthorizationContext.getRequest(), authentication.get()), + LOG); } diff --git a/src/main/java/org/italiangrid/storm/webdav/authz/voters/FineGrainedCopyMoveAuthzVoter.java b/src/main/java/org/italiangrid/storm/webdav/authz/managers/FineGrainedCopyMoveAuthzManager.java similarity index 59% rename from src/main/java/org/italiangrid/storm/webdav/authz/voters/FineGrainedCopyMoveAuthzVoter.java rename to src/main/java/org/italiangrid/storm/webdav/authz/managers/FineGrainedCopyMoveAuthzManager.java index 1de3c859..6e5d4d38 100644 --- a/src/main/java/org/italiangrid/storm/webdav/authz/voters/FineGrainedCopyMoveAuthzVoter.java +++ b/src/main/java/org/italiangrid/storm/webdav/authz/managers/FineGrainedCopyMoveAuthzManager.java @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.italiangrid.storm.webdav.authz.voters; +package org.italiangrid.storm.webdav.authz.managers; import static org.italiangrid.storm.webdav.server.servlet.WebDAVMethod.COPY; import static org.italiangrid.storm.webdav.server.servlet.WebDAVMethod.PUT; import java.net.MalformedURLException; -import java.util.Collection; +import java.util.function.Supplier; import org.italiangrid.storm.webdav.authz.pdp.PathAuthorizationPdp; import org.italiangrid.storm.webdav.authz.pdp.PathAuthorizationRequest; @@ -31,36 +31,38 @@ import org.italiangrid.storm.webdav.tpc.TransferConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.security.access.ConfigAttribute; +import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.security.core.Authentication; -import org.springframework.security.web.FilterInvocation; +import org.springframework.security.web.access.intercept.RequestAuthorizationContext; -public class FineGrainedCopyMoveAuthzVoter extends PathAuthzPdpVoterSupport { +public class FineGrainedCopyMoveAuthzManager extends PathAuthzPdpManagerSupport { - public static final Logger LOG = LoggerFactory.getLogger(FineGrainedCopyMoveAuthzVoter.class); + public static final Logger LOG = LoggerFactory.getLogger(FineGrainedCopyMoveAuthzManager.class); - public FineGrainedCopyMoveAuthzVoter(ServiceConfigurationProperties config, PathResolver resolver, - PathAuthorizationPdp pdp, LocalURLService localUrlService) { + public FineGrainedCopyMoveAuthzManager(ServiceConfigurationProperties config, + PathResolver resolver, PathAuthorizationPdp pdp, LocalURLService localUrlService) { super(config, resolver, pdp, localUrlService, true); } @Override - public int vote(Authentication authentication, FilterInvocation filter, - Collection<ConfigAttribute> attributes) { + public AuthorizationDecision check(Supplier<Authentication> authentication, + RequestAuthorizationContext requestAuthorizationContext) { - if (!isCopyOrMoveRequest(filter.getRequest())) { - return ACCESS_ABSTAIN; + if (!isCopyOrMoveRequest(requestAuthorizationContext.getRequest())) { + return null; } - String destination = filter.getRequest().getHeader(TransferConstants.DESTINATION_HEADER); + String destination = + requestAuthorizationContext.getRequest().getHeader(TransferConstants.DESTINATION_HEADER); if (destination == null) { - return ACCESS_ABSTAIN; + return null; } - if (COPY.name().equals(filter.getRequest().getMethod()) - && requestHasRemoteDestinationHeader(filter.getRequest(), localUrlService)) { - return ACCESS_ABSTAIN; + if (COPY.name().equals(requestAuthorizationContext.getRequest().getMethod()) + && requestHasRemoteDestinationHeader(requestAuthorizationContext.getRequest(), + localUrlService)) { + return null; } try { @@ -69,15 +71,16 @@ && requestHasRemoteDestinationHeader(filter.getRequest(), localUrlService)) { StorageAreaInfo sa = resolver.resolveStorageArea(destinationPath); if (sa == null) { - return ACCESS_ABSTAIN; + return null; } if (!sa.fineGrainedAuthzEnabled()) { - return ACCESS_ABSTAIN; + return null; } - return renderDecision(PathAuthorizationRequest - .newAuthorizationRequest(filter.getHttpRequest(), authentication, destinationPath, PUT), + return renderDecision( + PathAuthorizationRequest.newAuthorizationRequest(requestAuthorizationContext.getRequest(), + authentication.get(), destinationPath, PUT), LOG); } catch (MalformedURLException e) { diff --git a/src/main/java/org/italiangrid/storm/webdav/authz/voters/LocalAuthzVoter.java b/src/main/java/org/italiangrid/storm/webdav/authz/managers/LocalAuthzManager.java similarity index 61% rename from src/main/java/org/italiangrid/storm/webdav/authz/voters/LocalAuthzVoter.java rename to src/main/java/org/italiangrid/storm/webdav/authz/managers/LocalAuthzManager.java index 1dfdcfca..824a49f2 100644 --- a/src/main/java/org/italiangrid/storm/webdav/authz/voters/LocalAuthzVoter.java +++ b/src/main/java/org/italiangrid/storm/webdav/authz/managers/LocalAuthzManager.java @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.italiangrid.storm.webdav.authz.voters; +package org.italiangrid.storm.webdav.authz.managers; import static com.google.common.base.Strings.isNullOrEmpty; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Collection; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.function.Supplier; import org.italiangrid.storm.webdav.authz.pdp.PathAuthorizationPdp; import org.italiangrid.storm.webdav.authz.pdp.PathAuthorizationRequest; @@ -30,47 +30,52 @@ import org.italiangrid.storm.webdav.tpc.LocalURLService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.security.access.ConfigAttribute; +import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; -import org.springframework.security.web.FilterInvocation; +import org.springframework.security.web.access.intercept.RequestAuthorizationContext; -public class LocalAuthzVoter extends PathAuthzPdpVoterSupport { +public class LocalAuthzManager extends PathAuthzPdpManagerSupport { - public static final Logger LOG = LoggerFactory.getLogger(LocalAuthzVoter.class); + public static final Logger LOG = LoggerFactory.getLogger(LocalAuthzManager.class); - final URL localTokenIssuer; + final URI localTokenIssuer; - public LocalAuthzVoter(ServiceConfigurationProperties config, PathResolver resolver, + public LocalAuthzManager(ServiceConfigurationProperties config, PathResolver resolver, PathAuthorizationPdp pdp, LocalURLService localUrlService) { super(config, resolver, pdp, localUrlService, true); try { - localTokenIssuer = new URL(config.getAuthzServer().getIssuer()); - } catch (MalformedURLException e) { + localTokenIssuer = new URI(config.getAuthzServer().getIssuer()); + } catch (URISyntaxException e) { throw new StoRMIntializationError(e.getMessage()); } } private boolean isLocalAuthzToken(JwtAuthenticationToken token) { - return localTokenIssuer.equals(token.getToken().getIssuer()) + try { + return localTokenIssuer.equals(token.getToken().getIssuer().toURI()) && !isNullOrEmpty(token.getToken().getClaimAsString(DefaultJwtTokenIssuer.PATH_CLAIM)); + } catch (URISyntaxException e) { + LOG.warn("{}", e.getMessage()); + return false; + } } @Override - public int vote(Authentication authentication, FilterInvocation object, - Collection<ConfigAttribute> attributes) { + public AuthorizationDecision check(Supplier<Authentication> authentication, + RequestAuthorizationContext requestAuthorizationContext) { - if (!(authentication instanceof JwtAuthenticationToken)) { - return ACCESS_ABSTAIN; + if (!(authentication.get() instanceof JwtAuthenticationToken)) { + return null; } - JwtAuthenticationToken token = (JwtAuthenticationToken) authentication; + JwtAuthenticationToken token = (JwtAuthenticationToken) authentication.get(); if (!isLocalAuthzToken(token)) { - return ACCESS_ABSTAIN; + return null; } - return renderDecision( - PathAuthorizationRequest.newAuthorizationRequest(object.getRequest(), authentication), LOG); + return renderDecision(PathAuthorizationRequest.newAuthorizationRequest( + requestAuthorizationContext.getRequest(), authentication.get()), LOG); } } diff --git a/src/main/java/org/italiangrid/storm/webdav/authz/managers/MacaroonAuthzManager.java b/src/main/java/org/italiangrid/storm/webdav/authz/managers/MacaroonAuthzManager.java new file mode 100644 index 00000000..9dddb12d --- /dev/null +++ b/src/main/java/org/italiangrid/storm/webdav/authz/managers/MacaroonAuthzManager.java @@ -0,0 +1,41 @@ +/** + * Copyright (c) Istituto Nazionale di Fisica Nucleare, 2014-2023. + * + * 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.italiangrid.storm.webdav.authz.managers; + +import java.util.function.Supplier; + +import org.springframework.http.HttpMethod; +import org.springframework.security.authorization.AuthorizationDecision; +import org.springframework.security.authorization.AuthorizationManager; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.access.intercept.RequestAuthorizationContext; +import org.springframework.util.Assert; + +public class MacaroonAuthzManager implements AuthorizationManager<RequestAuthorizationContext> { + + @Override + public AuthorizationDecision check(Supplier<Authentication> authentication, + RequestAuthorizationContext requestAuthorizationContext) { + Assert.notNull(authentication.get(), "authentication must not be null"); + Assert.notNull(requestAuthorizationContext, "filterInvocation must not be null"); + + if (HttpMethod.POST.name().equals(requestAuthorizationContext.getRequest().getMethod())) { + return new AuthorizationDecision(true); + } + return null; + } + +} diff --git a/src/main/java/org/italiangrid/storm/webdav/authz/voters/PathAuthzPdpVoterSupport.java b/src/main/java/org/italiangrid/storm/webdav/authz/managers/PathAuthzPdpManagerSupport.java similarity index 75% rename from src/main/java/org/italiangrid/storm/webdav/authz/voters/PathAuthzPdpVoterSupport.java rename to src/main/java/org/italiangrid/storm/webdav/authz/managers/PathAuthzPdpManagerSupport.java index 4c04911d..03e2b0c5 100644 --- a/src/main/java/org/italiangrid/storm/webdav/authz/voters/PathAuthzPdpVoterSupport.java +++ b/src/main/java/org/italiangrid/storm/webdav/authz/managers/PathAuthzPdpManagerSupport.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.italiangrid.storm.webdav.authz.voters; +package org.italiangrid.storm.webdav.authz.managers; import java.util.EnumSet; import java.util.Optional; @@ -28,12 +28,12 @@ import org.italiangrid.storm.webdav.tpc.LocalURLService; import org.italiangrid.storm.webdav.tpc.TpcUtils; import org.slf4j.Logger; -import org.springframework.security.access.AccessDecisionVoter; -import org.springframework.security.access.ConfigAttribute; -import org.springframework.security.web.FilterInvocation; +import org.springframework.security.authorization.AuthorizationDecision; +import org.springframework.security.authorization.AuthorizationManager; +import org.springframework.security.web.access.intercept.RequestAuthorizationContext; -public abstract class PathAuthzPdpVoterSupport - implements MatcherUtils, TpcUtils, AccessDecisionVoter<FilterInvocation> { +public abstract class PathAuthzPdpManagerSupport + implements MatcherUtils, TpcUtils, AuthorizationManager<RequestAuthorizationContext> { protected static final Set<PathAuthorizationResult.Decision> ABSTAIN_DECISIONS = EnumSet.of(PathAuthorizationResult.Decision.INDETERMINATE, @@ -45,7 +45,7 @@ public abstract class PathAuthzPdpVoterSupport protected final LocalURLService localUrlService; protected final boolean permissive; - public PathAuthzPdpVoterSupport(ServiceConfigurationProperties config, PathResolver resolver, + protected PathAuthzPdpManagerSupport(ServiceConfigurationProperties config, PathResolver resolver, PathAuthorizationPdp pdp, LocalURLService localUrlService, boolean permissive) { this.config = config; this.resolver = resolver; @@ -54,17 +54,6 @@ public PathAuthzPdpVoterSupport(ServiceConfigurationProperties config, PathResol this.permissive = permissive; } - @Override - public boolean supports(ConfigAttribute attribute) { - return false; - } - - @Override - public boolean supports(Class<?> clazz) { - return FilterInvocation.class.isAssignableFrom(clazz); - } - - protected void logPdpDecision(PathAuthorizationRequest request, PathAuthorizationResult result, Logger logger) { String requestString = requestToString(request); @@ -73,19 +62,19 @@ protected void logPdpDecision(PathAuthorizationRequest request, PathAuthorizatio result.getPolicy()); } - public int renderDecision(PathAuthorizationResult result) { + public AuthorizationDecision renderDecision(PathAuthorizationResult result) { if (ABSTAIN_DECISIONS.contains(result.getDecision()) && permissive) { - return ACCESS_ABSTAIN; + return null; } if (PathAuthorizationResult.Decision.PERMIT.equals(result.getDecision())) { - return ACCESS_GRANTED; + return new AuthorizationDecision(true); } - return ACCESS_DENIED; + return new AuthorizationDecision(false); } - public int renderDecision(PathAuthorizationRequest request, Logger log) { + public AuthorizationDecision renderDecision(PathAuthorizationRequest request, Logger log) { PathAuthorizationResult result = pdp.authorizeRequest(request); logPdpDecision(request, result, log); @@ -93,7 +82,6 @@ public int renderDecision(PathAuthorizationRequest request, Logger log) { return renderDecision(result); } - public boolean isPermissive() { return permissive; } diff --git a/src/main/java/org/italiangrid/storm/webdav/authz/managers/UnanimousDelegatedManager.java b/src/main/java/org/italiangrid/storm/webdav/authz/managers/UnanimousDelegatedManager.java new file mode 100644 index 00000000..b5279138 --- /dev/null +++ b/src/main/java/org/italiangrid/storm/webdav/authz/managers/UnanimousDelegatedManager.java @@ -0,0 +1,81 @@ +/** + * Copyright (c) Istituto Nazionale di Fisica Nucleare, 2014-2023. + * + * 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.italiangrid.storm.webdav.authz.managers; + +import java.util.List; +import java.util.function.Supplier; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.authorization.AuthorizationDecision; +import org.springframework.security.authorization.AuthorizationManager; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.access.intercept.RequestAuthorizationContext; + +public class UnanimousDelegatedManager + implements AuthorizationManager<RequestAuthorizationContext> { + + public static final Logger LOG = LoggerFactory.getLogger(UnanimousDelegatedManager.class); + + private final List<AuthorizationManager<RequestAuthorizationContext>> managers; + + private final String name; + + private UnanimousDelegatedManager(String name, + List<AuthorizationManager<RequestAuthorizationContext>> managers) { + this.name = name; + this.managers = managers; + } + + @Override + public AuthorizationDecision check(Supplier<Authentication> authentication, + RequestAuthorizationContext filter) { + int grant = 0; + + for (AuthorizationManager<RequestAuthorizationContext> manager : managers) { + AuthorizationDecision result = manager.check(authentication, filter); + + if (LOG.isDebugEnabled()) { + LOG.debug("Voter: {}, returned: {}", manager, result); + } + + if (result != null) { + if (result.isGranted()) { + grant++; + } else { + return new AuthorizationDecision(false); + } + } + } + + // To get this far, there were no deny votes + if (grant > 0) { + return new AuthorizationDecision(true); + } + + return null; + } + + public static UnanimousDelegatedManager forVoters(String name, + List<AuthorizationManager<RequestAuthorizationContext>> accessDecisionManagers) { + return new UnanimousDelegatedManager(name, accessDecisionManagers); + } + + @Override + public String toString() { + return name; + } +} diff --git a/src/main/java/org/italiangrid/storm/webdav/authz/voters/WlcgScopeAuthzCopyMoveVoter.java b/src/main/java/org/italiangrid/storm/webdav/authz/managers/WlcgScopeAuthzCopyMoveManager.java similarity index 61% rename from src/main/java/org/italiangrid/storm/webdav/authz/voters/WlcgScopeAuthzCopyMoveVoter.java rename to src/main/java/org/italiangrid/storm/webdav/authz/managers/WlcgScopeAuthzCopyMoveManager.java index f1714411..f431743d 100644 --- a/src/main/java/org/italiangrid/storm/webdav/authz/voters/WlcgScopeAuthzCopyMoveVoter.java +++ b/src/main/java/org/italiangrid/storm/webdav/authz/managers/WlcgScopeAuthzCopyMoveManager.java @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.italiangrid.storm.webdav.authz.voters; +package org.italiangrid.storm.webdav.authz.managers; import static org.italiangrid.storm.webdav.server.servlet.WebDAVMethod.COPY; import static org.italiangrid.storm.webdav.server.servlet.WebDAVMethod.PUT; import java.net.MalformedURLException; -import java.util.Collection; +import java.util.function.Supplier; import org.italiangrid.storm.webdav.authz.pdp.PathAuthorizationPdp; import org.italiangrid.storm.webdav.authz.pdp.PathAuthorizationRequest; @@ -31,42 +31,44 @@ import org.italiangrid.storm.webdav.tpc.TransferConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.security.access.ConfigAttribute; +import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; -import org.springframework.security.web.FilterInvocation; +import org.springframework.security.web.access.intercept.RequestAuthorizationContext; -public class WlcgScopeAuthzCopyMoveVoter extends PathAuthzPdpVoterSupport { +public class WlcgScopeAuthzCopyMoveManager extends PathAuthzPdpManagerSupport { - public static final Logger LOG = LoggerFactory.getLogger(WlcgScopeAuthzCopyMoveVoter.class); + public static final Logger LOG = LoggerFactory.getLogger(WlcgScopeAuthzCopyMoveManager.class); - public WlcgScopeAuthzCopyMoveVoter(ServiceConfigurationProperties config, PathResolver resolver, + public WlcgScopeAuthzCopyMoveManager(ServiceConfigurationProperties config, PathResolver resolver, PathAuthorizationPdp pdp, LocalURLService localUrlService) { super(config, resolver, pdp, localUrlService, true); } @Override - public int vote(Authentication authentication, FilterInvocation filter, - Collection<ConfigAttribute> attributes) { + public AuthorizationDecision check(Supplier<Authentication> authentication, + RequestAuthorizationContext requestAuthorizationContext) { - if (!(authentication instanceof JwtAuthenticationToken)) { - return ACCESS_ABSTAIN; + if (!(authentication.get() instanceof JwtAuthenticationToken)) { + return null; } - if (!isCopyOrMoveRequest(filter.getRequest())) { - return ACCESS_ABSTAIN; + if (!isCopyOrMoveRequest(requestAuthorizationContext.getRequest())) { + return null; } - String destination = filter.getRequest().getHeader(TransferConstants.DESTINATION_HEADER); + String destination = + requestAuthorizationContext.getRequest().getHeader(TransferConstants.DESTINATION_HEADER); if (destination == null) { - return ACCESS_ABSTAIN; + return null; } - if (COPY.name().equals(filter.getRequest().getMethod()) - && requestHasRemoteDestinationHeader(filter.getRequest(), localUrlService)) { - return ACCESS_ABSTAIN; + if (COPY.name().equals(requestAuthorizationContext.getRequest().getMethod()) + && requestHasRemoteDestinationHeader(requestAuthorizationContext.getRequest(), + localUrlService)) { + return null; } try { @@ -75,16 +77,16 @@ && requestHasRemoteDestinationHeader(filter.getRequest(), localUrlService)) { StorageAreaInfo sa = resolver.resolveStorageArea(destinationPath); if (sa == null) { - return ACCESS_ABSTAIN; + return null; } if (!sa.wlcgScopeAuthzEnabled()) { - return ACCESS_ABSTAIN; + return null; } - return renderDecision(PathAuthorizationRequest - .newAuthorizationRequest(filter.getHttpRequest(), authentication, destinationPath, - PUT), + return renderDecision( + PathAuthorizationRequest.newAuthorizationRequest(requestAuthorizationContext.getRequest(), + authentication.get(), destinationPath, PUT), LOG); } catch (MalformedURLException e) { diff --git a/src/main/java/org/italiangrid/storm/webdav/authz/voters/WlcgScopeAuthzVoter.java b/src/main/java/org/italiangrid/storm/webdav/authz/managers/WlcgScopeAuthzManager.java similarity index 63% rename from src/main/java/org/italiangrid/storm/webdav/authz/voters/WlcgScopeAuthzVoter.java rename to src/main/java/org/italiangrid/storm/webdav/authz/managers/WlcgScopeAuthzManager.java index 5da7658a..36e0c2e5 100644 --- a/src/main/java/org/italiangrid/storm/webdav/authz/voters/WlcgScopeAuthzVoter.java +++ b/src/main/java/org/italiangrid/storm/webdav/authz/managers/WlcgScopeAuthzManager.java @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.italiangrid.storm.webdav.authz.voters; +package org.italiangrid.storm.webdav.authz.managers; import static org.italiangrid.storm.webdav.authz.pdp.PathAuthorizationRequest.newAuthorizationRequest; -import java.util.Collection; +import java.util.function.Supplier; import org.italiangrid.storm.webdav.authz.pdp.PathAuthorizationPdp; import org.italiangrid.storm.webdav.config.ServiceConfigurationProperties; @@ -26,41 +26,43 @@ import org.italiangrid.storm.webdav.tpc.LocalURLService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.security.access.ConfigAttribute; +import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; -import org.springframework.security.web.FilterInvocation; +import org.springframework.security.web.access.intercept.RequestAuthorizationContext; -public class WlcgScopeAuthzVoter extends PathAuthzPdpVoterSupport { +public class WlcgScopeAuthzManager extends PathAuthzPdpManagerSupport { - public static final Logger LOG = LoggerFactory.getLogger(WlcgScopeAuthzVoter.class); + public static final Logger LOG = LoggerFactory.getLogger(WlcgScopeAuthzManager.class); - public WlcgScopeAuthzVoter(ServiceConfigurationProperties config, PathResolver resolver, + public WlcgScopeAuthzManager(ServiceConfigurationProperties config, PathResolver resolver, PathAuthorizationPdp pdp, LocalURLService localUrlService) { super(config, resolver, pdp, localUrlService, true); } @Override - public int vote(Authentication authentication, FilterInvocation filter, - Collection<ConfigAttribute> attributes) { + public AuthorizationDecision check(Supplier<Authentication> authentication, + RequestAuthorizationContext requestAuthorizationContext) { - if (!(authentication instanceof JwtAuthenticationToken)) { - return ACCESS_ABSTAIN; + if (!(authentication.get() instanceof JwtAuthenticationToken)) { + return null; } - final String requestPath = getRequestPath(filter.getHttpRequest()); + final String requestPath = getRequestPath(requestAuthorizationContext.getRequest()); StorageAreaInfo sa = resolver.resolveStorageArea(requestPath); if (sa == null) { - return ACCESS_ABSTAIN; + return null; } if (!sa.wlcgScopeAuthzEnabled()) { - return ACCESS_ABSTAIN; + return null; } - return renderDecision(newAuthorizationRequest(filter.getHttpRequest(), authentication), LOG); + return renderDecision( + newAuthorizationRequest(requestAuthorizationContext.getRequest(), authentication.get()), + LOG); } diff --git a/src/main/java/org/italiangrid/storm/webdav/authz/voters/MacaroonAuthzVoter.java b/src/main/java/org/italiangrid/storm/webdav/authz/voters/MacaroonAuthzVoter.java deleted file mode 100644 index c8c0a69b..00000000 --- a/src/main/java/org/italiangrid/storm/webdav/authz/voters/MacaroonAuthzVoter.java +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright (c) Istituto Nazionale di Fisica Nucleare, 2014-2023. - * - * 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.italiangrid.storm.webdav.authz.voters; - -import java.util.Collection; - -import org.springframework.http.HttpMethod; -import org.springframework.security.access.AccessDecisionVoter; -import org.springframework.security.access.ConfigAttribute; -import org.springframework.security.core.Authentication; -import org.springframework.security.web.FilterInvocation; -import org.springframework.util.Assert; - -public class MacaroonAuthzVoter implements AccessDecisionVoter<FilterInvocation> { - - @Override - public boolean supports(ConfigAttribute attribute) { - return true; - } - - @Override - public boolean supports(Class<?> clazz) { - return true; - } - - @Override - public int vote(Authentication authentication, FilterInvocation filterInvocation, - Collection<ConfigAttribute> attributes) { - Assert.notNull(authentication, "authentication must not be null"); - Assert.notNull(filterInvocation, "filterInvocation must not be null"); - - if (HttpMethod.POST.name().equals(filterInvocation.getHttpRequest().getMethod())) { - return ACCESS_GRANTED; - } - return ACCESS_ABSTAIN; - } - -} diff --git a/src/main/java/org/italiangrid/storm/webdav/authz/voters/UnanimousDelegatedVoter.java b/src/main/java/org/italiangrid/storm/webdav/authz/voters/UnanimousDelegatedVoter.java deleted file mode 100644 index 5ef66557..00000000 --- a/src/main/java/org/italiangrid/storm/webdav/authz/voters/UnanimousDelegatedVoter.java +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Copyright (c) Istituto Nazionale di Fisica Nucleare, 2014-2023. - * - * 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.italiangrid.storm.webdav.authz.voters; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.security.access.AccessDecisionVoter; -import org.springframework.security.access.ConfigAttribute; -import org.springframework.security.core.Authentication; -import org.springframework.security.web.FilterInvocation; - -public class UnanimousDelegatedVoter implements AccessDecisionVoter<FilterInvocation> { - - public static final Logger LOG = LoggerFactory.getLogger(UnanimousDelegatedVoter.class); - - private final List<AccessDecisionVoter<FilterInvocation>> voters; - - private final String name; - - private UnanimousDelegatedVoter(String name, List<AccessDecisionVoter<FilterInvocation>> voters) { - this.name = name; - this.voters = voters; - } - - @Override - public boolean supports(ConfigAttribute attribute) { - return false; - } - - @Override - public boolean supports(Class<?> clazz) { - return FilterInvocation.class.isAssignableFrom(clazz); - } - - @Override - public int vote(Authentication authentication, FilterInvocation filter, - Collection<ConfigAttribute> attributes) { - - int grant = 0; - - List<ConfigAttribute> singleAttributeList = new ArrayList<>(1); - singleAttributeList.add(null); - - for (ConfigAttribute attribute : attributes) { - singleAttributeList.set(0, attribute); - - for (AccessDecisionVoter<FilterInvocation> voter : voters) { - int result = voter.vote(authentication, filter, singleAttributeList); - - if (LOG.isDebugEnabled()) { - LOG.debug("Voter: {}, returned: {}", voter, result); - } - - switch (result) { - case AccessDecisionVoter.ACCESS_GRANTED: - grant++; - - break; - - case AccessDecisionVoter.ACCESS_DENIED: - return AccessDecisionVoter.ACCESS_DENIED; - - default: - break; - } - } - } - - // To get this far, there were no deny votes - if (grant > 0) { - return AccessDecisionVoter.ACCESS_GRANTED; - } - - - return AccessDecisionVoter.ACCESS_ABSTAIN; - } - - public static UnanimousDelegatedVoter forVoters(String name, - List<AccessDecisionVoter<FilterInvocation>> accessDecisionVoters) { - return new UnanimousDelegatedVoter(name, accessDecisionVoters); - } - - @Override - public String toString() { - return name; - } -} diff --git a/src/main/java/org/italiangrid/storm/webdav/spring/web/SecurityConfig.java b/src/main/java/org/italiangrid/storm/webdav/spring/web/SecurityConfig.java index 0e85dbf0..dcb93070 100644 --- a/src/main/java/org/italiangrid/storm/webdav/spring/web/SecurityConfig.java +++ b/src/main/java/org/italiangrid/storm/webdav/spring/web/SecurityConfig.java @@ -16,7 +16,7 @@ package org.italiangrid.storm.webdav.spring.web; import static java.util.Arrays.asList; -import static org.italiangrid.storm.webdav.authz.voters.UnanimousDelegatedVoter.forVoters; +import static org.italiangrid.storm.webdav.authz.managers.UnanimousDelegatedManager.forVoters; import static org.springframework.http.HttpStatus.BAD_REQUEST; import static org.springframework.http.HttpStatus.FORBIDDEN; import static org.springframework.http.HttpStatus.METHOD_NOT_ALLOWED; @@ -41,13 +41,14 @@ import org.italiangrid.storm.webdav.authz.pdp.WlcgStructuredPathAuthorizationPdp; import org.italiangrid.storm.webdav.authz.util.ReadonlyHttpMethodMatcher; import org.italiangrid.storm.webdav.authz.util.SaveAuthnAccessDeniedHandler; -import org.italiangrid.storm.webdav.authz.voters.FineGrainedAuthzVoter; -import org.italiangrid.storm.webdav.authz.voters.FineGrainedCopyMoveAuthzVoter; -import org.italiangrid.storm.webdav.authz.voters.LocalAuthzVoter; -import org.italiangrid.storm.webdav.authz.voters.MacaroonAuthzVoter; -import org.italiangrid.storm.webdav.authz.voters.UnanimousDelegatedVoter; -import org.italiangrid.storm.webdav.authz.voters.WlcgScopeAuthzCopyMoveVoter; -import org.italiangrid.storm.webdav.authz.voters.WlcgScopeAuthzVoter; +import org.italiangrid.storm.webdav.authz.managers.ConsensusBasedManager; +import org.italiangrid.storm.webdav.authz.managers.FineGrainedAuthzManager; +import org.italiangrid.storm.webdav.authz.managers.FineGrainedCopyMoveAuthzManager; +import org.italiangrid.storm.webdav.authz.managers.LocalAuthzManager; +import org.italiangrid.storm.webdav.authz.managers.MacaroonAuthzManager; +import org.italiangrid.storm.webdav.authz.managers.UnanimousDelegatedManager; +import org.italiangrid.storm.webdav.authz.managers.WlcgScopeAuthzCopyMoveManager; +import org.italiangrid.storm.webdav.authz.managers.WlcgScopeAuthzManager; import org.italiangrid.storm.webdav.config.OAuthProperties; import org.italiangrid.storm.webdav.config.ServiceConfigurationProperties; import org.italiangrid.storm.webdav.config.StorageAreaConfiguration; @@ -65,24 +66,26 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; -import org.springframework.security.access.AccessDecisionManager; -import org.springframework.security.access.AccessDecisionVoter; -import org.springframework.security.access.vote.ConsensusBased; import org.springframework.security.authentication.InsufficientAuthenticationException; +import org.springframework.security.authorization.AuthorizationManager; +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer; +import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer.HstsConfig; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.access.AccessDeniedHandlerImpl; -import org.springframework.security.web.access.expression.WebExpressionVoter; +import org.springframework.security.web.access.expression.WebExpressionAuthorizationManager; +import org.springframework.security.web.access.intercept.RequestAuthorizationContext; import org.springframework.security.web.firewall.HttpFirewall; import org.springframework.security.web.firewall.RequestRejectedException; import org.springframework.security.web.firewall.RequestRejectedHandler; import org.springframework.security.web.firewall.StrictHttpFirewall; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import com.google.common.collect.Lists; @Configuration +@EnableMethodSecurity(proxyTargetClass = true) public class SecurityConfig { private static final Logger LOG = LoggerFactory.getLogger(SecurityConfig.class); @@ -148,51 +151,65 @@ SecurityFilterChain filterChain(HttpSecurity http, VOMSAuthenticationProvider vo if (serviceConfigurationProperties.getAuthz().isDisabled()) { LOG.warn("AUTHORIZATION DISABLED: this shouldn't be used in production!"); - http.authorizeRequests().anyRequest().permitAll(); + http.authorizeHttpRequests(authorize -> authorize.anyRequest().permitAll()); } else { - http.authorizeRequests().accessDecisionManager(fineGrainedAccessDecisionManager()); addAccessRules(http); addAnonymousAccessRules(http); } if (serviceConfigurationProperties.getRedirector().isEnabled()) { - http.headers().httpStrictTransportSecurity().disable(); + http.headers(headers -> headers.httpStrictTransportSecurity(HstsConfig::disable)); } - http.oauth2ResourceServer().jwt().jwtAuthenticationConverter(authConverter); - - http.authorizeRequests().antMatchers("/errors/**").permitAll(); - - http.authorizeRequests() - .antMatchers(HttpMethod.GET, "/.well-known/oauth-authorization-server", - "/.well-known/openid-configuration", "/.well-known/wlcg-tape-rest-api") - .permitAll(); + http.oauth2ResourceServer( + oauth2 -> oauth2.jwt(jwt -> jwt.jwtAuthenticationConverter(authConverter))); + + http.authorizeHttpRequests( + authorize -> authorize.requestMatchers(AntPathRequestMatcher.antMatcher("/errors/**")) + .permitAll()); + + http.authorizeHttpRequests(authorize -> authorize + .requestMatchers( + AntPathRequestMatcher.antMatcher(HttpMethod.GET, + "/.well-known/oauth-authorization-server"), + AntPathRequestMatcher.antMatcher(HttpMethod.GET, "/.well-known/openid-configuration"), + AntPathRequestMatcher.antMatcher(HttpMethod.GET, "/.well-known/wlcg-tape-rest-api")) + .permitAll()); + + http.authorizeHttpRequests( + authorize -> authorize + .requestMatchers(AntPathRequestMatcher.antMatcher("/css/*"), + AntPathRequestMatcher.antMatcher("/js/*")) + .permitAll()); + + if (!serviceConfigurationProperties.getAuthz().isDisabled()) { + http.authorizeHttpRequests( + authorize -> authorize.anyRequest().access(fineGrainedAuthorizationManager(null))); + } AccessDeniedHandlerImpl handler = new AccessDeniedHandlerImpl(); handler.setErrorPage("/errors/403"); - http.exceptionHandling() - .accessDeniedHandler(new SaveAuthnAccessDeniedHandler(principalHelper, handler)); + http.exceptionHandling(exception -> exception + .accessDeniedHandler(new SaveAuthnAccessDeniedHandler(principalHelper, handler))); - http.logout() - .logoutUrl("/logout") + http.logout(logout -> logout.logoutUrl("/logout") .clearAuthentication(true) .invalidateHttpSession(true) - .logoutSuccessUrl("/"); + .logoutSuccessUrl("/")); if (!oauthProperties.isEnableOidc()) { - http.exceptionHandling().authenticationEntryPoint(new ErrorPageAuthenticationEntryPoint()); + http.exceptionHandling( + exception -> exception.authenticationEntryPoint(new ErrorPageAuthenticationEntryPoint())); } configureOidcAuthn(http); - http.csrf().disable(); - http.cors().disable(); + http.csrf(csrf -> csrf.disable()); + http.cors(cors -> cors.disable()); return http.build(); } - - @Bean static ErrorPageRegistrar securityErrorPageRegistrar() { return r -> { @@ -206,11 +223,6 @@ static ErrorPageRegistrar securityErrorPageRegistrar() { }; } - @Bean - WebSecurityCustomizer webSecurityCustomizer() { - return web -> web.ignoring().antMatchers("/css/*", "/js/*"); - } - @Bean RequestRejectedHandler requestRejectedHandler() { return new HttpMethodRequestRejectedHandler(ALLOWED_METHODS); @@ -226,13 +238,13 @@ protected void addAnonymousAccessRules(HttpSecurity http) throws Exception { } if (!anonymousAccessPermissions.isEmpty()) { - http.anonymous().authorities(anonymousAccessPermissions); + http.anonymous(anonymous -> anonymous.authorities(anonymousAccessPermissions)); } } protected void configureOidcAuthn(HttpSecurity http) throws Exception { if (oauthProperties.isEnableOidc()) { - http.oauth2Login().loginPage("/oidc-login"); + http.oauth2Login(oauth2Login -> oauth2Login.loginPage("/oidc-login")); } } @@ -251,45 +263,56 @@ protected void addAccessRules(HttpSecurity http) throws Exception { String readAccessRule = String.format("hasAuthority('%s')", SAPermission.canRead(sa).getAuthority()); LOG.debug("Read access rule: {}", readAccessRule); - http.authorizeRequests() - .requestMatchers(new ReadonlyHttpMethodMatcher(ap + "/**")) - .access(readAccessRule); - - http.authorizeRequests().antMatchers(ap + "/**").access(writeAccessRule); + http.authorizeHttpRequests( + authorize -> authorize.requestMatchers(new ReadonlyHttpMethodMatcher(ap + "/**")) + .access(fineGrainedAuthorizationManager( + new WebExpressionAuthorizationManager(readAccessRule)))); + + http.authorizeHttpRequests( + authorize -> authorize.requestMatchers(AntPathRequestMatcher.antMatcher(ap + "/**")) + .access(fineGrainedAuthorizationManager( + new WebExpressionAuthorizationManager(writeAccessRule)))); } } - protected AccessDecisionManager fineGrainedAccessDecisionManager() throws MalformedURLException { - List<AccessDecisionVoter<?>> voters = new ArrayList<>(); + protected AuthorizationManager<RequestAuthorizationContext> fineGrainedAuthorizationManager( + WebExpressionAuthorizationManager webExpressionAuthorizationManager) { + List<AuthorizationManager<RequestAuthorizationContext>> voters = new ArrayList<>(); - UnanimousDelegatedVoter fineGrainedVoters = forVoters("FineGrainedAuthz", + UnanimousDelegatedManager fineGrainedVoters = forVoters("FineGrainedAuthz", asList( - new FineGrainedAuthzVoter(serviceConfigurationProperties, pathResolver, + new FineGrainedAuthzManager(serviceConfigurationProperties, pathResolver, fineGrainedAuthzPdp, localURLService), - new FineGrainedCopyMoveAuthzVoter(serviceConfigurationProperties, pathResolver, + new FineGrainedCopyMoveAuthzManager(serviceConfigurationProperties, pathResolver, fineGrainedAuthzPdp, localURLService))); WlcgStructuredPathAuthorizationPdp wlcgPdp = new WlcgStructuredPathAuthorizationPdp( serviceConfigurationProperties, pathResolver, localURLService); - UnanimousDelegatedVoter wlcgVoters = forVoters("WLCGScopeBasedAuthz", + UnanimousDelegatedManager wlcgVoters = forVoters("WLCGScopeBasedAuthz", asList( - new WlcgScopeAuthzVoter(serviceConfigurationProperties, pathResolver, wlcgPdp, + new WlcgScopeAuthzManager(serviceConfigurationProperties, pathResolver, wlcgPdp, localURLService), - new WlcgScopeAuthzCopyMoveVoter(serviceConfigurationProperties, pathResolver, wlcgPdp, + new WlcgScopeAuthzCopyMoveManager(serviceConfigurationProperties, pathResolver, wlcgPdp, localURLService))); if (serviceConfigurationProperties.getRedirector().isEnabled()) { - voters.add(new LocalAuthzVoter(serviceConfigurationProperties, pathResolver, - new LocalAuthorizationPdp(serviceConfigurationProperties), localURLService)); + try { + voters.add(new LocalAuthzManager(serviceConfigurationProperties, pathResolver, + new LocalAuthorizationPdp(serviceConfigurationProperties), localURLService)); + } catch (MalformedURLException e) { + LOG.error(e.getMessage(), e); + } } if (serviceConfigurationProperties.getAuthzServer().isEnabled() && serviceConfigurationProperties.getMacaroonFilter().isEnabled()) { - voters.add(new MacaroonAuthzVoter()); + voters.add(new MacaroonAuthzManager()); + } + if (webExpressionAuthorizationManager != null) { + voters.add(webExpressionAuthorizationManager); } - voters.add(new WebExpressionVoter()); voters.add(fineGrainedVoters); voters.add(wlcgVoters); - return new ConsensusBased(voters); + return new ConsensusBasedManager("Consensus", voters); } } diff --git a/src/main/java/org/italiangrid/storm/webdav/spring/web/SecurityExpressionHandlerConfiguration.java b/src/main/java/org/italiangrid/storm/webdav/spring/web/SecurityExpressionHandlerConfiguration.java deleted file mode 100644 index c4701882..00000000 --- a/src/main/java/org/italiangrid/storm/webdav/spring/web/SecurityExpressionHandlerConfiguration.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright (c) Istituto Nazionale di Fisica Nucleare, 2014-2023. - * - * 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.italiangrid.storm.webdav.spring.web; - -import org.springframework.context.annotation.Configuration; -import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; -import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; -import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; -import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration; - -@Configuration -@EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true) -public class SecurityExpressionHandlerConfiguration extends GlobalMethodSecurityConfiguration { - - @Override - protected MethodSecurityExpressionHandler createExpressionHandler() { - return new DefaultMethodSecurityExpressionHandler(); - } - -}