Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade to Spring Security 5.8 #70

Draft
wants to merge 2 commits into
base: develop
Choose a base branch
from
Draft
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: 2 additions & 2 deletions .github/workflows/maven.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ jobs:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
steps:
- uses: actions/checkout@v4
- name: Set up JDK 11
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 11
java-version: 17
- name: Cache Maven packages
uses: actions/cache@v4
with:
Expand Down
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# https://spring.io/guides/topicals/spring-boot-docker#_multi_stage_build
FROM eclipse-temurin:11-jdk-alpine as build
FROM eclipse-temurin:17-jdk-alpine as build
WORKDIR /workspace/app
RUN apk add maven
COPY pom.xml .
Expand All @@ -11,7 +11,7 @@ COPY src src
RUN mvn package -Dmaven.test.skip
RUN mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar)

FROM eclipse-temurin:11-centos7
FROM eclipse-temurin:17-centos7
ENV STORM_WEBDAV_JVM_OPTS="-Dspring.profiles.active=dev"
ARG DEPENDENCY=/workspace/app/target/dependency

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ The StoRM webdav service provides http/webdav access to resources shared on a fi
You will need:

- git
- java 11
- java 17
- maven 3

## Build instructions
Expand Down
3 changes: 2 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,11 @@
<plugin.license.version>1.9.0</plugin.license.version>
<plugin.jacoco.version>0.8.8</plugin.jacoco.version>

<java.version>11</java.version>
<java.version>17</java.version>

<!-- 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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 {
Expand All @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
}

}
Loading
Loading