From 466f9032be8e4e9395874f83fdfc6e57110359b9 Mon Sep 17 00:00:00 2001 From: Enrico Vianello Date: Mon, 3 Jul 2023 11:25:08 +0200 Subject: [PATCH 01/22] Bump version to 1.4.3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cadfe278..92ee39e4 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.italiangrid storm-webdav-server - 1.4.2 + 1.4.3 jar storm-webdav-server From 9e9dfd1d7972ee49cd14c6ce64d072d86bd085db Mon Sep 17 00:00:00 2001 From: Enrico Vianello Date: Wed, 2 Oct 2024 12:21:34 +0200 Subject: [PATCH 02/22] Upgrade dependencies, fix JWK caching and thread-pool initialization (#44) - Move spring boot version from 2.7.10 to 2.7.18 - Remove jetty-utils dependency by importing what's necessary - Fix Jetty thread-pool idle timeout to be configurable - Update CHANGELOG file and aligned drop-in file with missing env vars - Fix caching of remote JWK sets - Increase voms-api-java dependency version to v3.3.3 - Add more source headers to retrieve remote ip address when http request one is empty - Fix IP logging issue STOR-1583 --- .github/workflows/sonar.yml | 6 +- CHANGELOG.md | 66 +- etc/storm-webdav/logback-access.xml | 2 +- .../storm-webdav.service.d/storm-webdav.conf | 33 + pom.xml | 91 +-- .../storm/webdav/config/OAuthProperties.java | 11 + .../webdav/config/ServiceConfiguration.java | 4 + .../ServiceConfigurationProperties.java | 55 +- .../DefaultOidcConfigurationFetcher.java | 82 ++- .../oauth/utils/NoExpirationStringCache.java | 69 ++ .../oauth/utils/OidcConfigurationFetcher.java | 5 + .../utils/TrustedJwtDecoderCacheLoader.java | 11 +- .../server/DefaultJettyServerCustomizer.java | 1 - .../server/DefaultWebServerFactory.java | 23 +- .../server/TLSConnectorBuilderError.java | 34 + .../server/TLSServerConnectorBuilder.java | 664 ++++++++++++++++++ .../server/servlet/LogRequestFilter.java | 24 +- .../storm/webdav/spring/AppConfig.java | 2 +- src/main/resources/application-dev.yml | 11 + src/main/resources/application.yml | 27 +- .../server/TLSConnectorBuilderTest.java | 117 +++ .../jwk/OidcConfigurationFetcherTest.java | 307 ++++++++ .../jwk/TrustedJwtDecoderCacheLoaderTest.java | 109 +++ .../jwt/NoExpirationStringCacheTest.java | 45 ++ src/test/resources/jwk/test-keystore.jwks | 16 + 25 files changed, 1674 insertions(+), 141 deletions(-) create mode 100644 src/main/java/org/italiangrid/storm/webdav/oauth/utils/NoExpirationStringCache.java create mode 100644 src/main/java/org/italiangrid/storm/webdav/server/TLSConnectorBuilderError.java create mode 100644 src/main/java/org/italiangrid/storm/webdav/server/TLSServerConnectorBuilder.java create mode 100644 src/test/java/org/italiangrid/storm/webdav/server/TLSConnectorBuilderTest.java create mode 100644 src/test/java/org/italiangrid/storm/webdav/test/oauth/jwk/OidcConfigurationFetcherTest.java create mode 100644 src/test/java/org/italiangrid/storm/webdav/test/oauth/jwk/TrustedJwtDecoderCacheLoaderTest.java create mode 100644 src/test/java/org/italiangrid/storm/webdav/test/oauth/jwt/NoExpirationStringCacheTest.java create mode 100644 src/test/resources/jwk/test-keystore.jwks diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml index 23af7abd..303dc77f 100644 --- a/.github/workflows/sonar.yml +++ b/.github/workflows/sonar.yml @@ -13,10 +13,10 @@ jobs: - uses: actions/checkout@v2 with: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v1 with: - java-version: 11 + java-version: 17 - name: Cache SonarCloud packages uses: actions/cache@v1 with: @@ -33,4 +33,4 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - run: mvn -s maven/cnaf-mirror-settings.xml -B -U install org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=italiangrid_storm-webdav \ No newline at end of file + run: mvn -s maven/cnaf-mirror-settings.xml -B -U install org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=italiangrid_storm-webdav diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fb6334e..435384f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,36 +1,65 @@ # Changelog +## 1.4.2 (2023-06-27) + +## Description + +This release: + +* upgrades significant dependencies (spring-boot, canl, bouncycastle, jQuery) +* removes the support for TRACE method +* tunes some default values (default TPC timeout, default heap size, etc.) +* and fixes other minor bugs/issues. + +### fixes + +* [[STOR-1396](https://issues.infn.it/jira/browse/STOR-1396)] - Ensure adler32 checksums are always 8 chars long +* [[STOR-1450](https://issues.infn.it/jira/browse/STOR-1450)] - Increase default timeout for TPC to 30 seconds +* [[STOR-1500](https://issues.infn.it/jira/browse/STOR-1500)] - When redis is disabled the health indicator for redis should be disabled +* [[STOR-1574](https://issues.infn.it/jira/browse/STOR-1574)] - Old java/canl creates problems with encoding of subject/issuer names in self-signed certificates +* [[STOR-1440](https://issues.infn.it/jira/browse/STOR-1440)] - StoRM WebDAV should configure a bigger heap by default +* [[STOR-1497](https://issues.infn.it/jira/browse/STOR-1497)] - Upgrade canl-java to v2.6.0 +* [[STOR-1515](https://issues.infn.it/jira/browse/STOR-1515)] - StoRM WebDAV metrics on TPC.pull/push.throughput +* [[STOR-1555](https://issues.infn.it/jira/browse/STOR-1555)] - Upgrade jQuery version +* [[STOR-1556](https://issues.infn.it/jira/browse/STOR-1556)] - Remove TRACE from allowed methods +* [[STOR-1557](https://issues.infn.it/jira/browse/STOR-1557)] - Upgrade Spring Boot version to the latest +* [[STOR-1558](https://issues.infn.it/jira/browse/STOR-1558)] - Update bouncycastle version to 1.67 +* [[STOR-1576](https://issues.infn.it/jira/browse/STOR-1576)] - Add .well-known endpoint for StoRM WebDAV to point to the Tape REST endpoint + + +## 1.4.1 (2021-05-12) + +This release fixes the failed state shown on stop/restart of the service due to a misunderstood exit code meaning. + +### Fixed + +- [[STOR-1400](https://issues.infn.it/jira/browse/STOR-1400)] - StoRM WebDAV service enters failed state when stopped + ## 1.4.0 (2021-04-01) ### Added -- [Add support for externalized session management](https://issues.infn.it/jira/browse/STOR-1336) +- [[STOR-1336](https://issues.infn.it/jira/browse/STOR-1336)] - Add support for externalized session management ### Fixed -- [Login with OIDC button not shown for error - pages](https://issues.infn.it/jira/browse/STOR-1335) -- [StoRM WebDAV: Login with OIDC button displayed only on storage area index - page]( https://issues.infn.it/jira/browse/STOR-1332) -- [StoRM WebDAV rpm doesn't set the proper ownership on - /var/log/storm](https://issues.infn.it/jira/browse/STOR-1298) -- [StoRM WebDAV package should install Java - 11](https://issues.infn.it/jira/browse/STOR-1358) +- [[STOR-1335](https://issues.infn.it/jira/browse/STOR-1335)] - Login with OIDC button not shown for error + pages +- [[STOR-1332](https://issues.infn.it/jira/browse/STOR-1332)] - Login with OIDC button displayed only on storage area index page +- [[STOR-1298](https://issues.infn.it/jira/browse/STOR-1298)] - StoRM WebDAV RPM doesn't set the proper ownership on `/var/log/storm` +- [[STOR-1358](https://issues.infn.it/jira/browse/STOR-1358)] - StoRM WebDAV package should install Java 11 ## 1.2.0 (2019-08-??) ### Added -- [Spring boot updated to 2.1.4.RELEASE][STOR-1098] -- [Introduced support for Conscrypt JSSE provider to improve TLS - performace][STOR-1097] +- [[STOR-1098](https://issues.infn.it/jira/browse/STOR-1098)] - Spring boot updated to 2.1.4.RELEASE +- [[STOR-1097](https://issues.infn.it/jira/browse/STOR-1097)] - Introduced support for Conscrypt JSSE provider to improve TLS performance ### Fixed -- [StoRM WebDAV default configuration does not depend anymore on - iam-test.indigo-datacloud.eu][STOR-1095] -- [Unreachable OpenID Connect provider causes StoRM WebDAV startup - failure][STOR-1096] +- [[STOR-1095](https://issues.infn.it/jira/browse/STOR-1095)] - StoRM WebDAV default configuration does not depend anymore on `iam-test.indigo-datacloud.eu` +- [[STOR-1096](https://issues.infn.it/jira/browse/STOR-1096)] - Unreachable OpenID Connect provider causes StoRM WebDAV startup failure ## 1.1.0 (2019-02-28) @@ -46,8 +75,3 @@ - POST handled as GET fixed - -[STOR-1095]: https://issues.infn.it/jira/browse/STOR-1095 -[STOR-1096]: https://issues.infn.it/jira/browse/STOR-1096 -[STOR-1097]: https://issues.infn.it/jira/browse/STOR-1097 -[STOR-1098]: https://issues.infn.it/jira/browse/STOR-1098 diff --git a/etc/storm-webdav/logback-access.xml b/etc/storm-webdav/logback-access.xml index a5f130c0..629060cc 100644 --- a/etc/storm-webdav/logback-access.xml +++ b/etc/storm-webdav/logback-access.xml @@ -11,7 +11,7 @@ - %a %localPort "%reqAttribute{storm.remoteUser}" %date{"yyyy-MM-dd'T'HH:mm:ss.SSSXXX", UTC} "%reqAttribute{storm.requestId}" "%m %U %H" %s %b %D + %replace(%a){'^$','-'} %localPort "%reqAttribute{storm.remoteUser}" %date{"yyyy-MM-dd'T'HH:mm:ss.SSSXXX", UTC} "%reqAttribute{storm.requestId}" "%m %U %H" %s %b %D diff --git a/etc/systemd/system/storm-webdav.service.d/storm-webdav.conf b/etc/systemd/system/storm-webdav.service.d/storm-webdav.conf index f173bd26..5ecdd990 100644 --- a/etc/systemd/system/storm-webdav.service.d/storm-webdav.conf +++ b/etc/systemd/system/storm-webdav.service.d/storm-webdav.conf @@ -127,3 +127,36 @@ Environment="STORM_WEBDAV_TPC_MAX_CONNECTIONS_PER_ROUTE=25" # Source file for the tape REST API well-known endpoint # Default: '/etc/storm/webdav/wlcg-tape-rest-api.json' # Environment="STORM_WEBDAV_TAPE_WELLKNOWN_SOURCE=/etc/storm/webdav/wlcg-tape-rest-api.json" + +# Buffer size for both internal and third-party copy requests. +# This adds more efficiency than to write the whole data. Valid values are numbers >= 4096. +# Default: 1048576 +# Environment="STORM_WEBDAV_BUFFER_FILE_BUFFER_SIZE_BYTES=1048576" + +# Enable checksum filter which adds checksum as an header following RFC 3230. +# Default: true +# Environment="STORM_WEBDAV_CHECKSUM_FILTER_ENABLED=true" + +# Enable Macaroon filter to process Macaroon tokens. Requires authz server enabled. +# Default: true +# Environment="STORM_WEBDAV_MACAROON_FILTER_ENABLED=true" + +# TLS protocol for non-TPC requests +# Default: TLS +# Environment="STORM_WEBDAV_TLS_PROTOCOL=TLS" + +# VOMS Trust Store directory +# Default: /etc/grid-security/vomsdir +# Environment="STORM_WEBDAV_VOMS_TRUST_STORE_DIR=/etc/grid-security/vomsdir" + +# VOMS Trust Store refresh interval +# Default: 43200 +# Environment="STORM_WEBDAV_VOMS_TRUST_STORE_REFRESH_INTERVAL_SEC=43200" + +# Enable caching for VOMS certificate validation +# Default: true +# Environment="STORM_WEBDAV_VOMS_CACHE_ENABLE=true" + +# Cache entries lifetime, used if caching for VOMS certificate validation is enabled +# Default: 300 +# Environment="STORM_WEBDAV_VOMS_CACHE_ENTRY_LIFETIME_SEC=300" \ No newline at end of file diff --git a/pom.xml b/pom.xml index 92ee39e4..98940045 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ org.springframework.boot spring-boot-starter-parent - 2.7.10 + 2.7.18 @@ -20,8 +20,7 @@ UTF-8 UTF-8 - 3.6.0 - 11 + 3.8.0 2.4 2.4 @@ -32,14 +31,14 @@ 11 - 2.7.10 + 2.7.18 italiangrid_storm-webdav italiangrid https://sonarcloud.io - 0.4.6.v20220506 + 3.3.3 2.7.1.7 2.3 @@ -50,14 +49,13 @@ 4.2.2 4.2.1 - 31.1-jre + 32.0.0-jre 1.0.5.1 2.3.3.RELEASE 6.0.2 5.5.1 - 1.72 @@ -65,16 +63,6 @@ ${project.name} - - - org.apache.maven.plugins - maven-compiler-plugin - - 11 - 11 - - - org.springframework.boot spring-boot-maven-plugin @@ -99,7 +87,6 @@ src/assembly/tarball.xml - storm-webdav @@ -107,6 +94,9 @@ single + + false + @@ -190,13 +180,6 @@ org.springframework.boot spring-boot-starter-actuator - - - - org.apache.logging.log4j - log4j-api - - @@ -256,6 +239,12 @@ org.springframework.boot spring-boot-starter-test test + + + com.vaadin.external.google + android-json + + @@ -326,11 +315,6 @@ metrics-core - - io.dropwizard.metrics - metrics-jvm - - io.dropwizard.metrics metrics-jetty9 @@ -348,45 +332,30 @@ - org.italiangrid - jetty-utils - ${jetty-utils.version} - - - javax.activation - activation - - - javax.mail - mail - - - org.eclipse.jetty.aggregate - jetty-all - - - ch.qos.logback - logback-core - - - ch.qos.logback - logback-classic - - + org.eclipse.jetty.http2 + http2-server + + + + org.eclipse.jetty + jetty-alpn-conscrypt-server - org.bouncycastle - bcpkix-jdk18on - ${bouncycastle.version} + org.slf4j + slf4j-api - org.bouncycastle - bcprov-jdk18on - ${bouncycastle.version} + org.slf4j + log4j-over-slf4j + + org.italiangrid + voms-api-java + ${voms-api-java.version} + ch.qos.logback diff --git a/src/main/java/org/italiangrid/storm/webdav/config/OAuthProperties.java b/src/main/java/org/italiangrid/storm/webdav/config/OAuthProperties.java index bd833381..aab8d1f6 100644 --- a/src/main/java/org/italiangrid/storm/webdav/config/OAuthProperties.java +++ b/src/main/java/org/italiangrid/storm/webdav/config/OAuthProperties.java @@ -96,6 +96,9 @@ public boolean isEnforceAudienceChecks() { @Min(value = 1, message = "The refresh period must be a positive integer") int refreshPeriodMinutes = 60; + @Min(value = 1, message = "The refresh timeout must be a positive integer") + int refreshTimeoutSeconds = 30; + public List getIssuers() { return issuers; } @@ -112,6 +115,14 @@ public void setRefreshPeriodMinutes(int refreshPeriodMinutes) { this.refreshPeriodMinutes = refreshPeriodMinutes; } + public int getRefreshTimeoutSeconds() { + return refreshTimeoutSeconds; + } + + public void setRefreshTimeoutSeconds(int refreshTimeoutSeconds) { + this.refreshTimeoutSeconds = refreshTimeoutSeconds; + } + public void setEnableOidc(boolean enableOidc) { this.enableOidc = enableOidc; } diff --git a/src/main/java/org/italiangrid/storm/webdav/config/ServiceConfiguration.java b/src/main/java/org/italiangrid/storm/webdav/config/ServiceConfiguration.java index 9b3a0e03..c5fc69a0 100644 --- a/src/main/java/org/italiangrid/storm/webdav/config/ServiceConfiguration.java +++ b/src/main/java/org/italiangrid/storm/webdav/config/ServiceConfiguration.java @@ -33,10 +33,14 @@ public interface ServiceConfiguration { public long getTrustAnchorsRefreshIntervalInSeconds(); + public int getMinConnections(); + public int getMaxConnections(); public int getMaxQueueSize(); + public int getThreadPoolMaxIdleTimeInMsec(); + public int getConnectorMaxIdleTimeInMsec(); public String getSAConfigDir(); diff --git a/src/main/java/org/italiangrid/storm/webdav/config/ServiceConfigurationProperties.java b/src/main/java/org/italiangrid/storm/webdav/config/ServiceConfigurationProperties.java index b6e09321..dbacabad 100644 --- a/src/main/java/org/italiangrid/storm/webdav/config/ServiceConfigurationProperties.java +++ b/src/main/java/org/italiangrid/storm/webdav/config/ServiceConfigurationProperties.java @@ -249,6 +249,20 @@ public void setPolicies(List policies) { } } + public static class ServerProperties { + + @Positive + int maxIdleTimeMsec = 3600000; + + public int getMaxIdleTimeMsec() { + return maxIdleTimeMsec; + } + + public void setMaxIdleTimeMsec(int maxIdleTimeMsec) { + this.maxIdleTimeMsec = maxIdleTimeMsec; + } + } + public static class ConnectorProperties { @Positive @@ -260,10 +274,13 @@ public static class ConnectorProperties { int securePort = 8443; @Positive - int maxConnections = 200; + int minConnections = 50; + + @Positive + int maxConnections = 300; @Positive - int maxQueueSize = 512; + int maxQueueSize = 900; @Positive int maxIdleTimeMsec = 30000; @@ -292,6 +309,14 @@ public void setSecurePort(int securePort) { this.securePort = securePort; } + public int getMinConnections() { + return minConnections; + } + + public void setMinConnections(int minConnections) { + this.minConnections = minConnections; + } + public int getMaxConnections() { return maxConnections; } @@ -597,6 +622,8 @@ public void setTrustStore(VOMSTrustStoreProperties trustStore) { private MacaroonFilterProperties macaroonFilter; + private ServerProperties server; + private ConnectorProperties connector; private TLSProperties tls; @@ -643,12 +670,18 @@ public void setTls(TLSProperties tls) { this.tls = tls; } + public ServerProperties getServer() { + return server; + } + + public void setServer(ServerProperties server) { + this.server = server; + } public ConnectorProperties getConnector() { return connector; } - public void setConnector(ConnectorProperties connector) { this.connector = connector; } @@ -732,55 +765,56 @@ public long getTrustAnchorsRefreshIntervalInSeconds() { return getTls().getTrustAnchorsRefreshIntervalSecs(); } + @Override + public int getMinConnections() { + return getConnector().getMinConnections(); + } @Override public int getMaxConnections() { return getConnector().getMaxConnections(); } - @Override public int getMaxQueueSize() { return getConnector().getMaxQueueSize(); } + @Override + public int getThreadPoolMaxIdleTimeInMsec() { + return getServer().getMaxIdleTimeMsec(); + } @Override public int getConnectorMaxIdleTimeInMsec() { return getConnector().getMaxIdleTimeMsec(); } - @Override public String getSAConfigDir() { return getSa().getConfigDir(); } - @Override public boolean enableVOMapFiles() { return getVoMapFiles().isEnabled(); } - @Override public String getVOMapFilesConfigDir() { return getVoMapFiles().getConfigDir(); } - @Override public long getVOMapFilesRefreshIntervalInSeconds() { return getVoMapFiles().getRefreshIntervalSec(); } - @Override public boolean isAuthorizationDisabled() { return getAuthz().isDisabled(); } - @Override public boolean requireClientCertificateAuthentication() { return getTls().isRequireClientCert(); @@ -887,4 +921,5 @@ public TapeProperties getTape() { public void setTape(TapeProperties tape) { this.tape = tape; } + } diff --git a/src/main/java/org/italiangrid/storm/webdav/oauth/utils/DefaultOidcConfigurationFetcher.java b/src/main/java/org/italiangrid/storm/webdav/oauth/utils/DefaultOidcConfigurationFetcher.java index 090ee2db..4515bf3c 100644 --- a/src/main/java/org/italiangrid/storm/webdav/oauth/utils/DefaultOidcConfigurationFetcher.java +++ b/src/main/java/org/italiangrid/storm/webdav/oauth/utils/DefaultOidcConfigurationFetcher.java @@ -18,34 +18,47 @@ import static java.lang.String.format; import java.net.URI; +import java.time.Duration; +import java.util.Arrays; import java.util.Map; +import java.util.Objects; +import org.italiangrid.storm.webdav.config.OAuthProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; import org.springframework.http.RequestEntity; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; +import com.nimbusds.jose.KeySourceException; +import com.nimbusds.jose.RemoteKeySourceException; + @Service public class DefaultOidcConfigurationFetcher implements OidcConfigurationFetcher { public static final String WELL_KNOWN_FRAGMENT = "/.well-known/openid-configuration"; public static final String ISSUER_MISMATCH_ERROR_TEMPLATE = "Issuer in metadata '%s' does not match with requested issuer '%s'"; - public static final String NO_JWKS_URI_ERROR_TEMPLATE = + public static final String NO_JWKS_URI_ERROR_TEMPLATE = "No jwks_uri found in metadata for issuer '%s'"; + private static final MediaType APPLICATION_JWK_SET_JSON = + new MediaType("application", "jwk-set+json"); + public static final Logger LOG = LoggerFactory.getLogger(DefaultOidcConfigurationFetcher.class); - final RestTemplateBuilder restBuilder; + final RestTemplate restTemplate; - @Autowired - public DefaultOidcConfigurationFetcher(RestTemplateBuilder restBuilder) { - this.restBuilder = restBuilder; + public DefaultOidcConfigurationFetcher(RestTemplateBuilder restBuilder, + OAuthProperties oAuthProperties) { + final Duration timeout = Duration.ofSeconds(oAuthProperties.getRefreshTimeoutSeconds()); + this.restTemplate = restBuilder.setConnectTimeout(timeout).setReadTimeout(timeout).build(); } private void metadataChecks(String issuer, Map oidcConfiguration) { @@ -59,40 +72,63 @@ private void metadataChecks(String issuer, Map oidcConfiguration throw new OidcConfigurationResolutionError( format(ISSUER_MISMATCH_ERROR_TEMPLATE, metadataIssuer, issuer)); } - + if (!oidcConfiguration.containsKey("jwks_uri")) { - throw new OidcConfigurationResolutionError(format(NO_JWKS_URI_ERROR_TEMPLATE,issuer)); + throw new OidcConfigurationResolutionError(format(NO_JWKS_URI_ERROR_TEMPLATE, issuer)); } } @Override public Map loadConfigurationForIssuer(String issuer) { LOG.debug("Fetching OpenID configuration for {}", issuer); - + ParameterizedTypeReference> typeReference = new ParameterizedTypeReference>() {}; - RestTemplate rest = restBuilder.build(); - URI uri = UriComponentsBuilder.fromUriString(issuer + WELL_KNOWN_FRAGMENT).build().toUri(); - + ResponseEntity> response = null; try { - - RequestEntity request = RequestEntity.get(uri).build(); - Map conf = rest.exchange(request, typeReference).getBody(); - metadataChecks(issuer, conf); - return conf; + response = restTemplate.exchange(RequestEntity.get(uri).build(), typeReference); } catch (RuntimeException e) { - final String errorMsg = - format("Unable to resolve OpenID configuration for issuer '%s' from '%s': %s", issuer, - uri, e.getMessage()); - + final String errorMsg = format("Unable to resolve OpenID configuration from '%s'", uri); if (LOG.isDebugEnabled()) { - LOG.error(errorMsg, e); + LOG.error("{}: {}", errorMsg, e.getMessage()); } - throw new OidcConfigurationResolutionError(errorMsg, e); } + if (response.getStatusCodeValue() != 200) { + throw new OidcConfigurationResolutionError( + format("Received status code: %s", response.getStatusCodeValue())); + } + if (Objects.isNull(response.getBody())) { + throw new OidcConfigurationResolutionError("Received null body"); + } + metadataChecks(issuer, response.getBody()); + return response.getBody(); } + @Override + public String loadJWKSourceForURL(URI uri) throws KeySourceException { + + LOG.debug("Fetching JWK from {}", uri); + + HttpHeaders headers = new HttpHeaders(); + headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON, APPLICATION_JWK_SET_JSON)); + ResponseEntity response = null; + try { + RequestEntity request = RequestEntity.get(uri).headers(headers).build(); + response = restTemplate.exchange(request, String.class); + } catch (RuntimeException e) { + final String errorMsg = format("Unable to get JWK from '%s'", uri); + if (LOG.isDebugEnabled()) { + LOG.error("{}: {}", errorMsg, e.getMessage()); + } + throw new RemoteKeySourceException(errorMsg, e); + } + if (response.getStatusCodeValue() != 200) { + throw new KeySourceException(format("Unable to get JWK from '%s': received status code %s", + uri, response.getStatusCodeValue())); + } + return response.getBody(); + } } diff --git a/src/main/java/org/italiangrid/storm/webdav/oauth/utils/NoExpirationStringCache.java b/src/main/java/org/italiangrid/storm/webdav/oauth/utils/NoExpirationStringCache.java new file mode 100644 index 00000000..4bc9e253 --- /dev/null +++ b/src/main/java/org/italiangrid/storm/webdav/oauth/utils/NoExpirationStringCache.java @@ -0,0 +1,69 @@ +/** + * 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.oauth.utils; + +import java.util.concurrent.Callable; + +import org.springframework.cache.support.AbstractValueAdaptingCache; +import org.springframework.lang.Nullable; + +public class NoExpirationStringCache extends AbstractValueAdaptingCache { + + private static final String NAME = "NoExpirationCache"; + private final String value; + + public NoExpirationStringCache(String value) { + super(false); + this.value = value; + } + + @Override + public String getName() { + return NAME; + } + + @Override + public Object getNativeCache() { + return this; + } + + @Override + @Nullable + protected Object lookup(Object key) { + return value; + } + + @Override + public void put(Object key, Object value) { + // Nothing to do + } + + @Override + public void evict(Object key) { + // Nothing to do + } + + @Override + public void clear() { + // Nothing to do + } + + @SuppressWarnings("unchecked") + @Override + public T get(Object key, Callable valueLoader) { + return (T) fromStoreValue(value); + } +} diff --git a/src/main/java/org/italiangrid/storm/webdav/oauth/utils/OidcConfigurationFetcher.java b/src/main/java/org/italiangrid/storm/webdav/oauth/utils/OidcConfigurationFetcher.java index 786e9f1b..7aa30179 100644 --- a/src/main/java/org/italiangrid/storm/webdav/oauth/utils/OidcConfigurationFetcher.java +++ b/src/main/java/org/italiangrid/storm/webdav/oauth/utils/OidcConfigurationFetcher.java @@ -15,10 +15,15 @@ */ package org.italiangrid.storm.webdav.oauth.utils; +import java.net.URI; import java.util.Map; +import com.nimbusds.jose.KeySourceException; + public interface OidcConfigurationFetcher { Map loadConfigurationForIssuer(String issuer); + String loadJWKSourceForURL(URI uri) throws KeySourceException; + } diff --git a/src/main/java/org/italiangrid/storm/webdav/oauth/utils/TrustedJwtDecoderCacheLoader.java b/src/main/java/org/italiangrid/storm/webdav/oauth/utils/TrustedJwtDecoderCacheLoader.java index efde8ee9..f9e19765 100644 --- a/src/main/java/org/italiangrid/storm/webdav/oauth/utils/TrustedJwtDecoderCacheLoader.java +++ b/src/main/java/org/italiangrid/storm/webdav/oauth/utils/TrustedJwtDecoderCacheLoader.java @@ -15,6 +15,7 @@ */ package org.italiangrid.storm.webdav.oauth.utils; +import java.net.URI; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutorService; @@ -28,8 +29,8 @@ import org.italiangrid.storm.webdav.oauth.validator.WlcgProfileValidator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.cache.Cache; import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator; import org.springframework.security.oauth2.core.OAuth2TokenValidator; import org.springframework.security.oauth2.jwt.Jwt; @@ -53,7 +54,6 @@ public class TrustedJwtDecoderCacheLoader extends CacheLoader oidcConfiguration = fetcher.loadConfigurationForIssuer(issuer); + URI jwksUri = URI.create(oidcConfiguration.get("jwks_uri").toString()); + Cache noExpirationCache = + new NoExpirationStringCache(fetcher.loadJWKSourceForURL(jwksUri)); NimbusJwtDecoder decoder = - NimbusJwtDecoder.withJwkSetUri((oidcConfiguration.get("jwks_uri").toString())).build(); + NimbusJwtDecoder.withJwkSetUri((oidcConfiguration.get("jwks_uri").toString())) + .cache(noExpirationCache) + .build(); OAuth2TokenValidator jwtValidator = JwtValidators.createDefaultWithIssuer(issuer); OAuth2TokenValidator wlcgProfileValidator = new WlcgProfileValidator(); diff --git a/src/main/java/org/italiangrid/storm/webdav/server/DefaultJettyServerCustomizer.java b/src/main/java/org/italiangrid/storm/webdav/server/DefaultJettyServerCustomizer.java index 4e798c4e..82c028e0 100644 --- a/src/main/java/org/italiangrid/storm/webdav/server/DefaultJettyServerCustomizer.java +++ b/src/main/java/org/italiangrid/storm/webdav/server/DefaultJettyServerCustomizer.java @@ -32,7 +32,6 @@ import org.italiangrid.storm.webdav.config.ServiceConfiguration; import org.italiangrid.storm.webdav.config.ServiceConfigurationProperties; import org.italiangrid.storm.webdav.config.StorageAreaConfiguration; -import org.italiangrid.utils.jetty.TLSServerConnectorBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.web.ServerProperties; diff --git a/src/main/java/org/italiangrid/storm/webdav/server/DefaultWebServerFactory.java b/src/main/java/org/italiangrid/storm/webdav/server/DefaultWebServerFactory.java index b2fb8dfb..56d286cf 100644 --- a/src/main/java/org/italiangrid/storm/webdav/server/DefaultWebServerFactory.java +++ b/src/main/java/org/italiangrid/storm/webdav/server/DefaultWebServerFactory.java @@ -15,14 +15,16 @@ */ package org.italiangrid.storm.webdav.server; +import java.util.concurrent.ArrayBlockingQueue; + import org.italiangrid.storm.webdav.config.ServiceConfiguration; -import org.italiangrid.utils.jetty.ThreadPoolBuilder; import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.web.embedded.jetty.JettyServerCustomizer; import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory; import org.springframework.boot.web.server.WebServerFactoryCustomizer; import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.jetty9.InstrumentedQueuedThreadPool; public class DefaultWebServerFactory implements WebServerFactoryCustomizer { @@ -42,18 +44,19 @@ public DefaultWebServerFactory(ServiceConfiguration configuration, this.metricRegistry = registry; } + private InstrumentedQueuedThreadPool getInstrumentedThreadPool() { + InstrumentedQueuedThreadPool tPool = + new InstrumentedQueuedThreadPool(metricRegistry, configuration.getMaxConnections(), + configuration.getMinConnections(), configuration.getThreadPoolMaxIdleTimeInMsec(), + new ArrayBlockingQueue<>(configuration.getMaxQueueSize()), "storm.http"); + tPool.setName("thread-pool"); + return tPool; + } + @Override public void customize(JettyServletWebServerFactory factory) { - factory.setThreadPool(ThreadPoolBuilder.instance() - .withMaxRequestQueueSize(configuration.getMaxQueueSize()) - .withMaxThreads(serverProperties.getJetty().getThreads().getMax()) - .withMinThreads(serverProperties.getJetty().getThreads().getMin()) - .registry(metricRegistry) - .withPrefix("storm.http") - .withName("thread-pool") - .build()); - + factory.setThreadPool(getInstrumentedThreadPool()); factory.addServerCustomizers(serverCustomizer); } diff --git a/src/main/java/org/italiangrid/storm/webdav/server/TLSConnectorBuilderError.java b/src/main/java/org/italiangrid/storm/webdav/server/TLSConnectorBuilderError.java new file mode 100644 index 00000000..8d05acb8 --- /dev/null +++ b/src/main/java/org/italiangrid/storm/webdav/server/TLSConnectorBuilderError.java @@ -0,0 +1,34 @@ +/** + * 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.server; + +public class TLSConnectorBuilderError extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public TLSConnectorBuilderError(Throwable cause) { + super(cause); + } + + public TLSConnectorBuilderError(String message, Throwable cause) { + super(message, cause); + } + + public TLSConnectorBuilderError(String message) { + super(message); + } + +} diff --git a/src/main/java/org/italiangrid/storm/webdav/server/TLSServerConnectorBuilder.java b/src/main/java/org/italiangrid/storm/webdav/server/TLSServerConnectorBuilder.java new file mode 100644 index 00000000..67379b58 --- /dev/null +++ b/src/main/java/org/italiangrid/storm/webdav/server/TLSServerConnectorBuilder.java @@ -0,0 +1,664 @@ +/** + * 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.server; + +import static java.util.Objects.isNull; + +import java.io.File; +import java.io.IOException; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Security; +import java.security.cert.CertificateException; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.conscrypt.OpenSSLProvider; +import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory; +import org.eclipse.jetty.http.HttpVersion; +import org.eclipse.jetty.http2.HTTP2Cipher; +import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory; +import org.eclipse.jetty.server.ConnectionFactory; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.SecureRequestCustomizer; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.SslConnectionFactory; +import org.eclipse.jetty.util.ssl.SslContextFactory; + +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.jetty9.InstrumentedConnectionFactory; + +import eu.emi.security.authn.x509.X509CertChainValidatorExt; +import eu.emi.security.authn.x509.helpers.ssl.SSLTrustManager; +import eu.emi.security.authn.x509.impl.PEMCredential; + +/** + * A builder that configures a Jetty server TLS connector integrated with CANL + * {@link X509CertChainValidatorExt} certificate validation services. + * + */ +public class TLSServerConnectorBuilder { + + /** + * Conscrypt provider name. + */ + public static final String CONSCRYPT_PROVIDER = "Conscrypt"; + + /** + * Default service certificate file. + */ + public static final String DEFAULT_CERTIFICATE_FILE = "/etc/grid-security/hostcert.pem"; + + /** + * Default service certificate private key file. + */ + public static final String DEFAULT_CERTIFICATE_KEY_FILE = "/etc/grid-security/hostcert.pem"; + + /** + * The port for this connector. + */ + private int port; + + /** + * The certificate file location. + */ + private String certificateFile = DEFAULT_CERTIFICATE_FILE; + + /** + * The certificate private key file location. + */ + private String certificateKeyFile = DEFAULT_CERTIFICATE_KEY_FILE; + + /** + * The password to decrypt the certificate private key file. + */ + private char[] certicateKeyPassword = null; + + /** + * The certificate validator used by this connector builder. + */ + private final X509CertChainValidatorExt certificateValidator; + + /** + * Whether client auth will be required for this connector. + */ + private boolean tlsNeedClientAuth = false; + + /** + * Whether cluent auth is supported for this connector. + */ + private boolean tlsWantClientAuth = true; + + /** + * Supported SSL protocols. + */ + private String[] includeProtocols; + + /** + * Disabled SSL protocols. + */ + private String[] excludeProtocols; + + /** + * Supported cipher suites. + */ + private String[] includeCipherSuites; + + /** + * Disabled cipher suites. + */ + private String[] excludeCipherSuites; + + /** + * The HTTP configuration for the connector being created. + */ + private HttpConfiguration httpConfiguration; + + /** + * The key manager to use for the connector being created. + */ + private KeyManager keyManager; + + /** + * The server for which the connector is being created. + */ + private final Server server; + + /** + * The metric name to associate to the connector being built. + */ + private String metricName; + + /** + * The metric registry. + */ + private MetricRegistry registry; + + /** + * Whether the Conscrypt provider should be used instead of the default JSSE implementation + */ + private boolean useConscrypt = false; + + + /** + * Whether HTTP/2 should be configured + */ + private boolean enableHttp2 = false; + + /** + * Which TLS protocol string should be used + */ + private String tlsProtocol = "TLSv1.2"; + + /** + * Custom TLS hostname verifier + */ + private HostnameVerifier hostnameVerifier = null; + + /** + * Disable JSSE hostname verification + */ + private boolean disableJsseHostnameVerification = false; + + /** + * Number of acceptors threads for the connector + */ + private int acceptors = -1; + + /** + * Number of selector threads for the connector + */ + private int selectors = -1; + + /** + * Returns an instance of the {@link TLSServerConnectorBuilder}. + * + * @param s the {@link Server} for which the connector is being created + * @param certificateValidator a {@link X509CertChainValidatorExt} used to validate certificates + * @return an instance of the {@link TLSServerConnectorBuilder} + */ + public static TLSServerConnectorBuilder instance(Server s, + X509CertChainValidatorExt certificateValidator) { + + return new TLSServerConnectorBuilder(s, certificateValidator); + } + + /** + * Private ctor. + * + * @param s the {@link Server} for which the connector is being created + * @param certificateValidator a {@link X509CertChainValidatorExt} used to validate certificates + */ + private TLSServerConnectorBuilder(Server s, X509CertChainValidatorExt certificateValidator) { + + if (s == null) { + throw new IllegalArgumentException("Server cannot be null"); + } + + if (certificateValidator == null) { + throw new IllegalArgumentException("certificateValidator cannot be null"); + } + + this.server = s; + this.certificateValidator = certificateValidator; + } + + private void credentialsSanityChecks() { + + checkFileExistsAndIsReadable(new File(certificateFile), "Error accessing certificate file"); + + checkFileExistsAndIsReadable(new File(certificateKeyFile), + "Error accessing certificate key file"); + + } + + private void loadCredentials() { + + credentialsSanityChecks(); + + PEMCredential serviceCredentials = null; + + try { + + serviceCredentials = + new PEMCredential(certificateKeyFile, certificateFile, certicateKeyPassword); + + } catch (KeyStoreException | CertificateException | IOException e) { + + throw new TLSConnectorBuilderError("Error setting up service credentials", e); + } + + keyManager = serviceCredentials.getKeyManager(); + } + + /** + * Configures SSL session parameters for the jetty {@link SslContextFactory}. + * + * @param contextFactory the {@link SslContextFactory} being configured + */ + private void configureContextFactory(SslContextFactory.Server contextFactory) { + + if (excludeProtocols != null) { + contextFactory.setExcludeProtocols(excludeProtocols); + } + + if (includeProtocols != null) { + contextFactory.setIncludeProtocols(includeProtocols); + } + + if (excludeCipherSuites != null) { + contextFactory.setExcludeCipherSuites(excludeCipherSuites); + } + + if (includeCipherSuites != null) { + contextFactory.setIncludeCipherSuites(includeCipherSuites); + } + + contextFactory.setWantClientAuth(tlsWantClientAuth); + contextFactory.setNeedClientAuth(tlsNeedClientAuth); + + if (useConscrypt) { + contextFactory.setProvider(CONSCRYPT_PROVIDER); + } else { + contextFactory.setProvider(BouncyCastleProvider.PROVIDER_NAME); + } + + if (hostnameVerifier != null) { + contextFactory.setHostnameVerifier(hostnameVerifier); + } + + if (disableJsseHostnameVerification) { + contextFactory.setEndpointIdentificationAlgorithm(null); + } + + } + + /** + * Builds a default {@link HttpConfiguration} for the TLS-enabled connector being created + * + * @return the default {@link HttpConfiguration} + */ + private HttpConfiguration defaultHttpConfiguration() { + + HttpConfiguration httpsConfig = new HttpConfiguration(); + + httpsConfig.setSecureScheme("https"); + + httpsConfig.setSecurePort(port); + + httpsConfig.setOutputBufferSize(32768); + httpsConfig.setRequestHeaderSize(8192); + httpsConfig.setResponseHeaderSize(8192); + + httpsConfig.setSendServerVersion(true); + httpsConfig.setSendDateHeader(false); + + httpsConfig.addCustomizer(new SecureRequestCustomizer()); + + return httpsConfig; + + } + + /** + * Gives access to the {@link HttpConfiguration} used for the TLS-enabled connector being created. + * If the configuration is not set, it creates it using {@link #defaultHttpConfiguration()}. + * + * @return the {@link HttpConfiguration} being used for the TLS-enabled connector. + */ + public HttpConfiguration httpConfiguration() { + + if (httpConfiguration == null) { + httpConfiguration = defaultHttpConfiguration(); + } + + return httpConfiguration; + + } + + /** + * Sets the port for the connector being created. + * + * @param port the port for the connector + * @return this builder + */ + public TLSServerConnectorBuilder withPort(int port) { + + this.port = port; + return this; + } + + /** + * Sets the certificate file for the connector being created. + * + * @param certificateFile the certificate file + * @return this builder + */ + public TLSServerConnectorBuilder withCertificateFile(String certificateFile) { + + this.certificateFile = certificateFile; + return this; + } + + /** + * Sets the certificate key file for the connector being created. + * + * @param certificateKeyFile the certificate key file + * @return this builder + */ + public TLSServerConnectorBuilder withCertificateKeyFile(String certificateKeyFile) { + + this.certificateKeyFile = certificateKeyFile; + return this; + } + + /** + * The the certificate key password for the connector being built + * + * @param certificateKeyPassword the certificate key password + * @return this builder + */ + public TLSServerConnectorBuilder withCertificateKeyPassword(char[] certificateKeyPassword) { + + this.certicateKeyPassword = certificateKeyPassword; + return this; + } + + /** + * Sets the {@link SslContextFactory#setNeedClientAuth(boolean)} parameter for the connector being + * created. + * + * @param needClientAuth true if client authentication is required + * @return this builder + */ + public TLSServerConnectorBuilder withNeedClientAuth(boolean needClientAuth) { + + this.tlsNeedClientAuth = needClientAuth; + return this; + } + + /** + * Sets the {@link SslContextFactory#setWantClientAuth(boolean)} parameter for the connector being + * created. + * + * @param wantClientAuth true if client authentication is wanted + * @return this builder + */ + public TLSServerConnectorBuilder withWantClientAuth(boolean wantClientAuth) { + + this.tlsWantClientAuth = wantClientAuth; + return this; + } + + /** + * Sets SSL included protocols. See {@link SslContextFactory#setIncludeProtocols(String...)}. + * + * @param includeProtocols the array of included protocol names + * @return this builder + */ + public TLSServerConnectorBuilder withIncludeProtocols(String... includeProtocols) { + + this.includeProtocols = includeProtocols; + return this; + } + + /** + * Sets SSL excluded protocols. See {@link SslContextFactory#setExcludeProtocols(String...)}. + * + * @param excludeProtocols the array of excluded protocol names + * @return this builder + */ + public TLSServerConnectorBuilder withExcludeProtocols(String... excludeProtocols) { + + this.excludeProtocols = excludeProtocols; + return this; + } + + /** + * Sets the SSL included cipher suites. + * + * @param includeCipherSuites the array of included cipher suites. + * @return this builder + */ + public TLSServerConnectorBuilder withIncludeCipherSuites(String... includeCipherSuites) { + + this.includeCipherSuites = includeCipherSuites; + return this; + } + + /** + * Sets the SSL ecluded cipher suites. + * + * @param excludeCipherSuites the array of excluded cipher suites. + * @return this builder + */ + public TLSServerConnectorBuilder withExcludeCipherSuites(String... excludeCipherSuites) { + + this.excludeCipherSuites = excludeCipherSuites; + return this; + } + + /** + * Sets the {@link HttpConfiguration} for the connector being built. + * + * @param conf the {@link HttpConfiguration} to use + * @return this builder + */ + public TLSServerConnectorBuilder withHttpConfiguration(HttpConfiguration conf) { + + this.httpConfiguration = conf; + return this; + } + + /** + * Sets the {@link KeyManager} for the connector being built. + * + * @param km the {@link KeyManager} to use + * @return this builder + */ + public TLSServerConnectorBuilder withKeyManager(KeyManager km) { + + this.keyManager = km; + return this; + } + + public TLSServerConnectorBuilder withConscrypt(boolean conscryptEnabled) { + this.useConscrypt = conscryptEnabled; + return this; + } + + public TLSServerConnectorBuilder withHttp2(boolean http2Enabled) { + this.enableHttp2 = http2Enabled; + return this; + } + + public TLSServerConnectorBuilder metricRegistry(MetricRegistry registry) { + this.registry = registry; + return this; + } + + public TLSServerConnectorBuilder metricName(String metricName) { + this.metricName = metricName; + return this; + } + + public TLSServerConnectorBuilder withTlsProtocol(String tlsProtocol) { + this.tlsProtocol = tlsProtocol; + return this; + } + + public TLSServerConnectorBuilder withHostnameVerifier(HostnameVerifier verifier) { + this.hostnameVerifier = verifier; + return this; + } + + public TLSServerConnectorBuilder withDisableJsseHostnameVerification( + boolean disableJsseHostnameVerification) { + this.disableJsseHostnameVerification = disableJsseHostnameVerification; + return this; + } + + public TLSServerConnectorBuilder withAcceptors(int acceptors) { + this.acceptors = acceptors; + return this; + } + + public TLSServerConnectorBuilder withSelectors(int selectors) { + this.selectors = selectors; + return this; + } + + private SSLContext buildSSLContext() { + + SSLContext sslCtx; + + try { + + KeyManager[] kms = new KeyManager[] {keyManager}; + SSLTrustManager tm = new SSLTrustManager(certificateValidator); + + if (useConscrypt) { + + if (isNull(Security.getProvider(CONSCRYPT_PROVIDER))) { + Security.addProvider(new OpenSSLProvider()); + } + + sslCtx = SSLContext.getInstance(tlsProtocol, CONSCRYPT_PROVIDER); + } else { + sslCtx = SSLContext.getInstance(tlsProtocol); + } + + sslCtx.init(kms, new TrustManager[] {tm}, null); + + } catch (NoSuchAlgorithmException e) { + + throw new TLSConnectorBuilderError("TLS protocol not supported: " + e.getMessage(), e); + } catch (KeyManagementException e) { + throw new TLSConnectorBuilderError(e); + } catch (NoSuchProviderException e) { + throw new TLSConnectorBuilderError("TLS provider error: " + e.getMessage(), e); + } + + return sslCtx; + } + + /** + * Builds a {@link ServerConnector} based on the {@link TLSServerConnectorBuilder} parameters + * + * @return a {@link ServerConnector} + */ + public ServerConnector build() { + + if (keyManager == null) { + loadCredentials(); + } + + SSLContext sslContext = buildSSLContext(); + SslContextFactory.Server cf = new SslContextFactory.Server(); + + cf.setSslContext(sslContext); + + + configureContextFactory(cf); + + if (httpConfiguration == null) { + httpConfiguration = defaultHttpConfiguration(); + } + + + HttpConnectionFactory httpConnFactory = new HttpConnectionFactory(httpConfiguration); + ConnectionFactory connFactory = null; + + if (registry != null) { + connFactory = new InstrumentedConnectionFactory(httpConnFactory, registry.timer(metricName)); + } else { + connFactory = httpConnFactory; + } + + + ConnectionFactory h2ConnFactory = null; + ServerConnector connector = null; + + if (enableHttp2) { + + HTTP2ServerConnectionFactory h2cf = new HTTP2ServerConnectionFactory(httpConfiguration); + + if (registry != null) { + h2ConnFactory = new InstrumentedConnectionFactory(h2cf, registry.timer(metricName)); + } else { + h2ConnFactory = h2cf; + } + ALPNServerConnectionFactory alpn = createAlpnProtocolFactory(httpConnFactory); + cf.setCipherComparator(HTTP2Cipher.COMPARATOR); + cf.setUseCipherSuitesOrder(true); + + SslConnectionFactory sslCf = new SslConnectionFactory(cf, alpn.getProtocol()); + + connector = new ServerConnector(server, acceptors, selectors, sslCf, alpn, h2ConnFactory, + httpConnFactory); + + } else { + + connector = new ServerConnector(server, acceptors, selectors, + new SslConnectionFactory(cf, HttpVersion.HTTP_1_1.asString()), connFactory); + } + + connector.setPort(port); + return connector; + } + + private ALPNServerConnectionFactory createAlpnProtocolFactory( + HttpConnectionFactory httpConnectionFactory) { + ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory(); + alpn.setDefaultProtocol(httpConnectionFactory.getProtocol()); + return alpn; + } + + + /** + * Checks that file exists and is readable. + * + * @param f the {@link File} to be checked + * @param prefix A prefix string for the error message, in case the file does not exist and is not + * readable + * @throws RuntimeException if the file does not exist or is not readable + */ + private void checkFileExistsAndIsReadable(File f, String prefix) { + + String errorMessage = null; + + if (!f.exists()) { + errorMessage = "File does not exists"; + } else if (!f.canRead()) { + errorMessage = "File is not readable"; + } else if (f.isDirectory()) { + errorMessage = "File is a directory"; + } + + if (errorMessage != null) { + String msg = String.format("%s: %s [%s]", prefix, errorMessage, f.getAbsolutePath()); + throw new TLSConnectorBuilderError(msg); + } + + } +} diff --git a/src/main/java/org/italiangrid/storm/webdav/server/servlet/LogRequestFilter.java b/src/main/java/org/italiangrid/storm/webdav/server/servlet/LogRequestFilter.java index 9334759e..1c3b3abd 100644 --- a/src/main/java/org/italiangrid/storm/webdav/server/servlet/LogRequestFilter.java +++ b/src/main/java/org/italiangrid/storm/webdav/server/servlet/LogRequestFilter.java @@ -16,6 +16,8 @@ package org.italiangrid.storm.webdav.server.servlet; import java.io.IOException; +import java.util.List; +import java.util.Objects; import java.util.Optional; import javax.servlet.Filter; @@ -33,10 +35,15 @@ import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; +import com.google.common.collect.Lists; + public class LogRequestFilter implements Filter { public static final Logger log = LoggerFactory.getLogger(LogRequestFilter.class); + private static final List IP_HEADERS = Lists.newArrayList("X-Forwarded-For", + "Proxy-Client-IP", "WL-Proxy-Client-IP", "HTTP_CLIENT_IP", "HTTP_X_FORWARDED_FOR"); + @Override public void destroy() {} @@ -56,7 +63,7 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse res = (HttpServletResponse) response; - String resMsg = String.format("%s %s %s %d [user:<%s>, authorities:<%s>]", req.getRemoteAddr(), + String resMsg = String.format("%s %s %s %d [user:<%s>, authorities:<%s>]", getClientIpAddr(req), req.getMethod(), req.getRequestURI(), res.getStatus(), authn.isPresent() ? authn.get().getName() : null, authn.isPresent() ? authn.get().getAuthorities() : null); @@ -64,6 +71,21 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha log.debug(resMsg); } + + public static String getClientIpAddr(HttpServletRequest request) { + + String remoteIp = request.getRemoteAddr(); + if (remoteIp != null) { + return remoteIp; + } + return IP_HEADERS.stream() + .map(request::getHeader) + .filter(Objects::nonNull) + .filter(ip -> !ip.isEmpty() && !ip.equalsIgnoreCase("unknown")) + .findFirst() + .orElse("???.???.???.???"); + } + @Override public void init(FilterConfig config) throws ServletException {} diff --git a/src/main/java/org/italiangrid/storm/webdav/spring/AppConfig.java b/src/main/java/org/italiangrid/storm/webdav/spring/AppConfig.java index ce9a7a88..22d43a01 100644 --- a/src/main/java/org/italiangrid/storm/webdav/spring/AppConfig.java +++ b/src/main/java/org/italiangrid/storm/webdav/spring/AppConfig.java @@ -16,7 +16,7 @@ package org.italiangrid.storm.webdav.spring; import static java.util.Objects.isNull; -import static org.italiangrid.utils.jetty.TLSServerConnectorBuilder.CONSCRYPT_PROVIDER; +import static org.italiangrid.storm.webdav.server.TLSServerConnectorBuilder.CONSCRYPT_PROVIDER; import java.io.IOException; import java.net.MalformedURLException; diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 6b1c7356..8d9d02e0 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -2,8 +2,19 @@ server: jetty: accesslog: enabled: false + +management: + # endpoint: + # env: + # additional-keys-to-sanitize: client-secret + endpoints: + web: + exposure: + include: env + oauth: enable-oidc: false + storm: connector: port: 8086 diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 6eaaa236..4db620d9 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -11,16 +11,19 @@ spring: issuer-uri: https://iam-escape.cloud.cnaf.infn.it/ indigo: issuer-uri: https://iam-test.indigo-datacloud.eu/ + session: store-type: none server: - # StoRM webdav will bind on this address + # StoRM WebDAV will bind on this address address: ${STORM_WEBDAV_SERVER_ADDRESS:0.0.0.0} + # StoRM WebDAV server should support graceful shutdown, allowing active requests time to complete, or shut down immediately + # Values: graceful, immediate + shutdown: ${STORM_WEBDAV_SERVER_SHUTDOWN:graceful} error: whitelabel: enabled: false - jetty: threads: max: ${storm.connector.max-connections} @@ -28,7 +31,7 @@ server: management: health: redis: - enabled: false + enabled: false tpc: tls-protocol: ${STORM_WEBDAV_TPC_TLS_PROTOCOL:TLSv1.2} @@ -50,7 +53,8 @@ tpc: enable-expect-continue-threshold: ${STORM_WEBDAV_TPC_ENABLE_EXPECT_CONTINUE_THRESHOLD:1048576} oauth: - refresh-period-minutes: 60 + refresh-period-minutes: ${STORM_WEBDAV_OAUTH_REFRESH_PERIOD_MINUTES:60} + refresh-timeout-seconds: ${STORM_WEBDAV_OAUTH_REFRESH_TIMEOUT_SECONDS:30} issuers: storm: @@ -81,18 +85,28 @@ storm: macaroon-filter: enabled: ${STORM_WEBDAV_MACAROON_FILTER_ENABLED:true} + server: + # Jetty Thread-Pool maximum idle time (in milliseconds) + max-idle-time-msec: ${STORM_WEBDAV_SERVER_MAX_IDLE_TIME:3600000} + connector: # HTTP connector port port: ${STORM_WEBDAV_HTTP_PORT:8085} # HTTPS connector port secure-port: ${STORM_WEBDAV_HTTPS_PORT:8443} + # Min concurrent connections + min-connections: ${STORM_WEBDAV_MIN_CONNECTIONS:50} # Max concurrent connections max-connections: ${STORM_WEBDAV_MAX_CONNECTIONS:300} # Connection queue size max-queue-size: ${STORM_WEBDAV_MAX_QUEUE_SIZE:900} # Connector Maximum idle time (in milliseconds) - max-idle-time-msec: ${STORM_WEBDAV_CONNECTOR_MAX_IDLE_TIME:30000} + max-idle-time-msec: ${STORM_WEBDAV_CONNECTOR_MAX_IDLE_TIME:30000} output-buffer-size-bytes: ${storm.buffer.file-buffer-size-bytes} + # Number of acceptor threads to use. When the value is -1, the default, the number of acceptors is derived from the operating environment. + jetty-acceptors: ${STORM_WEBDAV_CONNECTOR_ACCEPTORS:-1} + # Number of selector threads to use. When the value is -1, the default, the number of selectors is derived from the operating environment. + jetty-selectors: ${STORM_WEBDAV_CONNECTOR_SELECTORS:-1} tls: # Path to the service certificate. @@ -158,4 +172,5 @@ storm: tape: well-known: - source: ${STORM_WEBDAV_TAPE_WELLKNOWN_SOURCE:/etc/storm/webdav/wlcg-tape-rest-api.json} \ No newline at end of file + source: ${STORM_WEBDAV_TAPE_WELLKNOWN_SOURCE:/etc/storm/webdav/wlcg-tape-rest-api.json} + diff --git a/src/test/java/org/italiangrid/storm/webdav/server/TLSConnectorBuilderTest.java b/src/test/java/org/italiangrid/storm/webdav/server/TLSConnectorBuilderTest.java new file mode 100644 index 00000000..b7fcbba9 --- /dev/null +++ b/src/test/java/org/italiangrid/storm/webdav/server/TLSConnectorBuilderTest.java @@ -0,0 +1,117 @@ +/** + * 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.server; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertThrows; + +import java.util.concurrent.TimeUnit; + +import javax.net.ssl.KeyManager; + +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.italiangrid.storm.webdav.server.util.CANLListener; +import org.italiangrid.voms.util.CertificateValidatorBuilder; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import eu.emi.security.authn.x509.CrlCheckingMode; +import eu.emi.security.authn.x509.NamespaceCheckingMode; +import eu.emi.security.authn.x509.OCSPCheckingMode; +import eu.emi.security.authn.x509.X509CertChainValidatorExt; + +@ExtendWith(MockitoExtension.class) +class TLSConnectorBuilderTest { + + @Test + void tlsConnectorBuilderErrorTests() { + + TLSConnectorBuilderError e = new TLSConnectorBuilderError("This is an error!"); + assertThat(e.getMessage(), is("This is an error!")); + e = new TLSConnectorBuilderError("This is an error!", new RuntimeException()); + assertThat(e.getMessage(), is("This is an error!")); + e = new TLSConnectorBuilderError(new RuntimeException("This is an error!")); + assertThat(e.getCause() instanceof RuntimeException, is(true)); + assertThat(e.getMessage(), containsString("This is an error!")); + } + + @Test + void illegalArgumentExceptionThrown() { + + Server server = Mockito.mock(Server.class); + X509CertChainValidatorExt validator = Mockito.mock(X509CertChainValidatorExt.class); + + assertThrows(IllegalArgumentException.class, () -> { + TLSServerConnectorBuilder.instance(null, validator); + }); + assertThrows(IllegalArgumentException.class, () -> { + TLSServerConnectorBuilder.instance(server, null); + }); + assertThrows(IllegalArgumentException.class, () -> { + TLSServerConnectorBuilder.instance(null, null); + }); + } + + private X509CertChainValidatorExt getValidator() { + + CANLListener l = new org.italiangrid.storm.webdav.server.util.CANLListener(); + CertificateValidatorBuilder builder = new CertificateValidatorBuilder(); + + long refreshInterval = TimeUnit.SECONDS.toMillis(3600); + + return builder.namespaceChecks(NamespaceCheckingMode.EUGRIDPMA_AND_GLOBUS_REQUIRE) + .crlChecks(CrlCheckingMode.IF_VALID) + .ocspChecks(OCSPCheckingMode.IGNORE) + .lazyAnchorsLoading(false) + .storeUpdateListener(l) + .validationErrorListener(l) + .trustAnchorsDir("src/test/resources/trust-anchors") + .trustAnchorsUpdateInterval(refreshInterval) + .build(); + } + + @Test + void tlsConnectorBuilderTests() { + + Server server = new Server(); + X509CertChainValidatorExt validator = getValidator(); + TLSServerConnectorBuilder builder = TLSServerConnectorBuilder.instance(server, validator); + HttpConfiguration httpConfiguration = builder.httpConfiguration(); + KeyManager keyManager = Mockito.mock(KeyManager.class); + builder.withPort(1234) + .withCertificateFile("fake-certificate") + .withCertificateKeyFile("fake-key") + .withCertificateKeyPassword("secret".toCharArray()) + .withHttpConfiguration(httpConfiguration) + .withKeyManager(keyManager) + .withExcludeCipherSuites("one", "two") + .withIncludeCipherSuites("three", "four") + .withIncludeProtocols("protocol", "another-protocol") + .withExcludeProtocols("another-more-protocol") + .withHostnameVerifier(new NoopHostnameVerifier()) + .withConscrypt(false); + + ServerConnector c = builder.build(); + assertThat(c.getPort(), is(1234)); + } +} \ No newline at end of file diff --git a/src/test/java/org/italiangrid/storm/webdav/test/oauth/jwk/OidcConfigurationFetcherTest.java b/src/test/java/org/italiangrid/storm/webdav/test/oauth/jwk/OidcConfigurationFetcherTest.java new file mode 100644 index 00000000..0725f914 --- /dev/null +++ b/src/test/java/org/italiangrid/storm/webdav/test/oauth/jwk/OidcConfigurationFetcherTest.java @@ -0,0 +1,307 @@ +/** + * 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.test.oauth.jwk; + +import static java.lang.String.format; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.italiangrid.storm.webdav.oauth.utils.DefaultOidcConfigurationFetcher.ISSUER_MISMATCH_ERROR_TEMPLATE; +import static org.italiangrid.storm.webdav.oauth.utils.DefaultOidcConfigurationFetcher.NO_JWKS_URI_ERROR_TEMPLATE; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.lenient; +import static org.springframework.http.HttpStatus.NOT_FOUND; +import static org.springframework.http.HttpStatus.OK; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.text.ParseException; +import java.util.Map; + +import org.apache.commons.io.FileUtils; +import org.italiangrid.storm.webdav.config.OAuthProperties; +import org.italiangrid.storm.webdav.oauth.utils.DefaultOidcConfigurationFetcher; +import org.italiangrid.storm.webdav.oauth.utils.OidcConfigurationFetcher; +import org.italiangrid.storm.webdav.oauth.utils.OidcConfigurationResolutionError; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; + +import com.google.common.collect.Maps; +import com.nimbusds.jose.KeySourceException; +import com.nimbusds.jose.RemoteKeySourceException; +import com.nimbusds.jose.jwk.JWKSet; +import com.nimbusds.jose.jwk.KeyType; + +@ExtendWith(MockitoExtension.class) +class OidcConfigurationFetcherTest { + + static final String ISSUER = "https://iam-dev.cloud.cnaf.infn.it/"; + static final String JWK_URI = ISSUER + "jwk"; + + static final String ANOTHER_ISSUER = "https://iam.cloud.infn.it/"; + static final String ANOTHER_JWK_URI = ANOTHER_ISSUER + "jwk"; + + static final String KID = "rsa1"; + + + final ParameterizedTypeReference> typeReference = + new ParameterizedTypeReference>() {}; + + @Mock + RestTemplate restTemplate; + @Mock + RestTemplateBuilder restBuilder; + @Mock + OAuthProperties oAuthProperties; + + private Map getMapWithIssuerAndJwkUri(String issuer, String jwkUri) { + Map m = Maps.newHashMap(); + m.put("issuer", issuer); + m.put("jwks_uri", jwkUri); + return m; + } + + @SuppressWarnings("unchecked") + private ResponseEntity> getWellKnownResponse(HttpStatus status, + Map map) { + + ResponseEntity> mockedEntity = + (ResponseEntity>) Mockito.mock(ResponseEntity.class); + lenient().when(mockedEntity.getStatusCode()).thenReturn(status); + lenient().when(mockedEntity.getStatusCodeValue()).thenReturn(status.value()); + lenient().when(mockedEntity.getBody()).thenReturn(map); + return mockedEntity; + } + + private String loadJwkFromFile() throws IOException { + + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("jwk/test-keystore.jwks").getFile()); + return FileUtils.readFileToString(file, "UTF-8"); + } + + @SuppressWarnings("unchecked") + private ResponseEntity getJWKURIResponse(HttpStatus status, String data) { + + ResponseEntity mockedEntity = + (ResponseEntity) Mockito.mock(ResponseEntity.class); + lenient().when(mockedEntity.getBody()).thenReturn(data); + lenient().when(mockedEntity.getStatusCode()).thenReturn(status); + lenient().when(mockedEntity.getStatusCodeValue()).thenReturn(status.value()); + return mockedEntity; + } + + private OidcConfigurationFetcher getFetcher(ResponseEntity> wellKnownResponse, + ResponseEntity jwkResponse) { + + lenient().when(restTemplate.exchange(any(), eq(typeReference))).thenReturn(wellKnownResponse); + lenient().when(restTemplate.exchange(any(), eq(String.class))).thenReturn(jwkResponse); + return getFetcher(restTemplate); + } + + private OidcConfigurationFetcher getFetcherWithException(ResponseEntity> wellKnownResponse) { + + lenient().when(restTemplate.exchange(any(), eq(typeReference))).thenReturn(wellKnownResponse); + lenient().when(restTemplate.exchange(any(), eq(String.class))).thenThrow(new RuntimeException("ERROR")); + return getFetcher(restTemplate); + } + + private OidcConfigurationFetcher getFetcher(RestTemplate restTemplate) { + + lenient().when(restBuilder.build()).thenReturn(restTemplate); + lenient().when(restBuilder.setConnectTimeout(any())).thenReturn(restBuilder); + lenient().when(restBuilder.setReadTimeout(any())).thenReturn(restBuilder); + lenient().when(oAuthProperties.getRefreshTimeoutSeconds()).thenReturn(30); + lenient().when(oAuthProperties.getRefreshPeriodMinutes()).thenReturn(1); + return new DefaultOidcConfigurationFetcher(restBuilder, oAuthProperties); + } + + private OidcConfigurationFetcher getSuccessfulFetcher() throws RestClientException, IOException { + + ResponseEntity> mockedResponseMapEntity = + getWellKnownResponse(OK, getMapWithIssuerAndJwkUri(ISSUER, JWK_URI)); + ResponseEntity mockedResponseStringEntity = getJWKURIResponse(OK, loadJwkFromFile()); + return getFetcher(mockedResponseMapEntity, mockedResponseStringEntity); + } + + private OidcConfigurationFetcher getSuccessfulFetcherWithWrongIssuer() + throws RestClientException, IOException { + + ResponseEntity> mockedResponseMapEntity = + getWellKnownResponse(OK, getMapWithIssuerAndJwkUri(ANOTHER_ISSUER, ANOTHER_JWK_URI)); + ResponseEntity mockedResponseStringEntity = getJWKURIResponse(OK, loadJwkFromFile()); + return getFetcher(mockedResponseMapEntity, mockedResponseStringEntity); + } + + private OidcConfigurationFetcher getSuccessfulFetcherWithNoIssuer() + throws RestClientException, IOException { + + Map map = getMapWithIssuerAndJwkUri(ANOTHER_ISSUER, ANOTHER_JWK_URI); + map.remove("issuer"); + ResponseEntity> mockedResponseMapEntity = getWellKnownResponse(OK, map); + ResponseEntity mockedResponseStringEntity = getJWKURIResponse(OK, loadJwkFromFile()); + return getFetcher(mockedResponseMapEntity, mockedResponseStringEntity); + } + + private OidcConfigurationFetcher getSuccessfulFetcherWithNoJwk() + throws RestClientException, IOException { + + Map map = getMapWithIssuerAndJwkUri(ISSUER, JWK_URI); + map.remove("jwks_uri"); + ResponseEntity> mockedResponseMapEntity = getWellKnownResponse(OK, map); + ResponseEntity mockedResponseStringEntity = getJWKURIResponse(OK, loadJwkFromFile()); + return getFetcher(mockedResponseMapEntity, mockedResponseStringEntity); + } + + private OidcConfigurationFetcher getFetcherWithErrorOnFetch() throws RestClientException { + + ResponseEntity> mockedResponseMapEntity = + getWellKnownResponse(NOT_FOUND, null); + return getFetcher(mockedResponseMapEntity, null); + } + + private OidcConfigurationFetcher getFetcherWithErrorOnGetJwk() throws RestClientException { + + ResponseEntity> mockedResponseMapEntity = + getWellKnownResponse(OK, getMapWithIssuerAndJwkUri(ISSUER, JWK_URI)); + ResponseEntity mockedResponseStringEntity = getJWKURIResponse(NOT_FOUND, null); + return getFetcher(mockedResponseMapEntity, mockedResponseStringEntity); + } + + private OidcConfigurationFetcher getFetcherWithRuntimeExceptionOnGetJwk() throws RestClientException { + + ResponseEntity> mockedResponseMapEntity = + getWellKnownResponse(OK, getMapWithIssuerAndJwkUri(ISSUER, JWK_URI)); + return getFetcherWithException(mockedResponseMapEntity); + } + + @BeforeEach + public void setDebugLevel() { + System.setProperty("logging.level.org.italiangrid.storm", "DEBUG"); + } + + @Test + void fetchWellKnownEndpointWithSuccessTests() throws RestClientException, IOException { + + OidcConfigurationFetcher fetcher = getSuccessfulFetcher(); + Map conf = fetcher.loadConfigurationForIssuer(ISSUER); + assertNotNull(conf); + assertThat(conf.get("issuer"), is(ISSUER)); + assertThat(conf.get("jwks_uri"), is(JWK_URI)); + } + + @Test + void fetchWellKnownEndpointWithErrorTests() throws RestClientException { + + OidcConfigurationFetcher fetcher = getFetcherWithErrorOnFetch(); + RuntimeException exception = assertThrows(RuntimeException.class, () -> { + fetcher.loadConfigurationForIssuer(ISSUER); + }); + String expectedMessage = "Received status code: 404"; + String actualMessage = exception.getMessage(); + + assertEquals(expectedMessage, actualMessage); + } + + @Test + void fetchWellKnownEndpointWrongIssuerTests() throws RestClientException, IOException { + + OidcConfigurationFetcher fetcher = getSuccessfulFetcherWithWrongIssuer(); + OidcConfigurationResolutionError exception = + assertThrows(OidcConfigurationResolutionError.class, () -> { + fetcher.loadConfigurationForIssuer(ISSUER); + }); + assertEquals(format(ISSUER_MISMATCH_ERROR_TEMPLATE, ANOTHER_ISSUER, ISSUER), + exception.getMessage()); + } + + @Test + void fetchWellKnownEndpointNoIssuerTests() throws RestClientException, IOException { + + OidcConfigurationFetcher fetcher = getSuccessfulFetcherWithNoIssuer(); + OidcConfigurationResolutionError exception = + assertThrows(OidcConfigurationResolutionError.class, () -> { + fetcher.loadConfigurationForIssuer(ISSUER); + }); + assertEquals(format(ISSUER_MISMATCH_ERROR_TEMPLATE, "(unavailable)", ISSUER), + exception.getMessage()); + } + + @Test + void fetchWellKnownEndpointNoJwkTests() throws RestClientException, IOException { + + OidcConfigurationFetcher fetcher = getSuccessfulFetcherWithNoJwk(); + OidcConfigurationResolutionError exception = + assertThrows(OidcConfigurationResolutionError.class, () -> { + fetcher.loadConfigurationForIssuer(ISSUER); + }); + assertEquals(format(NO_JWKS_URI_ERROR_TEMPLATE, ISSUER), exception.getMessage()); + } + + @Test + void fetchJWKEndpointTests() + throws RestClientException, IOException, ParseException, KeySourceException { + + OidcConfigurationFetcher fetcher = getSuccessfulFetcher(); + JWKSet key = JWKSet.parse(fetcher.loadJWKSourceForURL(URI.create(JWK_URI))); + + assertNotNull(key.getKeyByKeyId(KID)); + assertThat(key.getKeyByKeyId(KID).getKeyType(), is(KeyType.RSA)); + } + + @Test + void fetchJWKEndpointWithErrorTests() throws RestClientException { + + OidcConfigurationFetcher fetcher = getFetcherWithErrorOnGetJwk(); + final URI jwkUri = URI.create(JWK_URI); + KeySourceException exception = assertThrows(KeySourceException.class, () -> { + fetcher.loadJWKSourceForURL(jwkUri); + }); + String expectedMessage = + "Unable to get JWK from '" + jwkUri + "': received status code " + NOT_FOUND.value(); + String actualMessage = exception.getMessage(); + + assertEquals(expectedMessage, actualMessage); + } + + @Test + void fetchJWKEndpointWithRuntimeException() throws RestClientException { + + OidcConfigurationFetcher fetcher = getFetcherWithRuntimeExceptionOnGetJwk(); + final URI jwkUri = URI.create(JWK_URI); + RemoteKeySourceException exception = assertThrows(RemoteKeySourceException.class, () -> { + fetcher.loadJWKSourceForURL(jwkUri); + }); + String expectedMessage = "Unable to get JWK from 'https://iam-dev.cloud.cnaf.infn.it/jwk'"; + String actualMessage = exception.getMessage(); + + assertEquals(expectedMessage, actualMessage); + } +} diff --git a/src/test/java/org/italiangrid/storm/webdav/test/oauth/jwk/TrustedJwtDecoderCacheLoaderTest.java b/src/test/java/org/italiangrid/storm/webdav/test/oauth/jwk/TrustedJwtDecoderCacheLoaderTest.java new file mode 100644 index 00000000..62af0313 --- /dev/null +++ b/src/test/java/org/italiangrid/storm/webdav/test/oauth/jwk/TrustedJwtDecoderCacheLoaderTest.java @@ -0,0 +1,109 @@ +/** + * 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.test.oauth.jwk; + +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.lenient; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import org.apache.commons.io.FileUtils; +import org.italiangrid.storm.webdav.config.OAuthProperties; +import org.italiangrid.storm.webdav.config.OAuthProperties.AuthorizationServer; +import org.italiangrid.storm.webdav.config.ServiceConfigurationProperties; +import org.italiangrid.storm.webdav.config.ServiceConfigurationProperties.AuthorizationServerProperties; +import org.italiangrid.storm.webdav.oauth.utils.OidcConfigurationFetcher; +import org.italiangrid.storm.webdav.oauth.utils.TrustedJwtDecoderCacheLoader; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.security.oauth2.jwt.JwtDecoder; +import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.util.concurrent.ListenableFuture; +import com.nimbusds.jose.KeySourceException; + +@ExtendWith(MockitoExtension.class) +class TrustedJwtDecoderCacheLoaderTest { + + private static final String ISSUER = "https://wlcg.cloud.cnaf.infn.it/"; + private static final String JWK_URI = "https://wlcg.cloud.cnaf.infn.it/jwks"; + + @Mock + ServiceConfigurationProperties properties; + @Mock + OAuthProperties oauthProperties; + @Mock + RestTemplateBuilder builder; + @Mock + OidcConfigurationFetcher fetcher; + + private ExecutorService executor; + private TrustedJwtDecoderCacheLoader jwtLoader; + + @BeforeEach + public void setup() throws IOException, KeySourceException { + + AuthorizationServer as = new AuthorizationServer(); + as.setIssuer(ISSUER); + as.setJwkUri(JWK_URI); + List issuerServers = Lists.newArrayList(as); + lenient().when(oauthProperties.getIssuers()).thenReturn(issuerServers); + + Map oidcConfiguration = Maps.newHashMap(); + oidcConfiguration.put("issuer", ISSUER); + oidcConfiguration.put("jwks_uri", JWK_URI); + + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("jwk/test-keystore.jwks").getFile()); + String data = FileUtils.readFileToString(file, "UTF-8"); + + lenient().when(fetcher.loadConfigurationForIssuer(ISSUER)).thenReturn(oidcConfiguration); + lenient().when(fetcher.loadJWKSourceForURL(URI.create(JWK_URI))).thenReturn(data); + + AuthorizationServerProperties props = new AuthorizationServerProperties(); + props.setEnabled(false); + props.setIssuer("http://localhost"); + lenient().when(properties.getAuthzServer()).thenReturn(props); + + executor = Executors.newScheduledThreadPool(1); + + jwtLoader = + new TrustedJwtDecoderCacheLoader(properties, oauthProperties, builder, fetcher, executor); + + } + + @Test + void testLoadRemoteIssuerConfiguration() throws Exception { + + JwtDecoder decoder = jwtLoader.load(ISSUER); + assertTrue(decoder instanceof NimbusJwtDecoder); + ListenableFuture reloaded = jwtLoader.reload(ISSUER, decoder); + JwtDecoder newDecoder = reloaded.get(); + assertTrue(newDecoder instanceof NimbusJwtDecoder); + } +} diff --git a/src/test/java/org/italiangrid/storm/webdav/test/oauth/jwt/NoExpirationStringCacheTest.java b/src/test/java/org/italiangrid/storm/webdav/test/oauth/jwt/NoExpirationStringCacheTest.java new file mode 100644 index 00000000..a5c8653c --- /dev/null +++ b/src/test/java/org/italiangrid/storm/webdav/test/oauth/jwt/NoExpirationStringCacheTest.java @@ -0,0 +1,45 @@ +/** + * 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.test.oauth.jwt; + +import static org.junit.Assert.assertEquals; + +import org.italiangrid.storm.webdav.oauth.utils.NoExpirationStringCache; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class NoExpirationStringCacheTest { + + private static final String CACHED_VALUE = "this-is-my-cached-value"; + private static final String FAKE_ISSUER = "http://localhost"; + + @Test + void noExpirationCacheWorks() { + + NoExpirationStringCache cache = new NoExpirationStringCache(CACHED_VALUE); + + assertEquals("NoExpirationCache", cache.getName()); + assertEquals(cache, cache.getNativeCache()); + assertEquals(CACHED_VALUE, cache.get(FAKE_ISSUER).get()); + cache.clear(); + cache.put(FAKE_ISSUER, CACHED_VALUE); + assertEquals(CACHED_VALUE, cache.get(FAKE_ISSUER).get()); + cache.evict(FAKE_ISSUER); + assertEquals(CACHED_VALUE, cache.get(FAKE_ISSUER).get()); + } +} \ No newline at end of file diff --git a/src/test/resources/jwk/test-keystore.jwks b/src/test/resources/jwk/test-keystore.jwks new file mode 100644 index 00000000..cde1441e --- /dev/null +++ b/src/test/resources/jwk/test-keystore.jwks @@ -0,0 +1,16 @@ +{ + "keys": [ + { + "p": "-qdvzeHU7w_ToV2RlS2QlVggXNL2YfpRWQxvrO8pHZC_dVgYFwKz5nadOMzR1BK0tPuCTWuuI66sFgaA9VENGypdIYoCF2O1FBLFK6GjOO-uc0LZEbIDa6Xn0G7UYOWcLaiYriHTtC_Pzp11L7VGjrUlX4HRgU_B3X1oeGn0mbM", + "kty": "RSA", + "q": "5S2b9tYHi9zBcNGZ8X6GM4TAL4UU9mABH0rKIyzbudkG7Wxxbj6I18skuHzfOOPI4c8sTQSv6IVAr2n1bn3_E5RSyPpbtDSCTYGzhijXl9wZ0ba2NidFrVjnL-KPx_gcHKnUHebKvsIEdjxeuqaaZ1kqEJX326b450Frghd78p8", + "d": "oDb1qfQTaP73jEZHgkOG5C9dY5EJZW57fX_BYUJ-yYTuTHPWZBVDKw9I_Ir1tSYyuTF-Bfb4iPim46gnEBM3AdvMian2iajvrN_rJFUJHo65vtY9xCXCD0d_Jct5JMyOafP5LF3cP38yDcyZRS_JeyKGB6U1KhbL-gG4hrQGS8qO3rdY_JQiLDLVdRRptHsPphS44JHXdP2qeVNJ41-CTfPWKiMIUOC0fj-As-dbTzRXuLXs04NayAdM-yhvRiwKujEfL8YbKW9CDJIgJfm2vzWHXFus5Y11S2Zr65cWxxVvRfnAFbFO1AkIkJc2jHZ4xLxfDU2kTi20sOMq1UFrvQ", + "e": "AQAB", + "kid": "rsa1", + "qi": "bN1wnq_0VkJlECMGPmeRFdZCX2LgAMrgwbJpysRw5J04vO9YsVmAcB_4xqoDjDUg7koioAp3IOhMGjOJWpYzCqWzsaA_84kX4WKGsr2xz6oaFSgt1FvtBY4GqEeZj8RG0LMtEtKSyjHGieA0hd4TUcqSdNZ4osT98Bfd7z3peYc", + "dp": "YfyaxI2IRHyXavm9M-hAIWH2JNOD5gGJU5p8_cnw9NHlRuZNZJF16p5sEAxh6tn1MtsvsTxrMx_RvjqEp2IsEXaaOcZN0v7zhwlfcxMZT-TC-eQkH7rLg4Wz_dOVytt4FpFWPpySuloGjusXKLNhBeDi31dMo5SeYQvpj0k8iek", + "dq": "9_lhyLPNdohmxqwE5kkA7L23NbPJ-svmavWBwo3HMlCiLkQoeCEx8EzebsCux9-wfKSuSqfHrtCALU15QxUR6x2SdeRvVY17cGHm3kNTA_4j8cbBYdccjXSksitzZ-wOfvVDjxcqST2llkm8NjoO18Siv0-F4SXKLG-c5CaE9w", + "n": "4GRvJuFantVV3JdjwQOAkfREnwUFp2znRBTOIJhPamyH4gf4YlI5PQT79415NV4_HrWYzgooH5AK6-7WE-TLLGEAVK5vdk4vv79bG7ukvjvBPxAjEhQn6-Amln88iXtvicEGbh--3CKbQj1jryVU5aWM6jzweaabFSeCILVEd6ZT7ofXaAqan9eLzU5IEtTPy5MfrrOvWw5Q7D2yzMqc5LksmaQSw8XtmhA8gnENnIqjAMmPtRltf93wjtmiamgVENOVPdN-93Nd5w-pnMwEyoO6Q9JqXxV6lD6qBRxI7_5t4_vmVxcbbxcZbSAMoHqA2pbSMJ4Jcw-27Hct9jesLQ" + } + ] +} \ No newline at end of file From a0ac1914b24a010448c12877d5a8ada315aa8d89 Mon Sep 17 00:00:00 2001 From: Enrico Vianello Date: Thu, 3 Oct 2024 10:11:39 +0200 Subject: [PATCH 03/22] Update WLCG policy decision point logic (#45) * HEAD and OPTIONS no more require storage.read * Any storage.xxx scope can be used to allow HEAD or OPTIONS calls * Introduced storage.stage as a superset of storage.read --- .../WlcgStructuredPathAuthorizationPdp.java | 56 +++++------ .../authz/pdp/ScopePathAuthzPdpTests.java | 95 +++++++++++++++---- 2 files changed, 108 insertions(+), 43 deletions(-) diff --git a/src/main/java/org/italiangrid/storm/webdav/authz/pdp/WlcgStructuredPathAuthorizationPdp.java b/src/main/java/org/italiangrid/storm/webdav/authz/pdp/WlcgStructuredPathAuthorizationPdp.java index 054a0b87..5718da45 100644 --- a/src/main/java/org/italiangrid/storm/webdav/authz/pdp/WlcgStructuredPathAuthorizationPdp.java +++ b/src/main/java/org/italiangrid/storm/webdav/authz/pdp/WlcgStructuredPathAuthorizationPdp.java @@ -47,17 +47,21 @@ public class WlcgStructuredPathAuthorizationPdp implements PathAuthorizationPdp, MatcherUtils, TpcUtils { public static final String WLCG_STORAGE_SCOPE_PATTERN_STRING = - "^storage.(read|modify|create):(\\/.*)$"; + "^storage.(read|modify|create|stage):(\\/.*)$"; public static final Pattern WLCG_STORAGE_SCOPE_PATTERN = Pattern.compile(WLCG_STORAGE_SCOPE_PATTERN_STRING); public static final String SCOPE_CLAIM = "scope"; + public static final String STORAGE_STAGE = "storage.stage"; public static final String STORAGE_READ = "storage.read"; public static final String STORAGE_MODIFY = "storage.modify"; public static final String STORAGE_CREATE = "storage.create"; + protected static final Set ALL_STORAGE_SCOPES = + Sets.newHashSet(STORAGE_READ, STORAGE_MODIFY, STORAGE_CREATE, STORAGE_STAGE); + public static final String ERROR_INVALID_AUTHENTICATION = "Invalid authentication: expected a JwtAuthenticationToken object"; public static final String ERROR_INVALID_TOKEN_NO_SCOPE = @@ -68,12 +72,10 @@ public class WlcgStructuredPathAuthorizationPdp public static final String ERROR_UNKNOWN_TOKEN_ISSUER = "Unknown token issuer: %s"; - public static final Set READONLY_METHODS = - Sets.newHashSet("GET", "OPTIONS", "HEAD", "PROPFIND"); - - public static final Set REPLACE_METHODS = Sets.newHashSet("PUT", "MKCOL"); - - public static final Set MODIFY_METHODS = Sets.newHashSet("PATCH", "DELETE"); + protected static final Set READONLY_METHODS = Sets.newHashSet("GET", "PROPFIND"); + protected static final Set REPLACE_METHODS = Sets.newHashSet("PUT", "MKCOL"); + protected static final Set MODIFY_METHODS = Sets.newHashSet("PATCH", "DELETE"); + protected static final Set CATCHALL_METHODS = Sets.newHashSet("HEAD", "OPTIONS"); public static final String COPY_METHOD = "COPY"; public static final String MOVE_METHOD = "MOVE"; @@ -125,39 +127,39 @@ public static Set resolveWlcgScopes(JwtAuthenticationToken token) { boolean filterMatcherByRequest(HttpServletRequest request, String method, StructuredPathScopeMatcher m, boolean requestedResourceExists) { - String requiredScope = null; + if (CATCHALL_METHODS.contains(method)) { + return ALL_STORAGE_SCOPES.stream().anyMatch(prefix -> m.getPrefix().equals(prefix)); + } if (READONLY_METHODS.contains(method)) { - requiredScope = STORAGE_READ; - } else if (REPLACE_METHODS.contains(method)) { + return m.getPrefix().equals(STORAGE_READ) || m.getPrefix().equals(STORAGE_STAGE); + } + if (REPLACE_METHODS.contains(method)) { if (requestedResourceExists) { - requiredScope = STORAGE_MODIFY; - } else { - requiredScope = STORAGE_CREATE; + return m.getPrefix().equals(STORAGE_MODIFY); } - } else if (MODIFY_METHODS.contains(method)) { - requiredScope = STORAGE_MODIFY; - } else if (COPY_METHOD.equals(method)) { - - requiredScope = STORAGE_READ; + return m.getPrefix().equals(STORAGE_CREATE); + } + if (MODIFY_METHODS.contains(method)) { + return m.getPrefix().equals(STORAGE_MODIFY); + } + if (COPY_METHOD.equals(method)) { if (isPullTpc(request, localUrlService)) { if (requestedResourceExists) { - requiredScope = STORAGE_MODIFY; - } else { - requiredScope = STORAGE_CREATE; + return m.getPrefix().equals(STORAGE_MODIFY); } + return m.getPrefix().equals(STORAGE_CREATE); } + return m.getPrefix().equals(STORAGE_READ); - } else if (MOVE_METHOD.equals(method)) { - requiredScope = STORAGE_MODIFY; } - if (isNull(requiredScope)) { - throw new IllegalArgumentException(format(ERROR_UNSUPPORTED_METHOD_PATTERN, method)); + if (MOVE_METHOD.equals(method)) { + return m.getPrefix().equals(STORAGE_MODIFY); } - return m.getPrefix().equals(requiredScope); + throw new IllegalArgumentException(format(ERROR_UNSUPPORTED_METHOD_PATTERN, method)); } @@ -186,7 +188,7 @@ public PathAuthorizationResult authorizeRequest(PathAuthorizationRequest authzRe if (isNull(sa)) { return indeterminate(ERROR_SA_NOT_FOUND); } - + final String tokenIssuer = jwtAuth.getToken().getIssuer().toString(); if (!sa.orgs().contains(tokenIssuer)) { diff --git a/src/test/java/org/italiangrid/storm/webdav/test/authz/pdp/ScopePathAuthzPdpTests.java b/src/test/java/org/italiangrid/storm/webdav/test/authz/pdp/ScopePathAuthzPdpTests.java index 215122ea..18b3dfe6 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/authz/pdp/ScopePathAuthzPdpTests.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/authz/pdp/ScopePathAuthzPdpTests.java @@ -55,7 +55,8 @@ @ExtendWith(MockitoExtension.class) public class ScopePathAuthzPdpTests { - public static final String[] READ_METHODS = {"GET", "PROPFIND", "OPTIONS", "HEAD"}; + public static final String[] CATCHALL_METHODS = {"HEAD", "OPTIONS"}; + public static final String[] READ_METHODS = {"GET", "PROPFIND"}; public static final String[] REPLACE_METHODS = {"PUT", "MKCOL"}; public static final String[] MODIFY_METHODS = {"DELETE", "PATCH"}; public static final String COPY_METHOD = "COPY"; @@ -100,7 +101,7 @@ public void setup() throws MalformedURLException { } @Test - public void invalidAuthentication() { + void invalidAuthentication() { Authentication auth = mock(Authentication.class); assertThrows(IllegalArgumentException.class, () -> { @@ -109,7 +110,7 @@ public void invalidAuthentication() { } @Test - public void noScopeClaimYeldsIndeterminate() throws Exception { + void noScopeClaimYeldsIndeterminate() { when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn(null); PathAuthorizationResult result = pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); @@ -119,7 +120,7 @@ public void noScopeClaimYeldsIndeterminate() throws Exception { } @Test - public void noSaYeldsIndeterminate() throws Exception { + void noSaYeldsIndeterminate() { when(pathResolver.resolveStorageArea("/test/example")).thenReturn(null); PathAuthorizationResult result = @@ -130,7 +131,7 @@ public void noSaYeldsIndeterminate() throws Exception { } @Test - public void noStorageScopesYeldsDeny() throws Exception { + void noStorageScopesYeldsDeny() { when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid profile storage.read"); PathAuthorizationResult result = @@ -141,9 +142,62 @@ public void noStorageScopesYeldsDeny() throws Exception { assertThat(result.getMessage().get(), containsString("Insufficient token scope")); } + @Test + void noStorageScopesYeldsDenyForCatchallMethods() { + when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid profile"); + + for (String m : CATCHALL_METHODS) { + when(request.getMethod()).thenReturn(m); + PathAuthorizationResult result = + pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); + + assertThat(result.getDecision(), is(Decision.INDETERMINATE)); + assertThat(result.getMessage().isPresent(), is(true)); + assertThat(result.getMessage().get(), containsString("Insufficient token scope")); + } + } @Test - public void readMethodsRequestsRequireStorageRead() throws Exception { + void catchallMethodsRequestsAtLeastOneStorageScope() { + when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.modify:/"); + + for (String m : CATCHALL_METHODS) { + when(request.getMethod()).thenReturn(m); + PathAuthorizationResult result = + pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); + assertThat(result.getDecision(), is(Decision.PERMIT)); + } + + when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.read:/"); + + for (String m : CATCHALL_METHODS) { + when(request.getMethod()).thenReturn(m); + PathAuthorizationResult result = + pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); + assertThat(result.getDecision(), is(Decision.PERMIT)); + } + + when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.create:/"); + + for (String m : CATCHALL_METHODS) { + when(request.getMethod()).thenReturn(m); + PathAuthorizationResult result = + pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); + assertThat(result.getDecision(), is(Decision.PERMIT)); + } + + when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.stage:/"); + + for (String m : CATCHALL_METHODS) { + when(request.getMethod()).thenReturn(m); + PathAuthorizationResult result = + pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); + assertThat(result.getDecision(), is(Decision.PERMIT)); + } + } + + @Test + void readMethodsRequestsRequireStorageReadOrStage() { when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.modify:/"); for (String m : READ_METHODS) { @@ -163,10 +217,19 @@ public void readMethodsRequestsRequireStorageRead() throws Exception { pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); assertThat(result.getDecision(), is(Decision.PERMIT)); } + + when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.stage:/"); + + for (String m : READ_METHODS) { + when(request.getMethod()).thenReturn(m); + PathAuthorizationResult result = + pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); + assertThat(result.getDecision(), is(Decision.PERMIT)); + } } @Test - public void replaceMethodsRequestsRequireStorageModifyOrCreate() throws Exception { + void replaceMethodsRequestsRequireStorageModifyOrCreate() { when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.read:/"); for (String m : REPLACE_METHODS) { @@ -210,7 +273,7 @@ public void replaceMethodsRequestsRequireStorageModifyOrCreate() throws Exceptio } @Test - public void modifyMethodsRequestsRequireStorageModifyOrCreate() throws Exception { + void modifyMethodsRequestsRequireStorageModifyOrCreate() { when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.read:/ storage.create:/"); for (String m : MODIFY_METHODS) { @@ -233,7 +296,7 @@ public void modifyMethodsRequestsRequireStorageModifyOrCreate() throws Exception } @Test - public void testLocalCopyRequiresStorageCreateOrModify() throws Exception { + void testLocalCopyRequiresStorageCreateOrModify() { when(request.getMethod()).thenReturn(COPY_METHOD); when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.modify:/"); @@ -250,7 +313,7 @@ public void testLocalCopyRequiresStorageCreateOrModify() throws Exception { } @Test - public void testPullTpcRequiresCreateOrModify() throws Exception { + void testPullTpcRequiresCreateOrModify() { when(request.getMethod()).thenReturn(COPY_METHOD); when(request.getHeader("Source")).thenReturn("https://remote.example/test/example"); when(pathResolver.pathExists("/test/example")).thenReturn(true); @@ -278,7 +341,7 @@ public void testPullTpcRequiresCreateOrModify() throws Exception { } @Test - public void testPushTpcRequiresRead() throws Exception { + void testPushTpcRequiresRead() { when(request.getMethod()).thenReturn(COPY_METHOD); when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.create:/ storage.modify:/"); PathAuthorizationResult result = @@ -294,7 +357,7 @@ public void testPushTpcRequiresRead() throws Exception { } @Test - public void testMoveRequiresModify() throws Exception { + void testMoveRequiresModify() { when(request.getMethod()).thenReturn(MOVE_METHOD); when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.read:/ storage.create:/"); PathAuthorizationResult result = @@ -310,7 +373,7 @@ public void testMoveRequiresModify() throws Exception { } @Test - public void testModifyImpliesCreate() throws Exception { + void testModifyImpliesCreate() { when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.read:/ storage.modify:/"); when(pathResolver.pathExists("/test/example")).thenReturn(false); @@ -323,7 +386,7 @@ public void testModifyImpliesCreate() throws Exception { } @Test - public void testUnsupportedMethod() throws Exception { + void testUnsupportedMethod() { when(request.getMethod()).thenReturn("TRACE"); assertThrows(IllegalArgumentException.class, () -> { pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); @@ -331,7 +394,7 @@ public void testUnsupportedMethod() throws Exception { } @Test - public void testPathAuthzIsEnforced() throws Exception { + void testPathAuthzIsEnforced() { when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.read:/subfolder"); when(request.getMethod()).thenReturn("GET"); PathAuthorizationResult result = @@ -347,7 +410,7 @@ public void testPathAuthzIsEnforced() throws Exception { } @Test - public void issuerChecksAreEnforced() throws Exception { + void issuerChecksAreEnforced() throws Exception { when(jwt.getIssuer()).thenReturn(new URL("https://unknown.example")); when(request.getMethod()).thenReturn("GET"); PathAuthorizationResult result = From 29ee3e77d5679624885720b925acad18198b6585 Mon Sep 17 00:00:00 2001 From: Enrico Vianello Date: Wed, 16 Oct 2024 23:45:53 +0200 Subject: [PATCH 04/22] Fix creation of non-existent parent directory authZ with WLCG scopes (#46) --- .../WlcgStructuredPathAuthorizationPdp.java | 35 ++-- .../util/StructuredPathScopeMatcher.java | 12 +- .../authz/pdp/ScopePathAuthzPdpTests.java | 171 ++++++++++++------ 3 files changed, 147 insertions(+), 71 deletions(-) diff --git a/src/main/java/org/italiangrid/storm/webdav/authz/pdp/WlcgStructuredPathAuthorizationPdp.java b/src/main/java/org/italiangrid/storm/webdav/authz/pdp/WlcgStructuredPathAuthorizationPdp.java index 5718da45..76db289c 100644 --- a/src/main/java/org/italiangrid/storm/webdav/authz/pdp/WlcgStructuredPathAuthorizationPdp.java +++ b/src/main/java/org/italiangrid/storm/webdav/authz/pdp/WlcgStructuredPathAuthorizationPdp.java @@ -59,6 +59,8 @@ public class WlcgStructuredPathAuthorizationPdp public static final String STORAGE_MODIFY = "storage.modify"; public static final String STORAGE_CREATE = "storage.create"; + protected static final Set READ_SCOPES = Sets.newHashSet(STORAGE_READ, STORAGE_STAGE); + protected static final Set WRITE_SCOPES = Sets.newHashSet(STORAGE_CREATE, STORAGE_MODIFY); protected static final Set ALL_STORAGE_SCOPES = Sets.newHashSet(STORAGE_READ, STORAGE_MODIFY, STORAGE_CREATE, STORAGE_STAGE); @@ -128,35 +130,35 @@ boolean filterMatcherByRequest(HttpServletRequest request, String method, StructuredPathScopeMatcher m, boolean requestedResourceExists) { if (CATCHALL_METHODS.contains(method)) { - return ALL_STORAGE_SCOPES.stream().anyMatch(prefix -> m.getPrefix().equals(prefix)); + return ALL_STORAGE_SCOPES.stream().anyMatch(prefix -> prefix.equals(m.getPrefix())); } if (READONLY_METHODS.contains(method)) { - return m.getPrefix().equals(STORAGE_READ) || m.getPrefix().equals(STORAGE_STAGE); + return READ_SCOPES.contains(m.getPrefix()); } if (REPLACE_METHODS.contains(method)) { if (requestedResourceExists) { - return m.getPrefix().equals(STORAGE_MODIFY); + return STORAGE_MODIFY.equals(m.getPrefix()); } - return m.getPrefix().equals(STORAGE_CREATE); + return WRITE_SCOPES.contains(m.getPrefix()); } if (MODIFY_METHODS.contains(method)) { - return m.getPrefix().equals(STORAGE_MODIFY); + return STORAGE_MODIFY.equals(m.getPrefix()); } if (COPY_METHOD.equals(method)) { if (isPullTpc(request, localUrlService)) { if (requestedResourceExists) { - return m.getPrefix().equals(STORAGE_MODIFY); + return STORAGE_MODIFY.equals(m.getPrefix()); } - return m.getPrefix().equals(STORAGE_CREATE); + return WRITE_SCOPES.contains(m.getPrefix()); } - return m.getPrefix().equals(STORAGE_READ); + return READ_SCOPES.contains(m.getPrefix()); } if (MOVE_METHOD.equals(method)) { - return m.getPrefix().equals(STORAGE_MODIFY); + return STORAGE_MODIFY.equals(m.getPrefix()); } throw new IllegalArgumentException(format(ERROR_UNSUPPORTED_METHOD_PATTERN, method)); @@ -212,10 +214,17 @@ public PathAuthorizationResult authorizeRequest(PathAuthorizationRequest authzRe final boolean requestedResourceExists = pathResolver.pathExists(requestPath); final String saPath = getStorageAreaPath(requestPath, sa); - scopeMatchers = scopeMatchers.stream() - .filter(m -> filterMatcherByRequest(request, method, m, requestedResourceExists)) - .filter(m -> m.matchesPath(saPath)) - .collect(toList()); + if ("MKCOL".equals(method)) { + scopeMatchers = scopeMatchers.stream() + .filter(m -> filterMatcherByRequest(request, method, m, requestedResourceExists)) + .filter(m -> m.matchesPathIncludingParents(saPath)) + .collect(toList()); + } else { + scopeMatchers = scopeMatchers.stream() + .filter(m -> filterMatcherByRequest(request, method, m, requestedResourceExists)) + .filter(m -> m.matchesPath(saPath)) + .collect(toList()); + } if (scopeMatchers.isEmpty()) { return deny(ERROR_INSUFFICIENT_TOKEN_SCOPE); diff --git a/src/main/java/org/italiangrid/storm/webdav/authz/util/StructuredPathScopeMatcher.java b/src/main/java/org/italiangrid/storm/webdav/authz/util/StructuredPathScopeMatcher.java index 1172db03..853519b0 100644 --- a/src/main/java/org/italiangrid/storm/webdav/authz/util/StructuredPathScopeMatcher.java +++ b/src/main/java/org/italiangrid/storm/webdav/authz/util/StructuredPathScopeMatcher.java @@ -19,6 +19,7 @@ import static com.google.common.base.Strings.isNullOrEmpty; import static java.util.Objects.nonNull; +import java.nio.file.Path; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -37,7 +38,7 @@ public class StructuredPathScopeMatcher implements ScopeMatcher { private static final String SEP_STR = SEP.toString(); private final String prefix; - private final String path; + private final Path path; private final Pattern prefixMatchPattern; private final Pattern pathMatchPattern; @@ -45,7 +46,7 @@ public class StructuredPathScopeMatcher implements ScopeMatcher { private StructuredPathScopeMatcher(String prefix, String path) { this.prefix = prefix; - this.path = path; + this.path = Path.of(path); final String prefixMatchRegexp = String.format("^%s%c", prefix, SEP); prefixMatchPattern = Pattern.compile(prefixMatchRegexp); @@ -75,6 +76,11 @@ public boolean matchesScope(String scope) { public boolean matchesPath(String path) { return pathMatchPattern.matcher(path).matches(); } + + public boolean matchesPathIncludingParents(String path) { + Path targetPath = Path.of(path); + return this.path.startsWith(targetPath) || matchesPath(path); + } public static StructuredPathScopeMatcher fromString(String scope) { final int sepIndex = scope.indexOf(SEP); @@ -135,7 +141,7 @@ public String getPrefix() { } public String getPath() { - return path; + return path.toString(); } } diff --git a/src/test/java/org/italiangrid/storm/webdav/test/authz/pdp/ScopePathAuthzPdpTests.java b/src/test/java/org/italiangrid/storm/webdav/test/authz/pdp/ScopePathAuthzPdpTests.java index 18b3dfe6..803f7f59 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/authz/pdp/ScopePathAuthzPdpTests.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/authz/pdp/ScopePathAuthzPdpTests.java @@ -21,6 +21,7 @@ import static org.italiangrid.storm.webdav.authz.pdp.PathAuthorizationRequest.newAuthorizationRequest; import static org.italiangrid.storm.webdav.authz.pdp.WlcgStructuredPathAuthorizationPdp.SCOPE_CLAIM; import static org.junit.Assert.assertThrows; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -95,6 +96,7 @@ public void setup() throws MalformedURLException { lenient().when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("storage.read:/"); lenient().when(request.getServletPath()).thenReturn("/"); lenient().when(request.getPathInfo()).thenReturn("test/example"); + lenient().when(sa.rootPath()).thenReturn("/storage"); lenient().when(sa.accessPoints()).thenReturn(Lists.newArrayList("/test")); lenient().when(sa.orgs()).thenReturn(Sets.newHashSet("https://issuer.example")); lenient().when(pathResolver.resolveStorageArea("/test/example")).thenReturn(sa); @@ -111,7 +113,7 @@ void invalidAuthentication() { @Test void noScopeClaimYeldsIndeterminate() { - when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn(null); + lenient().when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn(null); PathAuthorizationResult result = pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); assertThat(result.getDecision(), is(Decision.INDETERMINATE)); @@ -122,7 +124,7 @@ void noScopeClaimYeldsIndeterminate() { @Test void noSaYeldsIndeterminate() { - when(pathResolver.resolveStorageArea("/test/example")).thenReturn(null); + lenient().when(pathResolver.resolveStorageArea("/test/example")).thenReturn(null); PathAuthorizationResult result = pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); assertThat(result.getDecision(), is(Decision.INDETERMINATE)); @@ -132,7 +134,7 @@ void noSaYeldsIndeterminate() { @Test void noStorageScopesYeldsDeny() { - when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid profile storage.read"); + lenient().when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid profile storage.read"); PathAuthorizationResult result = pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); @@ -144,10 +146,10 @@ void noStorageScopesYeldsDeny() { @Test void noStorageScopesYeldsDenyForCatchallMethods() { - when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid profile"); + lenient().when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid profile"); for (String m : CATCHALL_METHODS) { - when(request.getMethod()).thenReturn(m); + lenient().when(request.getMethod()).thenReturn(m); PathAuthorizationResult result = pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); @@ -159,37 +161,37 @@ void noStorageScopesYeldsDenyForCatchallMethods() { @Test void catchallMethodsRequestsAtLeastOneStorageScope() { - when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.modify:/"); + lenient().when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.modify:/"); for (String m : CATCHALL_METHODS) { - when(request.getMethod()).thenReturn(m); + lenient().when(request.getMethod()).thenReturn(m); PathAuthorizationResult result = pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); assertThat(result.getDecision(), is(Decision.PERMIT)); } - when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.read:/"); + lenient().when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.read:/"); for (String m : CATCHALL_METHODS) { - when(request.getMethod()).thenReturn(m); + lenient().when(request.getMethod()).thenReturn(m); PathAuthorizationResult result = pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); assertThat(result.getDecision(), is(Decision.PERMIT)); } - when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.create:/"); + lenient().when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.create:/"); for (String m : CATCHALL_METHODS) { - when(request.getMethod()).thenReturn(m); + lenient().when(request.getMethod()).thenReturn(m); PathAuthorizationResult result = pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); assertThat(result.getDecision(), is(Decision.PERMIT)); } - when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.stage:/"); + lenient().when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.stage:/"); for (String m : CATCHALL_METHODS) { - when(request.getMethod()).thenReturn(m); + lenient().when(request.getMethod()).thenReturn(m); PathAuthorizationResult result = pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); assertThat(result.getDecision(), is(Decision.PERMIT)); @@ -198,10 +200,10 @@ void catchallMethodsRequestsAtLeastOneStorageScope() { @Test void readMethodsRequestsRequireStorageReadOrStage() { - when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.modify:/"); + lenient().when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.modify:/"); for (String m : READ_METHODS) { - when(request.getMethod()).thenReturn(m); + lenient().when(request.getMethod()).thenReturn(m); PathAuthorizationResult result = pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); assertThat(result.getDecision(), is(Decision.DENY)); @@ -209,19 +211,19 @@ void readMethodsRequestsRequireStorageReadOrStage() { assertThat(result.getMessage().get(), containsString("Insufficient token scope")); } - when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.read:/"); + lenient().when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.read:/"); for (String m : READ_METHODS) { - when(request.getMethod()).thenReturn(m); + lenient().when(request.getMethod()).thenReturn(m); PathAuthorizationResult result = pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); assertThat(result.getDecision(), is(Decision.PERMIT)); } - when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.stage:/"); + lenient().when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.stage:/"); for (String m : READ_METHODS) { - when(request.getMethod()).thenReturn(m); + lenient().when(request.getMethod()).thenReturn(m); PathAuthorizationResult result = pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); assertThat(result.getDecision(), is(Decision.PERMIT)); @@ -230,10 +232,10 @@ void readMethodsRequestsRequireStorageReadOrStage() { @Test void replaceMethodsRequestsRequireStorageModifyOrCreate() { - when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.read:/"); + lenient().when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.read:/"); for (String m : REPLACE_METHODS) { - when(request.getMethod()).thenReturn(m); + lenient().when(request.getMethod()).thenReturn(m); PathAuthorizationResult result = pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); assertThat(result.getDecision(), is(Decision.DENY)); @@ -241,11 +243,12 @@ void replaceMethodsRequestsRequireStorageModifyOrCreate() { assertThat(result.getMessage().get(), containsString("Insufficient token scope")); } - when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.read:/ storage.create:/"); - when(pathResolver.pathExists("/test/example")).thenReturn(true); + lenient().when(jwt.getClaimAsString(SCOPE_CLAIM)) + .thenReturn("openid storage.read:/ storage.create:/"); + lenient().when(pathResolver.pathExists("/test/example")).thenReturn(true); for (String m : REPLACE_METHODS) { - when(request.getMethod()).thenReturn(m); + lenient().when(request.getMethod()).thenReturn(m); PathAuthorizationResult result = pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); assertThat(result.getDecision(), is(Decision.DENY)); @@ -253,7 +256,7 @@ void replaceMethodsRequestsRequireStorageModifyOrCreate() { assertThat(result.getMessage().get(), containsString("Insufficient token scope")); } - when(pathResolver.pathExists("/test/example")).thenReturn(false); + lenient().when(pathResolver.pathExists("/test/example")).thenReturn(false); for (String m : REPLACE_METHODS) { when(request.getMethod()).thenReturn(m); @@ -262,10 +265,10 @@ void replaceMethodsRequestsRequireStorageModifyOrCreate() { assertThat(result.getDecision(), is(Decision.PERMIT)); } - when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.modify:/"); + lenient().when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.modify:/"); for (String m : REPLACE_METHODS) { - when(request.getMethod()).thenReturn(m); + lenient().when(request.getMethod()).thenReturn(m); PathAuthorizationResult result = pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); assertThat(result.getDecision(), is(Decision.PERMIT)); @@ -274,10 +277,11 @@ void replaceMethodsRequestsRequireStorageModifyOrCreate() { @Test void modifyMethodsRequestsRequireStorageModifyOrCreate() { - when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.read:/ storage.create:/"); + lenient().when(jwt.getClaimAsString(SCOPE_CLAIM)) + .thenReturn("openid storage.read:/ storage.create:/"); for (String m : MODIFY_METHODS) { - when(request.getMethod()).thenReturn(m); + lenient().when(request.getMethod()).thenReturn(m); PathAuthorizationResult result = pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); assertThat(result.getDecision(), is(Decision.DENY)); @@ -285,10 +289,11 @@ void modifyMethodsRequestsRequireStorageModifyOrCreate() { assertThat(result.getMessage().get(), containsString("Insufficient token scope")); } - when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.read:/ storage.modify:/"); + lenient().when(jwt.getClaimAsString(SCOPE_CLAIM)) + .thenReturn("openid storage.read:/ storage.modify:/"); for (String m : MODIFY_METHODS) { - when(request.getMethod()).thenReturn(m); + lenient().when(request.getMethod()).thenReturn(m); PathAuthorizationResult result = pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); assertThat(result.getDecision(), is(Decision.PERMIT)); @@ -298,8 +303,8 @@ void modifyMethodsRequestsRequireStorageModifyOrCreate() { @Test void testLocalCopyRequiresStorageCreateOrModify() { - when(request.getMethod()).thenReturn(COPY_METHOD); - when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.modify:/"); + lenient().when(request.getMethod()).thenReturn(COPY_METHOD); + lenient().when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.modify:/"); PathAuthorizationResult result = pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); @@ -307,17 +312,18 @@ void testLocalCopyRequiresStorageCreateOrModify() { assertThat(result.getMessage().isPresent(), is(true)); assertThat(result.getMessage().get(), containsString("Insufficient token scope")); - when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.read:/ storage.write:/"); + lenient().when(jwt.getClaimAsString(SCOPE_CLAIM)) + .thenReturn("openid storage.read:/ storage.write:/"); result = pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); assertThat(result.getDecision(), is(Decision.PERMIT)); } @Test void testPullTpcRequiresCreateOrModify() { - when(request.getMethod()).thenReturn(COPY_METHOD); - when(request.getHeader("Source")).thenReturn("https://remote.example/test/example"); - when(pathResolver.pathExists("/test/example")).thenReturn(true); - when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.read:/"); + lenient().when(request.getMethod()).thenReturn(COPY_METHOD); + lenient().when(request.getHeader("Source")).thenReturn("https://remote.example/test/example"); + lenient().when(pathResolver.pathExists("/test/example")).thenReturn(true); + lenient().when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.read:/"); PathAuthorizationResult result = pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); @@ -325,25 +331,26 @@ void testPullTpcRequiresCreateOrModify() { assertThat(result.getMessage().isPresent(), is(true)); assertThat(result.getMessage().get(), containsString("Insufficient token scope")); - when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.create:/"); + lenient().when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.create:/"); result = pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); assertThat(result.getDecision(), is(Decision.DENY)); assertThat(result.getMessage().isPresent(), is(true)); assertThat(result.getMessage().get(), containsString("Insufficient token scope")); - when(pathResolver.pathExists("/test/example")).thenReturn(false); + lenient().when(pathResolver.pathExists("/test/example")).thenReturn(false); result = pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); assertThat(result.getDecision(), is(Decision.PERMIT)); - when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.modify:/"); + lenient().when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.modify:/"); result = pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); assertThat(result.getDecision(), is(Decision.PERMIT)); } @Test void testPushTpcRequiresRead() { - when(request.getMethod()).thenReturn(COPY_METHOD); - when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.create:/ storage.modify:/"); + lenient().when(request.getMethod()).thenReturn(COPY_METHOD); + lenient().when(jwt.getClaimAsString(SCOPE_CLAIM)) + .thenReturn("openid storage.create:/ storage.modify:/"); PathAuthorizationResult result = pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); @@ -351,15 +358,16 @@ void testPushTpcRequiresRead() { assertThat(result.getMessage().isPresent(), is(true)); assertThat(result.getMessage().get(), containsString("Insufficient token scope")); - when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.read:/"); + lenient().when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.read:/"); result = pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); assertThat(result.getDecision(), is(Decision.PERMIT)); } @Test void testMoveRequiresModify() { - when(request.getMethod()).thenReturn(MOVE_METHOD); - when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.read:/ storage.create:/"); + lenient().when(request.getMethod()).thenReturn(MOVE_METHOD); + lenient().when(jwt.getClaimAsString(SCOPE_CLAIM)) + .thenReturn("openid storage.read:/ storage.create:/"); PathAuthorizationResult result = pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); @@ -367,15 +375,16 @@ void testMoveRequiresModify() { assertThat(result.getMessage().isPresent(), is(true)); assertThat(result.getMessage().get(), containsString("Insufficient token scope")); - when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.modify:/"); + lenient().when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.modify:/"); result = pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); assertThat(result.getDecision(), is(Decision.PERMIT)); } @Test void testModifyImpliesCreate() { - when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.read:/ storage.modify:/"); - when(pathResolver.pathExists("/test/example")).thenReturn(false); + lenient().when(jwt.getClaimAsString(SCOPE_CLAIM)) + .thenReturn("openid storage.read:/ storage.modify:/"); + lenient().when(pathResolver.pathExists("/test/example")).thenReturn(false); for (String m : REPLACE_METHODS) { when(request.getMethod()).thenReturn(m); @@ -387,7 +396,7 @@ void testModifyImpliesCreate() { @Test void testUnsupportedMethod() { - when(request.getMethod()).thenReturn("TRACE"); + lenient().when(request.getMethod()).thenReturn("TRACE"); assertThrows(IllegalArgumentException.class, () -> { pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); }); @@ -395,15 +404,15 @@ void testUnsupportedMethod() { @Test void testPathAuthzIsEnforced() { - when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.read:/subfolder"); - when(request.getMethod()).thenReturn("GET"); + lenient().when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.read:/subfolder"); + lenient().when(request.getMethod()).thenReturn("GET"); PathAuthorizationResult result = pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); assertThat(result.getDecision(), is(Decision.DENY)); assertThat(result.getMessage().isPresent(), is(true)); assertThat(result.getMessage().get(), containsString("Insufficient token scope")); - when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.read:/"); + lenient().when(jwt.getClaimAsString(SCOPE_CLAIM)).thenReturn("openid storage.read:/"); result = pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); assertThat(result.getDecision(), is(Decision.PERMIT)); @@ -411,12 +420,64 @@ void testPathAuthzIsEnforced() { @Test void issuerChecksAreEnforced() throws Exception { - when(jwt.getIssuer()).thenReturn(new URL("https://unknown.example")); - when(request.getMethod()).thenReturn("GET"); + lenient().when(jwt.getIssuer()).thenReturn(new URL("https://unknown.example")); + lenient().when(request.getMethod()).thenReturn("GET"); PathAuthorizationResult result = pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); assertThat(result.getDecision(), is(Decision.DENY)); assertThat(result.getMessage().isPresent(), is(true)); assertThat(result.getMessage().get(), containsString("Unknown token issuer")); } + + @Test + void parentDirCreationIsAllowedWithStorageCreateOrModify() { + + lenient().when(pathResolver.resolveStorageArea(anyString())).thenReturn(sa); + lenient().when(request.getPathInfo()).thenReturn("test/dir/subdir"); + lenient().when(jwt.getClaimAsString(SCOPE_CLAIM)) + .thenReturn("openid storage.create:/dir/subdir"); + lenient().when(request.getMethod()).thenReturn("MKCOL"); + PathAuthorizationResult result = + pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); + assertThat(result.getDecision(), is(Decision.PERMIT)); + + lenient().when(request.getPathInfo()).thenReturn("test/dir"); + result = pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); + assertThat(result.getDecision(), is(Decision.PERMIT)); + + lenient().when(jwt.getClaimAsString(SCOPE_CLAIM)) + .thenReturn("openid storage.modify:/dir/subdir"); + result = pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); + assertThat(result.getDecision(), is(Decision.PERMIT)); + + lenient().when(request.getPathInfo()).thenReturn("test/dir"); + result = pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); + assertThat(result.getDecision(), is(Decision.PERMIT)); + } + + @Test + void parentDirCreationIsNotAllowedWithWrongScopes() { + + lenient().when(pathResolver.resolveStorageArea(anyString())).thenReturn(sa); + lenient().when(request.getPathInfo()).thenReturn("test/dir/subdir"); + lenient().when(jwt.getClaimAsString(SCOPE_CLAIM)) + .thenReturn("openid storage.read:/dir/subdir"); + lenient().when(request.getMethod()).thenReturn("MKCOL"); + PathAuthorizationResult result = + pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); + assertThat(result.getDecision(), is(Decision.DENY)); + + lenient().when(request.getPathInfo()).thenReturn("test/dir"); + result = pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); + assertThat(result.getDecision(), is(Decision.DENY)); + + lenient().when(jwt.getClaimAsString(SCOPE_CLAIM)) + .thenReturn("openid storage.stage:/dir/subdir"); + result = pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); + assertThat(result.getDecision(), is(Decision.DENY)); + + lenient().when(request.getPathInfo()).thenReturn("test/dir"); + result = pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); + assertThat(result.getDecision(), is(Decision.DENY)); + } } From 7212135e63e23df55fb9d4c30967d094d6953a90 Mon Sep 17 00:00:00 2001 From: Luca Bassi Date: Thu, 11 Apr 2024 14:05:18 +0200 Subject: [PATCH 05/22] Add SciTags support --- doc/tpc.md | 14 +++ .../storm-webdav.service.d/storm-webdav.conf | 6 +- .../storm/webdav/scitag/SciTag.java | 49 +++++++++ .../storm/webdav/scitag/SciTagTransfer.java | 80 ++++++++++++++ .../webdav/server/servlet/MiltonFilter.java | 14 +++ .../webdav/server/servlet/SciTagFilter.java | 64 +++++++++++ .../webdav/server/servlet/StoRMServlet.java | 8 +- .../resource/StormResourceService.java | 20 ++++ .../storm/webdav/spring/AppConfig.java | 8 +- .../spring/web/ServletConfiguration.java | 21 +++- .../tpc/TpcPlainConnectionSocketFactory.java | 56 ++++++++++ .../tpc/TpcSSLConnectionSocketFactory.java | 46 ++++++++ .../storm/webdav/tpc/TransferFilter.java | 16 +-- .../webdav/tpc/TransferFilterSupport.java | 9 ++ .../webdav/tpc/http/HttpTransferClient.java | 21 +++- .../transfer/GetTransferRequestBuilder.java | 2 +- .../transfer/PutTransferRequestBuilder.java | 2 +- .../webdav/tpc/transfer/RequestBuilder.java | 9 ++ .../webdav/tpc/transfer/TransferRequest.java | 4 + .../transfer/impl/GetTransferRequestImpl.java | 7 +- .../transfer/impl/PutTransferRequestImpl.java | 6 +- .../transfer/impl/TransferRequestImpl.java | 11 +- src/main/resources/application.yml | 3 + .../webdav/test/tpc/PullTransferTest.java | 36 ++++++- .../webdav/test/tpc/PushTransferTest.java | 31 +++++- .../test/tpc/SciTagFilterActivationTest.java | 100 ++++++++++++++++++ .../test/tpc/TransferFilterTestSupport.java | 4 + .../webdav/test/tpc/http/ClientTest.java | 7 +- .../integration/TpcClientRedirectionTest.java | 5 +- .../http/integration/TpcIntegrationTest.java | 8 +- src/test/resources/application-authz-test.yml | 5 +- 31 files changed, 627 insertions(+), 45 deletions(-) create mode 100644 src/main/java/org/italiangrid/storm/webdav/scitag/SciTag.java create mode 100644 src/main/java/org/italiangrid/storm/webdav/scitag/SciTagTransfer.java create mode 100644 src/main/java/org/italiangrid/storm/webdav/server/servlet/SciTagFilter.java create mode 100644 src/main/java/org/italiangrid/storm/webdav/tpc/TpcPlainConnectionSocketFactory.java create mode 100644 src/main/java/org/italiangrid/storm/webdav/tpc/TpcSSLConnectionSocketFactory.java create mode 100644 src/test/java/org/italiangrid/storm/webdav/test/tpc/SciTagFilterActivationTest.java diff --git a/doc/tpc.md b/doc/tpc.md index d93e0274..da193ad3 100644 --- a/doc/tpc.md +++ b/doc/tpc.md @@ -56,3 +56,17 @@ STORM_WEBDAV_AUTHZ_SERVER_MAX_TOKEN_LIFETIME_SEC="43200" STORM_WEBDAV_REQUIRE_CLIENT_CERT="false" ``` For other configuration options, see the /etc/sysconfig/storm-webdav file. + +## SciTags + +StoRM WebDAV supports the `SciTag` header. +To correctly mark the network packets and/or network flows, you need to install [flowd](https://github.com/scitags/flowd) and configure it to use the `np_api` plugin. + +Example flowd configuration (`/etc/flowd/flowd.cfg`): + +``` +PLUGIN='np_api' +BACKEND='udp_firefly' +FLOW_MAP_API='https://www.scitags.org/api.json' +IP_DISCOVERY_ENABLED=True +``` diff --git a/etc/systemd/system/storm-webdav.service.d/storm-webdav.conf b/etc/systemd/system/storm-webdav.service.d/storm-webdav.conf index 5ecdd990..e91a3069 100644 --- a/etc/systemd/system/storm-webdav.service.d/storm-webdav.conf +++ b/etc/systemd/system/storm-webdav.service.d/storm-webdav.conf @@ -159,4 +159,8 @@ Environment="STORM_WEBDAV_TPC_MAX_CONNECTIONS_PER_ROUTE=25" # Cache entries lifetime, used if caching for VOMS certificate validation is enabled # Default: 300 -# Environment="STORM_WEBDAV_VOMS_CACHE_ENTRY_LIFETIME_SEC=300" \ No newline at end of file +# Environment="STORM_WEBDAV_VOMS_CACHE_ENTRY_LIFETIME_SEC=300" + +# Enable SciTags support +# Default: false +# Environment="STORM_WEBDAV_SCITAG_ENABLED=false" diff --git a/src/main/java/org/italiangrid/storm/webdav/scitag/SciTag.java b/src/main/java/org/italiangrid/storm/webdav/scitag/SciTag.java new file mode 100644 index 00000000..1d39d2d1 --- /dev/null +++ b/src/main/java/org/italiangrid/storm/webdav/scitag/SciTag.java @@ -0,0 +1,49 @@ +/** + * 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.scitag; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SciTag { + + public static final Logger LOG = LoggerFactory.getLogger(SciTag.class); + public static final String SCITAG_HEADER = "SciTag"; + public static final String SCITAG_ATTRIBUTE = "scitag"; + + private final int experimentId; + private final int activityId; + private final boolean remoteAddressIsSource; + + public SciTag(int experimentId, int activityId, boolean remoteAddressIsSource) { + this.experimentId = experimentId; + this.activityId = activityId; + this.remoteAddressIsSource = remoteAddressIsSource; + } + + public int experimentId() { + return experimentId; + } + + public int activityId() { + return activityId; + } + + public boolean remoteAddressIsSource() { + return remoteAddressIsSource; + } + +} diff --git a/src/main/java/org/italiangrid/storm/webdav/scitag/SciTagTransfer.java b/src/main/java/org/italiangrid/storm/webdav/scitag/SciTagTransfer.java new file mode 100644 index 00000000..5a99d53a --- /dev/null +++ b/src/main/java/org/italiangrid/storm/webdav/scitag/SciTagTransfer.java @@ -0,0 +1,80 @@ +/** + * 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.scitag; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SciTagTransfer { + + public static final Logger LOG = LoggerFactory.getLogger(SciTagTransfer.class); + public static final String SCITAG_TRANSFER_ATTRIBUTE = "scitagTransfer"; + + private static final String FLOWD_PIPE_NAME = "/var/run/flowd"; + private SciTag scitag; + private String sourceAddress; + private int sourcePort; + private String destinationAddress; + private int destinationPort; + private File flowdPipeFile; + + public SciTagTransfer(SciTag scitag, String localAddress, int localPort, String remoteAddress, + int remotePort) { + this(scitag, localAddress, localPort, remoteAddress, remotePort, new File(FLOWD_PIPE_NAME)); + } + + public SciTagTransfer(SciTag scitag, String localAddress, int localPort, String remoteAddress, + int remotePort, File flowdPipeFile) { + this.scitag = scitag; + if (scitag.remoteAddressIsSource()) { + this.sourceAddress = remoteAddress; + this.sourcePort = remotePort; + this.destinationAddress = localAddress; + this.destinationPort = localPort; + } else { + this.sourceAddress = localAddress; + this.sourcePort = localPort; + this.destinationAddress = remoteAddress; + this.destinationPort = remotePort; + } + this.flowdPipeFile = flowdPipeFile; + } + + private String flowdEntry() { + return " tcp " + sourceAddress + " " + sourcePort + " " + destinationAddress + " " + + destinationPort + " " + scitag.experimentId() + " " + scitag.activityId() + "\n"; + } + + public void writeStart() { + try (RandomAccessFile flowdPipe = new RandomAccessFile(flowdPipeFile, "rw")) { + flowdPipe.writeBytes("start" + this.flowdEntry()); + } catch (IOException e) { + LOG.warn(e.getMessage(), e); + } + } + + public void writeEnd() { + try (RandomAccessFile flowdPipe = new RandomAccessFile(flowdPipeFile, "rw")) { + flowdPipe.writeBytes("end" + this.flowdEntry()); + } catch (IOException e) { + LOG.warn(e.getMessage(), e); + } + } + +} diff --git a/src/main/java/org/italiangrid/storm/webdav/server/servlet/MiltonFilter.java b/src/main/java/org/italiangrid/storm/webdav/server/servlet/MiltonFilter.java index 666ea2a4..a413980b 100644 --- a/src/main/java/org/italiangrid/storm/webdav/server/servlet/MiltonFilter.java +++ b/src/main/java/org/italiangrid/storm/webdav/server/servlet/MiltonFilter.java @@ -38,6 +38,8 @@ import org.italiangrid.storm.webdav.milton.StoRMResourceFactory; import org.italiangrid.storm.webdav.milton.util.ReplaceContentStrategy; import org.italiangrid.storm.webdav.server.PathResolver; +import org.italiangrid.storm.webdav.scitag.SciTag; +import org.italiangrid.storm.webdav.scitag.SciTagTransfer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -133,11 +135,23 @@ public void doMilton(HttpServletRequest request, HttpServletResponse response) { Request miltonReq = new StoRMMiltonRequest(request, servletContext); Response miltonRes = new io.milton.servlet.ServletResponse(response); + SciTag scitag = (SciTag) request.getAttribute(SciTag.SCITAG_ATTRIBUTE); + if (scitag != null) { + SciTagTransfer scitagTransfer = new SciTagTransfer(scitag, request.getLocalAddr(), + request.getLocalPort(), request.getRemoteAddr(), request.getRemotePort()); + scitagTransfer.writeStart(); + request.setAttribute(SciTagTransfer.SCITAG_TRANSFER_ATTRIBUTE, scitagTransfer); + } miltonHTTPManager.process(miltonReq, miltonRes); } finally { MiltonServlet.clearThreadlocals(); + SciTagTransfer scitagTransfer = + (SciTagTransfer) request.getAttribute(SciTagTransfer.SCITAG_TRANSFER_ATTRIBUTE); + if (scitagTransfer != null) { + scitagTransfer.writeEnd(); + } try { diff --git a/src/main/java/org/italiangrid/storm/webdav/server/servlet/SciTagFilter.java b/src/main/java/org/italiangrid/storm/webdav/server/servlet/SciTagFilter.java new file mode 100644 index 00000000..f01c0af1 --- /dev/null +++ b/src/main/java/org/italiangrid/storm/webdav/server/servlet/SciTagFilter.java @@ -0,0 +1,64 @@ +/** + * 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.server.servlet; + +import java.io.IOException; +import java.util.Optional; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; + +import org.italiangrid.storm.webdav.scitag.SciTag; +import org.italiangrid.storm.webdav.tpc.TransferConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SciTagFilter implements Filter { + + public static final Logger logger = LoggerFactory.getLogger(SciTagFilter.class); + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + HttpServletRequest req = (HttpServletRequest) request; + if (req.getHeader(SciTag.SCITAG_HEADER) != null) { + Optional source = Optional.ofNullable(req.getHeader(TransferConstants.SOURCE_HEADER)); + boolean remoteAddressIsSource = + req.getMethod().equals("PUT") || (req.getMethod().equals("COPY") && source.isPresent()); + // state prot src_ip src_port dst_ip dst_port exp act + // If the active party receives an HTTP-TPC COPY request with a SciTag request header with + // a valid value then the server SHOULD mark the resulting network traffic with the + // experiment ID and activity ID encoded in the value. + int scitagValue = Integer.parseInt(req.getHeader(SciTag.SCITAG_HEADER)); + // Valid value is a single positive integer > 64 and <65536 (16bit). Any other value is + // considered invalid. + if (scitagValue > 64 && scitagValue < 65536) { + request.setAttribute(SciTag.SCITAG_ATTRIBUTE, + new SciTag(scitagValue >> 6, scitagValue & ((1 << 6) - 1), remoteAddressIsSource)); + } else { + // If the active party receives an HTTP-TPC COPY request with a SciTag request header + // with an invalid value then the server SHOULD mark the resulting network traffic with + // the 0 as the experiment ID and the activity ID. + request.setAttribute(SciTag.SCITAG_ATTRIBUTE, new SciTag(0, 0, remoteAddressIsSource)); + } + } + chain.doFilter(request, response); + } +} diff --git a/src/main/java/org/italiangrid/storm/webdav/server/servlet/StoRMServlet.java b/src/main/java/org/italiangrid/storm/webdav/server/servlet/StoRMServlet.java index 03f83598..68145aa7 100644 --- a/src/main/java/org/italiangrid/storm/webdav/server/servlet/StoRMServlet.java +++ b/src/main/java/org/italiangrid/storm/webdav/server/servlet/StoRMServlet.java @@ -34,7 +34,7 @@ public class StoRMServlet extends DefaultServlet { /** - * + * */ private static final long serialVersionUID = 4204673943980786498L; @@ -74,6 +74,12 @@ public Resource getResource(String pathInContext) { } + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + resourceService.doGet(request, response); + } + @Override protected void doHead(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { diff --git a/src/main/java/org/italiangrid/storm/webdav/server/servlet/resource/StormResourceService.java b/src/main/java/org/italiangrid/storm/webdav/server/servlet/resource/StormResourceService.java index 64684415..aa606090 100644 --- a/src/main/java/org/italiangrid/storm/webdav/server/servlet/resource/StormResourceService.java +++ b/src/main/java/org/italiangrid/storm/webdav/server/servlet/resource/StormResourceService.java @@ -18,6 +18,7 @@ import java.io.FileNotFoundException; import java.io.IOException; +import javax.servlet.ServletException; import javax.servlet.RequestDispatcher; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -25,6 +26,8 @@ import org.eclipse.jetty.http.HttpContent; import org.eclipse.jetty.server.ResourceService; import org.eclipse.jetty.util.URIUtil; +import org.italiangrid.storm.webdav.scitag.SciTag; +import org.italiangrid.storm.webdav.scitag.SciTagTransfer; public class StormResourceService extends ResourceService { @@ -50,6 +53,23 @@ private String pathInContext(HttpServletRequest request) { return URIUtil.addPaths(servletPath, pathInfo); } + @Override + public boolean doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + SciTag scitag = (SciTag) request.getAttribute(SciTag.SCITAG_ATTRIBUTE); + SciTagTransfer scitagTransfer = null; + if (scitag != null) { + scitagTransfer = new SciTagTransfer(scitag, request.getLocalAddr(), request.getLocalPort(), + request.getRemoteAddr(), request.getRemotePort()); + scitagTransfer.writeStart(); + } + boolean result = super.doGet(request, response); + if (scitagTransfer != null) { + scitagTransfer.writeEnd(); + } + return result; + } + public boolean doHead(HttpServletRequest request, HttpServletResponse response) throws IOException { diff --git a/src/main/java/org/italiangrid/storm/webdav/spring/AppConfig.java b/src/main/java/org/italiangrid/storm/webdav/spring/AppConfig.java index 22d43a01..5c502f86 100644 --- a/src/main/java/org/italiangrid/storm/webdav/spring/AppConfig.java +++ b/src/main/java/org/italiangrid/storm/webdav/spring/AppConfig.java @@ -45,8 +45,6 @@ import org.apache.http.conn.HttpClientConnectionManager; import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.conn.socket.LayeredConnectionSocketFactory; -import org.apache.http.conn.socket.PlainConnectionSocketFactory; -import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; @@ -92,6 +90,8 @@ import org.italiangrid.storm.webdav.server.util.CANLListener; import org.italiangrid.storm.webdav.tpc.LocalURLService; import org.italiangrid.storm.webdav.tpc.StaticHostListLocalURLService; +import org.italiangrid.storm.webdav.tpc.TpcPlainConnectionSocketFactory; +import org.italiangrid.storm.webdav.tpc.TpcSSLConnectionSocketFactory; import org.italiangrid.storm.webdav.tpc.TransferConstants; import org.italiangrid.storm.webdav.tpc.http.SuperLaxRedirectStrategy; import org.italiangrid.voms.util.CertificateValidatorBuilder; @@ -263,8 +263,8 @@ HttpClientConnectionManager tpcClientConnectionManager(ThirdPartyCopyProperties ctx.init(null, new TrustManager[] {tm}, null); } - ConnectionSocketFactory sf = PlainConnectionSocketFactory.getSocketFactory(); - LayeredConnectionSocketFactory tlsSf = new SSLConnectionSocketFactory(ctx); + ConnectionSocketFactory sf = TpcPlainConnectionSocketFactory.getSocketFactory(); + LayeredConnectionSocketFactory tlsSf = new TpcSSLConnectionSocketFactory(ctx); Registry r = RegistryBuilder.create() .register(HTTP, sf) diff --git a/src/main/java/org/italiangrid/storm/webdav/spring/web/ServletConfiguration.java b/src/main/java/org/italiangrid/storm/webdav/spring/web/ServletConfiguration.java index b6813811..261fac93 100644 --- a/src/main/java/org/italiangrid/storm/webdav/spring/web/ServletConfiguration.java +++ b/src/main/java/org/italiangrid/storm/webdav/spring/web/ServletConfiguration.java @@ -39,6 +39,7 @@ import org.italiangrid.storm.webdav.server.servlet.MiltonFilter; import org.italiangrid.storm.webdav.server.servlet.MoveRequestSanityChecksFilter; import org.italiangrid.storm.webdav.server.servlet.SAIndexServlet; +import org.italiangrid.storm.webdav.server.servlet.SciTagFilter; import org.italiangrid.storm.webdav.server.servlet.StoRMServlet; import org.italiangrid.storm.webdav.server.servlet.resource.StormResourceService; import org.italiangrid.storm.webdav.server.tracing.LogbackAccessAuthnInfoFilter; @@ -69,11 +70,12 @@ public class ServletConfiguration { public static final Logger LOG = LoggerFactory.getLogger(ServletConfiguration.class); static final int REQUEST_ID_FILTER_ORDER = DEFAULT_FILTER_ORDER + 1000; - static final int LOGBACK_ACCESS_FILTER_ORDER = DEFAULT_FILTER_ORDER + 1002; - static final int LOG_REQ_FILTER_ORDER = DEFAULT_FILTER_ORDER + 1003; - static final int REDIRECT_REQ_FILTER_ORDER = DEFAULT_FILTER_ORDER + 1004; - static final int CHECKSUM_FILTER_ORDER = DEFAULT_FILTER_ORDER + 1005; - static final int MACAROON_REQ_FILTER_ORDER = DEFAULT_FILTER_ORDER + 1006; + static final int LOGBACK_ACCESS_FILTER_ORDER = DEFAULT_FILTER_ORDER + 1001; + static final int LOG_REQ_FILTER_ORDER = DEFAULT_FILTER_ORDER + 1002; + static final int REDIRECT_REQ_FILTER_ORDER = DEFAULT_FILTER_ORDER + 1003; + static final int CHECKSUM_FILTER_ORDER = DEFAULT_FILTER_ORDER + 1004; + static final int MACAROON_REQ_FILTER_ORDER = DEFAULT_FILTER_ORDER + 1005; + static final int SCITAG_FILTER_ORDER = DEFAULT_FILTER_ORDER + 1006; static final int TPC_FILTER_ORDER = DEFAULT_FILTER_ORDER + 1007; static final int MOVE_FILTER_ORDER = DEFAULT_FILTER_ORDER + 1008; static final int DELETE_FILTER_ORDER = DEFAULT_FILTER_ORDER + 1009; @@ -151,6 +153,15 @@ FilterRegistrationBean macaroonRequestFilter(ObjectMapper return filter; } + @Bean + @ConditionalOnProperty(name = "storm.scitag.enabled", havingValue = "true") + FilterRegistrationBean scitagFilter() { + LOG.info("SciTag filter enabled"); + FilterRegistrationBean filter = new FilterRegistrationBean<>(new SciTagFilter()); + filter.setOrder(SCITAG_FILTER_ORDER); + return filter; + } + @Bean FilterRegistrationBean miltonFilter(FilesystemAccess fsAccess, ExtendedAttributesHelper attrsHelper, PathResolver resolver, ReplaceContentStrategy rcs) { diff --git a/src/main/java/org/italiangrid/storm/webdav/tpc/TpcPlainConnectionSocketFactory.java b/src/main/java/org/italiangrid/storm/webdav/tpc/TpcPlainConnectionSocketFactory.java new file mode 100644 index 00000000..04289a9d --- /dev/null +++ b/src/main/java/org/italiangrid/storm/webdav/tpc/TpcPlainConnectionSocketFactory.java @@ -0,0 +1,56 @@ +/** + * 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.tpc; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Socket; +import org.apache.http.HttpHost; +import org.apache.http.conn.socket.PlainConnectionSocketFactory; +import org.apache.http.protocol.HttpContext; +import org.italiangrid.storm.webdav.scitag.SciTag; +import org.italiangrid.storm.webdav.scitag.SciTagTransfer; + +public class TpcPlainConnectionSocketFactory extends PlainConnectionSocketFactory { + + public static final TpcPlainConnectionSocketFactory INSTANCE = + new TpcPlainConnectionSocketFactory(); + + public static TpcPlainConnectionSocketFactory getSocketFactory() { + return INSTANCE; + } + + public TpcPlainConnectionSocketFactory() { + super(); + } + + @Override + public Socket connectSocket(int connectTimeout, Socket socket, HttpHost host, + InetSocketAddress remoteAddress, InetSocketAddress localAddress, HttpContext context) + throws IOException { + Socket s = + super.connectSocket(connectTimeout, socket, host, remoteAddress, localAddress, context); + SciTag scitag = (SciTag) context.getAttribute(SciTag.SCITAG_ATTRIBUTE); + if (scitag != null) { + SciTagTransfer scitagTransfer = + new SciTagTransfer(scitag, s.getLocalAddress().getHostAddress(), s.getLocalPort(), + s.getInetAddress().getHostAddress(), s.getPort()); + scitagTransfer.writeStart(); + context.setAttribute(SciTagTransfer.SCITAG_TRANSFER_ATTRIBUTE, scitagTransfer); + } + return s; + } +} diff --git a/src/main/java/org/italiangrid/storm/webdav/tpc/TpcSSLConnectionSocketFactory.java b/src/main/java/org/italiangrid/storm/webdav/tpc/TpcSSLConnectionSocketFactory.java new file mode 100644 index 00000000..d7660cb6 --- /dev/null +++ b/src/main/java/org/italiangrid/storm/webdav/tpc/TpcSSLConnectionSocketFactory.java @@ -0,0 +1,46 @@ +/** + * 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.tpc; + +import java.io.IOException; +import java.net.Socket; +import javax.net.ssl.SSLContext; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.protocol.HttpContext; +import org.italiangrid.storm.webdav.scitag.SciTag; +import org.italiangrid.storm.webdav.scitag.SciTagTransfer; + +public class TpcSSLConnectionSocketFactory extends SSLConnectionSocketFactory { + + public TpcSSLConnectionSocketFactory(SSLContext sslContext) { + super(sslContext); + } + + @Override + public Socket createLayeredSocket(Socket socket, String target, int port, HttpContext context) + throws IOException { + Socket s = super.createLayeredSocket(socket, target, port, context); + SciTag scitag = (SciTag) context.getAttribute(SciTag.SCITAG_ATTRIBUTE); + if (scitag != null) { + SciTagTransfer scitagTransfer = + new SciTagTransfer(scitag, s.getLocalAddress().getHostAddress(), s.getLocalPort(), + s.getInetAddress().getHostAddress(), s.getPort()); + scitagTransfer.writeStart(); + context.setAttribute(SciTagTransfer.SCITAG_TRANSFER_ATTRIBUTE, scitagTransfer); + } + return s; + } +} diff --git a/src/main/java/org/italiangrid/storm/webdav/tpc/TransferFilter.java b/src/main/java/org/italiangrid/storm/webdav/tpc/TransferFilter.java index d2dec61d..7788865a 100644 --- a/src/main/java/org/italiangrid/storm/webdav/tpc/TransferFilter.java +++ b/src/main/java/org/italiangrid/storm/webdav/tpc/TransferFilter.java @@ -36,6 +36,7 @@ import org.apache.http.client.HttpResponseException; import org.italiangrid.storm.webdav.error.BadRequest; import org.italiangrid.storm.webdav.error.ResourceNotFound; +import org.italiangrid.storm.webdav.scitag.SciTag; import org.italiangrid.storm.webdav.server.PathResolver; import org.italiangrid.storm.webdav.server.tracing.RequestIdHolder; import org.italiangrid.storm.webdav.tpc.transfer.GetTransferRequest; @@ -108,10 +109,11 @@ protected void handleTpc(HttpServletRequest request, HttpServletResponse respons if (validRequest(request, response)) { Optional source = Optional.ofNullable(request.getHeader(SOURCE_HEADER)); + SciTag scitag = (SciTag) request.getAttribute(SciTag.SCITAG_ATTRIBUTE); if (source.isPresent()) { - handlePullCopy(request, response); + handlePullCopy(request, response, scitag); } else { - handlePushCopy(request, response); + handlePushCopy(request, response, scitag); } } @@ -210,8 +212,8 @@ protected void logTransferException(TransferRequest request, Exception e) { } } - protected void handlePullCopy(HttpServletRequest request, HttpServletResponse response) - throws IOException { + protected void handlePullCopy(HttpServletRequest request, HttpServletResponse response, + SciTag scitag) throws IOException { URI uri = URI.create(request.getHeader(SOURCE_HEADER)); String path = getScopedPathInfo(request); @@ -221,6 +223,7 @@ protected void handlePullCopy(HttpServletRequest request, HttpServletResponse re .uri(uri) .path(path) .headers(getTransferHeaders(request, response)) + .scitag(scitag) .verifyChecksum(verifyChecksum && verifyChecksumRequested(request)) .overwrite(overwriteRequested(request)) .build(); @@ -249,8 +252,8 @@ protected void handlePullCopy(HttpServletRequest request, HttpServletResponse re } } - protected void handlePushCopy(HttpServletRequest request, HttpServletResponse response) - throws IOException { + protected void handlePushCopy(HttpServletRequest request, HttpServletResponse response, + SciTag scitag) throws IOException { URI uri = URI.create(request.getHeader(DESTINATION_HEADER)); String path = getScopedPathInfo(request); @@ -259,6 +262,7 @@ protected void handlePushCopy(HttpServletRequest request, HttpServletResponse re .uri(uri) .path(path) .headers(getTransferHeaders(request, response)) + .scitag(scitag) .verifyChecksum(verifyChecksum && verifyChecksumRequested(request)) .overwrite(overwriteRequested(request)) .build(); diff --git a/src/main/java/org/italiangrid/storm/webdav/tpc/TransferFilterSupport.java b/src/main/java/org/italiangrid/storm/webdav/tpc/TransferFilterSupport.java index 5a087e9e..54046d8e 100644 --- a/src/main/java/org/italiangrid/storm/webdav/tpc/TransferFilterSupport.java +++ b/src/main/java/org/italiangrid/storm/webdav/tpc/TransferFilterSupport.java @@ -34,6 +34,7 @@ import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpResponseException; +import org.italiangrid.storm.webdav.scitag.SciTag; import org.italiangrid.storm.webdav.server.PathResolver; import org.italiangrid.storm.webdav.tpc.transfer.TransferRequest; import org.italiangrid.storm.webdav.tpc.transfer.TransferStatus; @@ -87,6 +88,14 @@ protected Multimap getTransferHeaders(HttpServletRequest request LOG.warn("Ignoring invalid transfer header {}", headerName); continue; } + if (xferHeaderName.trim().equalsIgnoreCase(SciTag.SCITAG_HEADER) + && request.getHeader(SciTag.SCITAG_HEADER) != null) { + // If the active party receives an HTTP-TPC COPY request with both a SciTag request header + // and a TransferHeaderSciTag request header then it SHOULD ignore the + // TransferHeaderSciTag and continue to process the request. + LOG.warn("Ignoring TransferHeaderSciTag header because SciTag header is present"); + continue; + } xferHeaders.put(xferHeaderName.trim(), request.getHeader(headerName)); } } diff --git a/src/main/java/org/italiangrid/storm/webdav/tpc/http/HttpTransferClient.java b/src/main/java/org/italiangrid/storm/webdav/tpc/http/HttpTransferClient.java index f89f0154..87cad35d 100644 --- a/src/main/java/org/italiangrid/storm/webdav/tpc/http/HttpTransferClient.java +++ b/src/main/java/org/italiangrid/storm/webdav/tpc/http/HttpTransferClient.java @@ -39,11 +39,14 @@ import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpHead; import org.apache.http.client.methods.HttpPut; +import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.impl.client.CloseableHttpClient; import org.italiangrid.storm.webdav.config.ServiceConfigurationProperties; import org.italiangrid.storm.webdav.config.ThirdPartyCopyProperties; import org.italiangrid.storm.webdav.fs.attrs.ExtendedAttributesHelper; import org.italiangrid.storm.webdav.server.PathResolver; +import org.italiangrid.storm.webdav.scitag.SciTag; +import org.italiangrid.storm.webdav.scitag.SciTagTransfer; import org.italiangrid.storm.webdav.tpc.transfer.GetTransferRequest; import org.italiangrid.storm.webdav.tpc.transfer.PutTransferRequest; import org.italiangrid.storm.webdav.tpc.transfer.TransferClient; @@ -165,6 +168,7 @@ public void handle(GetTransferRequest request, TransferStatusCallback cb) { StormCountingOutputStream os = prepareOutputStream(resolver.resolvePath(request.path())); HttpGet get = prepareRequest(request); + HttpClientContext context = HttpClientContext.create(); ScheduledFuture reportTask = executorService.scheduleAtFixedRate(() -> { reportStatus(cb, request, statusBuilder.inProgress(os.getCount())); @@ -172,8 +176,9 @@ public void handle(GetTransferRequest request, TransferStatusCallback cb) { try { + context.setAttribute(SciTag.SCITAG_ATTRIBUTE, request.scitag()); httpClient.execute(get, new GetResponseHandler(request, os, attributesHelper, - MDC.getCopyOfContextMap(), socketBufferSize, true)); + MDC.getCopyOfContextMap(), socketBufferSize, true), context); reportTask.cancel(true); reportStatus(cb, request, statusBuilder.done(os.getCount())); @@ -196,6 +201,11 @@ public void handle(GetTransferRequest request, TransferStatusCallback cb) { if (!reportTask.isCancelled()) { reportTask.cancel(true); } + SciTagTransfer scitagTransfer = + (SciTagTransfer) context.getAttribute(SciTagTransfer.SCITAG_TRANSFER_ATTRIBUTE); + if (scitagTransfer != null) { + scitagTransfer.writeEnd(); + } } } @@ -221,6 +231,7 @@ public void handle(PutTransferRequest request, TransferStatusCallback cb) { CountingFileEntity cfe = prepareFileEntity(resolver.resolvePath(request.path())); HttpPut put = null; + HttpClientContext context = HttpClientContext.create(); try { put = prepareRequest(request, cfe); @@ -236,7 +247,8 @@ public void handle(PutTransferRequest request, TransferStatusCallback cb) { try { checkOverwrite(request); - httpClient.execute(put, new PutResponseHandler(MDC.getCopyOfContextMap())); + context.setAttribute(SciTag.SCITAG_ATTRIBUTE, request.scitag()); + httpClient.execute(put, new PutResponseHandler(MDC.getCopyOfContextMap()), context); reportTask.cancel(true); reportStatus(cb, request, statusBuilder.done(cfe.getCount())); } catch (HttpResponseException e) { @@ -255,6 +267,11 @@ public void handle(PutTransferRequest request, TransferStatusCallback cb) { if (!reportTask.isCancelled()) { reportTask.cancel(true); } + SciTagTransfer scitagTransfer = + (SciTagTransfer) context.getAttribute(SciTagTransfer.SCITAG_TRANSFER_ATTRIBUTE); + if (scitagTransfer != null) { + scitagTransfer.writeEnd(); + } } } diff --git a/src/main/java/org/italiangrid/storm/webdav/tpc/transfer/GetTransferRequestBuilder.java b/src/main/java/org/italiangrid/storm/webdav/tpc/transfer/GetTransferRequestBuilder.java index a993ff9e..ea9107ec 100644 --- a/src/main/java/org/italiangrid/storm/webdav/tpc/transfer/GetTransferRequestBuilder.java +++ b/src/main/java/org/italiangrid/storm/webdav/tpc/transfer/GetTransferRequestBuilder.java @@ -21,7 +21,7 @@ public class GetTransferRequestBuilder extends RequestBuilder { String uuid; @@ -28,6 +30,8 @@ public abstract class RequestBuilder { URI uri; + SciTag scitag; + boolean verifyChecksum = true; boolean overwrite = true; @@ -63,6 +67,11 @@ public RequestBuilder addHeader(String header, String value) { return this; } + public RequestBuilder scitag(SciTag scitag) { + this.scitag = scitag; + return this; + } + public RequestBuilder overwrite(boolean o) { overwrite = o; return this; diff --git a/src/main/java/org/italiangrid/storm/webdav/tpc/transfer/TransferRequest.java b/src/main/java/org/italiangrid/storm/webdav/tpc/transfer/TransferRequest.java index 5b149147..70e480e4 100644 --- a/src/main/java/org/italiangrid/storm/webdav/tpc/transfer/TransferRequest.java +++ b/src/main/java/org/italiangrid/storm/webdav/tpc/transfer/TransferRequest.java @@ -22,6 +22,8 @@ import com.google.common.collect.Multimap; +import org.italiangrid.storm.webdav.scitag.SciTag; + public interface TransferRequest { String uuid(); @@ -32,6 +34,8 @@ public interface TransferRequest { Multimap transferHeaders(); + SciTag scitag(); + boolean verifyChecksum(); boolean overwrite(); diff --git a/src/main/java/org/italiangrid/storm/webdav/tpc/transfer/impl/GetTransferRequestImpl.java b/src/main/java/org/italiangrid/storm/webdav/tpc/transfer/impl/GetTransferRequestImpl.java index 7cb4e2e5..f0368061 100644 --- a/src/main/java/org/italiangrid/storm/webdav/tpc/transfer/impl/GetTransferRequestImpl.java +++ b/src/main/java/org/italiangrid/storm/webdav/tpc/transfer/impl/GetTransferRequestImpl.java @@ -19,6 +19,7 @@ import java.net.URI; +import org.italiangrid.storm.webdav.scitag.SciTag; import org.italiangrid.storm.webdav.tpc.transfer.GetTransferRequest; import com.google.common.collect.Multimap; @@ -26,9 +27,9 @@ public class GetTransferRequestImpl extends TransferRequestImpl implements GetTransferRequest { public GetTransferRequestImpl(String uuid, String path, URI uri, - Multimap xferHeaders, - boolean verifyChecksum, boolean overwrite) { - super(uuid, path, uri, xferHeaders, verifyChecksum, overwrite); + Multimap xferHeaders, SciTag scitag, boolean verifyChecksum, + boolean overwrite) { + super(uuid, path, uri, xferHeaders, scitag, verifyChecksum, overwrite); } @Override diff --git a/src/main/java/org/italiangrid/storm/webdav/tpc/transfer/impl/PutTransferRequestImpl.java b/src/main/java/org/italiangrid/storm/webdav/tpc/transfer/impl/PutTransferRequestImpl.java index b2f4d37c..60f273eb 100644 --- a/src/main/java/org/italiangrid/storm/webdav/tpc/transfer/impl/PutTransferRequestImpl.java +++ b/src/main/java/org/italiangrid/storm/webdav/tpc/transfer/impl/PutTransferRequestImpl.java @@ -19,6 +19,7 @@ import java.net.URI; +import org.italiangrid.storm.webdav.scitag.SciTag; import org.italiangrid.storm.webdav.tpc.transfer.PutTransferRequest; import com.google.common.collect.Multimap; @@ -26,8 +27,9 @@ public class PutTransferRequestImpl extends TransferRequestImpl implements PutTransferRequest { public PutTransferRequestImpl(String uuid, String path, URI uri, - Multimap xferHeaders, boolean verifyChecksum, boolean overwrite) { - super(uuid, path, uri, xferHeaders, verifyChecksum, overwrite); + Multimap xferHeaders, SciTag scitag, boolean verifyChecksum, + boolean overwrite) { + super(uuid, path, uri, xferHeaders, scitag, verifyChecksum, overwrite); } @Override diff --git a/src/main/java/org/italiangrid/storm/webdav/tpc/transfer/impl/TransferRequestImpl.java b/src/main/java/org/italiangrid/storm/webdav/tpc/transfer/impl/TransferRequestImpl.java index a1d29eb4..737ac992 100644 --- a/src/main/java/org/italiangrid/storm/webdav/tpc/transfer/impl/TransferRequestImpl.java +++ b/src/main/java/org/italiangrid/storm/webdav/tpc/transfer/impl/TransferRequestImpl.java @@ -21,6 +21,7 @@ import java.util.Objects; import java.util.Optional; +import org.italiangrid.storm.webdav.scitag.SciTag; import org.italiangrid.storm.webdav.tpc.transfer.TransferRequest; import org.italiangrid.storm.webdav.tpc.transfer.TransferStatus; import org.italiangrid.storm.webdav.tpc.transfer.TransferStatus.Status; @@ -40,6 +41,8 @@ public abstract class TransferRequestImpl implements TransferRequest { final Multimap xferHeaders; + final SciTag scitag; + final boolean verifyChecksum; final boolean overwrite; @@ -51,12 +54,13 @@ public abstract class TransferRequestImpl implements TransferRequest { private Optional lastTransferStatus = Optional.empty(); TransferRequestImpl(String uuid, String path, URI uri, Multimap xferHeaders, - boolean verifyChecksum, boolean overwrite) { + SciTag scitag, boolean verifyChecksum, boolean overwrite) { this.uuid = uuid; this.path = path; this.uri = uri; this.xferHeaders = xferHeaders; + this.scitag = scitag; this.verifyChecksum = verifyChecksum; this.overwrite = overwrite; } @@ -76,6 +80,11 @@ public Multimap transferHeaders() { return xferHeaders; } + @Override + public SciTag scitag() { + return scitag; + } + @Override public boolean verifyChecksum() { return verifyChecksum; diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 4db620d9..e18c7e51 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -85,6 +85,9 @@ storm: macaroon-filter: enabled: ${STORM_WEBDAV_MACAROON_FILTER_ENABLED:true} + scitag: + enabled: ${STORM_WEBDAV_SCITAG_ENABLED:false} + server: # Jetty Thread-Pool maximum idle time (in milliseconds) max-idle-time-msec: ${STORM_WEBDAV_SERVER_MAX_IDLE_TIME:3600000} diff --git a/src/test/java/org/italiangrid/storm/webdav/test/tpc/PullTransferTest.java b/src/test/java/org/italiangrid/storm/webdav/test/tpc/PullTransferTest.java index 3c7e84a6..1eaee85c 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/tpc/PullTransferTest.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/tpc/PullTransferTest.java @@ -104,9 +104,11 @@ void checkTransferHeaderPassing() throws IOException, ServletException { .thenReturn(TRANSFER_HEADER_AUTHORIZATION_VALUE); when(request.getHeader(TRANSFER_HEADER_WHATEVER_KEY)) .thenReturn(TRANSFER_HEADER_WHATEVER_VALUE); + when(request.getHeader(TRANSFER_HEADER_SCITAG)).thenReturn(SCITAG_HEADER_VALUE); + when(request.getHeader(SCITAG_HEADER)).thenReturn(null); - when(request.getHeaderNames()).thenReturn( - enumeration(asList(TRANSFER_HEADER_AUTHORIZATION_KEY, TRANSFER_HEADER_WHATEVER_KEY))); + when(request.getHeaderNames()).thenReturn(enumeration( + asList(TRANSFER_HEADER_AUTHORIZATION_KEY, TRANSFER_HEADER_WHATEVER_KEY, TRANSFER_HEADER_SCITAG))); filter.doFilter(request, response, chain); verify(client).handle(getXferRequest.capture(), Mockito.any()); @@ -118,18 +120,20 @@ void checkTransferHeaderPassing() throws IOException, ServletException { Multimap xferHeaders = getXferRequest.getValue().transferHeaders(); - assertThat(xferHeaders.size(), is(2)); + assertThat(xferHeaders.size(), is(3)); assertThat(xferHeaders.containsKey("Authorization"), is(true)); assertThat(xferHeaders.get("Authorization").iterator().next(), is(TRANSFER_HEADER_AUTHORIZATION_VALUE)); assertThat(xferHeaders.containsKey("Whatever"), is(true)); assertThat(xferHeaders.get("Whatever").iterator().next(), is(TRANSFER_HEADER_WHATEVER_VALUE)); + assertThat(xferHeaders.containsKey("SciTag"), is(true)); + assertThat(xferHeaders.get("SciTag").iterator().next(), is(SCITAG_HEADER_VALUE)); } @Test void emptyTransferHeaderAreIgnored() throws IOException, ServletException { - when(request.getHeaderNames()) - .thenReturn(enumeration(asList(TRANSFER_HEADER, TRANSFER_HEADER_WHATEVER_KEY))); + when(request.getHeaderNames()).thenReturn( + enumeration(asList(TRANSFER_HEADER, TRANSFER_HEADER_WHATEVER_KEY))); when(request.getHeader(TRANSFER_HEADER_WHATEVER_KEY)) .thenReturn(TRANSFER_HEADER_WHATEVER_VALUE); @@ -150,4 +154,26 @@ void emptyTransferHeaderAreIgnored() throws IOException, ServletException { assertThat(xferHeaders.get("Whatever").iterator().next(), is(TRANSFER_HEADER_WHATEVER_VALUE)); } + @Test + void bothSciTagAndTransferHeaderSciTag() throws IOException, ServletException { + when(request.getHeaderNames()) + .thenReturn(enumeration(asList(SCITAG_HEADER, TRANSFER_HEADER_SCITAG))); + + when(request.getHeader(SCITAG_HEADER)).thenReturn(SCITAG_HEADER_VALUE); + + filter.doFilter(request, response, chain); + verify(client).handle(getXferRequest.capture(), Mockito.any()); + + assertThat(getXferRequest.getValue().path(), is(FULL_LOCAL_PATH)); + assertThat(getXferRequest.getValue().remoteURI(), is(HTTP_URL_URI)); + assertThat(getXferRequest.getValue().overwrite(), is(true)); + assertThat(getXferRequest.getValue().verifyChecksum(), is(true)); + + + Multimap xferHeaders = getXferRequest.getValue().transferHeaders(); + assertThat(xferHeaders.size(), is(0)); + + assertThat(xferHeaders.containsKey("SciTag"), is(false)); + } + } diff --git a/src/test/java/org/italiangrid/storm/webdav/test/tpc/PushTransferTest.java b/src/test/java/org/italiangrid/storm/webdav/test/tpc/PushTransferTest.java index e70feeb0..6569e33a 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/tpc/PushTransferTest.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/tpc/PushTransferTest.java @@ -113,8 +113,8 @@ void checkTransferHeaderPassing() throws IOException, ServletException { when(request.getHeader(TRANSFER_HEADER_WHATEVER_KEY)) .thenReturn(TRANSFER_HEADER_WHATEVER_VALUE); - when(request.getHeaderNames()).thenReturn( - enumeration(asList(TRANSFER_HEADER_AUTHORIZATION_KEY, TRANSFER_HEADER_WHATEVER_KEY))); + when(request.getHeaderNames()).thenReturn(enumeration( + asList(TRANSFER_HEADER_AUTHORIZATION_KEY, TRANSFER_HEADER_WHATEVER_KEY, SCITAG_HEADER))); filter.doFilter(request, response, chain); verify(client).handle(putXferRequest.capture(), Mockito.any()); @@ -132,12 +132,13 @@ void checkTransferHeaderPassing() throws IOException, ServletException { is(TRANSFER_HEADER_AUTHORIZATION_VALUE)); assertThat(xferHeaders.containsKey("Whatever"), is(true)); assertThat(xferHeaders.get("Whatever").iterator().next(), is(TRANSFER_HEADER_WHATEVER_VALUE)); + assertThat(xferHeaders.containsKey("SciTag"), is(false)); } @Test void emptyTransferHeaderAreIgnored() throws IOException, ServletException { - when(request.getHeaderNames()) - .thenReturn(enumeration(asList(TRANSFER_HEADER, TRANSFER_HEADER_WHATEVER_KEY))); + when(request.getHeaderNames()).thenReturn( + enumeration(asList(TRANSFER_HEADER, TRANSFER_HEADER_WHATEVER_KEY))); when(request.getHeader(TRANSFER_HEADER_WHATEVER_KEY)) .thenReturn(TRANSFER_HEADER_WHATEVER_VALUE); @@ -207,4 +208,26 @@ void checkExpectContinueHeaderIsNotSet() throws IOException, ServletException { assertThat(xferHeaders.containsKey(EXPECTED_HEADER), is(false)); } + + @Test + void bothSciTagAndTransferHeaderSciTag() throws IOException, ServletException { + when(request.getHeaderNames()) + .thenReturn(enumeration(asList(SCITAG_HEADER, TRANSFER_HEADER_SCITAG))); + + when(request.getHeader(SCITAG_HEADER)).thenReturn(SCITAG_HEADER_VALUE); + + filter.doFilter(request, response, chain); + verify(client).handle(putXferRequest.capture(), Mockito.any()); + + assertThat(putXferRequest.getValue().path(), is(FULL_LOCAL_PATH)); + assertThat(putXferRequest.getValue().remoteURI(), is(HTTPS_URL_URI)); + assertThat(putXferRequest.getValue().overwrite(), is(true)); + assertThat(putXferRequest.getValue().verifyChecksum(), is(true)); + + + Multimap xferHeaders = putXferRequest.getValue().transferHeaders(); + assertThat(xferHeaders.size(), is(0)); + + assertThat(xferHeaders.containsKey("SciTag"), is(false)); + } } diff --git a/src/test/java/org/italiangrid/storm/webdav/test/tpc/SciTagFilterActivationTest.java b/src/test/java/org/italiangrid/storm/webdav/test/tpc/SciTagFilterActivationTest.java new file mode 100644 index 00000000..4a87ae05 --- /dev/null +++ b/src/test/java/org/italiangrid/storm/webdav/test/tpc/SciTagFilterActivationTest.java @@ -0,0 +1,100 @@ +/** + * 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.test.tpc; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.io.IOException; + +import javax.servlet.ServletException; + +import org.italiangrid.storm.webdav.config.StorageAreaInfo; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.io.TempDir; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.italiangrid.storm.webdav.server.servlet.SciTagFilter; +import org.italiangrid.storm.webdav.scitag.SciTag; +import org.italiangrid.storm.webdav.scitag.SciTagTransfer; + +@ExtendWith(MockitoExtension.class) +class SciTagFilterActivationTest extends TransferFilterTestSupport { + @TempDir + File tempDir; + + SciTagFilter sciTagFilter; + + @Mock + StorageAreaInfo testSa; + + @Mock + StorageAreaInfo otherSa; + + @Override + @BeforeEach + public void setup() throws IOException { + super.setup(); + sciTagFilter = new SciTagFilter(); + lenient().when(request.getServletPath()).thenReturn(SERVLET_PATH); + lenient().when(request.getPathInfo()).thenReturn(LOCAL_PATH); + lenient().when(response.getWriter()).thenReturn(responseWriter); + lenient().when(resolver.resolveStorageArea(FULL_LOCAL_PATH)).thenReturn(testSa); + lenient().when(resolver.resolveStorageArea("/test/otherfile")).thenReturn(testSa); + lenient().when(resolver.resolveStorageArea("/other/file")).thenReturn(otherSa); + lenient().when(request.getHeader(SOURCE_HEADER)).thenReturn(null); + } + + @Test + void requestWithScitag() throws IOException, ServletException { + when(request.getMethod()).thenReturn("GET"); + when(request.getHeader(SCITAG_HEADER)).thenReturn("66"); + + sciTagFilter.doFilter(request, response, chain); + verify(chain).doFilter(request, response); + verify(request).setAttribute(eq(SciTag.SCITAG_ATTRIBUTE), any(SciTag.class)); + } + + @Test + void requestWithoutScitag() throws IOException, ServletException { + sciTagFilter.doFilter(request, response, chain); + verify(chain).doFilter(request, response); + verify(request, times(0)).setAttribute(eq(SciTag.SCITAG_ATTRIBUTE), any(SciTag.class)); + } + + @Test + void testSciTagWrite() { + SciTag scitag = new SciTag(1, 2, false); + assertThat(scitag.experimentId(), is(1)); + assertThat(scitag.activityId(), is(2)); + assertThat(scitag.remoteAddressIsSource(), is(false)); + File mockFile = new File(tempDir, "flowd"); + SciTagTransfer scitagTransfer = + new SciTagTransfer(scitag, "10.10.10.10", 8443, "10.10.10.11", 12345, mockFile); + scitagTransfer.writeStart(); + scitagTransfer.writeEnd(); + } + +} diff --git a/src/test/java/org/italiangrid/storm/webdav/test/tpc/TransferFilterTestSupport.java b/src/test/java/org/italiangrid/storm/webdav/test/tpc/TransferFilterTestSupport.java index 1702f996..6733e0e5 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/tpc/TransferFilterTestSupport.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/tpc/TransferFilterTestSupport.java @@ -67,6 +67,10 @@ public class TransferFilterTestSupport implements TransferConstants { public static final String TRANSFER_HEADER_WHATEVER_KEY = "TransferHeaderWhatever"; public static final String TRANSFER_HEADER_WHATEVER_VALUE = "papisilviobelluscona"; + public static final String SCITAG_HEADER = "SciTag"; + public static final String SCITAG_HEADER_VALUE = "65"; + public static final String TRANSFER_HEADER_SCITAG = "TransferHeaderSciTag"; + public static final URI HTTP_URL_URI = URI.create(HTTP_URL); public static final URI HTTPS_URL_URI = URI.create(HTTPS_URL); diff --git a/src/test/java/org/italiangrid/storm/webdav/test/tpc/http/ClientTest.java b/src/test/java/org/italiangrid/storm/webdav/test/tpc/http/ClientTest.java index 4cad306d..353df6ba 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/tpc/http/ClientTest.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/tpc/http/ClientTest.java @@ -30,6 +30,7 @@ import org.apache.http.client.ResponseHandler; import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.protocol.HttpClientContext; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -74,7 +75,8 @@ public void testClientCorrectlyBuildsHttpRequestNoHeaders() throws IOException { }); verify(httpClient).execute(getRequest.capture(), - ArgumentMatchers.>any()); + ArgumentMatchers.>any(), + ArgumentMatchers.any()); HttpGet httpGetReq = getRequest.getValue(); @@ -92,7 +94,8 @@ public void testClientCorrectlyBuildsHttpRequestWithHeaders() throws IOException }); verify(httpClient).execute(getRequest.capture(), - ArgumentMatchers.>any()); + ArgumentMatchers.>any(), + ArgumentMatchers.any()); HttpGet httpGetReq = getRequest.getValue(); diff --git a/src/test/java/org/italiangrid/storm/webdav/test/tpc/http/integration/TpcClientRedirectionTest.java b/src/test/java/org/italiangrid/storm/webdav/test/tpc/http/integration/TpcClientRedirectionTest.java index 73cebca4..498084a4 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/tpc/http/integration/TpcClientRedirectionTest.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/tpc/http/integration/TpcClientRedirectionTest.java @@ -149,9 +149,8 @@ public void handleCrossProtocolRedirectionCorrectly() { headers.put("Authorization", "Bearer this-is-a-fake-token"); - GetTransferRequest getRequest = - new GetTransferRequestImpl(UUID.randomUUID().toString(), - "/test/example", URI.create(mockHttpsUrl("/test/example")), headers, false, false); + GetTransferRequest getRequest = new GetTransferRequestImpl(UUID.randomUUID().toString(), + "/test/example", URI.create(mockHttpsUrl("/test/example")), headers, null, false, false); mockServer .when(request().withMethod("GET").withPath("/test/example").withSecure(true), diff --git a/src/test/java/org/italiangrid/storm/webdav/test/tpc/http/integration/TpcIntegrationTest.java b/src/test/java/org/italiangrid/storm/webdav/test/tpc/http/integration/TpcIntegrationTest.java index 27b3d7e3..ca1fba9f 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/tpc/http/integration/TpcIntegrationTest.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/tpc/http/integration/TpcIntegrationTest.java @@ -28,6 +28,7 @@ import java.net.URI; import java.util.UUID; +import org.italiangrid.storm.webdav.scitag.SciTag; import org.italiangrid.storm.webdav.tpc.http.HttpTransferClient; import org.italiangrid.storm.webdav.tpc.transfer.GetTransferRequest; import org.italiangrid.storm.webdav.tpc.transfer.PutTransferRequest; @@ -92,7 +93,8 @@ public void testPutRedirectHandled() { Multimap emptyHeaders = ArrayListMultimap.create(); PutTransferRequest putRequest = new PutTransferRequestImpl(UUID.randomUUID().toString(), - "/test/example", URI.create(mockUrl("/test/example")), emptyHeaders, false, true); + "/test/example", URI.create(mockUrl("/test/example")), emptyHeaders, new SciTag(1, 2, true), + false, true); mockServer.when(request().withMethod("PUT").withPath("/test/example"), Times.exactly(1)) .respond(HttpResponse.response() @@ -121,7 +123,7 @@ public void testAuthorizationHeaderIsDroppedOnRedirectForPut() { headers.put("Authorization", "Bearer this-is-a-fake-token"); PutTransferRequest putRequest = new PutTransferRequestImpl(UUID.randomUUID().toString(), - "/test/example", URI.create(mockUrl("/test/example")), headers, false, true); + "/test/example", URI.create(mockUrl("/test/example")), headers, null, false, true); mockServer.when(request().withMethod("PUT").withPath("/test/example"), Times.exactly(1)) .respond(HttpResponse.response() @@ -153,7 +155,7 @@ public void testAuthorizationHeaderIsDroppedOnRedirectForGet() { GetTransferRequest getRequest = new GetTransferRequestImpl(UUID.randomUUID().toString(), - "/test/example", URI.create(mockUrl("/test/example")), headers, false, false); + "/test/example", URI.create(mockUrl("/test/example")), headers, null, false, false); mockServer.when(request().withMethod("GET").withPath("/test/example"), Times.exactly(1)) diff --git a/src/test/resources/application-authz-test.yml b/src/test/resources/application-authz-test.yml index 3d9ceafd..df719d4f 100644 --- a/src/test/resources/application-authz-test.yml +++ b/src/test/resources/application-authz-test.yml @@ -20,6 +20,9 @@ storm: redirector: enabled: false + + scitag: + enabled: true authz: policies: @@ -52,4 +55,4 @@ storm: params: iss: https://issuer.example group: /example/admins - \ No newline at end of file + From c3a4a108d581b336000a9a9583dd76f548d93189 Mon Sep 17 00:00:00 2001 From: Enrico Vianello Date: Fri, 18 Oct 2024 12:59:37 +0200 Subject: [PATCH 06/22] Allow all POST requests to be processed as a macaroon request (#52) * Enable Macaroon voter only if authz server and macaroon filter are enabled --- .../authz/voters/MacaroonAuthzVoter.java | 51 ++++++++++ .../webdav/server/servlet/MiltonFilter.java | 8 +- .../webdav/spring/web/SecurityConfig.java | 5 + .../MacaroonRequestLhcbIntegrationTests.java | 97 +++++++++++++++++++ src/test/resources/application-lhcb.yml | 55 +++++++++++ src/test/resources/application.properties | 52 ++++++++++ src/test/resources/lhcb/sa.d/disk.properties | 12 +++ .../resources/lhcb/storage/lhcb_disk/source | 0 8 files changed, 275 insertions(+), 5 deletions(-) create mode 100644 src/main/java/org/italiangrid/storm/webdav/authz/voters/MacaroonAuthzVoter.java create mode 100644 src/test/java/org/italiangrid/storm/webdav/test/macaroon/MacaroonRequestLhcbIntegrationTests.java create mode 100644 src/test/resources/application-lhcb.yml create mode 100644 src/test/resources/application.properties create mode 100644 src/test/resources/lhcb/sa.d/disk.properties create mode 100644 src/test/resources/lhcb/storage/lhcb_disk/source 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 new file mode 100644 index 00000000..c8c0a69b --- /dev/null +++ b/src/main/java/org/italiangrid/storm/webdav/authz/voters/MacaroonAuthzVoter.java @@ -0,0 +1,51 @@ +/** + * 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 { + + @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 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/server/servlet/MiltonFilter.java b/src/main/java/org/italiangrid/storm/webdav/server/servlet/MiltonFilter.java index a413980b..251f6d54 100644 --- a/src/main/java/org/italiangrid/storm/webdav/server/servlet/MiltonFilter.java +++ b/src/main/java/org/italiangrid/storm/webdav/server/servlet/MiltonFilter.java @@ -18,7 +18,6 @@ import static java.util.Objects.isNull; import java.io.IOException; -import java.util.HashSet; import java.util.Set; import javax.servlet.Filter; @@ -42,7 +41,8 @@ import org.italiangrid.storm.webdav.scitag.SciTagTransfer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; + +import com.google.common.collect.Sets; import io.milton.http.HttpManager; import io.milton.http.Request; @@ -53,7 +53,7 @@ public class MiltonFilter implements Filter { public static final Logger LOG = LoggerFactory.getLogger(MiltonFilter.class); - static final Set WEBDAV_METHOD_SET = new HashSet(); + static final Set WEBDAV_METHOD_SET = Sets.newHashSet(); static final String SA_ROOT_PATH = "sa-root"; static { @@ -74,7 +74,6 @@ public class MiltonFilter implements Filter { private final ReplaceContentStrategy rcs; - @Autowired public MiltonFilter(FilesystemAccess fsAccess, ExtendedAttributesHelper attrsHelper, PathResolver resolver, ReplaceContentStrategy rcs) { @@ -121,7 +120,6 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha doMilton((HttpServletRequest) request, (HttpServletResponse) response); } else chain.doFilter(request, response); - } public void doMilton(HttpServletRequest request, HttpServletResponse response) { 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 1ce8148b..4d437001 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 @@ -44,6 +44,7 @@ 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; @@ -282,6 +283,10 @@ protected AccessDecisionManager fineGrainedAccessDecisionManager() throws Malfor voters.add(new LocalAuthzVoter(serviceConfigurationProperties, pathResolver, new LocalAuthorizationPdp(serviceConfigurationProperties), localURLService)); } + if (serviceConfigurationProperties.getAuthzServer().isEnabled() + && serviceConfigurationProperties.getMacaroonFilter().isEnabled()) { + voters.add(new MacaroonAuthzVoter()); + } voters.add(new WebExpressionVoter()); voters.add(fineGrainedVoters); voters.add(wlcgVoters); diff --git a/src/test/java/org/italiangrid/storm/webdav/test/macaroon/MacaroonRequestLhcbIntegrationTests.java b/src/test/java/org/italiangrid/storm/webdav/test/macaroon/MacaroonRequestLhcbIntegrationTests.java new file mode 100644 index 00000000..0333e561 --- /dev/null +++ b/src/test/java/org/italiangrid/storm/webdav/test/macaroon/MacaroonRequestLhcbIntegrationTests.java @@ -0,0 +1,97 @@ +/** + * 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.test.macaroon; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.time.Clock; +import java.time.Instant; +import java.time.ZoneId; +import java.time.temporal.ChronoUnit; +import java.util.concurrent.TimeUnit; + +import org.italiangrid.storm.webdav.authz.VOMSAuthenticationFilter; +import org.italiangrid.storm.webdav.config.ServiceConfigurationProperties; +import org.italiangrid.storm.webdav.macaroon.MacaroonRequestFilter; +import org.italiangrid.storm.webdav.test.utils.voms.WithMockVOMSUser; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; +import org.springframework.security.test.context.support.WithAnonymousUser; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; + +import com.fasterxml.jackson.databind.ObjectMapper; + +@RunWith(SpringRunner.class) +@SpringBootTest +@AutoConfigureMockMvc +@ActiveProfiles({"dev", "lhcb"}) +@WithAnonymousUser +public class MacaroonRequestLhcbIntegrationTests { + + public static final Instant NOW = Instant.parse("2018-01-01T00:00:00.00Z"); + public static final Instant NOW_PLUS_2H = + NOW.plusSeconds(TimeUnit.HOURS.toSeconds(2)).truncatedTo(ChronoUnit.SECONDS); + + public static final String EMPTY_JSON_OBJECT = "{}"; + + @TestConfiguration + static class Configuration { + @Bean + @Primary + Clock mockClock() { + return Clock.fixed(NOW, ZoneId.systemDefault()); + } + } + + @Autowired + MockMvc mvc; + + @Autowired + VOMSAuthenticationFilter filter; + + @Autowired + ServiceConfigurationProperties props; + + @Autowired + ObjectMapper mapper; + + @BeforeEach + public void setup() { + filter.setCheckForPrincipalChanges(false); + } + + @Test + @WithMockVOMSUser(saReadPermissions = {"lhcb_disk"}, vos = {"lhcb"}) + void macaroonIssuedWithNoWritePermissions() throws Exception { + mvc + .perform(post("/disk/lhcb/source").contentType(MacaroonRequestFilter.MACAROON_REQUEST_CONTENT_TYPE) + .content(EMPTY_JSON_OBJECT)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.macaroon").exists()); + } + +} diff --git a/src/test/resources/application-lhcb.yml b/src/test/resources/application-lhcb.yml new file mode 100644 index 00000000..093247b5 --- /dev/null +++ b/src/test/resources/application-lhcb.yml @@ -0,0 +1,55 @@ +server: + jetty: + accesslog: + enabled: false +oauth: + enable-oidc: false + +storm: + sa: + config-dir: src/test/resources/lhcb/sa.d + + tls: + trust-anchors-dir: src/test/resources/trust-anchors + certificate-path: src/test/resources/hostcert/hostcert.pem + private-key-path: src/test/resources/hostcert/hostkey.pem + + voms: + trust-store: + dir: src/test/resources/vomsdir + + redirector: + enabled: false + + authz: + policies: + - sa: lhcb_disk + description: Grant all access to lhcb VOMS group members for /failover and its subfolders + actions: + - all + paths: + - /failover/** + effect: permit + principals: + - type: vo + params: + vo: lhcb + - sa: lhcb_disk + description: Grant all access to lhcb prod VOMS group members + actions: + - all + effect: permit + principals: + - type: fqan + params: + fqan: /lhcb/Role=production/Capability=NULL + - sa: lhcb_disk + description: Grant only read and list access to lhcb VOMS group members + actions: + - read + - list + effect: permit + principals: + - type: vo + params: + vo: lhcb diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties new file mode 100644 index 00000000..d7758789 --- /dev/null +++ b/src/test/resources/application.properties @@ -0,0 +1,52 @@ +# +# 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. +# + +logging.pattern.dateformat="yyyy-MM-dd'T'HH:mm:ss.SSSXXX", UTC +logging.pattern.level= [%X{storm.requestId}] %X{tpc.clientInfo} %5p +logging.level.root=WARN +logging.level.org.italiangrid.storm=DEBUG + + +#logging.level.org.italiangrid.storm.webdav.tpc=DEBUG + +#logging.level.org.springframework.test.web.servlet.result=DEBUG + +#logging.level.org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory=WARN +#logging.level.org.italiangrid.storm.webdav.authz=DEBUG +#logging.level.org.springframework.web=DEBUG +#logging.level.org.italiangrid.storm.webdav.oauth=DEBUG +#logging.level.org.italiangrid.storm.webdav.oidc=DEBUG +#logging.level.org.springframework.security=DEBUG + + +## Voters +#logging.level.org.italiangrid.storm.webdav.authz.voters=DEBUG +#logging.level.org.italiangrid.storm.webdav.authz.pdp=DEBUG +#logging.level.org.springframework.security.web.util.matcher=DEBUG +#logging.level.org.springframework.security.access=DEBUG + +## HTTP Client +### Connection management +# logging.level.org.apache.http.impl.conn=DEBUG +# logging.level.org.apache.http.impl.client=DEBUG +#logging.level.org.apache.http.client=DEBUG +### Wire +#logging.level.org.apache.http=DEBUG +#logging.level.org.apache.http.wire=DEBUG + +#logging.level.org.eclipse.jetty.io=ERROR +#logging.level.org.eclipse.jetty.server=DEBUG +#logging.level.org.eclipse.jetty.server.HttpOutput=ERROR diff --git a/src/test/resources/lhcb/sa.d/disk.properties b/src/test/resources/lhcb/sa.d/disk.properties new file mode 100644 index 00000000..d7aba683 --- /dev/null +++ b/src/test/resources/lhcb/sa.d/disk.properties @@ -0,0 +1,12 @@ +name=lhcb_disk +rootPath=src/test/resources/lhcb/storage/lhcb_disk +filesystemType=posixfs +accessPoints=/disk/lhcb +vos= +orgs=https://lhcb-auth.web.cern.ch/ +anonymousReadEnabled=false +authenticatedReadEnabled=false +orgsGrantReadPermissions=false +orgsGrantWritePermissions=false +wlcgScopeAuthzEnabled=true +fineGrainedAuthzEnabled=true diff --git a/src/test/resources/lhcb/storage/lhcb_disk/source b/src/test/resources/lhcb/storage/lhcb_disk/source new file mode 100644 index 00000000..e69de29b From af6a379555d3c88d938308401873d884059a7162 Mon Sep 17 00:00:00 2001 From: Luca Bassi Date: Wed, 6 Nov 2024 13:27:16 +0100 Subject: [PATCH 07/22] Change Boolean to boolean --- .../authz/voters/FineGrainedAuthzVoter.java | 2 +- .../voters/FineGrainedCopyMoveAuthzVoter.java | 2 +- .../voters/WlcgScopeAuthzCopyMoveVoter.java | 2 +- .../authz/voters/WlcgScopeAuthzVoter.java | 2 +- .../webdav/config/OwnerStorageAreaInfo.java | 16 ++++++++-------- .../storm/webdav/config/StorageAreaInfo.java | 18 +++++++++--------- .../oauth/GrantedAuthoritiesMapperSupport.java | 6 +++--- .../webdav/spring/web/SecurityConfig.java | 2 +- 8 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/italiangrid/storm/webdav/authz/voters/FineGrainedAuthzVoter.java b/src/main/java/org/italiangrid/storm/webdav/authz/voters/FineGrainedAuthzVoter.java index a4f2ed04..7448ada5 100644 --- a/src/main/java/org/italiangrid/storm/webdav/authz/voters/FineGrainedAuthzVoter.java +++ b/src/main/java/org/italiangrid/storm/webdav/authz/voters/FineGrainedAuthzVoter.java @@ -48,7 +48,7 @@ public int vote(Authentication authentication, FilterInvocation filter, final String requestPath = getRequestPath(filter.getHttpRequest()); StorageAreaInfo sa = resolver.resolveStorageArea(requestPath); - if (isNull(sa) || Boolean.FALSE.equals(sa.fineGrainedAuthzEnabled())) { + if (isNull(sa) || !sa.fineGrainedAuthzEnabled()) { return ACCESS_ABSTAIN; } diff --git a/src/main/java/org/italiangrid/storm/webdav/authz/voters/FineGrainedCopyMoveAuthzVoter.java b/src/main/java/org/italiangrid/storm/webdav/authz/voters/FineGrainedCopyMoveAuthzVoter.java index 087244c1..08fde1f0 100644 --- a/src/main/java/org/italiangrid/storm/webdav/authz/voters/FineGrainedCopyMoveAuthzVoter.java +++ b/src/main/java/org/italiangrid/storm/webdav/authz/voters/FineGrainedCopyMoveAuthzVoter.java @@ -72,7 +72,7 @@ && requestHasRemoteDestinationHeader(filter.getRequest(), localUrlService)) { return ACCESS_ABSTAIN; } - if (Boolean.FALSE.equals(sa.fineGrainedAuthzEnabled())) { + if (!sa.fineGrainedAuthzEnabled()) { return ACCESS_ABSTAIN; } diff --git a/src/main/java/org/italiangrid/storm/webdav/authz/voters/WlcgScopeAuthzCopyMoveVoter.java b/src/main/java/org/italiangrid/storm/webdav/authz/voters/WlcgScopeAuthzCopyMoveVoter.java index 2017c71f..ca58610b 100644 --- a/src/main/java/org/italiangrid/storm/webdav/authz/voters/WlcgScopeAuthzCopyMoveVoter.java +++ b/src/main/java/org/italiangrid/storm/webdav/authz/voters/WlcgScopeAuthzCopyMoveVoter.java @@ -78,7 +78,7 @@ && requestHasRemoteDestinationHeader(filter.getRequest(), localUrlService)) { return ACCESS_ABSTAIN; } - if (Boolean.FALSE.equals(sa.wlcgScopeAuthzEnabled())) { + if (!sa.wlcgScopeAuthzEnabled()) { return ACCESS_ABSTAIN; } diff --git a/src/main/java/org/italiangrid/storm/webdav/authz/voters/WlcgScopeAuthzVoter.java b/src/main/java/org/italiangrid/storm/webdav/authz/voters/WlcgScopeAuthzVoter.java index bf728ba8..3c7aa973 100644 --- a/src/main/java/org/italiangrid/storm/webdav/authz/voters/WlcgScopeAuthzVoter.java +++ b/src/main/java/org/italiangrid/storm/webdav/authz/voters/WlcgScopeAuthzVoter.java @@ -57,7 +57,7 @@ public int vote(Authentication authentication, FilterInvocation filter, return ACCESS_ABSTAIN; } - if (Boolean.FALSE.equals(sa.wlcgScopeAuthzEnabled())) { + if (!sa.wlcgScopeAuthzEnabled()) { return ACCESS_ABSTAIN; } diff --git a/src/main/java/org/italiangrid/storm/webdav/config/OwnerStorageAreaInfo.java b/src/main/java/org/italiangrid/storm/webdav/config/OwnerStorageAreaInfo.java index 6b47c906..1f85128b 100644 --- a/src/main/java/org/italiangrid/storm/webdav/config/OwnerStorageAreaInfo.java +++ b/src/main/java/org/italiangrid/storm/webdav/config/OwnerStorageAreaInfo.java @@ -39,34 +39,34 @@ public interface OwnerStorageAreaInfo extends StorageAreaInfo, Config { public Set orgs(); @DefaultValue("false") - public Boolean anonymousReadEnabled(); + public boolean anonymousReadEnabled(); @Override @DefaultValue("false") - public Boolean authenticatedReadEnabled(); + public boolean authenticatedReadEnabled(); @Override @DefaultValue("true") - public Boolean voMapEnabled(); + public boolean voMapEnabled(); @Override @DefaultValue("false") - public Boolean voMapGrantsWritePermission(); + public boolean voMapGrantsWritePermission(); @Override @DefaultValue("true") - public Boolean orgsGrantReadPermission(); + public boolean orgsGrantReadPermission(); @Override @DefaultValue("true") - public Boolean orgsGrantWritePermission(); + public boolean orgsGrantWritePermission(); @Override @DefaultValue("false") - public Boolean wlcgScopeAuthzEnabled(); + public boolean wlcgScopeAuthzEnabled(); @Override @DefaultValue("false") - public Boolean fineGrainedAuthzEnabled(); + public boolean fineGrainedAuthzEnabled(); } diff --git a/src/main/java/org/italiangrid/storm/webdav/config/StorageAreaInfo.java b/src/main/java/org/italiangrid/storm/webdav/config/StorageAreaInfo.java index 58781227..230c13af 100644 --- a/src/main/java/org/italiangrid/storm/webdav/config/StorageAreaInfo.java +++ b/src/main/java/org/italiangrid/storm/webdav/config/StorageAreaInfo.java @@ -32,19 +32,19 @@ public interface StorageAreaInfo { public Set orgs(); - public Boolean anonymousReadEnabled(); + public boolean anonymousReadEnabled(); - public Boolean authenticatedReadEnabled(); + public boolean authenticatedReadEnabled(); - public Boolean voMapEnabled(); + public boolean voMapEnabled(); - public Boolean voMapGrantsWritePermission(); + public boolean voMapGrantsWritePermission(); - public Boolean orgsGrantReadPermission(); + public boolean orgsGrantReadPermission(); - public Boolean orgsGrantWritePermission(); + public boolean orgsGrantWritePermission(); - public Boolean wlcgScopeAuthzEnabled(); - - public Boolean fineGrainedAuthzEnabled(); + public boolean wlcgScopeAuthzEnabled(); + + public boolean fineGrainedAuthzEnabled(); } diff --git a/src/main/java/org/italiangrid/storm/webdav/oauth/GrantedAuthoritiesMapperSupport.java b/src/main/java/org/italiangrid/storm/webdav/oauth/GrantedAuthoritiesMapperSupport.java index c02d5f70..c2aca27a 100644 --- a/src/main/java/org/italiangrid/storm/webdav/oauth/GrantedAuthoritiesMapperSupport.java +++ b/src/main/java/org/italiangrid/storm/webdav/oauth/GrantedAuthoritiesMapperSupport.java @@ -54,18 +54,18 @@ public GrantedAuthoritiesMapperSupport(StorageAreaConfiguration conf, } for (StorageAreaInfo sa : conf.getStorageAreaInfo()) { - if (Boolean.TRUE.equals(sa.anonymousReadEnabled())) { + if (sa.anonymousReadEnabled()) { anonymousGrantedAuthorities.add(SAPermission.canRead(sa.name())); } } } protected void addSaGrantedAuthorities(StorageAreaInfo sa, String issuer) { - if (Boolean.TRUE.equals(sa.orgsGrantReadPermission())) { + if (sa.orgsGrantReadPermission()) { authzMap.put(issuer, SAPermission.canRead(sa.name())); } - if (Boolean.TRUE.equals(sa.orgsGrantWritePermission())) { + if (sa.orgsGrantWritePermission()) { authzMap.put(issuer, SAPermission.canWrite(sa.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 4d437001..8a7f093a 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 @@ -220,7 +220,7 @@ protected void addAnonymousAccessRules(HttpSecurity http) throws Exception { final List anonymousAccessPermissions = new ArrayList<>(); for (StorageAreaInfo sa : saConfiguration.getStorageAreaInfo()) { - if (Boolean.TRUE.equals(sa.anonymousReadEnabled())) { + if (sa.anonymousReadEnabled()) { anonymousAccessPermissions.add(SAPermission.canRead(sa.name())); } } From eeb76bc4de28228b5d62b9a83d784d62ebecd1f7 Mon Sep 17 00:00:00 2001 From: Luca Bassi Date: Wed, 6 Nov 2024 14:31:20 +0100 Subject: [PATCH 08/22] Prevent double CI build --- .github/workflows/maven.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index e9ecf12d..00f03e89 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -22,9 +22,8 @@ on: jobs: build: - runs-on: ubuntu-latest - + 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@v2 - name: Set up JDK 11 From a91997b7b482046e4b966449d8c63a574b83da8d Mon Sep 17 00:00:00 2001 From: Luca Bassi Date: Thu, 7 Nov 2024 09:38:45 +0100 Subject: [PATCH 09/22] Update actions versions --- .github/workflows/maven.yml | 7 ++++--- .github/workflows/sonar.yml | 9 +++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 00f03e89..048fb743 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -25,13 +25,14 @@ jobs: runs-on: ubuntu-latest 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@v2 + - uses: actions/checkout@v4 - name: Set up JDK 11 - uses: actions/setup-java@v1 + uses: actions/setup-java@v4 with: + distribution: 'temurin' java-version: 11 - name: Cache Maven packages - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.m2 key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml index 303dc77f..6f4f5747 100644 --- a/.github/workflows/sonar.yml +++ b/.github/workflows/sonar.yml @@ -10,21 +10,22 @@ jobs: name: Build and analyze runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - name: Set up JDK 17 - uses: actions/setup-java@v1 + uses: actions/setup-java@v4 with: + distribution: 'temurin' java-version: 17 - name: Cache SonarCloud packages - uses: actions/cache@v1 + uses: actions/cache@v4 with: path: ~/.sonar/cache key: ${{ runner.os }}-sonar restore-keys: ${{ runner.os }}-sonar - name: Cache Maven packages - uses: actions/cache@v1 + uses: actions/cache@v4 with: path: ~/.m2 key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} From d5dd04e79a39920a6941027f2a497ef539245d35 Mon Sep 17 00:00:00 2001 From: Luca Bassi Date: Wed, 6 Nov 2024 16:59:39 +0100 Subject: [PATCH 10/22] Remove non-idiomatic use of Objects.isNull This method exists to be used as a Predicate, filter(Objects::isNull) --- .../webdav/authn/AuthenticationUtils.java | 7 +-- .../storm/webdav/authn/PrincipalHelper.java | 3 +- .../storm/webdav/authz/VOMSPolicyService.java | 3 +- .../authz/pdp/PathAuthorizationPolicy.java | 3 +- .../WlcgStructuredPathAuthorizationPdp.java | 5 +-- .../authz/pdp/principal/AnonymousUser.java | 4 +- .../pdp/principal/AnyAuthenticatedUser.java | 4 +- .../authz/pdp/principal/AuthorityHolder.java | 4 +- .../util/StructuredPathScopeMatcher.java | 17 ++++--- .../authz/voters/FineGrainedAuthzVoter.java | 3 +- .../voters/FineGrainedCopyMoveAuthzVoter.java | 3 +- .../voters/WlcgScopeAuthzCopyMoveVoter.java | 3 +- .../authz/voters/WlcgScopeAuthzVoter.java | 3 +- .../config/validation/PrincipalValidator.java | 5 +-- .../metrics/StorageAreaStatsFilter.java | 3 +- .../webdav/milton/StoRMFileResource.java | 7 ++- .../GrantedAuthoritiesMapperSupport.java | 4 +- .../oauth/StormJwtAuthoritiesConverter.java | 5 +-- .../jwt/DefaultJwtTokenIssuer.java | 13 +++--- .../DefaultOidcConfigurationFetcher.java | 3 +- .../oauth/validator/AudienceValidator.java | 7 ++- .../oauth/validator/WlcgProfileValidator.java | 13 +++--- .../oidc/ClientRegistrationCacheLoader.java | 3 +- .../oidc/OidcGrantedAuthoritiesMapper.java | 4 +- .../webdav/redirector/RedirectFilter.java | 7 ++- .../webdav/server/DefaultPathResolver.java | 9 ++-- .../server/TLSServerConnectorBuilder.java | 44 +++++++++---------- .../webdav/server/servlet/MiltonFilter.java | 4 +- .../storm/webdav/spring/AppConfig.java | 3 +- .../tpc/StaticHostListLocalURLService.java | 4 +- .../webdav/tpc/TransferFilterSupport.java | 3 +- .../webdav/tpc/http/GetResponseHandler.java | 4 +- .../tpc/http/ResponseHandlerSupport.java | 6 +-- .../transfer/impl/TransferRequestImpl.java | 3 +- .../voms/VOMSSecurityContextBuilder.java | 7 ++- 35 files changed, 88 insertions(+), 135 deletions(-) diff --git a/src/main/java/org/italiangrid/storm/webdav/authn/AuthenticationUtils.java b/src/main/java/org/italiangrid/storm/webdav/authn/AuthenticationUtils.java index 0cd57a82..ae4b373b 100644 --- a/src/main/java/org/italiangrid/storm/webdav/authn/AuthenticationUtils.java +++ b/src/main/java/org/italiangrid/storm/webdav/authn/AuthenticationUtils.java @@ -15,10 +15,7 @@ */ package org.italiangrid.storm.webdav.authn; -import static java.util.Objects.isNull; - import java.util.Map; -import java.util.Objects; import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.core.Authentication; @@ -33,7 +30,7 @@ private AuthenticationUtils() { } public static String getPalatableSubject(Authentication authn) { - if (Objects.isNull(authn) || authn instanceof AnonymousAuthenticationToken) { + if (authn == null || authn instanceof AnonymousAuthenticationToken) { return "Anonymous user"; } else if (authn instanceof OAuth2AuthenticationToken) { OAuth2AuthenticationToken authToken = (OAuth2AuthenticationToken) authn; @@ -41,7 +38,7 @@ public static String getPalatableSubject(Authentication authn) { String subjectIssuer = String.format("%s @ %s", attributes.get("sub"), attributes.get("iss")); - if (!isNull(attributes.get("name"))) { + if (attributes.get("name") != null) { return (String) attributes.get("name"); } diff --git a/src/main/java/org/italiangrid/storm/webdav/authn/PrincipalHelper.java b/src/main/java/org/italiangrid/storm/webdav/authn/PrincipalHelper.java index 41cb1cc4..d2f0de9b 100644 --- a/src/main/java/org/italiangrid/storm/webdav/authn/PrincipalHelper.java +++ b/src/main/java/org/italiangrid/storm/webdav/authn/PrincipalHelper.java @@ -18,7 +18,6 @@ import java.net.MalformedURLException; import java.net.URL; import java.util.Map; -import java.util.Objects; import java.util.Optional; import org.italiangrid.storm.webdav.config.ServiceConfigurationProperties; @@ -43,7 +42,7 @@ public PrincipalHelper(ServiceConfigurationProperties config) throws MalformedUR } public String getPrincipalAsString(Authentication authn) { - if (Objects.isNull(authn) || authn instanceof AnonymousAuthenticationToken) { + if (authn == null || authn instanceof AnonymousAuthenticationToken) { return "anonymous"; } else if (authn instanceof OAuth2AuthenticationToken) { OAuth2AuthenticationToken authToken = (OAuth2AuthenticationToken) authn; diff --git a/src/main/java/org/italiangrid/storm/webdav/authz/VOMSPolicyService.java b/src/main/java/org/italiangrid/storm/webdav/authz/VOMSPolicyService.java index 1081737d..af579caf 100644 --- a/src/main/java/org/italiangrid/storm/webdav/authz/VOMSPolicyService.java +++ b/src/main/java/org/italiangrid/storm/webdav/authz/VOMSPolicyService.java @@ -15,7 +15,6 @@ */ package org.italiangrid.storm.webdav.authz; -import static java.util.Objects.isNull; import static org.italiangrid.storm.webdav.authz.SAPermission.canRead; import static org.italiangrid.storm.webdav.authz.SAPermission.canWrite; @@ -49,7 +48,7 @@ public VOMSPolicyService(StorageAreaConfiguration saConfig) { voMapPerms = ArrayListMultimap.create(); for (StorageAreaInfo sa : saConfig.getStorageAreaInfo()) { - if (!isNull(sa.vos())) { + if (sa.vos() != null) { for (String vo : sa.vos()) { voPerms.put(vo, canRead(sa.name())); voPerms.put(vo, canWrite(sa.name())); diff --git a/src/main/java/org/italiangrid/storm/webdav/authz/pdp/PathAuthorizationPolicy.java b/src/main/java/org/italiangrid/storm/webdav/authz/pdp/PathAuthorizationPolicy.java index e5a2efc1..ba29d611 100644 --- a/src/main/java/org/italiangrid/storm/webdav/authz/pdp/PathAuthorizationPolicy.java +++ b/src/main/java/org/italiangrid/storm/webdav/authz/pdp/PathAuthorizationPolicy.java @@ -16,7 +16,6 @@ package org.italiangrid.storm.webdav.authz.pdp; import java.util.List; -import java.util.Objects; import java.util.UUID; import javax.servlet.http.HttpServletRequest; @@ -158,7 +157,7 @@ public Builder withPrincipalMatcher(PrincipalMatcher m) { } public PathAuthorizationPolicy build() { - if (Objects.isNull(id)) { + if (id == null) { id = UUID.randomUUID().toString(); } return new PathAuthorizationPolicy(this); diff --git a/src/main/java/org/italiangrid/storm/webdav/authz/pdp/WlcgStructuredPathAuthorizationPdp.java b/src/main/java/org/italiangrid/storm/webdav/authz/pdp/WlcgStructuredPathAuthorizationPdp.java index 76db289c..6cb4d3ba 100644 --- a/src/main/java/org/italiangrid/storm/webdav/authz/pdp/WlcgStructuredPathAuthorizationPdp.java +++ b/src/main/java/org/italiangrid/storm/webdav/authz/pdp/WlcgStructuredPathAuthorizationPdp.java @@ -16,7 +16,6 @@ package org.italiangrid.storm.webdav.authz.pdp; import static java.lang.String.format; -import static java.util.Objects.isNull; import static java.util.stream.Collectors.toList; import static org.italiangrid.storm.webdav.authz.pdp.PathAuthorizationResult.deny; import static org.italiangrid.storm.webdav.authz.pdp.PathAuthorizationResult.indeterminate; @@ -181,13 +180,13 @@ public PathAuthorizationResult authorizeRequest(PathAuthorizationRequest authzRe JwtAuthenticationToken jwtAuth = (JwtAuthenticationToken) authentication; - if (isNull(jwtAuth.getToken().getClaimAsString(SCOPE_CLAIM))) { + if (jwtAuth.getToken().getClaimAsString(SCOPE_CLAIM) == null) { return indeterminate(ERROR_INVALID_TOKEN_NO_SCOPE); } StorageAreaInfo sa = pathResolver.resolveStorageArea(requestPath); - if (isNull(sa)) { + if (sa == null) { return indeterminate(ERROR_SA_NOT_FOUND); } diff --git a/src/main/java/org/italiangrid/storm/webdav/authz/pdp/principal/AnonymousUser.java b/src/main/java/org/italiangrid/storm/webdav/authz/pdp/principal/AnonymousUser.java index dc13c395..74fbc843 100644 --- a/src/main/java/org/italiangrid/storm/webdav/authz/pdp/principal/AnonymousUser.java +++ b/src/main/java/org/italiangrid/storm/webdav/authz/pdp/principal/AnonymousUser.java @@ -15,8 +15,6 @@ */ package org.italiangrid.storm.webdav.authz.pdp.principal; -import static java.util.Objects.isNull; - import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.core.Authentication; @@ -24,7 +22,7 @@ public class AnonymousUser implements PrincipalMatcher { @Override public boolean matchesPrincipal(Authentication authentication) { - return isNull(authentication) || authentication instanceof AnonymousAuthenticationToken; + return authentication == null || authentication instanceof AnonymousAuthenticationToken; } @Override diff --git a/src/main/java/org/italiangrid/storm/webdav/authz/pdp/principal/AnyAuthenticatedUser.java b/src/main/java/org/italiangrid/storm/webdav/authz/pdp/principal/AnyAuthenticatedUser.java index 40d99d38..0e309924 100644 --- a/src/main/java/org/italiangrid/storm/webdav/authz/pdp/principal/AnyAuthenticatedUser.java +++ b/src/main/java/org/italiangrid/storm/webdav/authz/pdp/principal/AnyAuthenticatedUser.java @@ -15,8 +15,6 @@ */ package org.italiangrid.storm.webdav.authz.pdp.principal; -import static java.util.Objects.isNull; - import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.core.Authentication; @@ -24,7 +22,7 @@ public class AnyAuthenticatedUser implements PrincipalMatcher { @Override public boolean matchesPrincipal(Authentication authentication) { - return !isNull(authentication) && !(authentication instanceof AnonymousAuthenticationToken) + return authentication != null && !(authentication instanceof AnonymousAuthenticationToken) && authentication.isAuthenticated(); } diff --git a/src/main/java/org/italiangrid/storm/webdav/authz/pdp/principal/AuthorityHolder.java b/src/main/java/org/italiangrid/storm/webdav/authz/pdp/principal/AuthorityHolder.java index 4b572723..c71c7f3f 100644 --- a/src/main/java/org/italiangrid/storm/webdav/authz/pdp/principal/AuthorityHolder.java +++ b/src/main/java/org/italiangrid/storm/webdav/authz/pdp/principal/AuthorityHolder.java @@ -15,8 +15,6 @@ */ package org.italiangrid.storm.webdav.authz.pdp.principal; -import java.util.Objects; - import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; @@ -39,7 +37,7 @@ public static AuthorityHolder fromAuthority(GrantedAuthority authority) { @Override public boolean matchesPrincipal(Authentication authentication) { - return !Objects.isNull(authentication) && authentication.getAuthorities() + return authentication != null && authentication.getAuthorities() .stream() .anyMatch(a -> a.getAuthority().equals(authority.getAuthority())); } diff --git a/src/main/java/org/italiangrid/storm/webdav/authz/util/StructuredPathScopeMatcher.java b/src/main/java/org/italiangrid/storm/webdav/authz/util/StructuredPathScopeMatcher.java index 853519b0..42017870 100644 --- a/src/main/java/org/italiangrid/storm/webdav/authz/util/StructuredPathScopeMatcher.java +++ b/src/main/java/org/italiangrid/storm/webdav/authz/util/StructuredPathScopeMatcher.java @@ -17,7 +17,6 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Strings.isNullOrEmpty; -import static java.util.Objects.nonNull; import java.nio.file.Path; import java.util.regex.Matcher; @@ -33,7 +32,7 @@ public class StructuredPathScopeMatcher implements ScopeMatcher { public static final Logger LOG = LoggerFactory.getLogger(StructuredPathScopeMatcher.class); private static final Pattern POINT_DIR_MATCHER = Pattern.compile("\\.\\./?"); - + private static final Character SEP = ':'; private static final String SEP_STR = SEP.toString(); @@ -55,24 +54,24 @@ private StructuredPathScopeMatcher(String prefix, String path) { @Override public boolean matchesScope(String scope) { - checkArgument(nonNull(scope), "scope must be non-null"); + checkArgument(scope != null, "scope must be non-null"); if (POINT_DIR_MATCHER.matcher(scope).find()) { throw new IllegalArgumentException("Scope contains relative path references"); } Matcher prefixMatcher = prefixMatchPattern.matcher(scope); - + boolean prefixMatches = prefixMatcher.find(); - + if (prefixMatches) { final String scopePath = scope.substring(prefix.length() + 1); - return pathMatchPattern.matcher(scopePath).matches(); + return pathMatchPattern.matcher(scopePath).matches(); } else { return false; } } - + public boolean matchesPath(String path) { return pathMatchPattern.matcher(path).matches(); } @@ -81,7 +80,7 @@ public boolean matchesPathIncludingParents(String path) { Path targetPath = Path.of(path); return this.path.startsWith(targetPath) || matchesPath(path); } - + public static StructuredPathScopeMatcher fromString(String scope) { final int sepIndex = scope.indexOf(SEP); final String prefix = scope.substring(0, sepIndex); @@ -143,5 +142,5 @@ public String getPrefix() { public String getPath() { return path.toString(); } - + } diff --git a/src/main/java/org/italiangrid/storm/webdav/authz/voters/FineGrainedAuthzVoter.java b/src/main/java/org/italiangrid/storm/webdav/authz/voters/FineGrainedAuthzVoter.java index 7448ada5..3b66ce2a 100644 --- a/src/main/java/org/italiangrid/storm/webdav/authz/voters/FineGrainedAuthzVoter.java +++ b/src/main/java/org/italiangrid/storm/webdav/authz/voters/FineGrainedAuthzVoter.java @@ -15,7 +15,6 @@ */ package org.italiangrid.storm.webdav.authz.voters; -import static java.util.Objects.isNull; import static org.italiangrid.storm.webdav.authz.pdp.PathAuthorizationRequest.newAuthorizationRequest; import java.util.Collection; @@ -48,7 +47,7 @@ public int vote(Authentication authentication, FilterInvocation filter, final String requestPath = getRequestPath(filter.getHttpRequest()); StorageAreaInfo sa = resolver.resolveStorageArea(requestPath); - if (isNull(sa) || !sa.fineGrainedAuthzEnabled()) { + if (sa == null || !sa.fineGrainedAuthzEnabled()) { return ACCESS_ABSTAIN; } diff --git a/src/main/java/org/italiangrid/storm/webdav/authz/voters/FineGrainedCopyMoveAuthzVoter.java b/src/main/java/org/italiangrid/storm/webdav/authz/voters/FineGrainedCopyMoveAuthzVoter.java index 08fde1f0..87dc77fd 100644 --- a/src/main/java/org/italiangrid/storm/webdav/authz/voters/FineGrainedCopyMoveAuthzVoter.java +++ b/src/main/java/org/italiangrid/storm/webdav/authz/voters/FineGrainedCopyMoveAuthzVoter.java @@ -15,7 +15,6 @@ */ package org.italiangrid.storm.webdav.authz.voters; -import static java.util.Objects.isNull; import static org.italiangrid.storm.webdav.server.servlet.WebDAVMethod.COPY; import static org.italiangrid.storm.webdav.server.servlet.WebDAVMethod.PUT; @@ -68,7 +67,7 @@ && requestHasRemoteDestinationHeader(filter.getRequest(), localUrlService)) { String destinationPath = getSanitizedPathFromUrl(destination); StorageAreaInfo sa = resolver.resolveStorageArea(destinationPath); - if (isNull(sa)) { + if (sa == null) { return ACCESS_ABSTAIN; } diff --git a/src/main/java/org/italiangrid/storm/webdav/authz/voters/WlcgScopeAuthzCopyMoveVoter.java b/src/main/java/org/italiangrid/storm/webdav/authz/voters/WlcgScopeAuthzCopyMoveVoter.java index ca58610b..83f16dd2 100644 --- a/src/main/java/org/italiangrid/storm/webdav/authz/voters/WlcgScopeAuthzCopyMoveVoter.java +++ b/src/main/java/org/italiangrid/storm/webdav/authz/voters/WlcgScopeAuthzCopyMoveVoter.java @@ -15,7 +15,6 @@ */ package org.italiangrid.storm.webdav.authz.voters; -import static java.util.Objects.isNull; import static org.italiangrid.storm.webdav.server.servlet.WebDAVMethod.COPY; import static org.italiangrid.storm.webdav.server.servlet.WebDAVMethod.PUT; @@ -74,7 +73,7 @@ && requestHasRemoteDestinationHeader(filter.getRequest(), localUrlService)) { String destinationPath = getSanitizedPathFromUrl(destination); StorageAreaInfo sa = resolver.resolveStorageArea(destinationPath); - if (isNull(sa)) { + if (sa == null) { return ACCESS_ABSTAIN; } diff --git a/src/main/java/org/italiangrid/storm/webdav/authz/voters/WlcgScopeAuthzVoter.java b/src/main/java/org/italiangrid/storm/webdav/authz/voters/WlcgScopeAuthzVoter.java index 3c7aa973..5da7658a 100644 --- a/src/main/java/org/italiangrid/storm/webdav/authz/voters/WlcgScopeAuthzVoter.java +++ b/src/main/java/org/italiangrid/storm/webdav/authz/voters/WlcgScopeAuthzVoter.java @@ -15,7 +15,6 @@ */ package org.italiangrid.storm.webdav.authz.voters; -import static java.util.Objects.isNull; import static org.italiangrid.storm.webdav.authz.pdp.PathAuthorizationRequest.newAuthorizationRequest; import java.util.Collection; @@ -53,7 +52,7 @@ public int vote(Authentication authentication, FilterInvocation filter, final String requestPath = getRequestPath(filter.getHttpRequest()); StorageAreaInfo sa = resolver.resolveStorageArea(requestPath); - if (isNull(sa)) { + if (sa == null) { return ACCESS_ABSTAIN; } diff --git a/src/main/java/org/italiangrid/storm/webdav/config/validation/PrincipalValidator.java b/src/main/java/org/italiangrid/storm/webdav/config/validation/PrincipalValidator.java index 7b191541..fc7d4dc0 100644 --- a/src/main/java/org/italiangrid/storm/webdav/config/validation/PrincipalValidator.java +++ b/src/main/java/org/italiangrid/storm/webdav/config/validation/PrincipalValidator.java @@ -17,7 +17,6 @@ import static com.google.common.base.Strings.isNullOrEmpty; import static java.lang.String.format; -import static java.util.Objects.isNull; import static org.italiangrid.storm.webdav.config.FineGrainedAuthzPolicyProperties.PrincipalProperties.PrincipalType.FQAN; import static org.italiangrid.storm.webdav.config.FineGrainedAuthzPolicyProperties.PrincipalProperties.PrincipalType.JWT_GROUP; import static org.italiangrid.storm.webdav.config.FineGrainedAuthzPolicyProperties.PrincipalProperties.PrincipalType.JWT_SCOPE; @@ -63,8 +62,8 @@ public boolean isValid( ConstraintValidatorContext context) { Collection requiredArgs = REQUIRED_ARGS.get(value.getType()); - - if (isNull(requiredArgs) || requiredArgs.isEmpty()) { + + if (requiredArgs == null || requiredArgs.isEmpty()) { return true; } diff --git a/src/main/java/org/italiangrid/storm/webdav/metrics/StorageAreaStatsFilter.java b/src/main/java/org/italiangrid/storm/webdav/metrics/StorageAreaStatsFilter.java index 14e71ff3..2903f6b3 100644 --- a/src/main/java/org/italiangrid/storm/webdav/metrics/StorageAreaStatsFilter.java +++ b/src/main/java/org/italiangrid/storm/webdav/metrics/StorageAreaStatsFilter.java @@ -16,7 +16,6 @@ package org.italiangrid.storm.webdav.metrics; import static com.codahale.metrics.MetricRegistry.name; -import static java.util.Objects.isNull; import java.io.IOException; @@ -52,7 +51,7 @@ public StorageAreaStatsFilter(MetricRegistry registry, PathResolver resolver) { private void updateStats(HttpServletRequest request, HttpServletResponse response) { StorageAreaInfo sa = resolver.resolveStorageArea(getSerlvetRequestPath(request)); - if (!isNull(sa)) { + if (sa != null) { String prefix = name(SA_KEY, sa.name(), REQUESTS_KEY); registry.meter(prefix).mark(); if (response.getStatus() >= 400) { diff --git a/src/main/java/org/italiangrid/storm/webdav/milton/StoRMFileResource.java b/src/main/java/org/italiangrid/storm/webdav/milton/StoRMFileResource.java index aefbd19f..5b56cc2a 100644 --- a/src/main/java/org/italiangrid/storm/webdav/milton/StoRMFileResource.java +++ b/src/main/java/org/italiangrid/storm/webdav/milton/StoRMFileResource.java @@ -16,7 +16,6 @@ package org.italiangrid.storm.webdav.milton; import static io.milton.property.PropertySource.PropertyAccessibility.READ_ONLY; -import static java.util.Objects.isNull; import java.io.BufferedInputStream; import java.io.File; @@ -126,7 +125,7 @@ public void replaceContent(InputStream in, Long length) protected void validateRange(Range range) { long fileSize = getFile().length(); - if (isNull(range.getStart())) { + if (range.getStart() == null) { throw new StoRMWebDAVError("Invalid range: range start not defined"); } @@ -134,7 +133,7 @@ protected void validateRange(Range range) { throw new StoRMWebDAVError("Invalid range: range start out of bounds"); } - if (!isNull(range.getFinish()) && range.getFinish() > fileSize) { + if (range.getFinish() != null && range.getFinish() > fileSize) { throw new StoRMWebDAVError("Invalid range: range end out of bounds"); } } @@ -147,7 +146,7 @@ public void replacePartialContent(Range range, InputStream in) { long rangeStart = range.getStart(); long rangeEnd = getFile().length(); - if (!isNull(range.getFinish()) && range.getFinish() < rangeEnd) { + if (range.getFinish() != null && range.getFinish() < rangeEnd) { rangeEnd = range.getFinish(); } diff --git a/src/main/java/org/italiangrid/storm/webdav/oauth/GrantedAuthoritiesMapperSupport.java b/src/main/java/org/italiangrid/storm/webdav/oauth/GrantedAuthoritiesMapperSupport.java index c2aca27a..e09483aa 100644 --- a/src/main/java/org/italiangrid/storm/webdav/oauth/GrantedAuthoritiesMapperSupport.java +++ b/src/main/java/org/italiangrid/storm/webdav/oauth/GrantedAuthoritiesMapperSupport.java @@ -15,8 +15,6 @@ */ package org.italiangrid.storm.webdav.oauth; -import static java.util.Objects.isNull; - import java.util.Collection; import java.util.Set; @@ -48,7 +46,7 @@ public GrantedAuthoritiesMapperSupport(StorageAreaConfiguration conf, authzServerProperties = props.getAuthzServer(); for (StorageAreaInfo sa : conf.getStorageAreaInfo()) { - if (!isNull(sa.orgs())) { + if (sa.orgs() != null) { sa.orgs().forEach(i -> addSaGrantedAuthorities(sa, i)); } } diff --git a/src/main/java/org/italiangrid/storm/webdav/oauth/StormJwtAuthoritiesConverter.java b/src/main/java/org/italiangrid/storm/webdav/oauth/StormJwtAuthoritiesConverter.java index 7ea26539..a1df6ac8 100644 --- a/src/main/java/org/italiangrid/storm/webdav/oauth/StormJwtAuthoritiesConverter.java +++ b/src/main/java/org/italiangrid/storm/webdav/oauth/StormJwtAuthoritiesConverter.java @@ -18,7 +18,6 @@ import static org.italiangrid.storm.webdav.oauth.authzserver.jwt.DefaultJwtTokenIssuer.CLAIM_AUTHORITIES; import java.util.Collection; -import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -58,7 +57,7 @@ protected Set extractAuthoritiesLocalAuthzServer(Jwt jwt) { Optional.ofNullable( jwt.getClaimAsStringList(CLAIM_AUTHORITIES)) .ifPresent(a -> a.forEach(at -> authorities.add(SAPermission.fromString(at)))); - + return authorities; } @@ -66,7 +65,7 @@ protected Set extractOauthScopeAuthorities(Jwt jwt) { Set scopeAuthorities = Sets.newHashSet(); - if (!Objects.isNull(jwt.getClaimAsString(SCOPE_CLAIM_NAME))) { + if (jwt.getClaimAsString(SCOPE_CLAIM_NAME) != null) { String tokenIssuer = jwt.getClaimAsString(JwtClaimNames.ISS); String[] scopes = jwt.getClaimAsString(SCOPE_CLAIM_NAME).split(" "); diff --git a/src/main/java/org/italiangrid/storm/webdav/oauth/authzserver/jwt/DefaultJwtTokenIssuer.java b/src/main/java/org/italiangrid/storm/webdav/oauth/authzserver/jwt/DefaultJwtTokenIssuer.java index 8905ac39..53dd655f 100644 --- a/src/main/java/org/italiangrid/storm/webdav/oauth/authzserver/jwt/DefaultJwtTokenIssuer.java +++ b/src/main/java/org/italiangrid/storm/webdav/oauth/authzserver/jwt/DefaultJwtTokenIssuer.java @@ -15,7 +15,6 @@ */ package org.italiangrid.storm.webdav.oauth.authzserver.jwt; -import static java.util.Objects.isNull; import static java.util.stream.Collectors.toList; import java.time.Clock; @@ -98,7 +97,7 @@ protected Date computeTokenExpirationTimestamp(AccessTokenRequest request, Instant defaultExpiration = now.plusSeconds(properties.getMaxTokenLifetimeSec()); Instant expiration = defaultExpiration; - if (!isNull(request.getLifetime()) && request.getLifetime() > 0) { + if (request.getLifetime() != null && request.getLifetime() > 0) { requestedExpiration = Optional.of(now.plusSeconds(request.getLifetime())); } @@ -126,19 +125,19 @@ protected Date computeResourceTokenExpiration(ResourceAccessTokenRequest request public SignedJWT createAccessToken(AccessTokenRequest request, Authentication authentication) { Set tokenAuthorities = Sets.newHashSet(); - + Set saAuthorities = policyService.getSAPermissions(authentication); tokenAuthorities.addAll(saAuthorities); - + tokenAuthorities.addAll(authentication.getAuthorities()); - + JWTClaimsSet.Builder claimsSet = new JWTClaimsSet.Builder(); claimsSet.issuer(properties.getIssuer()); claimsSet.audience(properties.getIssuer()); claimsSet.subject(authentication.getName()); claimsSet.expirationTime(computeTokenExpirationTimestamp(request, authentication)); - + claimsSet.claim(CLAIM_AUTHORITIES, tokenAuthorities.stream().map(Object::toString).collect(toList())); @@ -162,7 +161,7 @@ public SignedJWT createResourceAccessToken(ResourceAccessTokenRequest request, claimsSet.subject(helper.getPrincipalAsString(authentication)); claimsSet.expirationTime(computeResourceTokenExpiration(request)); - + claimsSet.claim(PATH_CLAIM, request.getPath()); claimsSet.claim(PERMS_CLAIM, request.getPermission().name()); claimsSet.claim(ORIGIN_CLAIM, request.getOrigin()); diff --git a/src/main/java/org/italiangrid/storm/webdav/oauth/utils/DefaultOidcConfigurationFetcher.java b/src/main/java/org/italiangrid/storm/webdav/oauth/utils/DefaultOidcConfigurationFetcher.java index 4515bf3c..f1c1c1f3 100644 --- a/src/main/java/org/italiangrid/storm/webdav/oauth/utils/DefaultOidcConfigurationFetcher.java +++ b/src/main/java/org/italiangrid/storm/webdav/oauth/utils/DefaultOidcConfigurationFetcher.java @@ -21,7 +21,6 @@ import java.time.Duration; import java.util.Arrays; import java.util.Map; -import java.util.Objects; import org.italiangrid.storm.webdav.config.OAuthProperties; import org.slf4j.Logger; @@ -100,7 +99,7 @@ public Map loadConfigurationForIssuer(String issuer) { throw new OidcConfigurationResolutionError( format("Received status code: %s", response.getStatusCodeValue())); } - if (Objects.isNull(response.getBody())) { + if (response.getBody() == null) { throw new OidcConfigurationResolutionError("Received null body"); } metadataChecks(issuer, response.getBody()); diff --git a/src/main/java/org/italiangrid/storm/webdav/oauth/validator/AudienceValidator.java b/src/main/java/org/italiangrid/storm/webdav/oauth/validator/AudienceValidator.java index 1403f880..252d57cc 100644 --- a/src/main/java/org/italiangrid/storm/webdav/oauth/validator/AudienceValidator.java +++ b/src/main/java/org/italiangrid/storm/webdav/oauth/validator/AudienceValidator.java @@ -16,7 +16,6 @@ package org.italiangrid.storm.webdav.oauth.validator; import static com.google.common.base.Preconditions.checkArgument; -import static java.util.Objects.isNull; import java.util.Set; @@ -45,7 +44,7 @@ public class AudienceValidator implements OAuth2TokenValidator { OAuth2TokenValidatorResult.failure(INVALID_AUDIENCE_ERROR); public AudienceValidator(AuthorizationServer server) { - checkArgument(!isNull(server.getAudiences()), "null audiences"); + checkArgument(server.getAudiences() != null, "null audiences"); checkArgument(!server.getAudiences().isEmpty(), "empty audiences"); requiredAudiences.addAll(server.getAudiences()); } @@ -53,7 +52,7 @@ public AudienceValidator(AuthorizationServer server) { @Override public OAuth2TokenValidatorResult validate(Jwt jwt) { - if (isNull(jwt.getAudience()) || jwt.getAudience().isEmpty()) { + if (jwt.getAudience() == null || jwt.getAudience().isEmpty()) { return SUCCESS; } @@ -65,7 +64,7 @@ public OAuth2TokenValidatorResult validate(Jwt jwt) { LOG.debug("Audience check failed. Token audience: {}, local audience: {}", jwt.getAudience(), requiredAudiences); - + return INVALID_AUDIENCE; } diff --git a/src/main/java/org/italiangrid/storm/webdav/oauth/validator/WlcgProfileValidator.java b/src/main/java/org/italiangrid/storm/webdav/oauth/validator/WlcgProfileValidator.java index 5c55ffa0..2c59c7a0 100644 --- a/src/main/java/org/italiangrid/storm/webdav/oauth/validator/WlcgProfileValidator.java +++ b/src/main/java/org/italiangrid/storm/webdav/oauth/validator/WlcgProfileValidator.java @@ -16,7 +16,6 @@ package org.italiangrid.storm.webdav.oauth.validator; import static com.google.common.base.Strings.isNullOrEmpty; -import static java.util.Objects.isNull; import java.util.Collections; import java.util.Set; @@ -81,23 +80,23 @@ public OAuth2TokenValidatorResult validate(Jwt token) { return OAuth2TokenValidatorResult.failure(MISSING_SCOPE); } - if (isNull(token.getNotBefore())) { + if (token.getNotBefore() == null) { return OAuth2TokenValidatorResult.failure(MISSING_NBF); } - if (isNull(token.getExpiresAt())) { + if (token.getExpiresAt() == null) { return OAuth2TokenValidatorResult.failure(MISSING_EXP); } - if (isNull(token.getSubject())) { + if (token.getSubject() == null) { return OAuth2TokenValidatorResult.failure(MISSING_SUB); } - if (isNull(token.getAudience()) || token.getAudience().isEmpty()) { + if (token.getAudience() == null || token.getAudience().isEmpty()) { return OAuth2TokenValidatorResult.failure(MISSING_AUD); } - - if (isNull(token.getId())) { + + if (token.getId() == null) { return OAuth2TokenValidatorResult.failure(MISSING_JTI); } diff --git a/src/main/java/org/italiangrid/storm/webdav/oidc/ClientRegistrationCacheLoader.java b/src/main/java/org/italiangrid/storm/webdav/oidc/ClientRegistrationCacheLoader.java index 61321d47..46bf17ac 100644 --- a/src/main/java/org/italiangrid/storm/webdav/oidc/ClientRegistrationCacheLoader.java +++ b/src/main/java/org/italiangrid/storm/webdav/oidc/ClientRegistrationCacheLoader.java @@ -16,7 +16,6 @@ package org.italiangrid.storm.webdav.oidc; import static java.lang.String.format; -import static java.util.Objects.isNull; import java.util.Map; import java.util.concurrent.ExecutorService; @@ -111,7 +110,7 @@ private static Builder getBuilder(Builder builder, Provider provider) { public ClientRegistration load(String key) throws Exception { OAuth2ClientProperties.Registration reg = clientProperties.getRegistration().get(key); - if (isNull(reg)) { + if (reg == null) { return null; } diff --git a/src/main/java/org/italiangrid/storm/webdav/oidc/OidcGrantedAuthoritiesMapper.java b/src/main/java/org/italiangrid/storm/webdav/oidc/OidcGrantedAuthoritiesMapper.java index 6c5629b3..d30036ba 100644 --- a/src/main/java/org/italiangrid/storm/webdav/oidc/OidcGrantedAuthoritiesMapper.java +++ b/src/main/java/org/italiangrid/storm/webdav/oidc/OidcGrantedAuthoritiesMapper.java @@ -15,8 +15,6 @@ */ package org.italiangrid.storm.webdav.oidc; -import static java.util.Objects.isNull; - import java.util.Collection; import java.util.List; import java.util.Set; @@ -51,7 +49,7 @@ protected Collection grantGroupAuthorities(OidcUserAuthority u for (String groupClaimName : OAUTH_GROUP_CLAIM_NAMES) { List groups = userAuthority.getIdToken().getClaimAsStringList(groupClaimName); - if (!isNull(groups)) { + if (groups != null) { groups.stream() .map(g -> new JwtGroupAuthority(idTokenIssuer, g)) .forEach(groupAuthorities::add); diff --git a/src/main/java/org/italiangrid/storm/webdav/redirector/RedirectFilter.java b/src/main/java/org/italiangrid/storm/webdav/redirector/RedirectFilter.java index 12390af5..5b7d2ab8 100644 --- a/src/main/java/org/italiangrid/storm/webdav/redirector/RedirectFilter.java +++ b/src/main/java/org/italiangrid/storm/webdav/redirector/RedirectFilter.java @@ -17,7 +17,6 @@ import java.io.IOException; import java.nio.file.Path; -import java.util.Objects; import javax.servlet.Filter; import javax.servlet.FilterChain; @@ -60,7 +59,7 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha SecurityContext context = resolveSecurityContext(); if (isRedirectable(req)) { - + res.setHeader(LOCATION, service.buildRedirect(context.getAuthentication(), req, res)); res.setStatus(HttpServletResponse.SC_TEMPORARY_REDIRECT); @@ -73,7 +72,7 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha private SecurityContext resolveSecurityContext() { SecurityContext context = SecurityContextHolder.getContext(); - if (Objects.isNull(context)) { + if (context == null) { throw new RedirectError("Failed to enstabilish a valid security context"); } @@ -103,7 +102,7 @@ private boolean requestedResourceExistsAndIsAFile(HttpServletRequest req) { String path = getSerlvetRequestPath(req); Path p = pathResolver.getPath(path); - if (Objects.isNull(p)) { + if (p == null) { return false; } diff --git a/src/main/java/org/italiangrid/storm/webdav/server/DefaultPathResolver.java b/src/main/java/org/italiangrid/storm/webdav/server/DefaultPathResolver.java index c56d551d..a4415917 100644 --- a/src/main/java/org/italiangrid/storm/webdav/server/DefaultPathResolver.java +++ b/src/main/java/org/italiangrid/storm/webdav/server/DefaultPathResolver.java @@ -16,7 +16,6 @@ package org.italiangrid.storm.webdav.server; import static java.nio.file.LinkOption.NOFOLLOW_LINKS; -import static java.util.Objects.isNull; import java.nio.file.Files; import java.nio.file.Path; @@ -67,13 +66,13 @@ protected String stripContextPath(String context, String path) { @Override public String resolvePath(String pathInContext) { - if (isNull(pathInContext)) { + if (pathInContext == null) { return null; } Path p = getPath(pathInContext); - if (!isNull(p)) { + if (p != null) { return p.toString(); } @@ -102,7 +101,7 @@ public StorageAreaInfo resolveStorageArea(String pathInContext) { public boolean pathExists(String pathInContext) { String resolvedPath = resolvePath(pathInContext); - if (isNull(resolvedPath)) { + if (resolvedPath == null) { return false; } @@ -112,7 +111,7 @@ public boolean pathExists(String pathInContext) { @Override public Path getPath(String pathInContext) { - if (isNull(pathInContext)) { + if (pathInContext == null) { return null; } diff --git a/src/main/java/org/italiangrid/storm/webdav/server/TLSServerConnectorBuilder.java b/src/main/java/org/italiangrid/storm/webdav/server/TLSServerConnectorBuilder.java index 67379b58..5d735f3d 100644 --- a/src/main/java/org/italiangrid/storm/webdav/server/TLSServerConnectorBuilder.java +++ b/src/main/java/org/italiangrid/storm/webdav/server/TLSServerConnectorBuilder.java @@ -15,8 +15,6 @@ */ package org.italiangrid.storm.webdav.server; -import static java.util.Objects.isNull; - import java.io.File; import java.io.IOException; import java.security.KeyManagementException; @@ -56,7 +54,7 @@ /** * A builder that configures a Jetty server TLS connector integrated with CANL * {@link X509CertChainValidatorExt} certificate validation services. - * + * */ public class TLSServerConnectorBuilder { @@ -193,7 +191,7 @@ public class TLSServerConnectorBuilder { /** * Returns an instance of the {@link TLSServerConnectorBuilder}. - * + * * @param s the {@link Server} for which the connector is being created * @param certificateValidator a {@link X509CertChainValidatorExt} used to validate certificates * @return an instance of the {@link TLSServerConnectorBuilder} @@ -206,7 +204,7 @@ public static TLSServerConnectorBuilder instance(Server s, /** * Private ctor. - * + * * @param s the {@link Server} for which the connector is being created * @param certificateValidator a {@link X509CertChainValidatorExt} used to validate certificates */ @@ -254,7 +252,7 @@ private void loadCredentials() { /** * Configures SSL session parameters for the jetty {@link SslContextFactory}. - * + * * @param contextFactory the {@link SslContextFactory} being configured */ private void configureContextFactory(SslContextFactory.Server contextFactory) { @@ -296,7 +294,7 @@ private void configureContextFactory(SslContextFactory.Server contextFactory) { /** * Builds a default {@link HttpConfiguration} for the TLS-enabled connector being created - * + * * @return the default {@link HttpConfiguration} */ private HttpConfiguration defaultHttpConfiguration() { @@ -323,7 +321,7 @@ private HttpConfiguration defaultHttpConfiguration() { /** * Gives access to the {@link HttpConfiguration} used for the TLS-enabled connector being created. * If the configuration is not set, it creates it using {@link #defaultHttpConfiguration()}. - * + * * @return the {@link HttpConfiguration} being used for the TLS-enabled connector. */ public HttpConfiguration httpConfiguration() { @@ -338,7 +336,7 @@ public HttpConfiguration httpConfiguration() { /** * Sets the port for the connector being created. - * + * * @param port the port for the connector * @return this builder */ @@ -350,7 +348,7 @@ public TLSServerConnectorBuilder withPort(int port) { /** * Sets the certificate file for the connector being created. - * + * * @param certificateFile the certificate file * @return this builder */ @@ -362,7 +360,7 @@ public TLSServerConnectorBuilder withCertificateFile(String certificateFile) { /** * Sets the certificate key file for the connector being created. - * + * * @param certificateKeyFile the certificate key file * @return this builder */ @@ -374,7 +372,7 @@ public TLSServerConnectorBuilder withCertificateKeyFile(String certificateKeyFil /** * The the certificate key password for the connector being built - * + * * @param certificateKeyPassword the certificate key password * @return this builder */ @@ -387,7 +385,7 @@ public TLSServerConnectorBuilder withCertificateKeyPassword(char[] certificateKe /** * Sets the {@link SslContextFactory#setNeedClientAuth(boolean)} parameter for the connector being * created. - * + * * @param needClientAuth true if client authentication is required * @return this builder */ @@ -400,7 +398,7 @@ public TLSServerConnectorBuilder withNeedClientAuth(boolean needClientAuth) { /** * Sets the {@link SslContextFactory#setWantClientAuth(boolean)} parameter for the connector being * created. - * + * * @param wantClientAuth true if client authentication is wanted * @return this builder */ @@ -412,7 +410,7 @@ public TLSServerConnectorBuilder withWantClientAuth(boolean wantClientAuth) { /** * Sets SSL included protocols. See {@link SslContextFactory#setIncludeProtocols(String...)}. - * + * * @param includeProtocols the array of included protocol names * @return this builder */ @@ -424,7 +422,7 @@ public TLSServerConnectorBuilder withIncludeProtocols(String... includeProtocols /** * Sets SSL excluded protocols. See {@link SslContextFactory#setExcludeProtocols(String...)}. - * + * * @param excludeProtocols the array of excluded protocol names * @return this builder */ @@ -436,7 +434,7 @@ public TLSServerConnectorBuilder withExcludeProtocols(String... excludeProtocols /** * Sets the SSL included cipher suites. - * + * * @param includeCipherSuites the array of included cipher suites. * @return this builder */ @@ -448,7 +446,7 @@ public TLSServerConnectorBuilder withIncludeCipherSuites(String... includeCipher /** * Sets the SSL ecluded cipher suites. - * + * * @param excludeCipherSuites the array of excluded cipher suites. * @return this builder */ @@ -460,7 +458,7 @@ public TLSServerConnectorBuilder withExcludeCipherSuites(String... excludeCipher /** * Sets the {@link HttpConfiguration} for the connector being built. - * + * * @param conf the {@link HttpConfiguration} to use * @return this builder */ @@ -472,7 +470,7 @@ public TLSServerConnectorBuilder withHttpConfiguration(HttpConfiguration conf) { /** * Sets the {@link KeyManager} for the connector being built. - * + * * @param km the {@link KeyManager} to use * @return this builder */ @@ -539,7 +537,7 @@ private SSLContext buildSSLContext() { if (useConscrypt) { - if (isNull(Security.getProvider(CONSCRYPT_PROVIDER))) { + if (Security.getProvider(CONSCRYPT_PROVIDER) == null) { Security.addProvider(new OpenSSLProvider()); } @@ -564,7 +562,7 @@ private SSLContext buildSSLContext() { /** * Builds a {@link ServerConnector} based on the {@link TLSServerConnectorBuilder} parameters - * + * * @return a {@link ServerConnector} */ public ServerConnector build() { @@ -637,7 +635,7 @@ private ALPNServerConnectionFactory createAlpnProtocolFactory( /** * Checks that file exists and is readable. - * + * * @param f the {@link File} to be checked * @param prefix A prefix string for the error message, in case the file does not exist and is not * readable diff --git a/src/main/java/org/italiangrid/storm/webdav/server/servlet/MiltonFilter.java b/src/main/java/org/italiangrid/storm/webdav/server/servlet/MiltonFilter.java index 251f6d54..54cd7762 100644 --- a/src/main/java/org/italiangrid/storm/webdav/server/servlet/MiltonFilter.java +++ b/src/main/java/org/italiangrid/storm/webdav/server/servlet/MiltonFilter.java @@ -15,8 +15,6 @@ */ package org.italiangrid.storm.webdav.server.servlet; -import static java.util.Objects.isNull; - import java.io.IOException; import java.util.Set; @@ -101,7 +99,7 @@ public void init(FilterConfig config) throws ServletException { servletContext = config.getServletContext(); - if (isNull(miltonHTTPManager)) { + if (miltonHTTPManager == null) { initMiltonHTTPManager(servletContext); } diff --git a/src/main/java/org/italiangrid/storm/webdav/spring/AppConfig.java b/src/main/java/org/italiangrid/storm/webdav/spring/AppConfig.java index 5c502f86..46dccccb 100644 --- a/src/main/java/org/italiangrid/storm/webdav/spring/AppConfig.java +++ b/src/main/java/org/italiangrid/storm/webdav/spring/AppConfig.java @@ -15,7 +15,6 @@ */ package org.italiangrid.storm.webdav.spring; -import static java.util.Objects.isNull; import static org.italiangrid.storm.webdav.server.TLSServerConnectorBuilder.CONSCRYPT_PROVIDER; import java.io.IOException; @@ -247,7 +246,7 @@ HttpClientConnectionManager tpcClientConnectionManager(ThirdPartyCopyProperties SSLContext ctx; if (props.isUseConscrypt()) { - if (isNull(Security.getProvider(CONSCRYPT_PROVIDER))) { + if (Security.getProvider(CONSCRYPT_PROVIDER) == null) { Security.addProvider(new OpenSSLProvider()); } ctx = SSLContext.getInstance(props.getTlsProtocol(), CONSCRYPT_PROVIDER); diff --git a/src/main/java/org/italiangrid/storm/webdav/tpc/StaticHostListLocalURLService.java b/src/main/java/org/italiangrid/storm/webdav/tpc/StaticHostListLocalURLService.java index 10240e45..d405041f 100644 --- a/src/main/java/org/italiangrid/storm/webdav/tpc/StaticHostListLocalURLService.java +++ b/src/main/java/org/italiangrid/storm/webdav/tpc/StaticHostListLocalURLService.java @@ -17,7 +17,6 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; -import static java.util.Objects.isNull; import java.net.URI; import java.net.URISyntaxException; @@ -42,8 +41,7 @@ public boolean isLocalURL(String url) { URI uri = new URI(url); - if (isNull(uri.getScheme()) - || (!isNull(uri.getHost()) && uri.getHost().equals("localhost"))) { + if (uri.getScheme() == null || (uri.getHost() != null && uri.getHost().equals("localhost"))) { return true; } diff --git a/src/main/java/org/italiangrid/storm/webdav/tpc/TransferFilterSupport.java b/src/main/java/org/italiangrid/storm/webdav/tpc/TransferFilterSupport.java index 54046d8e..448f09d1 100644 --- a/src/main/java/org/italiangrid/storm/webdav/tpc/TransferFilterSupport.java +++ b/src/main/java/org/italiangrid/storm/webdav/tpc/TransferFilterSupport.java @@ -16,7 +16,6 @@ package org.italiangrid.storm.webdav.tpc; import static java.lang.String.format; -import static java.util.Objects.isNull; import static javax.servlet.http.HttpServletResponse.SC_PRECONDITION_FAILED; import static org.springframework.http.HttpStatus.BAD_REQUEST; @@ -131,7 +130,7 @@ protected boolean overwriteRequested(HttpServletRequest request) { protected boolean isSupportedTransferURI(URI uri) { - return SUPPORTED_PROTOCOLS.contains(uri.getScheme()) && !isNull(uri.getPath()); + return SUPPORTED_PROTOCOLS.contains(uri.getScheme()) && uri.getPath() != null; } protected boolean validTransferURI(String xferUri) { diff --git a/src/main/java/org/italiangrid/storm/webdav/tpc/http/GetResponseHandler.java b/src/main/java/org/italiangrid/storm/webdav/tpc/http/GetResponseHandler.java index 1a2941d6..98b16e66 100644 --- a/src/main/java/org/italiangrid/storm/webdav/tpc/http/GetResponseHandler.java +++ b/src/main/java/org/italiangrid/storm/webdav/tpc/http/GetResponseHandler.java @@ -15,8 +15,6 @@ */ package org.italiangrid.storm.webdav.tpc.http; -import static java.util.Objects.isNull; - import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -70,7 +68,7 @@ private void writeEntityToStream(HttpEntity entity, OutputStream os) final InputStream inStream = entity.getContent(); - if (!isNull(inStream)) { + if (inStream != null) { try { int l; final byte[] tmp = new byte[bufferSize]; diff --git a/src/main/java/org/italiangrid/storm/webdav/tpc/http/ResponseHandlerSupport.java b/src/main/java/org/italiangrid/storm/webdav/tpc/http/ResponseHandlerSupport.java index 759cd55d..7ef4bb7d 100644 --- a/src/main/java/org/italiangrid/storm/webdav/tpc/http/ResponseHandlerSupport.java +++ b/src/main/java/org/italiangrid/storm/webdav/tpc/http/ResponseHandlerSupport.java @@ -15,8 +15,6 @@ */ package org.italiangrid.storm.webdav.tpc.http; -import static java.util.Objects.isNull; - import java.util.Map; import org.apache.http.StatusLine; @@ -30,9 +28,9 @@ public abstract class ResponseHandlerSupport { protected ResponseHandlerSupport(Map mdcContextMap) { this.mdcContextMap = mdcContextMap; } - + protected void setupMDC() { - if (!isNull(mdcContextMap)) { + if (mdcContextMap != null) { MDC.setContextMap(mdcContextMap); } } diff --git a/src/main/java/org/italiangrid/storm/webdav/tpc/transfer/impl/TransferRequestImpl.java b/src/main/java/org/italiangrid/storm/webdav/tpc/transfer/impl/TransferRequestImpl.java index 737ac992..67100293 100644 --- a/src/main/java/org/italiangrid/storm/webdav/tpc/transfer/impl/TransferRequestImpl.java +++ b/src/main/java/org/italiangrid/storm/webdav/tpc/transfer/impl/TransferRequestImpl.java @@ -18,7 +18,6 @@ import java.net.URI; import java.time.Duration; import java.time.Instant; -import java.util.Objects; import java.util.Optional; import org.italiangrid.storm.webdav.scitag.SciTag; @@ -165,7 +164,7 @@ public long bytesTransferred() { @Override public Duration duration() { - if (Objects.isNull(startTime) || Objects.isNull(endTime)) { + if (startTime == null || endTime == null) { LOG.debug("Duration called before end of trasnfer, will return ZERO"); return Duration.ZERO; } diff --git a/src/test/java/org/italiangrid/storm/webdav/test/utils/voms/VOMSSecurityContextBuilder.java b/src/test/java/org/italiangrid/storm/webdav/test/utils/voms/VOMSSecurityContextBuilder.java index 892ee721..66fc1e19 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/utils/voms/VOMSSecurityContextBuilder.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/utils/voms/VOMSSecurityContextBuilder.java @@ -15,7 +15,6 @@ */ package org.italiangrid.storm.webdav.test.utils.voms; -import static java.util.Objects.isNull; import static org.mockito.Mockito.when; import java.time.Clock; @@ -64,7 +63,7 @@ public VOMSSecurityContextBuilder subject(String s) { public VOMSSecurityContextBuilder vos(String... vos) { for (String vo : vos) { - if (!isNull(authorities)) { + if (authorities != null) { authorities.add(new VOMSVOAuthority(vo)); } else { @@ -76,7 +75,7 @@ public VOMSSecurityContextBuilder vos(String... vos) { public VOMSSecurityContextBuilder saReadPermissions(String... sas) { for (String sa : sas) { - if (!isNull(authorities)) { + if (authorities != null) { authorities.add(SAPermission.canRead(sa)); } else { authorities = Lists.newArrayList(SAPermission.canRead(sa)); @@ -87,7 +86,7 @@ public VOMSSecurityContextBuilder saReadPermissions(String... sas) { public VOMSSecurityContextBuilder saWritePermissions(String... sas) { for (String sa : sas) { - if (!isNull(authorities)) { + if (authorities != null) { authorities.add(SAPermission.canWrite(sa)); } else { authorities = Lists.newArrayList(SAPermission.canWrite(sa)); From 208f499e8b060106f4ec0432134c15dbfe4a484a Mon Sep 17 00:00:00 2001 From: Luca Bassi Date: Thu, 31 Oct 2024 15:45:45 +0100 Subject: [PATCH 11/22] Fix sonar issues --- .../ErrorPageAuthenticationEntryPoint.java | 4 +- .../storm/webdav/authn/PrincipalHelper.java | 6 +- .../authz/VOMSAuthenticationFilter.java | 4 +- .../authz/VOMSPreAuthDetailsSource.java | 2 +- .../authz/pdp/LocalAuthorizationPdp.java | 5 +- .../vomap/DefaultVOMembershipProvider.java | 1 - .../vomap/VOMapDetailServiceBuilder.java | 14 +---- .../voters/FineGrainedCopyMoveAuthzVoter.java | 3 +- .../voters/WlcgScopeAuthzCopyMoveVoter.java | 3 +- .../config/FineGrainedAuthzPolicyParser.java | 2 +- .../webdav/config/SAConfigurationParser.java | 21 +++---- .../DefaultExtendedFileAttributesHelper.java | 20 +++---- .../webdav/milton/StoRMFileResource.java | 15 +++-- .../webdav/milton/StoRMMiltonRequest.java | 6 +- .../oauth/StormJwtAuthoritiesConverter.java | 2 +- .../oauth/authzserver/ErrorResponseDTO.java | 6 +- .../ResourceAccessTokenRequest.java | 2 +- .../oauth/authzserver/TokenResponseDTO.java | 4 +- .../authzserver/web/AuthzServerMetadata.java | 4 +- .../utils/TrustedJwtDecoderCacheLoader.java | 2 +- .../oauth/validator/WlcgProfileValidator.java | 19 +++--- .../oidc/ClientRegistrationCacheLoader.java | 6 +- .../webdav/redirector/RedirectConstants.java | 6 +- .../webdav/redirector/RedirectFilter.java | 4 +- .../webdav/server/servlet/MiltonFilter.java | 2 +- .../webdav/server/util/CANLListener.java | 6 +- .../storm/webdav/spring/AppConfig.java | 14 ++--- .../webdav/spring/web/SecurityConfig.java | 2 +- .../spring/web/ServletConfiguration.java | 13 ++--- .../storm/webdav/tpc/TpcUtils.java | 18 +++--- .../storm/webdav/tpc/TransferConstants.java | 40 +++++++------ .../storm/webdav/tpc/TransferFilter.java | 18 +++--- .../webdav/tpc/TransferFilterSupport.java | 50 +++++++++------- .../webdav/tpc/http/HttpTransferClient.java | 16 ++--- .../tpc/utils/Adler32DigestHeaderHelper.java | 4 +- .../storm/webdav/tpc/utils/ClientInfo.java | 14 +++-- .../storm/webdav/tpc/utils/UrlHelper.java | 8 +-- .../storm/webdav/utils/RangeCopyHelper.java | 2 + .../webdav/web/ViewUtilsInterceptor.java | 4 +- src/main/resources/templates/400.html | 4 +- src/main/resources/templates/401.html | 4 +- src/main/resources/templates/403.html | 4 +- src/main/resources/templates/404.html | 4 +- src/main/resources/templates/405.html | 4 +- src/main/resources/templates/authn-info.html | 4 +- src/main/resources/templates/error.html | 4 +- src/main/resources/templates/jetty-dir.html | 4 +- src/main/resources/templates/layout.html | 4 +- src/main/resources/templates/oidc-login.html | 4 +- src/main/resources/templates/sa-index.html | 4 +- .../LocalAuthzIntegrationTests.java | 10 ++-- .../webdav/test/authz/pdp/AuthzPdpTests.java | 18 +++--- .../test/authz/pdp/LocalAuthzPdpTests.java | 12 ++-- .../test/authz/pdp/PolicyParserTests.java | 6 +- .../pdp/PolicyPropertiesValidationTests.java | 10 ++-- .../test/authz/vomap/PathResolverTests.java | 12 ++-- .../webdav/test/authz/vomap/VOMSMapTests.java | 14 ++--- .../test/checksum/ChecksumHelperTest.java | 8 +-- .../MacaroonRequestIntegrationTests.java | 10 ++-- .../OAuthAuthzServerIntegrationTests.java | 16 ++--- .../webdav/test/oauth/jwt/JwtIssuerTest.java | 12 ++-- .../test/oauth/jwt/TokenServiceTest.java | 2 +- .../validator/AudienceValidatorTests.java | 14 ++--- .../RandomReplicaSelectorTests.java | 8 +-- .../test/redirector/RedirectFilterTests.java | 16 ++--- .../redirector/RedirectionServiceTests.java | 10 ++-- .../webdav/test/tpc/ClientInfoParserTest.java | 4 +- .../webdav/test/tpc/PullTransferTest.java | 20 ++++--- .../webdav/test/tpc/PushTransferTest.java | 20 ++++--- .../test/tpc/SciTagFilterActivationTest.java | 3 +- .../tpc/TransferFilterActivationTest.java | 39 +++++++------ .../test/tpc/TransferFilterTestSupport.java | 5 +- .../tpc/TransferRequestValidationTest.java | 58 ++++++++++--------- .../test/tpc/TransferReturnStatusTest.java | 16 ++--- .../webdav/test/tpc/TransferStatsTest.java | 6 +- .../webdav/test/tpc/http/ClientTest.java | 7 ++- .../webdav/test/tpc/http/DigestTest.java | 4 +- .../test/tpc/http/GetResponseHandlerTest.java | 21 +++---- .../integration/TpcClientRedirectionTest.java | 11 +--- .../http/integration/TpcIntegrationTest.java | 10 ++-- .../test/tpc/urlservice/URLServiceTest.java | 10 ++-- .../storm/webdav/test/utils/IOUtilsTest.java | 12 ++-- 82 files changed, 421 insertions(+), 419 deletions(-) diff --git a/src/main/java/org/italiangrid/storm/webdav/authn/ErrorPageAuthenticationEntryPoint.java b/src/main/java/org/italiangrid/storm/webdav/authn/ErrorPageAuthenticationEntryPoint.java index 7f869a1c..80216572 100644 --- a/src/main/java/org/italiangrid/storm/webdav/authn/ErrorPageAuthenticationEntryPoint.java +++ b/src/main/java/org/italiangrid/storm/webdav/authn/ErrorPageAuthenticationEntryPoint.java @@ -35,7 +35,7 @@ public class ErrorPageAuthenticationEntryPoint implements AuthenticationEntryPoint { - final String errorPage = "/errors/401"; + static final String ERROR_PAGE = "/errors/401"; @Override public void commence(HttpServletRequest request, HttpServletResponse response, @@ -73,7 +73,7 @@ public void commence(HttpServletRequest request, HttpServletResponse response, request.setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION, authException); - RequestDispatcher dispatcher = request.getRequestDispatcher(errorPage); + RequestDispatcher dispatcher = request.getRequestDispatcher(ERROR_PAGE); dispatcher.forward(request, response); } } diff --git a/src/main/java/org/italiangrid/storm/webdav/authn/PrincipalHelper.java b/src/main/java/org/italiangrid/storm/webdav/authn/PrincipalHelper.java index d2f0de9b..1b2427dc 100644 --- a/src/main/java/org/italiangrid/storm/webdav/authn/PrincipalHelper.java +++ b/src/main/java/org/italiangrid/storm/webdav/authn/PrincipalHelper.java @@ -43,13 +43,11 @@ public PrincipalHelper(ServiceConfigurationProperties config) throws MalformedUR public String getPrincipalAsString(Authentication authn) { if (authn == null || authn instanceof AnonymousAuthenticationToken) { - return "anonymous"; + return ANONYMOUS; } else if (authn instanceof OAuth2AuthenticationToken) { OAuth2AuthenticationToken authToken = (OAuth2AuthenticationToken) authn; Map attributes = authToken.getPrincipal().getAttributes(); - String subjectIssuer = String.format("%s@%s", attributes.get("sub"), attributes.get("iss")); - return subjectIssuer; - + return String.format("%s@%s", attributes.get("sub"), attributes.get("iss")); } else if (authn instanceof PreAuthenticatedAuthenticationToken) { return authn.getName(); } else if (authn instanceof JwtAuthenticationToken) { diff --git a/src/main/java/org/italiangrid/storm/webdav/authz/VOMSAuthenticationFilter.java b/src/main/java/org/italiangrid/storm/webdav/authz/VOMSAuthenticationFilter.java index d6ea1680..843424ec 100644 --- a/src/main/java/org/italiangrid/storm/webdav/authz/VOMSAuthenticationFilter.java +++ b/src/main/java/org/italiangrid/storm/webdav/authz/VOMSAuthenticationFilter.java @@ -50,9 +50,9 @@ protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) { public Object extractPrincipal(X509Certificate cert) { - return cert.getSubjectDN().getName(); + return cert.getSubjectX500Principal().getName(); } - + @Override public boolean principalChanged(HttpServletRequest request, Authentication currentAuthentication) { diff --git a/src/main/java/org/italiangrid/storm/webdav/authz/VOMSPreAuthDetailsSource.java b/src/main/java/org/italiangrid/storm/webdav/authz/VOMSPreAuthDetailsSource.java index fa4c3cd9..8bd704fc 100644 --- a/src/main/java/org/italiangrid/storm/webdav/authz/VOMSPreAuthDetailsSource.java +++ b/src/main/java/org/italiangrid/storm/webdav/authz/VOMSPreAuthDetailsSource.java @@ -109,7 +109,7 @@ protected Optional getSubjectAuthority(HttpServletRequest Optional chain = Utils.getCertificateChainFromRequest(request); if (chain.isPresent()) { return Optional.of(new X509SubjectAuthority( - ProxyUtils.getEndUserCertificate(chain.get()).getSubjectDN().getName())); + ProxyUtils.getEndUserCertificate(chain.get()).getSubjectX500Principal().getName())); } return Optional.empty(); diff --git a/src/main/java/org/italiangrid/storm/webdav/authz/pdp/LocalAuthorizationPdp.java b/src/main/java/org/italiangrid/storm/webdav/authz/pdp/LocalAuthorizationPdp.java index f762177a..03c294df 100644 --- a/src/main/java/org/italiangrid/storm/webdav/authz/pdp/LocalAuthorizationPdp.java +++ b/src/main/java/org/italiangrid/storm/webdav/authz/pdp/LocalAuthorizationPdp.java @@ -25,6 +25,7 @@ import java.net.URL; import java.util.EnumSet; import java.util.Optional; +import java.util.Set; import java.util.function.Supplier; import javax.servlet.http.HttpServletRequest; @@ -42,8 +43,8 @@ public class LocalAuthorizationPdp implements PathAuthorizationPdp, TpcUtils { public static final Logger LOG = LoggerFactory.getLogger(LocalAuthorizationPdp.class); - public static final EnumSet READ_PERMS = EnumSet.of(Permission.r, Permission.rw); - public static final EnumSet WRITE_PERMS = EnumSet.of(Permission.w, Permission.rw); + private static final Set READ_PERMS = EnumSet.of(Permission.r, Permission.rw); + private static final Set WRITE_PERMS = EnumSet.of(Permission.w, Permission.rw); private final URL localAuthzServerIssuer; diff --git a/src/main/java/org/italiangrid/storm/webdav/authz/vomap/DefaultVOMembershipProvider.java b/src/main/java/org/italiangrid/storm/webdav/authz/vomap/DefaultVOMembershipProvider.java index 331b1d34..a4676653 100644 --- a/src/main/java/org/italiangrid/storm/webdav/authz/vomap/DefaultVOMembershipProvider.java +++ b/src/main/java/org/italiangrid/storm/webdav/authz/vomap/DefaultVOMembershipProvider.java @@ -58,7 +58,6 @@ public boolean hasSubjectAsMember(String subject) { } finally { refreshLock.readLock().unlock(); - ; } } diff --git a/src/main/java/org/italiangrid/storm/webdav/authz/vomap/VOMapDetailServiceBuilder.java b/src/main/java/org/italiangrid/storm/webdav/authz/vomap/VOMapDetailServiceBuilder.java index 5c5e4de2..e2657a8c 100644 --- a/src/main/java/org/italiangrid/storm/webdav/authz/vomap/VOMapDetailServiceBuilder.java +++ b/src/main/java/org/italiangrid/storm/webdav/authz/vomap/VOMapDetailServiceBuilder.java @@ -18,7 +18,6 @@ import static java.util.Collections.emptySet; import java.io.File; -import java.io.FilenameFilter; import java.util.HashSet; import java.util.Set; @@ -76,15 +75,7 @@ public VOMapDetailsService build() { File configDir = new File(serviceConf.getVOMapFilesConfigDir()); directorySanityChecks(configDir); - File[] files = configDir.listFiles(new FilenameFilter() { - - @Override - public boolean accept(File dir, String name) { - - return name.endsWith(VOMAPFILE_SUFFIX); - - } - }); + File[] files = configDir.listFiles((dir, name) -> name.endsWith(VOMAPFILE_SUFFIX)); if (files.length == 0) { logger.warn("No mapfiles found in {}. Was looking for files ending in {}", configDir, @@ -94,7 +85,7 @@ public boolean accept(File dir, String name) { - Set providers = new HashSet(); + Set providers = new HashSet<>(); for (File f : files) { try { String voName = FilenameUtils.removeExtension(f.getName()); @@ -106,7 +97,6 @@ public boolean accept(File dir, String name) { } catch (Throwable t) { logger.error("Error parsing mapfile {}: {}", f.getAbsolutePath(), t.getMessage(), t); - continue; } } diff --git a/src/main/java/org/italiangrid/storm/webdav/authz/voters/FineGrainedCopyMoveAuthzVoter.java b/src/main/java/org/italiangrid/storm/webdav/authz/voters/FineGrainedCopyMoveAuthzVoter.java index 87dc77fd..1de3c859 100644 --- a/src/main/java/org/italiangrid/storm/webdav/authz/voters/FineGrainedCopyMoveAuthzVoter.java +++ b/src/main/java/org/italiangrid/storm/webdav/authz/voters/FineGrainedCopyMoveAuthzVoter.java @@ -28,6 +28,7 @@ import org.italiangrid.storm.webdav.config.StorageAreaInfo; import org.italiangrid.storm.webdav.server.PathResolver; import org.italiangrid.storm.webdav.tpc.LocalURLService; +import org.italiangrid.storm.webdav.tpc.TransferConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.access.ConfigAttribute; @@ -51,7 +52,7 @@ public int vote(Authentication authentication, FilterInvocation filter, return ACCESS_ABSTAIN; } - String destination = filter.getRequest().getHeader(DESTINATION_HEADER); + String destination = filter.getRequest().getHeader(TransferConstants.DESTINATION_HEADER); if (destination == null) { return ACCESS_ABSTAIN; diff --git a/src/main/java/org/italiangrid/storm/webdav/authz/voters/WlcgScopeAuthzCopyMoveVoter.java b/src/main/java/org/italiangrid/storm/webdav/authz/voters/WlcgScopeAuthzCopyMoveVoter.java index 83f16dd2..f1714411 100644 --- a/src/main/java/org/italiangrid/storm/webdav/authz/voters/WlcgScopeAuthzCopyMoveVoter.java +++ b/src/main/java/org/italiangrid/storm/webdav/authz/voters/WlcgScopeAuthzCopyMoveVoter.java @@ -28,6 +28,7 @@ import org.italiangrid.storm.webdav.config.StorageAreaInfo; import org.italiangrid.storm.webdav.server.PathResolver; import org.italiangrid.storm.webdav.tpc.LocalURLService; +import org.italiangrid.storm.webdav.tpc.TransferConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.access.ConfigAttribute; @@ -57,7 +58,7 @@ public int vote(Authentication authentication, FilterInvocation filter, return ACCESS_ABSTAIN; } - String destination = filter.getRequest().getHeader(DESTINATION_HEADER); + String destination = filter.getRequest().getHeader(TransferConstants.DESTINATION_HEADER); if (destination == null) { return ACCESS_ABSTAIN; diff --git a/src/main/java/org/italiangrid/storm/webdav/config/FineGrainedAuthzPolicyParser.java b/src/main/java/org/italiangrid/storm/webdav/config/FineGrainedAuthzPolicyParser.java index 1d7922b9..715a61e1 100644 --- a/src/main/java/org/italiangrid/storm/webdav/config/FineGrainedAuthzPolicyParser.java +++ b/src/main/java/org/italiangrid/storm/webdav/config/FineGrainedAuthzPolicyParser.java @@ -162,7 +162,7 @@ PathAuthorizationPolicy parsePolicy(FineGrainedAuthzPolicyProperties policy) { policy.getPrincipals() .stream() .map(this::parsePrincipal) - .forEach(builder::withPrincipalMatcher);; + .forEach(builder::withPrincipalMatcher); return builder.build(); } diff --git a/src/main/java/org/italiangrid/storm/webdav/config/SAConfigurationParser.java b/src/main/java/org/italiangrid/storm/webdav/config/SAConfigurationParser.java index 569eba8f..753c352d 100644 --- a/src/main/java/org/italiangrid/storm/webdav/config/SAConfigurationParser.java +++ b/src/main/java/org/italiangrid/storm/webdav/config/SAConfigurationParser.java @@ -17,7 +17,6 @@ import java.io.File; import java.io.FileReader; -import java.io.FilenameFilter; import java.util.ArrayList; import java.util.List; import java.util.Properties; @@ -32,12 +31,12 @@ public class SAConfigurationParser implements StorageAreaConfiguration { - private final Set RESERVED_SA_NAMES = - Sets.newHashSet("oauth", ".well-known", "actuator", "assets", "authn-info", "logout", "oidc-login"); + private static final Set RESERVED_SA_NAMES = Sets.newHashSet("oauth", ".well-known", + "actuator", "assets", "authn-info", "logout", "oidc-login"); private final ServiceConfiguration serviceConfig; - private String PROPERTIES_FILENAME_SUFFIX = ".properties"; + private static final String PROPERTIES_FILENAME_SUFFIX = ".properties"; private List saInfos; @@ -54,15 +53,11 @@ public SAConfigurationParser(ServiceConfiguration sc) { File dir = new File(saConfDir); directorySanityChecks(dir); - File[] saFiles = dir.listFiles(new FilenameFilter() { - - @Override - public boolean accept(File file, String name) { - if (RESERVED_SA_NAMES.contains(name) && name.endsWith(PROPERTIES_FILENAME_SUFFIX)) { - log.warn("Skipping {} as it is a reserved storage area name"); - } - return (!RESERVED_SA_NAMES.contains(name) && name.endsWith(PROPERTIES_FILENAME_SUFFIX)); + File[] saFiles = dir.listFiles((file, name) -> { + if (RESERVED_SA_NAMES.contains(name) && name.endsWith(PROPERTIES_FILENAME_SUFFIX)) { + log.warn("Skipping {} as it is a reserved storage area name", name); } + return (!RESERVED_SA_NAMES.contains(name) && name.endsWith(PROPERTIES_FILENAME_SUFFIX)); }); if (saFiles.length == 0) { @@ -72,7 +67,7 @@ public boolean accept(File file, String name) { throw new StoRMIntializationError(msg); } - saInfos = new ArrayList(); + saInfos = new ArrayList<>(); for (File f : saFiles) { diff --git a/src/main/java/org/italiangrid/storm/webdav/fs/attrs/DefaultExtendedFileAttributesHelper.java b/src/main/java/org/italiangrid/storm/webdav/fs/attrs/DefaultExtendedFileAttributesHelper.java index 8bb6a7dc..04392647 100644 --- a/src/main/java/org/italiangrid/storm/webdav/fs/attrs/DefaultExtendedFileAttributesHelper.java +++ b/src/main/java/org/italiangrid/storm/webdav/fs/attrs/DefaultExtendedFileAttributesHelper.java @@ -31,6 +31,9 @@ public class DefaultExtendedFileAttributesHelper implements ExtendedAttributesHelper { + private static final String USERDEFINEDFILEATTRIBUTEVIEW_NOT_SUPPORTED_MESSAGE = + "UserDefinedFileAttributeView not supported on file "; + public static final String STORM_ADLER32_CHECKSUM_ATTR_NAME = "storm.checksum.adler32"; public DefaultExtendedFileAttributesHelper() { @@ -62,8 +65,7 @@ public void setExtendedFileAttribute(File f, String attributeName, if (faView == null) { throw new IOException( - "UserDefinedFileAttributeView not supported on file " - + f.getAbsolutePath()); + USERDEFINEDFILEATTRIBUTEVIEW_NOT_SUPPORTED_MESSAGE + f.getAbsolutePath()); } faView.write(attributeName, StandardCharsets.UTF_8.encode(attributeValue)); @@ -75,14 +77,13 @@ public String getExtendedFileAttributeValue(File f, String attributeName) checkNotNull(f); checkArgument(!isNullOrEmpty(attributeName)); - + UserDefinedFileAttributeView faView = Files.getFileAttributeView( f.toPath(), UserDefinedFileAttributeView.class); if (faView == null) { throw new IOException( - "UserDefinedFileAttributeView not supported on file " - + f.getAbsolutePath()); + USERDEFINEDFILEATTRIBUTEVIEW_NOT_SUPPORTED_MESSAGE + f.getAbsolutePath()); } return getAttributeValue(faView, attributeName); @@ -93,14 +94,13 @@ public String getExtendedFileAttributeValue(File f, String attributeName) public List getExtendedFileAttributeNames(File f) throws IOException { checkNotNull(f); - + UserDefinedFileAttributeView faView = Files.getFileAttributeView( f.toPath(), UserDefinedFileAttributeView.class); if (faView == null) { throw new IOException( - "UserDefinedFileAttributeView not supported on file " - + f.getAbsolutePath()); + USERDEFINEDFILEATTRIBUTEVIEW_NOT_SUPPORTED_MESSAGE + f.getAbsolutePath()); } return faView.list(); @@ -127,7 +127,7 @@ public String getChecksumAttribute(File f) throws IOException { public boolean fileSupportsExtendedAttributes(File f) throws IOException { checkNotNull(f); - + UserDefinedFileAttributeView faView = Files.getFileAttributeView( f.toPath(), UserDefinedFileAttributeView.class); @@ -137,7 +137,7 @@ public boolean fileSupportsExtendedAttributes(File f) throws IOException { @Override public void setChecksumAttribute(Path p, String checksumValue) throws IOException { setChecksumAttribute(p.toFile(), checksumValue); - + } @Override diff --git a/src/main/java/org/italiangrid/storm/webdav/milton/StoRMFileResource.java b/src/main/java/org/italiangrid/storm/webdav/milton/StoRMFileResource.java index 5b56cc2a..14812812 100644 --- a/src/main/java/org/italiangrid/storm/webdav/milton/StoRMFileResource.java +++ b/src/main/java/org/italiangrid/storm/webdav/milton/StoRMFileResource.java @@ -184,14 +184,13 @@ protected void calculateChecksum() { @Override public Object getProperty(QName name) { - if (name.getNamespaceURI().equals(STORM_NAMESPACE_URI)) { - if (name.getLocalPart().equals(PROPERTY_CHECKSUM)) { - try { - return getExtendedAttributesHelper().getChecksumAttribute(getFile()); - } catch (IOException e) { - logger.warn("Errror getting checksum value for file: {}", getFile().getAbsolutePath(), e); - return null; - } + if (name.getNamespaceURI().equals(STORM_NAMESPACE_URI) + && name.getLocalPart().equals(PROPERTY_CHECKSUM)) { + try { + return getExtendedAttributesHelper().getChecksumAttribute(getFile()); + } catch (IOException e) { + logger.warn("Errror getting checksum value for file: {}", getFile().getAbsolutePath(), e); + return null; } } diff --git a/src/main/java/org/italiangrid/storm/webdav/milton/StoRMMiltonRequest.java b/src/main/java/org/italiangrid/storm/webdav/milton/StoRMMiltonRequest.java index ecc0d7c1..d1021cc4 100644 --- a/src/main/java/org/italiangrid/storm/webdav/milton/StoRMMiltonRequest.java +++ b/src/main/java/org/italiangrid/storm/webdav/milton/StoRMMiltonRequest.java @@ -28,8 +28,8 @@ public class StoRMMiltonRequest extends ServletRequest { - private static final String regex = "(http.*:\\d*)/webdav/(.*)$"; - private static final Pattern p = Pattern.compile(regex); + private static final String REGEX = "(http.*:\\d*)/webdav/(.*)$"; + private static final Pattern p = Pattern.compile(REGEX); public StoRMMiltonRequest(HttpServletRequest r, ServletContext servletContext) { @@ -60,5 +60,5 @@ public Auth getAuthorization() { // Always return null as milton is confused by the OAuth2 Bearer scheme return null; } - + } diff --git a/src/main/java/org/italiangrid/storm/webdav/oauth/StormJwtAuthoritiesConverter.java b/src/main/java/org/italiangrid/storm/webdav/oauth/StormJwtAuthoritiesConverter.java index a1df6ac8..a9f9160d 100644 --- a/src/main/java/org/italiangrid/storm/webdav/oauth/StormJwtAuthoritiesConverter.java +++ b/src/main/java/org/italiangrid/storm/webdav/oauth/StormJwtAuthoritiesConverter.java @@ -85,7 +85,7 @@ protected Set extractOauthGroupAuthorities(Jwt jwt) { String tokenIssuer = jwt.getClaimAsString(JwtClaimNames.ISS); for (String groupClaim : OAUTH_GROUP_CLAIM_NAMES) { - if (Boolean.TRUE.equals(jwt.containsClaim(groupClaim))) { + if (jwt.hasClaim(groupClaim)) { jwt.getClaimAsStringList(groupClaim) .forEach(gc -> groupAuthorities.add(new JwtGroupAuthority(tokenIssuer, gc))); break; diff --git a/src/main/java/org/italiangrid/storm/webdav/oauth/authzserver/ErrorResponseDTO.java b/src/main/java/org/italiangrid/storm/webdav/oauth/authzserver/ErrorResponseDTO.java index 23d7e6ce..2521dc6f 100644 --- a/src/main/java/org/italiangrid/storm/webdav/oauth/authzserver/ErrorResponseDTO.java +++ b/src/main/java/org/italiangrid/storm/webdav/oauth/authzserver/ErrorResponseDTO.java @@ -18,13 +18,13 @@ import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.databind.PropertyNamingStrategy; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; import com.fasterxml.jackson.databind.annotation.JsonNaming; @JsonInclude(NON_EMPTY) -@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) public class ErrorResponseDTO { - + public static final String UNSUPPORTED_GRANT_TYPE = "unsupported_grant_type"; public static final String INVALID_REQUEST = "invalid_request"; public static final String INVALID_SCOPE = "invalid_scope"; diff --git a/src/main/java/org/italiangrid/storm/webdav/oauth/authzserver/ResourceAccessTokenRequest.java b/src/main/java/org/italiangrid/storm/webdav/oauth/authzserver/ResourceAccessTokenRequest.java index e3bce43f..26a3645c 100644 --- a/src/main/java/org/italiangrid/storm/webdav/oauth/authzserver/ResourceAccessTokenRequest.java +++ b/src/main/java/org/italiangrid/storm/webdav/oauth/authzserver/ResourceAccessTokenRequest.java @@ -21,7 +21,7 @@ public enum Permission { r, w, rw - }; + } final String path; final Permission permission; diff --git a/src/main/java/org/italiangrid/storm/webdav/oauth/authzserver/TokenResponseDTO.java b/src/main/java/org/italiangrid/storm/webdav/oauth/authzserver/TokenResponseDTO.java index 06f9967a..48ef1316 100644 --- a/src/main/java/org/italiangrid/storm/webdav/oauth/authzserver/TokenResponseDTO.java +++ b/src/main/java/org/italiangrid/storm/webdav/oauth/authzserver/TokenResponseDTO.java @@ -18,11 +18,11 @@ import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.databind.PropertyNamingStrategy; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; import com.fasterxml.jackson.databind.annotation.JsonNaming; @JsonInclude(NON_EMPTY) -@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) public class TokenResponseDTO { String accessToken; diff --git a/src/main/java/org/italiangrid/storm/webdav/oauth/authzserver/web/AuthzServerMetadata.java b/src/main/java/org/italiangrid/storm/webdav/oauth/authzserver/web/AuthzServerMetadata.java index dba834b0..efb85698 100644 --- a/src/main/java/org/italiangrid/storm/webdav/oauth/authzserver/web/AuthzServerMetadata.java +++ b/src/main/java/org/italiangrid/storm/webdav/oauth/authzserver/web/AuthzServerMetadata.java @@ -21,11 +21,11 @@ import java.util.List; import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.databind.PropertyNamingStrategy; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; import com.fasterxml.jackson.databind.annotation.JsonNaming; @JsonInclude(NON_EMPTY) -@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) public class AuthzServerMetadata { private String issuer; diff --git a/src/main/java/org/italiangrid/storm/webdav/oauth/utils/TrustedJwtDecoderCacheLoader.java b/src/main/java/org/italiangrid/storm/webdav/oauth/utils/TrustedJwtDecoderCacheLoader.java index f9e19765..161e1625 100644 --- a/src/main/java/org/italiangrid/storm/webdav/oauth/utils/TrustedJwtDecoderCacheLoader.java +++ b/src/main/java/org/italiangrid/storm/webdav/oauth/utils/TrustedJwtDecoderCacheLoader.java @@ -96,7 +96,7 @@ public JwtDecoder load(String issuer) throws Exception { validators.add(new AudienceValidator(as)); } - decoder.setJwtValidator(new DelegatingOAuth2TokenValidator(validators)); + decoder.setJwtValidator(new DelegatingOAuth2TokenValidator<>(validators)); return decoder; } diff --git a/src/main/java/org/italiangrid/storm/webdav/oauth/validator/WlcgProfileValidator.java b/src/main/java/org/italiangrid/storm/webdav/oauth/validator/WlcgProfileValidator.java index 2c59c7a0..5b11a118 100644 --- a/src/main/java/org/italiangrid/storm/webdav/oauth/validator/WlcgProfileValidator.java +++ b/src/main/java/org/italiangrid/storm/webdav/oauth/validator/WlcgProfileValidator.java @@ -31,8 +31,9 @@ public class WlcgProfileValidator implements OAuth2TokenValidator { - public static final Logger LOG = LoggerFactory.getLogger(AudienceValidator.class); + public static final Logger LOG = LoggerFactory.getLogger(WlcgProfileValidator.class); + public static final String INVALID_TOKEN_ERROR_CODE = "invalid_token"; public static final String WLCG_VER_CLAIM = "wlcg.ver"; public static final String SCOPE_CLAIM = "scope"; @@ -40,25 +41,25 @@ public class WlcgProfileValidator implements OAuth2TokenValidator { Collections.unmodifiableSet(Sets.newHashSet("1.0")); private static final OAuth2Error INVALID_PROFILE_VERSION = - new OAuth2Error("invalid_token", "Unsupported WLCG token profile version", null); + new OAuth2Error(INVALID_TOKEN_ERROR_CODE, "Unsupported WLCG token profile version", null); private static final OAuth2Error MISSING_SCOPE = - new OAuth2Error("invalid_token", "scope claim not found in token", null); + new OAuth2Error(INVALID_TOKEN_ERROR_CODE, "scope claim not found in token", null); private static final OAuth2Error MISSING_NBF = - new OAuth2Error("invalid_token", "nbf claim not found in token", null); + new OAuth2Error(INVALID_TOKEN_ERROR_CODE, "nbf claim not found in token", null); private static final OAuth2Error MISSING_EXP = - new OAuth2Error("invalid_token", "exp claim not found in token", null); + new OAuth2Error(INVALID_TOKEN_ERROR_CODE, "exp claim not found in token", null); private static final OAuth2Error MISSING_SUB = - new OAuth2Error("invalid_token", "sub claim not found in token", null); + new OAuth2Error(INVALID_TOKEN_ERROR_CODE, "sub claim not found in token", null); private static final OAuth2Error MISSING_AUD = - new OAuth2Error("invalid_token", "aud claim not found in token", null); + new OAuth2Error(INVALID_TOKEN_ERROR_CODE, "aud claim not found in token", null); private static final OAuth2Error MISSING_JTI = - new OAuth2Error("invalid_token", "jti claim not found in token", null); + new OAuth2Error(INVALID_TOKEN_ERROR_CODE, "jti claim not found in token", null); private static final OAuth2TokenValidatorResult SUCCESS = OAuth2TokenValidatorResult.success(); @@ -76,7 +77,7 @@ public OAuth2TokenValidatorResult validate(Jwt token) { return OAuth2TokenValidatorResult.failure(INVALID_PROFILE_VERSION); } - if (Boolean.FALSE.equals(token.containsClaim(SCOPE_CLAIM))) { + if (!token.hasClaim(SCOPE_CLAIM)) { return OAuth2TokenValidatorResult.failure(MISSING_SCOPE); } diff --git a/src/main/java/org/italiangrid/storm/webdav/oidc/ClientRegistrationCacheLoader.java b/src/main/java/org/italiangrid/storm/webdav/oidc/ClientRegistrationCacheLoader.java index 46bf17ac..805ed969 100644 --- a/src/main/java/org/italiangrid/storm/webdav/oidc/ClientRegistrationCacheLoader.java +++ b/src/main/java/org/italiangrid/storm/webdav/oidc/ClientRegistrationCacheLoader.java @@ -70,10 +70,8 @@ private ClientRegistration getClientRegistration(String registrationId, map.from(properties::getAuthorizationGrantType) .as(AuthorizationGrantType::new) .to(builder::authorizationGrantType); - map.from(properties::getRedirectUri).to(builder::redirectUriTemplate); - map.from(properties::getScope) - .as((scope) -> StringUtils.toStringArray(scope)) - .to(builder::scope); + map.from(properties::getRedirectUri).to(builder::redirectUri); + map.from(properties::getScope).as(StringUtils::toStringArray).to(builder::scope); map.from(properties::getClientName).to(builder::clientName); return builder.build(); } diff --git a/src/main/java/org/italiangrid/storm/webdav/redirector/RedirectConstants.java b/src/main/java/org/italiangrid/storm/webdav/redirector/RedirectConstants.java index fa1d9fa6..90a24b21 100644 --- a/src/main/java/org/italiangrid/storm/webdav/redirector/RedirectConstants.java +++ b/src/main/java/org/italiangrid/storm/webdav/redirector/RedirectConstants.java @@ -15,8 +15,10 @@ */ package org.italiangrid.storm.webdav.redirector; -public interface RedirectConstants { +public final class RedirectConstants { - String ACCESS_TOKEN_PARAMETER = "access_token"; + public static final String ACCESS_TOKEN_PARAMETER = "access_token"; + + private RedirectConstants() {} } diff --git a/src/main/java/org/italiangrid/storm/webdav/redirector/RedirectFilter.java b/src/main/java/org/italiangrid/storm/webdav/redirector/RedirectFilter.java index 5b7d2ab8..7150b8e4 100644 --- a/src/main/java/org/italiangrid/storm/webdav/redirector/RedirectFilter.java +++ b/src/main/java/org/italiangrid/storm/webdav/redirector/RedirectFilter.java @@ -35,7 +35,7 @@ import com.google.common.base.Strings; -public class RedirectFilter implements Filter, TpcUtils, RedirectConstants { +public class RedirectFilter implements Filter, TpcUtils { public static final String LOCATION = "Location"; public static final String NO_REDIRECT_QUERY_PARAM = "no_redirect"; @@ -93,7 +93,7 @@ private boolean isGetOrPutRequest(HttpServletRequest req) { private boolean requestDoesNotHaveAccessToken(HttpServletRequest req) { - String accessToken = req.getParameter(ACCESS_TOKEN_PARAMETER); + String accessToken = req.getParameter(RedirectConstants.ACCESS_TOKEN_PARAMETER); return Strings.isNullOrEmpty(accessToken); } diff --git a/src/main/java/org/italiangrid/storm/webdav/server/servlet/MiltonFilter.java b/src/main/java/org/italiangrid/storm/webdav/server/servlet/MiltonFilter.java index 54cd7762..813c805a 100644 --- a/src/main/java/org/italiangrid/storm/webdav/server/servlet/MiltonFilter.java +++ b/src/main/java/org/italiangrid/storm/webdav/server/servlet/MiltonFilter.java @@ -126,7 +126,7 @@ public void doMilton(HttpServletRequest request, HttpServletResponse response) { try { // Is this really needed? - MiltonServlet.setThreadlocals((HttpServletRequest) request, (HttpServletResponse) response); + MiltonServlet.setThreadlocals(request, response); Request miltonReq = new StoRMMiltonRequest(request, servletContext); diff --git a/src/main/java/org/italiangrid/storm/webdav/server/util/CANLListener.java b/src/main/java/org/italiangrid/storm/webdav/server/util/CANLListener.java index 59b5f57e..872eb24e 100644 --- a/src/main/java/org/italiangrid/storm/webdav/server/util/CANLListener.java +++ b/src/main/java/org/italiangrid/storm/webdav/server/util/CANLListener.java @@ -35,13 +35,13 @@ public void loadingNotification(String location, String type, Severity level, Ex location = location.substring(5, location.length()); if (level.equals(Severity.ERROR)) { - LOG.error("Error for {} {}: {}.", new Object[] {type, location, cause.getMessage()}); + LOG.error("Error for {} {}: {}.", type, location, cause.getMessage()); } else if (level.equals(Severity.WARNING)) { - LOG.debug("Warning for {} {}: {}.", new Object[] {type, location, cause.getMessage()}); + LOG.debug("Warning for {} {}: {}.", type, location, cause.getMessage()); } else if (level.equals(Severity.NOTIFICATION)) { - LOG.debug("Loading {} {}.", new Object[] {type, location}); + LOG.debug("Loading {} {}.", type, location); } } diff --git a/src/main/java/org/italiangrid/storm/webdav/spring/AppConfig.java b/src/main/java/org/italiangrid/storm/webdav/spring/AppConfig.java index 46dccccb..08b344a8 100644 --- a/src/main/java/org/italiangrid/storm/webdav/spring/AppConfig.java +++ b/src/main/java/org/italiangrid/storm/webdav/spring/AppConfig.java @@ -125,7 +125,7 @@ import eu.emi.security.authn.x509.impl.PEMCredential; @Configuration -public class AppConfig implements TransferConstants { +public class AppConfig { public static final Logger LOG = LoggerFactory.getLogger(AppConfig.class); @@ -266,10 +266,10 @@ HttpClientConnectionManager tpcClientConnectionManager(ThirdPartyCopyProperties LayeredConnectionSocketFactory tlsSf = new TpcSSLConnectionSocketFactory(ctx); Registry r = RegistryBuilder.create() - .register(HTTP, sf) - .register(HTTPS, tlsSf) - .register(DAV, sf) - .register(DAVS, tlsSf) + .register(TransferConstants.HTTP, sf) + .register(TransferConstants.HTTPS, tlsSf) + .register(TransferConstants.DAV, sf) + .register(TransferConstants.DAVS, tlsSf) .build(); PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(r); @@ -331,7 +331,7 @@ ClientRegistrationRepository clientRegistrationRepository( LOG.info("OpenID providers configuration will be refreshed every {} minutes", props.getRefreshPeriodMinutes()); - return (k) -> { + return k -> { try { return clients.get(k); } catch (ExecutionException e) { @@ -428,7 +428,7 @@ PathAuthorizationPdp fineGrainedAuthzPdpd(PathAuthorizationPolicyRepository repo @Bean @ConditionalOnProperty(name = "oauth.enable-oidc", havingValue = "false") ClientRegistrationRepository emptyClientRegistrationRepository() { - return (id) -> null; + return id -> null; } 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 8a7f093a..0e85dbf0 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 @@ -87,7 +87,7 @@ public class SecurityConfig { private static final Logger LOG = LoggerFactory.getLogger(SecurityConfig.class); - private final static List ALLOWED_METHODS; + private static final List ALLOWED_METHODS; static { ALLOWED_METHODS = Lists.newArrayList(); diff --git a/src/main/java/org/italiangrid/storm/webdav/spring/web/ServletConfiguration.java b/src/main/java/org/italiangrid/storm/webdav/spring/web/ServletConfiguration.java index 261fac93..23ce8b49 100644 --- a/src/main/java/org/italiangrid/storm/webdav/spring/web/ServletConfiguration.java +++ b/src/main/java/org/italiangrid/storm/webdav/spring/web/ServletConfiguration.java @@ -121,8 +121,8 @@ FilterRegistrationBean redirectFilter(PathResolver pathResolver, RedirectionService redirectionService) { LOG.info("Redirector filter enabled"); - FilterRegistrationBean filter = new FilterRegistrationBean( - new RedirectFilter(pathResolver, redirectionService)); + FilterRegistrationBean filter = + new FilterRegistrationBean<>(new RedirectFilter(pathResolver, redirectionService)); filter.addUrlPatterns("/*"); filter.setOrder(REDIRECT_REQ_FILTER_ORDER); @@ -200,9 +200,9 @@ FilterRegistrationBean tpcFilter(Clock clock, FilesystemAccess f TransferClient metricsClient = new HttpTransferClientMetricsWrapper(registry, client); - FilterRegistrationBean tpcFilter = new FilterRegistrationBean<>( - new TransferFilter(clock, metricsClient, resolver, lus, props.isVerifyChecksum(), - props.getEnableExpectContinueThreshold())); + FilterRegistrationBean tpcFilter = + new FilterRegistrationBean<>(new TransferFilter(clock, metricsClient, resolver, lus, + props.isVerifyChecksum(), props.getEnableExpectContinueThreshold())); tpcFilter.addUrlPatterns("/*"); tpcFilter.setOrder(TPC_FILTER_ORDER); return tpcFilter; @@ -213,8 +213,7 @@ FilterRegistrationBean statsFilter(MetricRegistry regist PathResolver resolver) { FilterRegistrationBean filter = - new FilterRegistrationBean( - new StorageAreaStatsFilter(registry, resolver)); + new FilterRegistrationBean<>(new StorageAreaStatsFilter(registry, resolver)); filter.addUrlPatterns("/*"); filter.setOrder(STATS_FILTER_ORDER); return filter; diff --git a/src/main/java/org/italiangrid/storm/webdav/tpc/TpcUtils.java b/src/main/java/org/italiangrid/storm/webdav/tpc/TpcUtils.java index 57ae36bc..5e7a1ec0 100644 --- a/src/main/java/org/italiangrid/storm/webdav/tpc/TpcUtils.java +++ b/src/main/java/org/italiangrid/storm/webdav/tpc/TpcUtils.java @@ -30,7 +30,7 @@ import org.italiangrid.storm.webdav.error.ResourceNotFound; import org.italiangrid.storm.webdav.server.PathResolver; -public interface TpcUtils extends TransferConstants { +public interface TpcUtils { default Supplier resourceNotFoundError(String path) { return () -> new ResourceNotFound(format("No storage area found matching path: %s", path)); @@ -47,27 +47,29 @@ default String getSerlvetRequestPath(HttpServletRequest request) { } default String destinationHeader(HttpServletRequest request) { - return request.getHeader(DESTINATION_HEADER); + return request.getHeader(TransferConstants.DESTINATION_HEADER); } default boolean requestHasSourceHeader(HttpServletRequest request) { - return Optional.ofNullable(request.getHeader(SOURCE_HEADER)).isPresent(); + return Optional.ofNullable(request.getHeader(TransferConstants.SOURCE_HEADER)).isPresent(); } default boolean requestHasDestinationHeader(HttpServletRequest request) { - return Optional.ofNullable(request.getHeader(DESTINATION_HEADER)).isPresent(); + return Optional.ofNullable(request.getHeader(TransferConstants.DESTINATION_HEADER)).isPresent(); } default boolean requestHasLocalDestinationHeader(HttpServletRequest request, LocalURLService localURLService) { - Optional destination = Optional.ofNullable(request.getHeader(DESTINATION_HEADER)); + Optional destination = + Optional.ofNullable(request.getHeader(TransferConstants.DESTINATION_HEADER)); return (destination.isPresent() && localURLService.isLocalURL(destination.get())); } default boolean requestHasRemoteDestinationHeader(HttpServletRequest request, LocalURLService localURLService) { - Optional destination = Optional.ofNullable(request.getHeader(DESTINATION_HEADER)); + Optional destination = + Optional.ofNullable(request.getHeader(TransferConstants.DESTINATION_HEADER)); return (destination.isPresent() && !localURLService.isLocalURL(destination.get())); } @@ -112,7 +114,7 @@ default boolean requestHasTranferHeader(HttpServletRequest request) { Enumeration headerNames = request.getHeaderNames(); while (headerNames.hasMoreElements()) { String headerName = headerNames.nextElement(); - if (headerName.toLowerCase().startsWith(TRANSFER_HEADER_LC)) { + if (headerName.toLowerCase().startsWith(TransferConstants.TRANSFER_HEADER_LC)) { return true; } } @@ -130,7 +132,7 @@ default boolean isCopyOrMoveRequest(HttpServletRequest request) { } default String dropSlashWebdavFromPath(String path) { - Matcher m = WEBDAV_PATH_PATTERN.matcher(path); + Matcher m = TransferConstants.WEBDAV_PATH_PATTERN.matcher(path); if (m.matches()) { return String.format("/%s", m.group(1)); diff --git a/src/main/java/org/italiangrid/storm/webdav/tpc/TransferConstants.java b/src/main/java/org/italiangrid/storm/webdav/tpc/TransferConstants.java index fa6a55fb..64f0c25a 100644 --- a/src/main/java/org/italiangrid/storm/webdav/tpc/TransferConstants.java +++ b/src/main/java/org/italiangrid/storm/webdav/tpc/TransferConstants.java @@ -20,32 +20,34 @@ import com.google.common.collect.ImmutableSet; -public interface TransferConstants { +public final class TransferConstants { - String AUTHORIZATION_HEADER = "Authorization"; - String CLIENT_INFO_HEADER = "ClientInfo"; - String SOURCE_HEADER = "Source"; - String DESTINATION_HEADER = "Destination"; - String OVERWRITE_HEADER = "Overwrite"; - String REQUIRE_CHECKSUM_HEADER = "RequireChecksumVerification"; - String CREDENTIAL_HEADER = "Credential"; + public static final String AUTHORIZATION_HEADER = "Authorization"; + public static final String CLIENT_INFO_HEADER = "ClientInfo"; + public static final String SOURCE_HEADER = "Source"; + public static final String DESTINATION_HEADER = "Destination"; + public static final String OVERWRITE_HEADER = "Overwrite"; + public static final String REQUIRE_CHECKSUM_HEADER = "RequireChecksumVerification"; + public static final String CREDENTIAL_HEADER = "Credential"; - String CREDENTIAL_HEADER_NONE_VALUE = "none"; + public static final String CREDENTIAL_HEADER_NONE_VALUE = "none"; - String TRANSFER_HEADER = "TransferHeader"; - String TRANSFER_HEADER_LC = TRANSFER_HEADER.toLowerCase(); - int TRANFER_HEADER_LENGTH = TRANSFER_HEADER.length(); + public static final String TRANSFER_HEADER = "TransferHeader"; + public static final String TRANSFER_HEADER_LC = TRANSFER_HEADER.toLowerCase(); + public static final int TRANFER_HEADER_LENGTH = TRANSFER_HEADER.length(); - String HTTP = "http"; - String HTTPS = "https"; + public static final String HTTP = "http"; + public static final String HTTPS = "https"; - String DAV = "dav"; - String DAVS = "davs"; + public static final String DAV = "dav"; + public static final String DAVS = "davs"; - Set SUPPORTED_PROTOCOLS = + public static final Set SUPPORTED_PROTOCOLS = ImmutableSet.builder().add(HTTP, HTTPS, DAV, DAVS).build(); - String WEBDAV_PATH_REGEX = "/webdav/(.*)$"; - Pattern WEBDAV_PATH_PATTERN = Pattern.compile(WEBDAV_PATH_REGEX); + public static final String WEBDAV_PATH_REGEX = "/webdav/(.*)$"; + public static final Pattern WEBDAV_PATH_PATTERN = Pattern.compile(WEBDAV_PATH_REGEX); + + private TransferConstants() {} } diff --git a/src/main/java/org/italiangrid/storm/webdav/tpc/TransferFilter.java b/src/main/java/org/italiangrid/storm/webdav/tpc/TransferFilter.java index 7788865a..c40d2a56 100644 --- a/src/main/java/org/italiangrid/storm/webdav/tpc/TransferFilter.java +++ b/src/main/java/org/italiangrid/storm/webdav/tpc/TransferFilter.java @@ -108,7 +108,8 @@ protected void handleTpc(HttpServletRequest request, HttpServletResponse respons try { if (validRequest(request, response)) { - Optional source = Optional.ofNullable(request.getHeader(SOURCE_HEADER)); + Optional source = + Optional.ofNullable(request.getHeader(TransferConstants.SOURCE_HEADER)); SciTag scitag = (SciTag) request.getAttribute(SciTag.SCITAG_ATTRIBUTE); if (source.isPresent()) { handlePullCopy(request, response, scitag); @@ -123,7 +124,8 @@ protected void handleTpc(HttpServletRequest request, HttpServletResponse respons } private void setupLoggingContext(HttpServletRequest request) { - Optional clientInfoHeader = Optional.ofNullable(request.getHeader(CLIENT_INFO_HEADER)); + Optional clientInfoHeader = + Optional.ofNullable(request.getHeader(TransferConstants.CLIENT_INFO_HEADER)); if (clientInfoHeader.isPresent()) { try { @@ -147,8 +149,8 @@ protected void logTransferStart(GetTransferRequest req) { LOG.info( "Pull third-party transfer requested: Source: {}, Destination: {}, hasAuthorizationHeader: {}, id: {}", - req.remoteURI(), req.path(), req.transferHeaders().containsKey(AUTHORIZATION_HEADER), - req.uuid()); + req.remoteURI(), req.path(), + req.transferHeaders().containsKey(TransferConstants.AUTHORIZATION_HEADER), req.uuid()); if (LOG.isDebugEnabled()) { LOG.debug("{}", req); } @@ -157,8 +159,8 @@ protected void logTransferStart(GetTransferRequest req) { protected void logTransferStart(PutTransferRequest req) { LOG.info( "Push third-party transfer requested: Source: {}, Destination: {}, hasAuthorizationHeader: {}, id: {}", - req.path(), req.remoteURI(), req.transferHeaders().containsKey(AUTHORIZATION_HEADER), - req.uuid()); + req.path(), req.remoteURI(), + req.transferHeaders().containsKey(TransferConstants.AUTHORIZATION_HEADER), req.uuid()); if (LOG.isDebugEnabled()) { LOG.debug("{}", req); } @@ -215,7 +217,7 @@ protected void logTransferException(TransferRequest request, Exception e) { protected void handlePullCopy(HttpServletRequest request, HttpServletResponse response, SciTag scitag) throws IOException { - URI uri = URI.create(request.getHeader(SOURCE_HEADER)); + URI uri = URI.create(request.getHeader(TransferConstants.SOURCE_HEADER)); String path = getScopedPathInfo(request); GetTransferRequest xferRequest = GetTransferRequestBuilder.create() @@ -254,7 +256,7 @@ protected void handlePullCopy(HttpServletRequest request, HttpServletResponse re protected void handlePushCopy(HttpServletRequest request, HttpServletResponse response, SciTag scitag) throws IOException { - URI uri = URI.create(request.getHeader(DESTINATION_HEADER)); + URI uri = URI.create(request.getHeader(TransferConstants.DESTINATION_HEADER)); String path = getScopedPathInfo(request); PutTransferRequest xferRequest = PutTransferRequestBuilder.create() diff --git a/src/main/java/org/italiangrid/storm/webdav/tpc/TransferFilterSupport.java b/src/main/java/org/italiangrid/storm/webdav/tpc/TransferFilterSupport.java index 448f09d1..ad0b487d 100644 --- a/src/main/java/org/italiangrid/storm/webdav/tpc/TransferFilterSupport.java +++ b/src/main/java/org/italiangrid/storm/webdav/tpc/TransferFilterSupport.java @@ -46,7 +46,7 @@ import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; -public class TransferFilterSupport implements TransferConstants, TpcUtils { +public class TransferFilterSupport implements TpcUtils { public static final Logger LOG = LoggerFactory.getLogger(TransferFilterSupport.class); @@ -81,8 +81,8 @@ protected Multimap getTransferHeaders(HttpServletRequest request while (headerNames.hasMoreElements()) { String headerName = headerNames.nextElement(); - if (headerName.toLowerCase().startsWith(TRANSFER_HEADER_LC)) { - String xferHeaderName = headerName.substring(TRANFER_HEADER_LENGTH); + if (headerName.toLowerCase().startsWith(TransferConstants.TRANSFER_HEADER_LC)) { + String xferHeaderName = headerName.substring(TransferConstants.TRANFER_HEADER_LENGTH); if (xferHeaderName.trim().length() == 0) { LOG.warn("Ignoring invalid transfer header {}", headerName); continue; @@ -109,7 +109,7 @@ protected Multimap getTransferHeaders(HttpServletRequest request protected boolean verifyChecksumRequested(HttpServletRequest request) { Optional verifyChecksumFromHeader = - Optional.ofNullable(request.getHeader(REQUIRE_CHECKSUM_HEADER)); + Optional.ofNullable(request.getHeader(TransferConstants.REQUIRE_CHECKSUM_HEADER)); if (verifyChecksumFromHeader.isPresent()) { return "true".equals(verifyChecksumFromHeader.get()); @@ -119,7 +119,8 @@ protected boolean verifyChecksumRequested(HttpServletRequest request) { } protected boolean overwriteRequested(HttpServletRequest request) { - Optional overwrite = Optional.ofNullable(request.getHeader(OVERWRITE_HEADER)); + Optional overwrite = + Optional.ofNullable(request.getHeader(TransferConstants.OVERWRITE_HEADER)); if (overwrite.isPresent()) { return "T".equalsIgnoreCase(overwrite.get()); @@ -130,7 +131,7 @@ protected boolean overwriteRequested(HttpServletRequest request) { protected boolean isSupportedTransferURI(URI uri) { - return SUPPORTED_PROTOCOLS.contains(uri.getScheme()) && uri.getPath() != null; + return TransferConstants.SUPPORTED_PROTOCOLS.contains(uri.getScheme()) && uri.getPath() != null; } protected boolean validTransferURI(String xferUri) { @@ -242,26 +243,33 @@ protected boolean validLocalDestinationPath(HttpServletRequest request, protected boolean validRequest(HttpServletRequest request, HttpServletResponse response) throws IOException { - Optional source = Optional.ofNullable(request.getHeader(SOURCE_HEADER)); - Optional dest = Optional.ofNullable(request.getHeader(DESTINATION_HEADER)); - Optional overwrite = Optional.ofNullable(request.getHeader(OVERWRITE_HEADER)); - Optional checksum = Optional.ofNullable(request.getHeader(REQUIRE_CHECKSUM_HEADER)); - Optional credential = Optional.ofNullable(request.getHeader(CREDENTIAL_HEADER)); + Optional source = + Optional.ofNullable(request.getHeader(TransferConstants.SOURCE_HEADER)); + Optional dest = + Optional.ofNullable(request.getHeader(TransferConstants.DESTINATION_HEADER)); + Optional overwrite = + Optional.ofNullable(request.getHeader(TransferConstants.OVERWRITE_HEADER)); + Optional checksum = + Optional.ofNullable(request.getHeader(TransferConstants.REQUIRE_CHECKSUM_HEADER)); + Optional credential = + Optional.ofNullable(request.getHeader(TransferConstants.CREDENTIAL_HEADER)); if (source.isPresent() && dest.isPresent()) { invalidRequest(response, "Source and Destination headers are both present!"); return false; } - if (source.isPresent() && !validTransferURI(request.getHeader(SOURCE_HEADER))) { - invalidRequest(response, - format("Invalid %s header: %s", SOURCE_HEADER, request.getHeader(SOURCE_HEADER))); + if (source.isPresent() + && !validTransferURI(request.getHeader(TransferConstants.SOURCE_HEADER))) { + invalidRequest(response, format("Invalid %s header: %s", TransferConstants.SOURCE_HEADER, + request.getHeader(TransferConstants.SOURCE_HEADER))); return false; } - if (dest.isPresent() && !validTransferURI(request.getHeader(DESTINATION_HEADER))) { - invalidRequest(response, format("Invalid %s header: %s", DESTINATION_HEADER, - request.getHeader(DESTINATION_HEADER))); + if (dest.isPresent() + && !validTransferURI(request.getHeader(TransferConstants.DESTINATION_HEADER))) { + invalidRequest(response, format("Invalid %s header: %s", TransferConstants.DESTINATION_HEADER, + request.getHeader(TransferConstants.DESTINATION_HEADER))); return false; } @@ -284,7 +292,8 @@ protected boolean validRequest(HttpServletRequest request, HttpServletResponse r } if (invalidOverwrite) { - invalidRequest(response, format("Invalid %s header value: %s", OVERWRITE_HEADER, val)); + invalidRequest(response, + format("Invalid %s header value: %s", TransferConstants.OVERWRITE_HEADER, val)); return false; } } @@ -301,12 +310,13 @@ protected boolean validRequest(HttpServletRequest request, HttpServletResponse r if (invalidChecksum) { invalidRequest(response, - format("Invalid %s header value: %s", REQUIRE_CHECKSUM_HEADER, val)); + format("Invalid %s header value: %s", TransferConstants.REQUIRE_CHECKSUM_HEADER, val)); return false; } } - if (credential.isPresent() && !CREDENTIAL_HEADER_NONE_VALUE.equals(credential.get())) { + if (credential.isPresent() + && !TransferConstants.CREDENTIAL_HEADER_NONE_VALUE.equals(credential.get())) { invalidRequest(response, "Unsupported Credential header value: " + credential.get()); return false; } diff --git a/src/main/java/org/italiangrid/storm/webdav/tpc/http/HttpTransferClient.java b/src/main/java/org/italiangrid/storm/webdav/tpc/http/HttpTransferClient.java index 87cad35d..a4f77887 100644 --- a/src/main/java/org/italiangrid/storm/webdav/tpc/http/HttpTransferClient.java +++ b/src/main/java/org/italiangrid/storm/webdav/tpc/http/HttpTransferClient.java @@ -116,7 +116,7 @@ HttpGet prepareRequest(GetTransferRequest request) { return get; } - HttpPut prepareRequest(PutTransferRequest request, HttpEntity cfe) throws IOException { + HttpPut prepareRequest(PutTransferRequest request, HttpEntity cfe) { request.setTransferStatus(TransferStatus.builder(clock).inProgress(0)); @@ -170,9 +170,9 @@ public void handle(GetTransferRequest request, TransferStatusCallback cb) { HttpGet get = prepareRequest(request); HttpClientContext context = HttpClientContext.create(); - ScheduledFuture reportTask = executorService.scheduleAtFixedRate(() -> { - reportStatus(cb, request, statusBuilder.inProgress(os.getCount())); - }, reportDelaySec, reportDelaySec, TimeUnit.SECONDS); + ScheduledFuture reportTask = executorService.scheduleAtFixedRate( + () -> reportStatus(cb, request, statusBuilder.inProgress(os.getCount())), reportDelaySec, + reportDelaySec, TimeUnit.SECONDS); try { @@ -233,13 +233,7 @@ public void handle(PutTransferRequest request, TransferStatusCallback cb) { HttpPut put = null; HttpClientContext context = HttpClientContext.create(); - try { - put = prepareRequest(request, cfe); - } catch (IOException e) { - logException(e); - reportStatus(cb, request, statusBuilder - .error(format("Error pushing %s: %s", request.remoteURI().toString(), e.getMessage()))); - } + put = prepareRequest(request, cfe); ScheduledFuture reportTask = executorService.scheduleAtFixedRate( () -> reportStatus(cb, request, statusBuilder.inProgress(cfe.getCount())), reportDelaySec, diff --git a/src/main/java/org/italiangrid/storm/webdav/tpc/utils/Adler32DigestHeaderHelper.java b/src/main/java/org/italiangrid/storm/webdav/tpc/utils/Adler32DigestHeaderHelper.java index 6c6d8f32..c75c991f 100644 --- a/src/main/java/org/italiangrid/storm/webdav/tpc/utils/Adler32DigestHeaderHelper.java +++ b/src/main/java/org/italiangrid/storm/webdav/tpc/utils/Adler32DigestHeaderHelper.java @@ -31,6 +31,8 @@ public class Adler32DigestHeaderHelper { public static final String DIGEST_HEADER_REGEX = "^\\s*adler32\\s*=\\s*([0-9a-zA-Z]{8})\\s*"; public static final Pattern DIGEST_HEADER_PATTERN = Pattern.compile(DIGEST_HEADER_REGEX); + private Adler32DigestHeaderHelper() {} + public static Optional extractAdler32DigestFromResponse(HttpResponse response) { checkNotNull(response); @@ -40,7 +42,7 @@ public static Optional extractAdler32DigestFromResponse(HttpResponse res if (digestHeader.isPresent()) { String digestHeaderValue = digestHeader.get().getValue(); - + if (!isNullOrEmpty(digestHeaderValue)) { Matcher m = DIGEST_HEADER_PATTERN.matcher(digestHeaderValue); diff --git a/src/main/java/org/italiangrid/storm/webdav/tpc/utils/ClientInfo.java b/src/main/java/org/italiangrid/storm/webdav/tpc/utils/ClientInfo.java index a3ceca4d..1e099704 100644 --- a/src/main/java/org/italiangrid/storm/webdav/tpc/utils/ClientInfo.java +++ b/src/main/java/org/italiangrid/storm/webdav/tpc/utils/ClientInfo.java @@ -27,8 +27,10 @@ public class ClientInfo { + private static final String INVALID_CLIENTINFO_HEADER_MESSAGE = "Invalid ClientInfo header: %s"; + public static final String CLIENT_INFO_MDC_KEY = "tpc.clientInfo"; - + public static final String JOB_ID_KEY = "job-id"; public static final String FILE_ID_KEY = "file-id"; public static final String RETRY_COUNT_KEY = "retry"; @@ -59,13 +61,13 @@ public int getRetryCount() { } public static ClientInfo fromHeaderString(String headerString) { - checkArgument(!isNullOrEmpty(headerString), "Invalid ClientInfo header: %s", headerString); + checkArgument(!isNullOrEmpty(headerString), INVALID_CLIENTINFO_HEADER_MESSAGE, headerString); Map splitResult = SPLITTER.split(headerString); - checkArgument(splitResult.containsKey(JOB_ID_KEY), "Invalid ClientInfo header: %s", + checkArgument(splitResult.containsKey(JOB_ID_KEY), INVALID_CLIENTINFO_HEADER_MESSAGE, headerString); - checkArgument(splitResult.containsKey(FILE_ID_KEY), "Invalid ClientInfo header: %s", + checkArgument(splitResult.containsKey(FILE_ID_KEY), INVALID_CLIENTINFO_HEADER_MESSAGE, headerString); - checkArgument(splitResult.containsKey(RETRY_COUNT_KEY), "Invalid ClientInfo header: %s", + checkArgument(splitResult.containsKey(RETRY_COUNT_KEY), INVALID_CLIENTINFO_HEADER_MESSAGE, headerString); return new ClientInfo(splitResult.get(JOB_ID_KEY), splitResult.get(FILE_ID_KEY), Integer.parseInt(splitResult.get(RETRY_COUNT_KEY))); @@ -74,6 +76,6 @@ public static ClientInfo fromHeaderString(String headerString) { public void addToMDC() { final String ciStr = String.format("job-id:%s,file-id:%s,retry:%d", jobId, fileId, retryCount); MDC.put(CLIENT_INFO_MDC_KEY, ciStr); - + } } diff --git a/src/main/java/org/italiangrid/storm/webdav/tpc/utils/UrlHelper.java b/src/main/java/org/italiangrid/storm/webdav/tpc/utils/UrlHelper.java index 5462e239..6dc72458 100644 --- a/src/main/java/org/italiangrid/storm/webdav/tpc/utils/UrlHelper.java +++ b/src/main/java/org/italiangrid/storm/webdav/tpc/utils/UrlHelper.java @@ -15,8 +15,6 @@ */ package org.italiangrid.storm.webdav.tpc.utils; -import static java.util.Objects.isNull; - import java.net.URI; import java.net.URISyntaxException; @@ -31,11 +29,7 @@ public static boolean isRemoteUrl(String url) { URI uri = new URI(url); - if (isNull(uri.getScheme())) { - return false; - } - - return true; + return uri.getScheme() != null; } catch (URISyntaxException e) { return false; diff --git a/src/main/java/org/italiangrid/storm/webdav/utils/RangeCopyHelper.java b/src/main/java/org/italiangrid/storm/webdav/utils/RangeCopyHelper.java index c442df09..cef6fa60 100644 --- a/src/main/java/org/italiangrid/storm/webdav/utils/RangeCopyHelper.java +++ b/src/main/java/org/italiangrid/storm/webdav/utils/RangeCopyHelper.java @@ -28,6 +28,8 @@ public class RangeCopyHelper { + private RangeCopyHelper() {} + public static long rangeCopy(InputStream is, File f, long rangeStart, long rangeCount) throws IOException { checkNotNull(is); diff --git a/src/main/java/org/italiangrid/storm/webdav/web/ViewUtilsInterceptor.java b/src/main/java/org/italiangrid/storm/webdav/web/ViewUtilsInterceptor.java index f4df47dc..f1b54e2b 100644 --- a/src/main/java/org/italiangrid/storm/webdav/web/ViewUtilsInterceptor.java +++ b/src/main/java/org/italiangrid/storm/webdav/web/ViewUtilsInterceptor.java @@ -25,9 +25,9 @@ import org.italiangrid.storm.webdav.server.servlet.SAIndexServlet; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; +import org.springframework.web.servlet.HandlerInterceptor; -public class ViewUtilsInterceptor extends HandlerInterceptorAdapter { +public class ViewUtilsInterceptor implements HandlerInterceptor { final ServiceConfigurationProperties serviceConfig; final StorageAreaConfiguration saConfig; diff --git a/src/main/resources/templates/400.html b/src/main/resources/templates/400.html index 14d78a16..9bf8a90e 100644 --- a/src/main/resources/templates/400.html +++ b/src/main/resources/templates/400.html @@ -1,5 +1,5 @@ - Bad request! @@ -10,4 +10,4 @@

Bad request!

The request you submitted is malformed!

Go back to the storage area index page - \ No newline at end of file + diff --git a/src/main/resources/templates/401.html b/src/main/resources/templates/401.html index dee6c977..b6cf5c92 100644 --- a/src/main/resources/templates/401.html +++ b/src/main/resources/templates/401.html @@ -1,5 +1,5 @@ - Access denied! @@ -10,4 +10,4 @@

Unauthorized!

You are not authenticated and are trying to access content that requires authenticated access!

Go back to the storage area index page - \ No newline at end of file + diff --git a/src/main/resources/templates/403.html b/src/main/resources/templates/403.html index bdf4065d..3b7c6306 100644 --- a/src/main/resources/templates/403.html +++ b/src/main/resources/templates/403.html @@ -1,5 +1,5 @@ - Access denied! @@ -10,4 +10,4 @@

Forbidden!

You do not have the rights to access the requested resources!

Go back to the storage area index page - \ No newline at end of file + diff --git a/src/main/resources/templates/404.html b/src/main/resources/templates/404.html index a895f025..18bc284a 100644 --- a/src/main/resources/templates/404.html +++ b/src/main/resources/templates/404.html @@ -1,5 +1,5 @@ - Not found! @@ -10,4 +10,4 @@

Not found!

We could not find the resource you're looking for...

Go back to the storage area index page - \ No newline at end of file + diff --git a/src/main/resources/templates/405.html b/src/main/resources/templates/405.html index 4a228df8..6200622f 100644 --- a/src/main/resources/templates/405.html +++ b/src/main/resources/templates/405.html @@ -1,5 +1,5 @@ - Access denied! @@ -10,4 +10,4 @@

Request method not allowed!

The http method in your request is not allowed for the target resource!

Go back to the storage area index page - \ No newline at end of file + diff --git a/src/main/resources/templates/authn-info.html b/src/main/resources/templates/authn-info.html index 8c9570cc..3980a10a 100644 --- a/src/main/resources/templates/authn-info.html +++ b/src/main/resources/templates/authn-info.html @@ -1,5 +1,5 @@ - Authentication info @@ -26,4 +26,4 @@ Go back to the storage area index page - \ No newline at end of file + diff --git a/src/main/resources/templates/error.html b/src/main/resources/templates/error.html index 3e127e9b..438b6d21 100644 --- a/src/main/resources/templates/error.html +++ b/src/main/resources/templates/error.html @@ -1,5 +1,5 @@ - Error! @@ -10,4 +10,4 @@

Ops!

Something went wrong, and this page is not very informative

Go back to the storage area index page - \ No newline at end of file + diff --git a/src/main/resources/templates/jetty-dir.html b/src/main/resources/templates/jetty-dir.html index 6963c475..b06c5b35 100644 --- a/src/main/resources/templates/jetty-dir.html +++ b/src/main/resources/templates/jetty-dir.html @@ -1,5 +1,5 @@ - Directory @@ -31,4 +31,4 @@

Directory

- \ No newline at end of file + diff --git a/src/main/resources/templates/layout.html b/src/main/resources/templates/layout.html index 8f38e1f5..b5689f46 100644 --- a/src/main/resources/templates/layout.html +++ b/src/main/resources/templates/layout.html @@ -1,5 +1,5 @@ - + StoRM WebDAV @@ -39,4 +39,4 @@

- \ No newline at end of file + diff --git a/src/main/resources/templates/oidc-login.html b/src/main/resources/templates/oidc-login.html index 0cdb116e..4b4843bd 100644 --- a/src/main/resources/templates/oidc-login.html +++ b/src/main/resources/templates/oidc-login.html @@ -1,5 +1,5 @@ - StoRM WebDAV service - Login @@ -24,4 +24,4 @@

Login error

Go back to the storage area index page - \ No newline at end of file + diff --git a/src/main/resources/templates/sa-index.html b/src/main/resources/templates/sa-index.html index 560b8174..2f2820a9 100644 --- a/src/main/resources/templates/sa-index.html +++ b/src/main/resources/templates/sa-index.html @@ -1,5 +1,5 @@ - Storage areas index @@ -18,4 +18,4 @@

Storage areas:

- \ No newline at end of file + diff --git a/src/test/java/org/italiangrid/storm/webdav/test/authz/integration/LocalAuthzIntegrationTests.java b/src/test/java/org/italiangrid/storm/webdav/test/authz/integration/LocalAuthzIntegrationTests.java index 2935fa19..d59a1bbe 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/authz/integration/LocalAuthzIntegrationTests.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/authz/integration/LocalAuthzIntegrationTests.java @@ -77,12 +77,12 @@ public void setup() { } @Test - public void testLocalAuthz() throws Exception { + void testLocalAuthz() throws Exception { mvc.perform(put(SLASH_WLCG_SLASH_FILE)).andExpect(status().isUnauthorized()); } @Test - public void testInvalidTokenAuthz() throws Exception { + void testInvalidTokenAuthz() throws Exception { Jwt token = Jwt.withTokenValue("test") .header("kid", "rsa1") .issuer(UNKNOWN_ISSUER) @@ -104,7 +104,7 @@ public void testInvalidTokenAuthz() throws Exception { } @Test - public void testValidLocalTokenAuthz() throws Exception { + void testValidLocalTokenAuthz() throws Exception { Jwt token = Jwt.withTokenValue("test") .header("kid", "rsa1") .issuer(LOCAL_ISSUER) @@ -123,7 +123,7 @@ public void testValidLocalTokenAuthz() throws Exception { } @Test - public void testInvalidPathLocalTokenAuthz() throws Exception { + void testInvalidPathLocalTokenAuthz() throws Exception { Jwt token = Jwt.withTokenValue("test") .header("kid", "rsa1") .issuer(LOCAL_ISSUER) @@ -139,7 +139,7 @@ public void testInvalidPathLocalTokenAuthz() throws Exception { } @Test - public void testInvalidLocalToken() throws Exception { + void testInvalidLocalToken() throws Exception { Jwt token = Jwt.withTokenValue("test") .header("kid", "rsa1") .issuer(LOCAL_ISSUER) diff --git a/src/test/java/org/italiangrid/storm/webdav/test/authz/pdp/AuthzPdpTests.java b/src/test/java/org/italiangrid/storm/webdav/test/authz/pdp/AuthzPdpTests.java index 04347431..e7bdf69e 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/authz/pdp/AuthzPdpTests.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/authz/pdp/AuthzPdpTests.java @@ -83,7 +83,7 @@ public void setup() { @Test - public void notApplicableWithEmptyPolicies() { + void notApplicableWithEmptyPolicies() { PathAuthorizationResult result = pdp.authorizeRequest(newAuthorizationRequest(request, authentication)); @@ -92,7 +92,7 @@ public void notApplicableWithEmptyPolicies() { @Test - public void denyPolicyApplied() { + void denyPolicyApplied() { PathAuthorizationPolicy denyAllPolicy = PathAuthorizationPolicy.builder() .withDeny() @@ -112,7 +112,7 @@ public void denyPolicyApplied() { } @Test - public void firstApplicablePolicyApplied() { + void firstApplicablePolicyApplied() { PathAuthorizationPolicy denyAllPolicy = PathAuthorizationPolicy.builder() .withDeny() @@ -138,7 +138,7 @@ public void firstApplicablePolicyApplied() { } @Test - public void oauthGroupHolderPolicyNotAppliedAsItDoesNotMatchPath() { + void oauthGroupHolderPolicyNotAppliedAsItDoesNotMatchPath() { PathAuthorizationPolicy oauthTestPolicy = PathAuthorizationPolicy.builder() .withPermit() @@ -164,7 +164,7 @@ public void oauthGroupHolderPolicyNotAppliedAsItDoesNotMatchPath() { } @Test - public void oauthGroupHolderPolicyNotAppliedDueToWrongGroup() { + void oauthGroupHolderPolicyNotAppliedDueToWrongGroup() { when(authentication.getAuthorities()) .thenReturn(authorities(new JwtGroupAuthority(TEST_ISSUER, "/test/subgroup"))); @@ -197,7 +197,7 @@ public void oauthGroupHolderPolicyNotAppliedDueToWrongGroup() { } @Test - public void oauthGroupHolderPolicyNotAppliedDueToWrongIssuer() { + void oauthGroupHolderPolicyNotAppliedDueToWrongIssuer() { when(authentication.getAuthorities()) .thenReturn(authorities(new JwtGroupAuthority(TEST2_ISSUER, "/test"))); @@ -228,7 +228,7 @@ public void oauthGroupHolderPolicyNotAppliedDueToWrongIssuer() { } @Test - public void oauthGroupHolderPolicyApplied() { + void oauthGroupHolderPolicyApplied() { when(request.getServletPath()).thenReturn("/test/file0"); when(request.getMethod()).thenReturn("GET"); @@ -260,7 +260,7 @@ public void oauthGroupHolderPolicyApplied() { } @Test - public void multiplePrincipalMatchersWorkAsExpected() { + void multiplePrincipalMatchersWorkAsExpected() { when(request.getServletPath()).thenReturn("/test/file0"); when(request.getMethod()).thenReturn("GET"); @@ -310,7 +310,7 @@ public void multiplePrincipalMatchersWorkAsExpected() { } @Test - public void multiplePathsMatchersWorkAsExpected() { + void multiplePathsMatchersWorkAsExpected() { when(request.getServletPath()).thenReturn("/test/file0"); when(request.getMethod()).thenReturn("GET"); diff --git a/src/test/java/org/italiangrid/storm/webdav/test/authz/pdp/LocalAuthzPdpTests.java b/src/test/java/org/italiangrid/storm/webdav/test/authz/pdp/LocalAuthzPdpTests.java index 128e5ee3..3f082100 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/authz/pdp/LocalAuthzPdpTests.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/authz/pdp/LocalAuthzPdpTests.java @@ -100,7 +100,7 @@ public void setup() throws MalformedURLException { } @Test - public void noPathRaisesException() throws Exception { + void noPathRaisesException() { when(jwt.getClaimAsString("path")).thenReturn(null); Exception e = assertThrows(IllegalArgumentException.class, () -> { @@ -110,7 +110,7 @@ public void noPathRaisesException() throws Exception { } @Test - public void noPermsRaisesException() throws Exception { + void noPermsRaisesException() { when(jwt.getClaimAsString("perms")).thenReturn(null); Exception e = assertThrows(IllegalArgumentException.class, () -> { @@ -120,7 +120,7 @@ public void noPermsRaisesException() throws Exception { } @Test - public void pathMismatchYeldsDeny() throws Exception { + void pathMismatchYeldsDeny() { when(jwt.getClaimAsString("path")).thenReturn("/test/another"); PathAuthorizationResult result = @@ -130,7 +130,7 @@ public void pathMismatchYeldsDeny() throws Exception { } @Test - public void permMismatchYeldsDeny() throws Exception { + void permMismatchYeldsDeny() throws Exception { when(request.getMethod()).thenReturn("PUT"); PathAuthorizationResult result = @@ -140,7 +140,7 @@ public void permMismatchYeldsDeny() throws Exception { } @Test - public void originMismatchYeldsDeny() throws Exception { + void originMismatchYeldsDeny() { when(request.getRemoteAddr()).thenReturn(REMOTE_ADDR); when(jwt.getClaimAsString(ORIGIN_CLAIM)).thenReturn(ANOTHER_REMOTE_ADDR); @@ -151,7 +151,7 @@ public void originMismatchYeldsDeny() throws Exception { } @Test - public void testPermit() throws Exception { + void testPermit() throws Exception { PathAuthorizationResult result = pdp.authorizeRequest(newAuthorizationRequest(request, jwtAuth)); diff --git a/src/test/java/org/italiangrid/storm/webdav/test/authz/pdp/PolicyParserTests.java b/src/test/java/org/italiangrid/storm/webdav/test/authz/pdp/PolicyParserTests.java index 1fdf6d43..e65eec29 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/authz/pdp/PolicyParserTests.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/authz/pdp/PolicyParserTests.java @@ -43,7 +43,7 @@ import com.google.common.collect.Lists; @ExtendWith(MockitoExtension.class) -public class PolicyParserTests { +class PolicyParserTests { ServiceConfigurationProperties properties = new ServiceConfigurationProperties(); @@ -65,13 +65,13 @@ public void setup() { } @Test - public void testNoPolicyParsing() throws Exception { + void testNoPolicyParsing() throws Exception { assertThat(parser.parsePolicies(), empty()); } @Test - public void testSimplePolicyParsing() throws Exception { + void testSimplePolicyParsing() throws Exception { FineGrainedAuthzPolicyProperties.PrincipalProperties anonymous = new FineGrainedAuthzPolicyProperties.PrincipalProperties(); diff --git a/src/test/java/org/italiangrid/storm/webdav/test/authz/pdp/PolicyPropertiesValidationTests.java b/src/test/java/org/italiangrid/storm/webdav/test/authz/pdp/PolicyPropertiesValidationTests.java index e2912024..31cfea4f 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/authz/pdp/PolicyPropertiesValidationTests.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/authz/pdp/PolicyPropertiesValidationTests.java @@ -42,7 +42,7 @@ import com.google.common.collect.Lists; @ExtendWith(MockitoExtension.class) -public class PolicyPropertiesValidationTests { +class PolicyPropertiesValidationTests { private Validator validator; @@ -69,7 +69,7 @@ public FineGrainedAuthzPolicyProperties minimalValidPolicy() { } @Test - public void testValidAuthzPolicyPassesValidation() throws Exception { + void testValidAuthzPolicyPassesValidation() throws Exception { FineGrainedAuthzPolicyProperties props = minimalValidPolicy(); @@ -81,7 +81,7 @@ public void testValidAuthzPolicyPassesValidation() throws Exception { } @Test - public void testDescriptionRequired() throws Exception { + void testDescriptionRequired() throws Exception { FineGrainedAuthzPolicyProperties props = minimalValidPolicy(); props.setDescription(null); @@ -96,7 +96,7 @@ public void testDescriptionRequired() throws Exception { } @Test - public void testSaRequired() throws Exception { + void testSaRequired() throws Exception { FineGrainedAuthzPolicyProperties props = minimalValidPolicy(); props.setSa(null); @@ -111,7 +111,7 @@ public void testSaRequired() throws Exception { } @Test - public void testPrincipalsNotEmpty() throws Exception { + void testPrincipalsNotEmpty() throws Exception { FineGrainedAuthzPolicyProperties props = minimalValidPolicy(); props.setPrincipals(emptyList()); diff --git a/src/test/java/org/italiangrid/storm/webdav/test/authz/vomap/PathResolverTests.java b/src/test/java/org/italiangrid/storm/webdav/test/authz/vomap/PathResolverTests.java index 5031c0f1..cb11dfad 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/authz/vomap/PathResolverTests.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/authz/vomap/PathResolverTests.java @@ -36,7 +36,7 @@ public class PathResolverTests { - private final String ROOTDIR = "/storage"; + private static final String ROOTDIR = "/storage"; /* * Map SA-name -> VO name @@ -93,29 +93,29 @@ private StorageAreaInfo getMockSAInfo(String name, String voname) { } @Test - public void checkResolvedRootPath() { + void checkResolvedRootPath() { for (String name : input.keySet()) { String pathToTest = "/".concat(name).concat("/testdir"); String expectedRootPath = ROOTDIR.concat("/").concat(input.get(name)) .concat("/testdir"); - + String rootPath = pathResolver.resolvePath(pathToTest); Assert.assertEquals(expectedRootPath, rootPath); } } - + @Test - public void checkResolvedStorageArea() { + void checkResolvedStorageArea() { for (String name : input.keySet()) { String pathToTest = "/".concat(name).concat("/testdir"); String expectedRootPath = ROOTDIR.concat("/").concat(input.get(name)) .concat("/testdir"); - + StorageAreaInfo sa = pathResolver.resolveStorageArea(pathToTest); Assert.assertEquals(expectedRootPath, sa.rootPath() + "/testdir"); } diff --git a/src/test/java/org/italiangrid/storm/webdav/test/authz/vomap/VOMSMapTests.java b/src/test/java/org/italiangrid/storm/webdav/test/authz/vomap/VOMSMapTests.java index 9f59ef0c..e52d3974 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/authz/vomap/VOMSMapTests.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/authz/vomap/VOMSMapTests.java @@ -24,18 +24,18 @@ public class VOMSMapTests { - public static final String mySubject = "CN=Andrea Ceccanti,L=CNAF,OU=Personal Certificate,O=INFN,C=IT"; + public static final String MY_SUBJECT = "CN=Andrea Ceccanti,L=CNAF,OU=Personal Certificate,O=INFN,C=IT"; @Test - public void VOMapParserTest() { + void VOMapParserTest() { - MapfileVOMembershipSource m = new MapfileVOMembershipSource("testers", + MapfileVOMembershipSource m = new MapfileVOMembershipSource("testers", new File("src/test/resources/vomsmap/testers.map")); - + Assert.assertEquals("testers",m.getVOName()); - Assert.assertTrue(m.getVOMembers().contains(mySubject)); - + Assert.assertTrue(m.getVOMembers().contains(MY_SUBJECT)); + Assert.assertFalse(m.getVOMembers().contains("CN=I am not Real, L=CNAF")); - + } } diff --git a/src/test/java/org/italiangrid/storm/webdav/test/checksum/ChecksumHelperTest.java b/src/test/java/org/italiangrid/storm/webdav/test/checksum/ChecksumHelperTest.java index 09c7287c..97d40ee8 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/checksum/ChecksumHelperTest.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/checksum/ChecksumHelperTest.java @@ -32,7 +32,7 @@ import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) -public class ChecksumHelperTest { +class ChecksumHelperTest { private File testFile; private Adler32ChecksumInputStream cis; @@ -60,12 +60,12 @@ public void setup() throws IOException { } @AfterEach - public void finalize() throws IOException { + public void cleanup() throws IOException { cis.close(); } @Test - public void testGetChecksumValueFromFile() throws IOException { + void testGetChecksumValueFromFile() { String newChecksum = cis.getChecksumValue(); String oldChecksum = Long.toHexString(cis.getChecksum().getValue()); @@ -76,7 +76,7 @@ public void testGetChecksumValueFromFile() throws IOException { } @Test - public void testChecksumHelperAddLeadingZero() { + void testChecksumHelperAddLeadingZero() { final String CHECKSUM_VALUE = "abcdefgh"; assertEquals(CHECKSUM_VALUE, ChecksumHelper.addLeadingZero(CHECKSUM_VALUE, 8)); diff --git a/src/test/java/org/italiangrid/storm/webdav/test/macaroon/MacaroonRequestIntegrationTests.java b/src/test/java/org/italiangrid/storm/webdav/test/macaroon/MacaroonRequestIntegrationTests.java index c2c87b7d..5c85f963 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/macaroon/MacaroonRequestIntegrationTests.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/macaroon/MacaroonRequestIntegrationTests.java @@ -95,19 +95,19 @@ public MacaroonRequestIntegrationTests() { } @Test - public void getNotSupported() throws Exception { + void getNotSupported() throws Exception { mvc.perform(get("/whatever").contentType(MacaroonRequestFilter.MACAROON_REQUEST_CONTENT_TYPE)) .andExpect(status().isMethodNotAllowed()); } @Test - public void emptyRequestFails() throws Exception { + void emptyRequestFails() throws Exception { mvc.perform(post("/whatever").contentType(MacaroonRequestFilter.MACAROON_REQUEST_CONTENT_TYPE)) .andExpect(status().isBadRequest()); } @Test - public void vomsRequired() throws Exception { + void vomsRequired() throws Exception { mvc .perform(post("/whatever").contentType(MacaroonRequestFilter.MACAROON_REQUEST_CONTENT_TYPE) .content(EMPTY_JSON_OBJECT)) @@ -116,7 +116,7 @@ public void vomsRequired() throws Exception { @Test @WithMockVOMSUser - public void macaroonIssued() throws Exception { + void macaroonIssued() throws Exception { mvc .perform(post("/whatever").contentType(MacaroonRequestFilter.MACAROON_REQUEST_CONTENT_TYPE) .content(EMPTY_JSON_OBJECT)) @@ -127,7 +127,7 @@ public void macaroonIssued() throws Exception { @Test @WithMockVOMSUser(acExpirationSecs = 43200) - public void validityEnforced() throws Exception { + void validityEnforced() throws Exception { MacaroonRequestDTO dto = new MacaroonRequestDTO(); dto.setValidity("PT2H"); diff --git a/src/test/java/org/italiangrid/storm/webdav/test/oauth/integration/OAuthAuthzServerIntegrationTests.java b/src/test/java/org/italiangrid/storm/webdav/test/oauth/integration/OAuthAuthzServerIntegrationTests.java index c1e8e6b6..41eaaac3 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/oauth/integration/OAuthAuthzServerIntegrationTests.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/oauth/integration/OAuthAuthzServerIntegrationTests.java @@ -93,27 +93,27 @@ public void setup() { } @Test - public void getNotSupported() throws Exception { + void getNotSupported() throws Exception { mvc.perform(get("/oauth/token").contentType(APPLICATION_FORM_URLENCODED)) .andExpect(status().isMethodNotAllowed()); } @Test - public void postNotSupportedForAnonymous() throws Exception { + void postNotSupportedForAnonymous() throws Exception { mvc.perform(post("/oauth/token").contentType(APPLICATION_FORM_URLENCODED)) .andExpect(status().isUnauthorized()); } @Test @WithMockUser(username = "test") - public void postNotSupportedForAuthenticatedNonVomsUsers() throws Exception { + void postNotSupportedForAuthenticatedNonVomsUsers() throws Exception { mvc.perform(post("/oauth/token").content(CONTENT).contentType(APPLICATION_FORM_URLENCODED)) .andExpect(status().isForbidden()); } @Test @WithMockVOMSUser(acExpirationSecs = 200) - public void postSupportedForAuthenticatedVomsUsers() throws Exception { + void postSupportedForAuthenticatedVomsUsers() throws Exception { mvc.perform(post("/oauth/token").content(CONTENT).contentType(APPLICATION_FORM_URLENCODED)) .andExpect(status().isOk()) .andExpect(jsonPath("$.access_token").exists()) @@ -123,7 +123,7 @@ public void postSupportedForAuthenticatedVomsUsers() throws Exception { @Test @WithMockVOMSUser - public void invalidGrantTypeRejected() throws Exception { + void invalidGrantTypeRejected() throws Exception { mvc .perform( post("/oauth/token").content(CONTENT_CUSTOM).contentType(APPLICATION_FORM_URLENCODED)) @@ -135,7 +135,7 @@ public void invalidGrantTypeRejected() throws Exception { @Test @WithMockVOMSUser - public void requestedLifetimeHonoured() throws Exception { + void requestedLifetimeHonoured() throws Exception { mvc .perform(post("/oauth/token").content(format("%s&lifetime=50", CONTENT)) .contentType(APPLICATION_FORM_URLENCODED)) @@ -147,7 +147,7 @@ public void requestedLifetimeHonoured() throws Exception { @Test @WithMockVOMSUser(acExpirationSecs = 200) - public void requestedLifetimeLimited() throws Exception { + void requestedLifetimeLimited() throws Exception { mvc .perform(post("/oauth/token").content(format("%s&lifetime=200000", CONTENT)) .contentType(APPLICATION_FORM_URLENCODED)) @@ -159,7 +159,7 @@ public void requestedLifetimeLimited() throws Exception { @Test @WithMockVOMSUser - public void scopeLengthIsChecked() throws Exception { + void scopeLengthIsChecked() throws Exception { String randomAlphabetic = randomAlphabetic(AccessTokenRequest.MAX_SCOPE_LENGTH); diff --git a/src/test/java/org/italiangrid/storm/webdav/test/oauth/jwt/JwtIssuerTest.java b/src/test/java/org/italiangrid/storm/webdav/test/oauth/jwt/JwtIssuerTest.java index 73a15f41..3ecd3946 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/oauth/jwt/JwtIssuerTest.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/oauth/jwt/JwtIssuerTest.java @@ -143,7 +143,7 @@ public void setup() { } @Test - public void canCreateSignedJWT() throws ParseException, JOSEException { + void canCreateSignedJWT() throws ParseException, JOSEException { SignedJWT jwt = issuer.createAccessToken(req, authn); assertThat(jwt, notNullValue()); @@ -162,7 +162,7 @@ public void canCreateSignedJWT() throws ParseException, JOSEException { @Test - public void returnsAuthoritiesAsExpected() throws ParseException { + void returnsAuthoritiesAsExpected() throws ParseException { SAPermission canReadTest = SAPermission.canRead("test"); SAPermission canWriteTest = SAPermission.canWrite("test"); @@ -184,7 +184,7 @@ public void returnsAuthoritiesAsExpected() throws ParseException { } @Test - public void tokenIssuerLimitsTokenValidityToAcLifetime() throws ParseException { + void tokenIssuerLimitsTokenValidityToAcLifetime() throws ParseException { SAPermission canReadTest = SAPermission.canRead("test"); SAPermission canWriteTest = SAPermission.canWrite("test"); @@ -201,7 +201,7 @@ public void tokenIssuerLimitsTokenValidityToAcLifetime() throws ParseException { } @Test - public void tokenIssuerLimitsTokenValidtyWithRequestedLifetime() throws ParseException { + void tokenIssuerLimitsTokenValidtyWithRequestedLifetime() throws ParseException { SAPermission canReadTest = SAPermission.canRead("test"); SAPermission canWriteTest = SAPermission.canWrite("test"); when(ps.getSAPermissions(authn)).thenReturn(Sets.newHashSet(canReadTest, canWriteTest)); @@ -218,7 +218,7 @@ public void tokenIssuerLimitsTokenValidtyWithRequestedLifetime() throws ParseExc @Test - public void tokenIssuerIgnoresRequestedLifetimeWhenExceedsInternalLimit() throws ParseException { + void tokenIssuerIgnoresRequestedLifetimeWhenExceedsInternalLimit() throws ParseException { SAPermission canReadTest = SAPermission.canRead("test"); SAPermission canWriteTest = SAPermission.canWrite("test"); when(ps.getSAPermissions(authn)).thenReturn(Sets.newHashSet(canReadTest, canWriteTest)); @@ -234,7 +234,7 @@ public void tokenIssuerIgnoresRequestedLifetimeWhenExceedsInternalLimit() throws } @Test - public void tokenIssuerCreatesResourceAccessToken() throws ParseException { + void tokenIssuerCreatesResourceAccessToken() throws ParseException { when(resourceAtRequest.getPath()).thenReturn("/example/resource"); when(resourceAtRequest.getPermission()).thenReturn(Permission.r); diff --git a/src/test/java/org/italiangrid/storm/webdav/test/oauth/jwt/TokenServiceTest.java b/src/test/java/org/italiangrid/storm/webdav/test/oauth/jwt/TokenServiceTest.java index 89fbdd8c..9a70ae63 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/oauth/jwt/TokenServiceTest.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/oauth/jwt/TokenServiceTest.java @@ -82,7 +82,7 @@ public void setup() throws ParseException { } @Test - public void canGenerateTokenResponse() { + void canGenerateTokenResponse() { TokenResponseDTO response = issuerService.createAccessToken(request, auth); assertThat(response.getTokenType(), is(BEARER_TOKEN_TYPE)); diff --git a/src/test/java/org/italiangrid/storm/webdav/test/oauth/validator/AudienceValidatorTests.java b/src/test/java/org/italiangrid/storm/webdav/test/oauth/validator/AudienceValidatorTests.java index 39159bb5..c0e8419d 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/oauth/validator/AudienceValidatorTests.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/oauth/validator/AudienceValidatorTests.java @@ -35,7 +35,7 @@ import com.google.common.collect.Lists; @ExtendWith(MockitoExtension.class) -public class AudienceValidatorTests { +class AudienceValidatorTests { @Mock AuthorizationServer server; @@ -52,7 +52,7 @@ public void setup() { } @Test - public void testNullAudiences() { + void testNullAudiences() { when(server.getAudiences()).thenReturn(null); assertThrows(IllegalArgumentException.class, () -> { validator = new AudienceValidator(server); @@ -60,7 +60,7 @@ public void testNullAudiences() { } @Test - public void testEmptyAudiences() { + void testEmptyAudiences() { when(server.getAudiences()).thenReturn(emptyList()); assertThrows(IllegalArgumentException.class, () -> { validator = new AudienceValidator(server); @@ -68,28 +68,28 @@ public void testEmptyAudiences() { } @Test - public void testNoAudienceInTokenYeldsSuccess() { + void testNoAudienceInTokenYeldsSuccess() { when(jwt.getAudience()).thenReturn(null); validator = new AudienceValidator(server); assertThat(validator.validate(jwt).hasErrors(), is(false)); } @Test - public void testEmptyAudienceInTokenYeldsSuccess() { + void testEmptyAudienceInTokenYeldsSuccess() { when(jwt.getAudience()).thenReturn(emptyList()); validator = new AudienceValidator(server); assertThat(validator.validate(jwt).hasErrors(), is(false)); } @Test - public void testInvalidAudienceIsError() { + void testInvalidAudienceIsError() { when(jwt.getAudience()).thenReturn(Lists.newArrayList("testAudience")); validator = new AudienceValidator(server); assertThat(validator.validate(jwt).hasErrors(), is(true)); } @Test - public void testAudienceValidationSuccess() { + void testAudienceValidationSuccess() { when(jwt.getAudience()).thenReturn(Lists.newArrayList("any")); validator = new AudienceValidator(server); assertThat(validator.validate(jwt).hasErrors(), is(false)); diff --git a/src/test/java/org/italiangrid/storm/webdav/test/redirector/RandomReplicaSelectorTests.java b/src/test/java/org/italiangrid/storm/webdav/test/redirector/RandomReplicaSelectorTests.java index 1c0cb171..5407e143 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/redirector/RandomReplicaSelectorTests.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/redirector/RandomReplicaSelectorTests.java @@ -32,7 +32,7 @@ import com.google.common.collect.Sets; @RunWith(MockitoJUnitRunner.class) -public class RandomReplicaSelectorTests extends RedirectorTestSupport { +class RandomReplicaSelectorTests extends RedirectorTestSupport { ServiceConfigurationProperties config; RandomReplicaSelector selector; @@ -44,14 +44,14 @@ public void setup() { } @Test - public void testEmptyOptionalOnEmptyEndpointList() { + void testEmptyOptionalOnEmptyEndpointList() { assertThat(selector.selectReplica().isPresent(), is(false)); } @Test - public void testSingleEndpointList() { + void testSingleEndpointList() { ReplicaEndpointProperties replica = new ReplicaEndpointProperties(); replica.setEndpoint(ENDPOINT_URI_0); @@ -65,7 +65,7 @@ public void testSingleEndpointList() { @Test - public void testDoubleEndpointList() { + void testDoubleEndpointList() { ReplicaEndpointProperties replica0 = new ReplicaEndpointProperties(); replica0.setEndpoint(ENDPOINT_URI_0); diff --git a/src/test/java/org/italiangrid/storm/webdav/test/redirector/RedirectFilterTests.java b/src/test/java/org/italiangrid/storm/webdav/test/redirector/RedirectFilterTests.java index 10110200..ec96bbc7 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/redirector/RedirectFilterTests.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/redirector/RedirectFilterTests.java @@ -94,7 +94,7 @@ public void setup() { } @Test - public void filterIgnoresPlainHttpRequests() throws IOException, ServletException { + void filterIgnoresPlainHttpRequests() throws IOException, ServletException { when(request.getScheme()).thenReturn("http"); filter.doFilter(request, response, filterChain); @@ -104,7 +104,7 @@ public void filterIgnoresPlainHttpRequests() throws IOException, ServletExceptio } @Test - public void filterIgnoresPlainDavRequests() throws IOException, ServletException { + void filterIgnoresPlainDavRequests() throws IOException, ServletException { when(request.getScheme()).thenReturn("dav"); filter.doFilter(request, response, filterChain); @@ -114,7 +114,7 @@ public void filterIgnoresPlainDavRequests() throws IOException, ServletException } @Test - public void filterIgnoresRequestWithAccessToken() throws IOException, ServletException { + void filterIgnoresRequestWithAccessToken() throws IOException, ServletException { when(request.getParameter("access_token")).thenReturn(RANDOM_TOKEN_STRING); filter.doFilter(request, response, filterChain); @@ -124,7 +124,7 @@ public void filterIgnoresRequestWithAccessToken() throws IOException, ServletExc } @Test - public void filterIgnoresRequestWithNoRedirect() throws IOException, ServletException { + void filterIgnoresRequestWithNoRedirect() throws IOException, ServletException { Map parameterMap = Maps.newHashMap(); parameterMap.put("no_redirect", new String[] {}); @@ -137,7 +137,7 @@ public void filterIgnoresRequestWithNoRedirect() throws IOException, ServletExce } @Test - public void filterIgnoresDirectoryRequest() throws IOException, ServletException { + void filterIgnoresDirectoryRequest() throws IOException, ServletException { when(file.isFile()).thenReturn(false); @@ -148,7 +148,7 @@ public void filterIgnoresDirectoryRequest() throws IOException, ServletException } @Test - public void filterIgnoresResourceNotFound() throws IOException, ServletException { + void filterIgnoresResourceNotFound() throws IOException, ServletException { when(pathResolver.getPath("/example/file")).thenReturn(null); @@ -159,13 +159,13 @@ public void filterIgnoresResourceNotFound() throws IOException, ServletException } @Test - public void filterSendsRedirect() throws IOException, ServletException { + void filterSendsRedirect() throws IOException, ServletException { filter.doFilter(request, response, filterChain); verify(redirectionService).buildRedirect(Mockito.any(), Mockito.eq(request), Mockito.eq(response)); - verify(response).setStatus(Mockito.eq(SC_TEMPORARY_REDIRECT)); + verify(response).setStatus(SC_TEMPORARY_REDIRECT); verify(response).setHeader(Mockito.eq("Location"), redirectUrl.capture()); assertThat(redirectUrl.getValue(), is(REDIRECTED_URL)); diff --git a/src/test/java/org/italiangrid/storm/webdav/test/redirector/RedirectionServiceTests.java b/src/test/java/org/italiangrid/storm/webdav/test/redirector/RedirectionServiceTests.java index 788f1781..6f7d4489 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/redirector/RedirectionServiceTests.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/redirector/RedirectionServiceTests.java @@ -49,7 +49,7 @@ import com.nimbusds.jwt.SignedJWT; @ExtendWith(MockitoExtension.class) -public class RedirectionServiceTests extends RedirectorTestSupport { +class RedirectionServiceTests extends RedirectorTestSupport { @Mock @@ -91,7 +91,7 @@ public void setup() { @Test - public void testRedirectFailureOnEmptyReplica() { + void testRedirectFailureOnEmptyReplica() { Exception e = assertThrows(RedirectError.class, () -> { service.buildRedirect(authentication, request, response); @@ -100,7 +100,7 @@ public void testRedirectFailureOnEmptyReplica() { } @Test - public void testRedirectUriConstruction() { + void testRedirectUriConstruction() { when(selector.selectReplica()).thenReturn(Optional.of(REPLICA_0)); String uriString = service.buildRedirect(authentication, request, response); @@ -121,7 +121,7 @@ public void testRedirectUriConstruction() { } @Test - public void testPutRedirectUriConstruction() { + void testPutRedirectUriConstruction() { when(selector.selectReplica()).thenReturn(Optional.of(REPLICA_0)); when(request.getMethod()).thenReturn("PUT"); @@ -143,7 +143,7 @@ public void testPutRedirectUriConstruction() { } @Test - public void testRedirectUriWithPrefixConstruction() { + void testRedirectUriWithPrefixConstruction() { when(selector.selectReplica()).thenReturn(Optional.of(REPLICA_WITH_PREFIX)); String uriString = service.buildRedirect(authentication, request, response); diff --git a/src/test/java/org/italiangrid/storm/webdav/test/tpc/ClientInfoParserTest.java b/src/test/java/org/italiangrid/storm/webdav/test/tpc/ClientInfoParserTest.java index 35afedc9..85aac3e2 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/tpc/ClientInfoParserTest.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/tpc/ClientInfoParserTest.java @@ -24,10 +24,10 @@ import org.junit.runners.JUnit4; @RunWith(JUnit4.class) -public class ClientInfoParserTest { +class ClientInfoParserTest { @Test - public void testClientInfoHeaderParsing() { + void testClientInfoHeaderParsing() { ClientInfo ci = ClientInfo .fromHeaderString("job-id=34f98a5e-1e49-11e9-ab17-fa163edecedf;file-id=8764139989;retry=0"); diff --git a/src/test/java/org/italiangrid/storm/webdav/test/tpc/PullTransferTest.java b/src/test/java/org/italiangrid/storm/webdav/test/tpc/PullTransferTest.java index 1eaee85c..09527a6a 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/tpc/PullTransferTest.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/tpc/PullTransferTest.java @@ -35,25 +35,27 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; +import org.italiangrid.storm.webdav.tpc.TransferConstants; import com.google.common.collect.Multimap; @ExtendWith(MockitoExtension.class) -public class PullTransferTest extends TransferFilterTestSupport { +class PullTransferTest extends TransferFilterTestSupport { + @Override @BeforeEach public void setup() throws IOException { super.setup(); lenient().when(request.getMethod()).thenReturn(COPY.name()); lenient().when(request.getServletPath()).thenReturn(SERVLET_PATH); lenient().when(request.getPathInfo()).thenReturn(LOCAL_PATH); - lenient().when(request.getHeader(SOURCE_HEADER)).thenReturn(HTTP_URL); - lenient().when(request.getHeader(OVERWRITE_HEADER)).thenReturn(null); - lenient().when(request.getHeader(DESTINATION_HEADER)).thenReturn(null); - lenient().when(request.getHeader(CLIENT_INFO_HEADER)).thenReturn(null); - lenient().when(request.getHeader(CREDENTIAL_HEADER)).thenReturn(null); - lenient().when(request.getHeader(REQUIRE_CHECKSUM_HEADER)).thenReturn(null); + lenient().when(request.getHeader(TransferConstants.SOURCE_HEADER)).thenReturn(HTTP_URL); + lenient().when(request.getHeader(TransferConstants.OVERWRITE_HEADER)).thenReturn(null); + lenient().when(request.getHeader(TransferConstants.DESTINATION_HEADER)).thenReturn(null); + lenient().when(request.getHeader(TransferConstants.CLIENT_INFO_HEADER)).thenReturn(null); + lenient().when(request.getHeader(TransferConstants.CREDENTIAL_HEADER)).thenReturn(null); + lenient().when(request.getHeader(TransferConstants.REQUIRE_CHECKSUM_HEADER)).thenReturn(null); lenient().when(request.getHeaderNames()).thenReturn(emptyEnumeration()); lenient().when(resolver.pathExists(FULL_LOCAL_PATH)).thenReturn(false); lenient().when(resolver.pathExists(FULL_LOCAL_PATH_PARENT)).thenReturn(true); @@ -73,7 +75,7 @@ void pullEmptyTransferHeaders() throws IOException, ServletException { @Test void overwriteHeaderRecognized() throws IOException, ServletException { - when(request.getHeader(OVERWRITE_HEADER)).thenReturn("F"); + when(request.getHeader(TransferConstants.OVERWRITE_HEADER)).thenReturn("F"); filter.doFilter(request, response, chain); verify(client).handle(getXferRequest.capture(), Mockito.any()); assertThat(getXferRequest.getValue().path(), is(FULL_LOCAL_PATH)); @@ -86,7 +88,7 @@ void overwriteHeaderRecognized() throws IOException, ServletException { @Test void checksumRecognized() throws IOException, ServletException { - when(request.getHeader(REQUIRE_CHECKSUM_HEADER)).thenReturn("false"); + when(request.getHeader(TransferConstants.REQUIRE_CHECKSUM_HEADER)).thenReturn("false"); filter.doFilter(request, response, chain); verify(client).handle(getXferRequest.capture(), Mockito.any()); assertThat(getXferRequest.getValue().path(), is(FULL_LOCAL_PATH)); diff --git a/src/test/java/org/italiangrid/storm/webdav/test/tpc/PushTransferTest.java b/src/test/java/org/italiangrid/storm/webdav/test/tpc/PushTransferTest.java index 6569e33a..8806e50c 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/tpc/PushTransferTest.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/tpc/PushTransferTest.java @@ -37,27 +37,29 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; +import org.italiangrid.storm.webdav.tpc.TransferConstants; import com.google.common.collect.Multimap; @ExtendWith(MockitoExtension.class) -public class PushTransferTest extends TransferFilterTestSupport { +class PushTransferTest extends TransferFilterTestSupport { + @Override @BeforeEach public void setup() throws IOException { super.setup(); lenient().when(request.getMethod()).thenReturn(COPY.name()); lenient().when(request.getServletPath()).thenReturn(SERVLET_PATH); lenient().when(request.getPathInfo()).thenReturn(LOCAL_PATH); - lenient().when(request.getHeader(DESTINATION_HEADER)).thenReturn(HTTPS_URL); + lenient().when(request.getHeader(TransferConstants.DESTINATION_HEADER)).thenReturn(HTTPS_URL); lenient().when(request.getHeaderNames()).thenReturn(emptyEnumeration()); lenient().when(resolver.pathExists(FULL_LOCAL_PATH)).thenReturn(true); - lenient().when(request.getHeader(SOURCE_HEADER)).thenReturn(null); - lenient().when(request.getHeader(CLIENT_INFO_HEADER)).thenReturn(null); - lenient().when(request.getHeader(OVERWRITE_HEADER)).thenReturn(null); - lenient().when(request.getHeader(REQUIRE_CHECKSUM_HEADER)).thenReturn(null); - lenient().when(request.getHeader(CREDENTIAL_HEADER)).thenReturn(null); + lenient().when(request.getHeader(TransferConstants.SOURCE_HEADER)).thenReturn(null); + lenient().when(request.getHeader(TransferConstants.CLIENT_INFO_HEADER)).thenReturn(null); + lenient().when(request.getHeader(TransferConstants.OVERWRITE_HEADER)).thenReturn(null); + lenient().when(request.getHeader(TransferConstants.REQUIRE_CHECKSUM_HEADER)).thenReturn(null); + lenient().when(request.getHeader(TransferConstants.CREDENTIAL_HEADER)).thenReturn(null); } @@ -81,7 +83,7 @@ void pushEmptyTransferHeaders() throws IOException, ServletException { @Test void overwriteHeaderRecognized() throws IOException, ServletException { - when(request.getHeader(OVERWRITE_HEADER)).thenReturn("F"); + when(request.getHeader(TransferConstants.OVERWRITE_HEADER)).thenReturn("F"); filter.doFilter(request, response, chain); verify(client).handle(putXferRequest.capture(), Mockito.any()); assertThat(putXferRequest.getValue().path(), is(FULL_LOCAL_PATH)); @@ -94,7 +96,7 @@ void overwriteHeaderRecognized() throws IOException, ServletException { @Test void checksumRecognized() throws IOException, ServletException { - when(request.getHeader(REQUIRE_CHECKSUM_HEADER)).thenReturn("false"); + when(request.getHeader(TransferConstants.REQUIRE_CHECKSUM_HEADER)).thenReturn("false"); filter.doFilter(request, response, chain); verify(client).handle(putXferRequest.capture(), Mockito.any()); assertThat(putXferRequest.getValue().path(), is(FULL_LOCAL_PATH)); diff --git a/src/test/java/org/italiangrid/storm/webdav/test/tpc/SciTagFilterActivationTest.java b/src/test/java/org/italiangrid/storm/webdav/test/tpc/SciTagFilterActivationTest.java index 4a87ae05..51e587c9 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/tpc/SciTagFilterActivationTest.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/tpc/SciTagFilterActivationTest.java @@ -39,6 +39,7 @@ import org.italiangrid.storm.webdav.server.servlet.SciTagFilter; import org.italiangrid.storm.webdav.scitag.SciTag; import org.italiangrid.storm.webdav.scitag.SciTagTransfer; +import org.italiangrid.storm.webdav.tpc.TransferConstants; @ExtendWith(MockitoExtension.class) class SciTagFilterActivationTest extends TransferFilterTestSupport { @@ -64,7 +65,7 @@ public void setup() throws IOException { lenient().when(resolver.resolveStorageArea(FULL_LOCAL_PATH)).thenReturn(testSa); lenient().when(resolver.resolveStorageArea("/test/otherfile")).thenReturn(testSa); lenient().when(resolver.resolveStorageArea("/other/file")).thenReturn(otherSa); - lenient().when(request.getHeader(SOURCE_HEADER)).thenReturn(null); + lenient().when(request.getHeader(TransferConstants.SOURCE_HEADER)).thenReturn(null); } @Test diff --git a/src/test/java/org/italiangrid/storm/webdav/test/tpc/TransferFilterActivationTest.java b/src/test/java/org/italiangrid/storm/webdav/test/tpc/TransferFilterActivationTest.java index 7fea4844..b7b9a3a0 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/tpc/TransferFilterActivationTest.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/tpc/TransferFilterActivationTest.java @@ -31,6 +31,7 @@ import org.italiangrid.storm.webdav.config.StorageAreaInfo; import org.italiangrid.storm.webdav.server.servlet.WebDAVMethod; +import org.italiangrid.storm.webdav.tpc.TransferConstants; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -39,7 +40,7 @@ import org.springframework.http.HttpMethod; @ExtendWith(MockitoExtension.class) -public class TransferFilterActivationTest extends TransferFilterTestSupport { +class TransferFilterActivationTest extends TransferFilterTestSupport { @Mock StorageAreaInfo testSa; @@ -47,6 +48,7 @@ public class TransferFilterActivationTest extends TransferFilterTestSupport { @Mock StorageAreaInfo otherSa; + @Override @BeforeEach public void setup() throws IOException { super.setup(); @@ -56,11 +58,11 @@ public void setup() throws IOException { lenient().when(resolver.resolveStorageArea(FULL_LOCAL_PATH)).thenReturn(testSa); lenient().when(resolver.resolveStorageArea("/test/otherfile")).thenReturn(testSa); lenient().when(resolver.resolveStorageArea("/other/file")).thenReturn(otherSa); - lenient().when(request.getHeader(SOURCE_HEADER)).thenReturn(null); + lenient().when(request.getHeader(TransferConstants.SOURCE_HEADER)).thenReturn(null); } @Test - public void filterIgnoresOtherHttpOrWebdavMethods() throws IOException, ServletException { + void filterIgnoresOtherHttpOrWebdavMethods() throws IOException, ServletException { // Ignore Http methods for (HttpMethod m : HttpMethod.values()) { when(request.getMethod()).thenReturn(m.toString()); @@ -80,12 +82,12 @@ public void filterIgnoresOtherHttpOrWebdavMethods() throws IOException, ServletE } @Test - public void filterSkippedIfSourceAndDestionationHeaderMissing() + void filterSkippedIfSourceAndDestionationHeaderMissing() throws IOException, ServletException { // No source or destination header - when(request.getHeader(SOURCE_HEADER)).thenReturn(null); - when(request.getHeader(DESTINATION_HEADER)).thenReturn(null); + when(request.getHeader(TransferConstants.SOURCE_HEADER)).thenReturn(null); + when(request.getHeader(TransferConstants.DESTINATION_HEADER)).thenReturn(null); when(request.getMethod()).thenReturn(COPY.toString()); filter.doFilter(request, response, chain); @@ -93,9 +95,10 @@ public void filterSkippedIfSourceAndDestionationHeaderMissing() } @Test - public void filterBlocksLocalCopyAcrossStorageAreas() throws IOException, ServletException { + void filterBlocksLocalCopyAcrossStorageAreas() throws IOException, ServletException { when(request.getMethod()).thenReturn(COPY.toString()); - when(request.getHeader(DESTINATION_HEADER)).thenReturn("https://localhost/other/file"); + when(request.getHeader(TransferConstants.DESTINATION_HEADER)) + .thenReturn("https://localhost/other/file"); filter.doFilter(request, response, chain); verify(responseWriter).print(error.capture()); assertThat(error.getValue(), is("Local copy across storage areas is not supported")); @@ -103,19 +106,21 @@ public void filterBlocksLocalCopyAcrossStorageAreas() throws IOException, Servle } @Test - public void filterIgnoresLocalCopyInSameStorageArea() throws IOException, ServletException { + void filterIgnoresLocalCopyInSameStorageArea() throws IOException, ServletException { when(request.getMethod()).thenReturn(COPY.toString()); - when(request.getHeader(DESTINATION_HEADER)).thenReturn("https://localhost/test/otherfile"); + when(request.getHeader(TransferConstants.DESTINATION_HEADER)) + .thenReturn("https://localhost/test/otherfile"); filter.doFilter(request, response, chain); verify(chain).doFilter(request, response); } @Test - public void filterHandlesLocalCopyWithTransferHeader() throws IOException, ServletException { + void filterHandlesLocalCopyWithTransferHeader() throws IOException, ServletException { when(request.getMethod()).thenReturn(COPY.toString()); - when(request.getHeader(DESTINATION_HEADER)).thenReturn("https://localhost/test/otherfile"); + when(request.getHeader(TransferConstants.DESTINATION_HEADER)) + .thenReturn("https://localhost/test/otherfile"); when(requestHeaderNames.hasMoreElements()).thenReturn(true, true, false); - when(requestHeaderNames.nextElement()).thenReturn(DESTINATION_HEADER, + when(requestHeaderNames.nextElement()).thenReturn(TransferConstants.DESTINATION_HEADER, TRANSFER_HEADER_AUTHORIZATION_KEY); filter.doFilter(request, response, chain); @@ -123,17 +128,17 @@ public void filterHandlesLocalCopyWithTransferHeader() throws IOException, Servl } @Test - public void filterHandlesRemoteSourceHeader() throws IOException, ServletException { + void filterHandlesRemoteSourceHeader() throws IOException, ServletException { when(request.getMethod()).thenReturn(COPY.toString()); - when(request.getHeader(SOURCE_HEADER)).thenReturn(HTTP_URL); + when(request.getHeader(TransferConstants.SOURCE_HEADER)).thenReturn(HTTP_URL); filter.doFilter(request, response, chain); verifyNoInteractions(chain); } @Test - public void filterHandlesRemoteDestinationHeader() throws IOException, ServletException { + void filterHandlesRemoteDestinationHeader() throws IOException, ServletException { when(request.getMethod()).thenReturn(COPY.toString()); - when(request.getHeader(DESTINATION_HEADER)).thenReturn(HTTP_URL); + when(request.getHeader(TransferConstants.DESTINATION_HEADER)).thenReturn(HTTP_URL); filter.doFilter(request, response, chain); verifyNoInteractions(chain); } diff --git a/src/test/java/org/italiangrid/storm/webdav/test/tpc/TransferFilterTestSupport.java b/src/test/java/org/italiangrid/storm/webdav/test/tpc/TransferFilterTestSupport.java index 6733e0e5..6f81b5c1 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/tpc/TransferFilterTestSupport.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/tpc/TransferFilterTestSupport.java @@ -33,7 +33,6 @@ import org.italiangrid.storm.webdav.server.PathResolver; import org.italiangrid.storm.webdav.tpc.LocalURLService; import org.italiangrid.storm.webdav.tpc.StaticHostListLocalURLService; -import org.italiangrid.storm.webdav.tpc.TransferConstants; import org.italiangrid.storm.webdav.tpc.TransferFilter; import org.italiangrid.storm.webdav.tpc.transfer.GetTransferRequest; import org.italiangrid.storm.webdav.tpc.transfer.PutTransferRequest; @@ -42,7 +41,7 @@ import org.mockito.Captor; import org.mockito.Mock; -public class TransferFilterTestSupport implements TransferConstants { +public class TransferFilterTestSupport { public static final Instant NOW = Instant.parse("2021-01-01T00:00:00.00Z"); @@ -74,7 +73,7 @@ public class TransferFilterTestSupport implements TransferConstants { public static final URI HTTP_URL_URI = URI.create(HTTP_URL); public static final URI HTTPS_URL_URI = URI.create(HTTPS_URL); - public static String[] INVALID_URLs = + public static final String[] INVALID_URLs = {"http:whatever", "httpg://storm.example/test", "gsiftp://whatever/test"}; public static final String EXPECTED_HEADER = org.apache.http.protocol.HTTP.EXPECT_DIRECTIVE; diff --git a/src/test/java/org/italiangrid/storm/webdav/test/tpc/TransferRequestValidationTest.java b/src/test/java/org/italiangrid/storm/webdav/test/tpc/TransferRequestValidationTest.java index e4b8786a..619e7782 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/tpc/TransferRequestValidationTest.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/tpc/TransferRequestValidationTest.java @@ -36,10 +36,12 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; +import org.italiangrid.storm.webdav.tpc.TransferConstants; @ExtendWith(MockitoExtension.class) -public class TransferRequestValidationTest extends TransferFilterTestSupport { +class TransferRequestValidationTest extends TransferFilterTestSupport { + @Override @BeforeEach public void setup() throws IOException { super.setup(); @@ -49,18 +51,18 @@ public void setup() throws IOException { lenient().when(request.getHeaderNames()).thenReturn(emptyEnumeration()); lenient().when(resolver.pathExists(FULL_LOCAL_PATH)).thenReturn(false); lenient().when(resolver.pathExists(FULL_LOCAL_PATH_PARENT)).thenReturn(true); - lenient().when(request.getHeader(SOURCE_HEADER)).thenReturn(null); - lenient().when(request.getHeader(OVERWRITE_HEADER)).thenReturn(null); - lenient().when(request.getHeader(DESTINATION_HEADER)).thenReturn(null); - lenient().when(request.getHeader(CLIENT_INFO_HEADER)).thenReturn(null); - lenient().when(request.getHeader(CREDENTIAL_HEADER)).thenReturn(null); - lenient().when(request.getHeader(REQUIRE_CHECKSUM_HEADER)).thenReturn(null); + lenient().when(request.getHeader(TransferConstants.SOURCE_HEADER)).thenReturn(null); + lenient().when(request.getHeader(TransferConstants.OVERWRITE_HEADER)).thenReturn(null); + lenient().when(request.getHeader(TransferConstants.DESTINATION_HEADER)).thenReturn(null); + lenient().when(request.getHeader(TransferConstants.CLIENT_INFO_HEADER)).thenReturn(null); + lenient().when(request.getHeader(TransferConstants.CREDENTIAL_HEADER)).thenReturn(null); + lenient().when(request.getHeader(TransferConstants.REQUIRE_CHECKSUM_HEADER)).thenReturn(null); } @Test - public void sourceAndDestHeaderPresent() throws IOException, ServletException { - when(request.getHeader(SOURCE_HEADER)).thenReturn(HTTP_URL); - when(request.getHeader(DESTINATION_HEADER)).thenReturn(HTTP_URL); + void sourceAndDestHeaderPresent() throws IOException, ServletException { + when(request.getHeader(TransferConstants.SOURCE_HEADER)).thenReturn(HTTP_URL); + when(request.getHeader(TransferConstants.DESTINATION_HEADER)).thenReturn(HTTP_URL); filter.doFilter(request, response, chain); verify(response).sendError(httpStatus.capture(), error.capture()); @@ -69,9 +71,9 @@ public void sourceAndDestHeaderPresent() throws IOException, ServletException { } @Test - public void invalidDestinationURIs() throws IOException, ServletException { + void invalidDestinationURIs() throws IOException, ServletException { for (String u : INVALID_URLs) { - when(request.getHeader(DESTINATION_HEADER)).thenReturn(u); + when(request.getHeader(TransferConstants.DESTINATION_HEADER)).thenReturn(u); filter.doFilter(request, response, chain); verify(response).sendError(httpStatus.capture(), error.capture()); assertThat(httpStatus.getValue(), is(BAD_REQUEST.value())); @@ -82,9 +84,9 @@ public void invalidDestinationURIs() throws IOException, ServletException { @Test - public void invalidSourceURIs() throws IOException, ServletException { + void invalidSourceURIs() throws IOException, ServletException { for (String u : INVALID_URLs) { - when(request.getHeader(SOURCE_HEADER)).thenReturn(u); + when(request.getHeader(TransferConstants.SOURCE_HEADER)).thenReturn(u); filter.doFilter(request, response, chain); verify(response).sendError(httpStatus.capture(), error.capture()); assertThat(httpStatus.getValue(), is(BAD_REQUEST.value())); @@ -94,14 +96,14 @@ public void invalidSourceURIs() throws IOException, ServletException { } @Test - public void invalidOverwriteHeader() throws IOException, ServletException { + void invalidOverwriteHeader() throws IOException, ServletException { String[] invalidValues = {"", "cccc", "whatever", "true", "false"}; - when(request.getHeader(SOURCE_HEADER)).thenReturn(HTTP_URL); + when(request.getHeader(TransferConstants.SOURCE_HEADER)).thenReturn(HTTP_URL); for (String s : invalidValues) { - when(request.getHeader(OVERWRITE_HEADER)).thenReturn(s); + when(request.getHeader(TransferConstants.OVERWRITE_HEADER)).thenReturn(s); filter.doFilter(request, response, chain); verify(response).sendError(httpStatus.capture(), error.capture()); assertThat(httpStatus.getValue(), is(BAD_REQUEST.value())); @@ -111,12 +113,12 @@ public void invalidOverwriteHeader() throws IOException, ServletException { } @Test - public void invalidRequireChecksumHeader() throws IOException, ServletException { + void invalidRequireChecksumHeader() throws IOException, ServletException { String[] invalidValues = {"t", "F", ""}; - when(request.getHeader(SOURCE_HEADER)).thenReturn(HTTP_URL); + when(request.getHeader(TransferConstants.SOURCE_HEADER)).thenReturn(HTTP_URL); for (String s : invalidValues) { - when(request.getHeader(REQUIRE_CHECKSUM_HEADER)).thenReturn(s); + when(request.getHeader(TransferConstants.REQUIRE_CHECKSUM_HEADER)).thenReturn(s); filter.doFilter(request, response, chain); verify(response).sendError(httpStatus.capture(), error.capture()); assertThat(httpStatus.getValue(), is(BAD_REQUEST.value())); @@ -126,8 +128,8 @@ public void invalidRequireChecksumHeader() throws IOException, ServletException } @Test - public void emptyOrNullPathInfo() throws IOException, ServletException { - when(request.getHeader(SOURCE_HEADER)).thenReturn(HTTP_URL); + void emptyOrNullPathInfo() throws IOException, ServletException { + when(request.getHeader(TransferConstants.SOURCE_HEADER)).thenReturn(HTTP_URL); String[] invalidPathInfos = {null, "", "does/not/start/with/slash"}; String[] expectedErrorMsgs = {"Null or empty", "Null or empty", "Invalid local path"}; @@ -145,9 +147,9 @@ public void emptyOrNullPathInfo() throws IOException, ServletException { } @Test - public void invalidCredentialHeader() throws IOException, ServletException { - when(request.getHeader(SOURCE_HEADER)).thenReturn(HTTP_URL); - when(request.getHeader(CREDENTIAL_HEADER)).thenReturn("gridsite"); + void invalidCredentialHeader() throws IOException, ServletException { + when(request.getHeader(TransferConstants.SOURCE_HEADER)).thenReturn(HTTP_URL); + when(request.getHeader(TransferConstants.CREDENTIAL_HEADER)).thenReturn("gridsite"); filter.doFilter(request, response, chain); verify(response).sendError(httpStatus.capture(), error.capture()); assertThat(httpStatus.getValue(), is(SC_BAD_REQUEST)); @@ -155,9 +157,9 @@ public void invalidCredentialHeader() throws IOException, ServletException { } @Test - public void noneCredentialHeaderAccepted() throws IOException, ServletException { - when(request.getHeader(SOURCE_HEADER)).thenReturn(HTTP_URL); - when(request.getHeader(CREDENTIAL_HEADER)).thenReturn("none"); + void noneCredentialHeaderAccepted() throws IOException, ServletException { + when(request.getHeader(TransferConstants.SOURCE_HEADER)).thenReturn(HTTP_URL); + when(request.getHeader(TransferConstants.CREDENTIAL_HEADER)).thenReturn("none"); filter.doFilter(request, response, chain); verify(response).setStatus(httpStatus.capture()); diff --git a/src/test/java/org/italiangrid/storm/webdav/test/tpc/TransferReturnStatusTest.java b/src/test/java/org/italiangrid/storm/webdav/test/tpc/TransferReturnStatusTest.java index 22bd4478..b54b1bac 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/tpc/TransferReturnStatusTest.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/tpc/TransferReturnStatusTest.java @@ -31,6 +31,7 @@ import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpResponseException; +import org.italiangrid.storm.webdav.tpc.TransferConstants; import org.italiangrid.storm.webdav.tpc.transfer.GetTransferRequest; import org.italiangrid.storm.webdav.tpc.transfer.error.ChecksumVerificationError; import org.italiangrid.storm.webdav.tpc.transfer.error.TransferError; @@ -42,28 +43,29 @@ import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) -public class TransferReturnStatusTest extends TransferFilterTestSupport { +class TransferReturnStatusTest extends TransferFilterTestSupport { + @Override @BeforeEach public void setup() throws IOException { super.setup(); when(request.getMethod()).thenReturn(COPY.name()); when(request.getServletPath()).thenReturn(SERVLET_PATH); when(request.getPathInfo()).thenReturn(LOCAL_PATH); - when(request.getHeader(SOURCE_HEADER)).thenReturn(HTTP_URL); + when(request.getHeader(TransferConstants.SOURCE_HEADER)).thenReturn(HTTP_URL); when(request.getHeaderNames()).thenReturn(emptyEnumeration()); when(resolver.pathExists(FULL_LOCAL_PATH_PARENT)).thenReturn(true); } @Test - public void filterAnswers202() throws IOException, ServletException { + void filterAnswers202() throws IOException, ServletException { filter.doFilter(request, response, chain); verify(response).setStatus(httpStatus.capture()); assertThat(httpStatus.getValue(), is(SC_ACCEPTED)); } @Test - public void filterAnswers412ForClientProtocolException() throws IOException, ServletException { + void filterAnswers412ForClientProtocolException() throws IOException, ServletException { Mockito.doThrow(new ClientProtocolException("Connection error")) .when(client) .handle(ArgumentMatchers.any(), ArgumentMatchers.any()); @@ -75,7 +77,7 @@ public void filterAnswers412ForClientProtocolException() throws IOException, Ser } @Test - public void filterAnswers412ForHttpExceptionError() throws IOException, ServletException { + void filterAnswers412ForHttpExceptionError() throws IOException, ServletException { Mockito.doThrow(new HttpResponseException(HttpServletResponse.SC_FORBIDDEN, "Access denied")) .when(client) .handle(ArgumentMatchers.any(), ArgumentMatchers.any()); @@ -88,7 +90,7 @@ public void filterAnswers412ForHttpExceptionError() throws IOException, ServletE } @Test - public void filterAnswers412ForChecksumVerificationError() throws IOException, ServletException { + void filterAnswers412ForChecksumVerificationError() throws IOException, ServletException { Mockito.doThrow(new ChecksumVerificationError("Checksum verification error")) .when(client) .handle(ArgumentMatchers.any(), ArgumentMatchers.any()); @@ -100,7 +102,7 @@ public void filterAnswers412ForChecksumVerificationError() throws IOException, S } @Test - public void filterAnswers412ForGenericTransferError() throws IOException, ServletException { + void filterAnswers412ForGenericTransferError() throws IOException, ServletException { Mockito.doThrow(new TransferError("Error")) .when(client) .handle(ArgumentMatchers.any(), ArgumentMatchers.any()); diff --git a/src/test/java/org/italiangrid/storm/webdav/test/tpc/TransferStatsTest.java b/src/test/java/org/italiangrid/storm/webdav/test/tpc/TransferStatsTest.java index c0e3a6d0..f602a5e7 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/tpc/TransferStatsTest.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/tpc/TransferStatsTest.java @@ -40,7 +40,7 @@ public class TransferStatsTest { TransferStatus.Builder status = TransferStatus.builder(clock); @Test - public void testByteCountPull() { + void testByteCountPull() { GetTransferRequest req = GetTransferRequestBuilder.create().build(); @@ -57,7 +57,7 @@ public void testByteCountPull() { } @Test - public void testOneMsecByteCountPull() { + void testOneMsecByteCountPull() { GetTransferRequest req = GetTransferRequestBuilder.create().build(); req.setTransferStatus(status.inProgress(0)); @@ -72,7 +72,7 @@ public void testOneMsecByteCountPull() { } @Test - public void testHalfMsecByteCountPull() { + void testHalfMsecByteCountPull() { GetTransferRequest req = GetTransferRequestBuilder.create().build(); req.setTransferStatus(status.inProgress(0)); diff --git a/src/test/java/org/italiangrid/storm/webdav/test/tpc/http/ClientTest.java b/src/test/java/org/italiangrid/storm/webdav/test/tpc/http/ClientTest.java index 353df6ba..efd8b40b 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/tpc/http/ClientTest.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/tpc/http/ClientTest.java @@ -42,12 +42,13 @@ import com.google.common.collect.ArrayListMultimap; @ExtendWith(MockitoExtension.class) -public class ClientTest extends ClientTestSupport { +class ClientTest extends ClientTestSupport { @TempDir public File storage; @SuppressWarnings("unchecked") + @Override @BeforeEach public void setup() throws IOException { @@ -69,7 +70,7 @@ public void setup() throws IOException { } @Test - public void testClientCorrectlyBuildsHttpRequestNoHeaders() throws IOException { + void testClientCorrectlyBuildsHttpRequestNoHeaders() throws IOException { client.handle(req, (r, s) -> { }); @@ -86,7 +87,7 @@ public void testClientCorrectlyBuildsHttpRequestNoHeaders() throws IOException { } @Test - public void testClientCorrectlyBuildsHttpRequestWithHeaders() throws IOException { + void testClientCorrectlyBuildsHttpRequestWithHeaders() throws IOException { when(req.transferHeaders()).thenReturn(HEADER_MAP); diff --git a/src/test/java/org/italiangrid/storm/webdav/test/tpc/http/DigestTest.java b/src/test/java/org/italiangrid/storm/webdav/test/tpc/http/DigestTest.java index 5007bf71..5ace7612 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/tpc/http/DigestTest.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/tpc/http/DigestTest.java @@ -37,7 +37,7 @@ public class DigestTest { @Mock Header header; - public static String[] INVALID_HEADERS = + public static final String[] INVALID_HEADERS = {"", "adler54=1233456", "adler32=8a23d4f889", "sha256:437648", null}; public static final String[] VALID_HEADERS = @@ -51,7 +51,7 @@ protected void instrumentResponse(String headerValue) { } @Test - public void testInvalidHeader() { + void testInvalidHeader() { for (String s : INVALID_HEADERS) { instrumentResponse(s); diff --git a/src/test/java/org/italiangrid/storm/webdav/test/tpc/http/GetResponseHandlerTest.java b/src/test/java/org/italiangrid/storm/webdav/test/tpc/http/GetResponseHandlerTest.java index 257b060c..498be64d 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/tpc/http/GetResponseHandlerTest.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/tpc/http/GetResponseHandlerTest.java @@ -36,36 +36,37 @@ import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) -public class GetResponseHandlerTest extends ClientTestSupport { +class GetResponseHandlerTest extends ClientTestSupport { @Mock StatusLine status; - + @Mock HttpEntity entity; - + @Mock HttpResponse response; - + @Mock StormCountingOutputStream os; - + GetResponseHandler handler; + @Override @BeforeEach public void setup() { - + handler = new GetResponseHandler(null, os, eah); lenient().when(response.getStatusLine()).thenReturn(status); lenient().when(response.getEntity()).thenReturn(entity); } - + @Test - public void handlerWritesToStream() throws IOException { + void handlerWritesToStream() throws IOException { when(status.getStatusCode()).thenReturn(200); - + handler.handleResponse(response); - + verify(entity).getContent(); verify(eah).setChecksumAttribute(ArgumentMatchers.any(), any()); } diff --git a/src/test/java/org/italiangrid/storm/webdav/test/tpc/http/integration/TpcClientRedirectionTest.java b/src/test/java/org/italiangrid/storm/webdav/test/tpc/http/integration/TpcClientRedirectionTest.java index 498084a4..33e51df0 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/tpc/http/integration/TpcClientRedirectionTest.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/tpc/http/integration/TpcClientRedirectionTest.java @@ -21,13 +21,7 @@ import static org.mockserver.model.NottableString.not; import static org.mockserver.verify.VerificationTimes.exactly; -import java.io.IOException; import java.net.URI; -import java.security.KeyManagementException; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.cert.CertificateException; import java.util.UUID; import javax.net.ssl.SSLContext; @@ -93,8 +87,7 @@ public static class TestConfig { @Bean("tpcConnectionManager") @Primary public HttpClientConnectionManager tpcClientConnectionManager(ThirdPartyCopyProperties props, - ServiceConfiguration conf) throws KeyStoreException, CertificateException, IOException, - NoSuchAlgorithmException, NoSuchProviderException, KeyManagementException { + ServiceConfiguration conf) { SSLContext ctx = KeyStoreFactory.keyStoreFactory().sslContext(); ConnectionSocketFactory sf = PlainConnectionSocketFactory.getSocketFactory(); @@ -144,7 +137,7 @@ private String mockHttpUrl(String path) { } @Test - public void handleCrossProtocolRedirectionCorrectly() { + void handleCrossProtocolRedirectionCorrectly() { Multimap headers = ArrayListMultimap.create(); headers.put("Authorization", "Bearer this-is-a-fake-token"); diff --git a/src/test/java/org/italiangrid/storm/webdav/test/tpc/http/integration/TpcIntegrationTest.java b/src/test/java/org/italiangrid/storm/webdav/test/tpc/http/integration/TpcIntegrationTest.java index ca1fba9f..9c8d8e86 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/tpc/http/integration/TpcIntegrationTest.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/tpc/http/integration/TpcIntegrationTest.java @@ -23,7 +23,7 @@ import static org.mockserver.model.HttpRequest.request; import static org.mockserver.model.NottableString.not; import static org.mockserver.verify.VerificationTimes.exactly; -import static org.springframework.util.SocketUtils.findAvailableTcpPort; +import static org.springframework.test.util.TestSocketUtils.findAvailableTcpPort; import java.net.URI; import java.util.UUID; @@ -70,7 +70,7 @@ public class TpcIntegrationTest { @BeforeAll public static void startMockServer() { - port = findAvailableTcpPort(15000); + port = findAvailableTcpPort(); mockServer = startClientAndServer(port); } @@ -89,7 +89,7 @@ private String mockUrl(String path) { } @Test - public void testPutRedirectHandled() { + void testPutRedirectHandled() { Multimap emptyHeaders = ArrayListMultimap.create(); PutTransferRequest putRequest = new PutTransferRequestImpl(UUID.randomUUID().toString(), @@ -118,7 +118,7 @@ public void testPutRedirectHandled() { } @Test - public void testAuthorizationHeaderIsDroppedOnRedirectForPut() { + void testAuthorizationHeaderIsDroppedOnRedirectForPut() { Multimap headers = ArrayListMultimap.create(); headers.put("Authorization", "Bearer this-is-a-fake-token"); @@ -149,7 +149,7 @@ public void testAuthorizationHeaderIsDroppedOnRedirectForPut() { } @Test - public void testAuthorizationHeaderIsDroppedOnRedirectForGet() { + void testAuthorizationHeaderIsDroppedOnRedirectForGet() { Multimap headers = ArrayListMultimap.create(); headers.put("Authorization", "Bearer this-is-a-fake-token"); diff --git a/src/test/java/org/italiangrid/storm/webdav/test/tpc/urlservice/URLServiceTest.java b/src/test/java/org/italiangrid/storm/webdav/test/tpc/urlservice/URLServiceTest.java index 1e370480..5b73f499 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/tpc/urlservice/URLServiceTest.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/tpc/urlservice/URLServiceTest.java @@ -29,13 +29,13 @@ import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) -public class URLServiceTest { +class URLServiceTest { public static final String[] SERVICE_ALIASES = {"storm.example", "alias.storm.example", "localhost"}; @Test - public void testEmptyList() { + void testEmptyList() { assertThrows(IllegalArgumentException.class, () -> { new StaticHostListLocalURLService(Collections.emptyList()); @@ -43,7 +43,7 @@ public void testEmptyList() { } @Test - public void testNullList() { + void testNullList() { assertThrows(NullPointerException.class, () -> { new StaticHostListLocalURLService(null); @@ -51,7 +51,7 @@ public void testNullList() { } @Test - public void testResolution() { + void testResolution() { StaticHostListLocalURLService service = new StaticHostListLocalURLService(Arrays.asList(SERVICE_ALIASES)); @@ -66,7 +66,7 @@ public void testResolution() { } @Test - public void testInvalidUrlResolution() { + void testInvalidUrlResolution() { StaticHostListLocalURLService service = new StaticHostListLocalURLService(Arrays.asList(SERVICE_ALIASES)); diff --git a/src/test/java/org/italiangrid/storm/webdav/test/utils/IOUtilsTest.java b/src/test/java/org/italiangrid/storm/webdav/test/utils/IOUtilsTest.java index 6c10477b..ec27df7e 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/utils/IOUtilsTest.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/utils/IOUtilsTest.java @@ -30,7 +30,7 @@ import org.junit.runners.JUnit4; @RunWith(JUnit4.class) -public class IOUtilsTest { +class IOUtilsTest { @TempDir public File testFolder; @@ -48,7 +48,7 @@ public File tempFileOfChar(String name, int b, int size) throws IOException { } @Test - public void testAllFileCopy() throws IOException { + void testAllFileCopy() throws IOException { File source = tempFileOfChar("source", 0, 128); File dest = tempFileOfChar("dest", 1, 128); @@ -66,7 +66,7 @@ public void testAllFileCopy() throws IOException { } @Test - public void testLongerSourceFileCopy() throws IOException { + void testLongerSourceFileCopy() throws IOException { File source = tempFileOfChar("source", 0, 200); File dest = tempFileOfChar("dest", 1, 64); @@ -84,7 +84,7 @@ public void testLongerSourceFileCopy() throws IOException { } @Test - public void testShorterSourceFileCopy() throws IOException { + void testShorterSourceFileCopy() throws IOException { File source = tempFileOfChar("source", 0, 16); File dest = tempFileOfChar("dest", 1, 512); @@ -102,7 +102,7 @@ public void testShorterSourceFileCopy() throws IOException { } @Test - public void testPartialWriteCopy() throws IOException { + void testPartialWriteCopy() throws IOException { File source = tempFileOfChar("source", 0, 128); File dest = tempFileOfChar("dest", 1, 512); @@ -120,7 +120,7 @@ public void testPartialWriteCopy() throws IOException { } @Test - public void testMiddleWrite() throws IOException { + void testMiddleWrite() throws IOException { File source = tempFileOfChar("source", 0, 640); File dest = tempFileOfChar("dest", 1, 256); From aee7266b058c7cb2ad3ec39bf3e9113f61338b38 Mon Sep 17 00:00:00 2001 From: Enrico Vianello Date: Fri, 8 Nov 2024 12:05:01 +0100 Subject: [PATCH 12/22] Raise IOException in case of errors during file removal Fix for https://issues.infn.it/jira/browse/STOR-1568 --- .../storm/webdav/fs/DefaultFSStrategy.java | 11 +++++------ .../storm/webdav/fs/FilesystemAccess.java | 3 ++- .../storm/webdav/fs/MetricsFSStrategyWrapper.java | 5 +++-- .../webdav/milton/StoRMDirectoryResource.java | 14 ++++++++++++-- .../storm/webdav/milton/StoRMFileResource.java | 11 +++++++++-- 5 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/italiangrid/storm/webdav/fs/DefaultFSStrategy.java b/src/main/java/org/italiangrid/storm/webdav/fs/DefaultFSStrategy.java index aa3202d5..bb59126e 100644 --- a/src/main/java/org/italiangrid/storm/webdav/fs/DefaultFSStrategy.java +++ b/src/main/java/org/italiangrid/storm/webdav/fs/DefaultFSStrategy.java @@ -19,6 +19,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.nio.file.Files; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; @@ -30,8 +31,6 @@ import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; -import com.google.common.io.Files; - @Component public class DefaultFSStrategy implements FilesystemAccess { @@ -60,10 +59,10 @@ public File mkdir(File parentDirectory, String dirName) { } @Override - public boolean rm(File f) { + public void rm(File f) throws IOException { LOG.debug("rm: {}", f.getAbsolutePath()); - return f.delete(); + Files.delete(f.toPath()); } @Override @@ -79,7 +78,7 @@ public void mv(File source, File dest) { } // Overwrites the destination, if it exists - Files.move(source, dest); + Files.move(source.toPath(), dest.toPath()); } catch (IOException e) { throw new StoRMWebDAVError(e.getMessage(), e); @@ -112,7 +111,7 @@ public void cp(File source, File dest) { } else { - Files.copy(source, dest); + Files.copy(source.toPath(), dest.toPath()); } diff --git a/src/main/java/org/italiangrid/storm/webdav/fs/FilesystemAccess.java b/src/main/java/org/italiangrid/storm/webdav/fs/FilesystemAccess.java index 3d6bcdf5..503b67b1 100644 --- a/src/main/java/org/italiangrid/storm/webdav/fs/FilesystemAccess.java +++ b/src/main/java/org/italiangrid/storm/webdav/fs/FilesystemAccess.java @@ -16,13 +16,14 @@ package org.italiangrid.storm.webdav.fs; import java.io.File; +import java.io.IOException; import java.io.InputStream; public interface FilesystemAccess { public File mkdir(File parentDirectory, String dirName); - public boolean rm(File f); + public void rm(File f) throws IOException; public void mv(File source, File dest); diff --git a/src/main/java/org/italiangrid/storm/webdav/fs/MetricsFSStrategyWrapper.java b/src/main/java/org/italiangrid/storm/webdav/fs/MetricsFSStrategyWrapper.java index 2083d56d..bfdf6e83 100644 --- a/src/main/java/org/italiangrid/storm/webdav/fs/MetricsFSStrategyWrapper.java +++ b/src/main/java/org/italiangrid/storm/webdav/fs/MetricsFSStrategyWrapper.java @@ -18,6 +18,7 @@ import static com.codahale.metrics.MetricRegistry.name; import java.io.File; +import java.io.IOException; import java.io.InputStream; import com.codahale.metrics.MetricRegistry; @@ -68,12 +69,12 @@ public File mkdir(File parentDirectory, String dirName) { } @Override - public boolean rm(File f) { + public void rm(File f) throws IOException { final Timer.Context context = rmTimer.time(); try { - return delegate.rm(f); + delegate.rm(f); } finally { context.stop(); diff --git a/src/main/java/org/italiangrid/storm/webdav/milton/StoRMDirectoryResource.java b/src/main/java/org/italiangrid/storm/webdav/milton/StoRMDirectoryResource.java index cc914b40..1102a935 100644 --- a/src/main/java/org/italiangrid/storm/webdav/milton/StoRMDirectoryResource.java +++ b/src/main/java/org/italiangrid/storm/webdav/milton/StoRMDirectoryResource.java @@ -20,12 +20,15 @@ import java.io.InputStream; import java.nio.file.DirectoryStream; import java.nio.file.Files; +import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import org.italiangrid.storm.webdav.error.DirectoryNotEmpty; import org.italiangrid.storm.webdav.error.StoRMWebDAVError; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import io.milton.http.Request; import io.milton.http.exceptions.BadRequestException; @@ -42,6 +45,8 @@ public class StoRMDirectoryResource extends StoRMResource implements PutableResource, MakeCollectionableResource, DeletableResource, DeletableCollectionResource, CopyableResource { + private static final Logger logger = LoggerFactory.getLogger(StoRMDirectoryResource.class); + public StoRMDirectoryResource(StoRMResourceFactory factory, File f) { super(factory, f); @@ -112,8 +117,13 @@ public void delete() throws NotAuthorizedException, ConflictException, BadReques throw new StoRMWebDAVError(e); } - getFilesystemAccess().rm(getFile()); - + try { + getFilesystemAccess().rm(getFile()); + } catch (NoSuchFileException e) { + logger.warn("Unable to remove directory {}: {}", getFile(), e.getMessage()); + } catch (IOException e) { + throw new StoRMWebDAVError(e); + } } @Override diff --git a/src/main/java/org/italiangrid/storm/webdav/milton/StoRMFileResource.java b/src/main/java/org/italiangrid/storm/webdav/milton/StoRMFileResource.java index 14812812..e33c6dd6 100644 --- a/src/main/java/org/italiangrid/storm/webdav/milton/StoRMFileResource.java +++ b/src/main/java/org/italiangrid/storm/webdav/milton/StoRMFileResource.java @@ -26,6 +26,7 @@ import java.io.OutputStream; import java.net.FileNameMap; import java.net.URLConnection; +import java.nio.file.NoSuchFileException; import java.util.List; import java.util.Map; @@ -84,8 +85,14 @@ public StoRMFileResource(StoRMResourceFactory factory, File f) { @Override public void delete() throws NotAuthorizedException, ConflictException, BadRequestException { - getFilesystemAccess().rm(getFile()); - + try { + getFilesystemAccess().rm(getFile()); + } catch (NoSuchFileException e) { + logger.warn("Unable to remove file {}: {}", getFile(), e.getMessage()); + } catch (IOException e) { + logger.error("Unable to remove file {}: {}", getFile(), e.getMessage()); + throw new StoRMWebDAVError(e); + } } protected void handleIOException(IOException e) { From c8af5a2dcdd752e9362bf897be6c672ab835371a Mon Sep 17 00:00:00 2001 From: Federica Agostini Date: Thu, 14 Nov 2024 18:05:43 +0100 Subject: [PATCH 13/22] Restore StoRM WebDAV testsuite This commit restores the StoRM WebDAV test-suite continuous integration tests. All tests from the legacy StoRM test-suite have been migrated/added. Fined grained authorization tests added too. --- .github/workflows/run-testsuite.yml | 52 ++++ .gitignore | 2 + Dockerfile | 35 +++ compose/.env | 8 + compose/README.md | 25 ++ compose/assets/certs/hostcert.pem | 121 +++++----- compose/assets/certs/hostkey.pem | 50 ++-- compose/assets/etc/storm/webdav/README.md | 19 -- .../webdav/config/application-issuers.yml | 6 + .../webdav/config/application-policies.yml | 54 +++++ .../etc/storm/webdav/sa.d/auth.properties | 24 -- .../etc/storm/webdav/sa.d/fga.properties | 13 +- ...uthz.properties => oauth-authz.properties} | 2 +- .../storm/webdav/sa.d/sa.properties.template | 29 --- ...{test_vo.properties => test.vo.properties} | 0 .../etc/storm/webdav/vo-mapfiles.d/README.md | 39 --- compose/assets/nginx/nginx.conf | 17 +- compose/assets/nginx/srm.conf | 13 +- compose/assets/oidc-agent/dev-wlcg | 7 + compose/assets/scripts/ci-run-testsuite.sh | 17 ++ compose/assets/scripts/init-certs.sh | 7 - compose/assets/scripts/init-sa-config.sh | 11 - compose/assets/scripts/init-storage.sh | 2 +- compose/assets/scripts/init-usercerts.sh | 3 +- compose/assets/scripts/run-service.sh | 35 --- compose/assets/scripts/run.sh | 5 - compose/assets/scripts/setup.sh | 6 - compose/assets/scripts/unpack-tarball.sh | 9 - .../assets/scripts/wait-and-run-testsuite.sh | 18 -- compose/assets/scripts/wait-for-it.sh | 177 -------------- compose/assets/usercerts/test0.p12 | Bin 2533 -> 2533 bytes .../test.vo.2/vgrid02.cnaf.infn.it.lsc | 2 - .../assets/vomsdir/test.vo.2/voms.example.lsc | 2 - .../vomsdir/test.vo/vgrid02.cnaf.infn.it.lsc | 2 - .../test.vo/voms-dev.cloud.cnaf.infn.it.lsc | 2 + .../assets/vomsdir/test.vo/voms.example.lsc | 2 - compose/assets/vomses/test.vo | 2 +- compose/docker-compose.yml | 107 +++++---- docker/testsuite/Dockerfile | 7 - docker/testsuite/build-image.sh | 3 - docker/webdav-centos7/Dockerfile | 3 +- robot/.gitignore | 3 - robot/README.md | 94 ++++++++ robot/assets/README.md | 1 - .../default/config/application-hackathon.yml | 70 ------ .../default/config/application-oidc.yml | 24 -- .../fixtures/default/config/application.yml | 62 ----- robot/assets/fixtures/default/sa.d/README.md | 2 - .../fixtures/default/sa.d/auth.properties | 7 - .../fixtures/default/sa.d/fga.properties | 5 - .../fixtures/default/sa.d/noauth.properties | 7 - .../default/sa.d/oauth_authz.properties | 8 - .../fixtures/default/sa.d/test_vo.properties | 9 - .../fixtures/default/sa.d/tf.properties | 12 - .../redirector/config/application-oidc.yml | 24 -- .../redirector/config/application.yml | 70 ------ .../assets/fixtures/redirector/sa.d/README.md | 2 - .../fixtures/redirector/sa.d/auth.properties | 7 - .../fixtures/redirector/sa.d/fga.properties | 5 - .../redirector/sa.d/noauth.properties | 7 - .../redirector/sa.d/oauth_authz.properties | 8 - .../redirector/sa.d/test_vo.properties | 9 - .../fixtures/redirector/sa.d/tf.properties | 12 - .../fixtures/redirector/sa.d/wlcg.properties | 12 - robot/common/credentials.robot | 26 +- robot/common/curl.robot | 59 ++++- robot/common/oidc-agent.robot | 16 ++ robot/common/setup_and_teardown.robot | 30 +++ robot/common/storage_areas.robot | 13 +- robot/common/utils.robot | 4 + robot/reports/.gitignore | 3 - robot/run-testsuite.sh | 6 +- robot/test/authorization.robot | 223 ++++++++++++++++++ robot/test/basic_tests.robot | 160 ++----------- robot/test/checksum.robot | 46 ++-- robot/test/copy.robot | 93 +++++--- robot/test/delete.robot | 51 ++++ robot/test/get.robot | 56 +++++ robot/test/head.robot | 65 +++++ robot/test/mkcol.robot | 38 +++ robot/test/move.robot | 96 +++++--- robot/test/oauth.robot | 5 +- robot/test/options.robot | 39 +++ robot/test/partial_transfer.robot | 93 ++++++++ robot/test/propfind.robot | 74 ++++++ robot/test/put.robot | 67 ++++++ robot/test/token_request.robot | 4 +- robot/test/tpc.robot | 6 +- 88 files changed, 1480 insertions(+), 1191 deletions(-) create mode 100644 .github/workflows/run-testsuite.yml create mode 100644 Dockerfile create mode 100644 compose/README.md create mode 100644 compose/assets/etc/storm/webdav/config/application-issuers.yml create mode 100644 compose/assets/etc/storm/webdav/config/application-policies.yml delete mode 100644 compose/assets/etc/storm/webdav/sa.d/auth.properties rename robot/assets/fixtures/default/sa.d/wlcg.properties => compose/assets/etc/storm/webdav/sa.d/fga.properties (61%) rename compose/assets/etc/storm/webdav/sa.d/{oauth_authz.properties => oauth-authz.properties} (91%) delete mode 100644 compose/assets/etc/storm/webdav/sa.d/sa.properties.template rename compose/assets/etc/storm/webdav/sa.d/{test_vo.properties => test.vo.properties} (100%) delete mode 100644 compose/assets/etc/storm/webdav/vo-mapfiles.d/README.md create mode 100644 compose/assets/oidc-agent/dev-wlcg create mode 100755 compose/assets/scripts/ci-run-testsuite.sh delete mode 100755 compose/assets/scripts/init-certs.sh delete mode 100755 compose/assets/scripts/init-sa-config.sh delete mode 100755 compose/assets/scripts/run-service.sh delete mode 100755 compose/assets/scripts/run.sh delete mode 100755 compose/assets/scripts/setup.sh delete mode 100755 compose/assets/scripts/unpack-tarball.sh delete mode 100755 compose/assets/scripts/wait-and-run-testsuite.sh delete mode 100755 compose/assets/scripts/wait-for-it.sh delete mode 100644 compose/assets/vomsdir/test.vo.2/vgrid02.cnaf.infn.it.lsc delete mode 100644 compose/assets/vomsdir/test.vo.2/voms.example.lsc delete mode 100644 compose/assets/vomsdir/test.vo/vgrid02.cnaf.infn.it.lsc create mode 100644 compose/assets/vomsdir/test.vo/voms-dev.cloud.cnaf.infn.it.lsc delete mode 100644 compose/assets/vomsdir/test.vo/voms.example.lsc delete mode 100644 docker/testsuite/Dockerfile delete mode 100755 docker/testsuite/build-image.sh delete mode 100644 robot/.gitignore delete mode 100644 robot/assets/README.md delete mode 100644 robot/assets/fixtures/default/config/application-hackathon.yml delete mode 100644 robot/assets/fixtures/default/config/application-oidc.yml delete mode 100644 robot/assets/fixtures/default/config/application.yml delete mode 100644 robot/assets/fixtures/default/sa.d/README.md delete mode 100644 robot/assets/fixtures/default/sa.d/auth.properties delete mode 100644 robot/assets/fixtures/default/sa.d/fga.properties delete mode 100644 robot/assets/fixtures/default/sa.d/noauth.properties delete mode 100644 robot/assets/fixtures/default/sa.d/oauth_authz.properties delete mode 100644 robot/assets/fixtures/default/sa.d/test_vo.properties delete mode 100644 robot/assets/fixtures/default/sa.d/tf.properties delete mode 100644 robot/assets/fixtures/redirector/config/application-oidc.yml delete mode 100644 robot/assets/fixtures/redirector/config/application.yml delete mode 100644 robot/assets/fixtures/redirector/sa.d/README.md delete mode 100644 robot/assets/fixtures/redirector/sa.d/auth.properties delete mode 100644 robot/assets/fixtures/redirector/sa.d/fga.properties delete mode 100644 robot/assets/fixtures/redirector/sa.d/noauth.properties delete mode 100644 robot/assets/fixtures/redirector/sa.d/oauth_authz.properties delete mode 100644 robot/assets/fixtures/redirector/sa.d/test_vo.properties delete mode 100644 robot/assets/fixtures/redirector/sa.d/tf.properties delete mode 100644 robot/assets/fixtures/redirector/sa.d/wlcg.properties create mode 100644 robot/common/oidc-agent.robot create mode 100644 robot/common/setup_and_teardown.robot delete mode 100644 robot/reports/.gitignore create mode 100644 robot/test/authorization.robot create mode 100644 robot/test/delete.robot create mode 100644 robot/test/get.robot create mode 100644 robot/test/head.robot create mode 100644 robot/test/mkcol.robot create mode 100644 robot/test/options.robot create mode 100644 robot/test/partial_transfer.robot create mode 100644 robot/test/propfind.robot create mode 100644 robot/test/put.robot diff --git a/.github/workflows/run-testsuite.yml b/.github/workflows/run-testsuite.yml new file mode 100644 index 00000000..fd97bcaa --- /dev/null +++ b/.github/workflows/run-testsuite.yml @@ -0,0 +1,52 @@ +name: Run testsuite + +on: + push: + +jobs: + run-testsuite: + name: WebDAV test suite + + runs-on: ubuntu-latest + + env: + ARTIFACTS: ${HOME}/artifacts + ROBOT_ARGS: -L DEBUG --variable dav.host:storm.test.example --variable remote.dav.host:storm-alias.test.example --variable remote.davs.port:443 --exclude known-issue + OIDC_AGENT_SECRET: ${{ secrets.OIDC_AGENT_SECRET }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Provide trustanchors + working-directory: compose + run: docker compose up trust + + - name: Start services + working-directory: compose + run: docker compose up --build -d storage-setup webdav nginx + + - name: Run testsuite + working-directory: compose + run: | + docker compose up -d ts + docker compose exec -T ts bash -c '/scripts/ci-run-testsuite.sh' + continue-on-error: true + + - name: Create artifacts directory + if: ${{ always() }} + run: mkdir -p ${ARTIFACTS} + + - name: Collect test reports + run: docker cp storm-webdav-ts-1:/home/test/robot/reports ${ARTIFACTS} + + - name: Collect service log + if: ${{ always() }} + run: docker logs storm-webdav-webdav-1 > ${ARTIFACTS}/storm-webdav-server.log 2>&1 + + - name: Archive reports + if: ${{ always() }} + uses: actions/upload-artifact@v4 + with: + name: logs-and-reports + path: ${{ env.ARTIFACTS }} diff --git a/.gitignore b/.gitignore index c8aa1829..2a738a16 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ .springBeans .idea storm-webdav-server.iml +/robot/reports +.vscode diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..21fd5b1d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,35 @@ +# https://spring.io/guides/topicals/spring-boot-docker#_multi_stage_build +FROM eclipse-temurin:11-jdk-alpine as build +WORKDIR /workspace/app +RUN apk add maven +COPY pom.xml . +COPY maven maven +RUN mvn dependency:resolve -s maven/cnaf-mirror-settings.xml +RUN mvn dependency:resolve-plugins -s maven/cnaf-mirror-settings.xml +COPY .git .git +COPY etc etc +COPY src src +RUN mvn package -s maven/cnaf-mirror-settings.xml -Dmaven.test.skip +RUN mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar) + +FROM eclipse-temurin:11-centos7 +ENV STORM_WEBDAV_JVM_OPTS="-Dspring.profiles.active=dev" +ARG DEPENDENCY=/workspace/app/target/dependency + +#WORKDIR /app +COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib +COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF +COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app +COPY src src + +ARG USERNAME=storm +ARG USER_UID=1000 +ARG USER_GID=${USER_UID} + +RUN groupadd --gid ${USER_GID} ${USERNAME} +RUN useradd --uid ${USER_UID} --gid ${USER_GID} -m ${USERNAME} +RUN echo ${USERNAME} ALL=\(root\) NOPASSWD:ALL > /etc/sudoers +RUN chmod 0440 /etc/sudoers +USER ${USERNAME} + +ENTRYPOINT java ${STORM_WEBDAV_JVM_OPTS} -cp app:app/lib/* org.italiangrid.storm.webdav.WebdavService diff --git a/compose/.env b/compose/.env index 80bf19fb..27d31cbe 100644 --- a/compose/.env +++ b/compose/.env @@ -1 +1,9 @@ COMPOSE_PROJECT_NAME=storm-webdav +TRUST_IMAGE=indigoiam/egi-trustanchors +TRUST_IMAGE_TAG=igi-test-ca +WEBDAV_IMAGE=italiangrid/storm-webdav-centos7 +WEBDAV_IMAGE_TAG=latest +TS_IMAGE=indigoiam/robot-framework +TS_IMAGE_TAG=latest +NGINX_IMAGE=baltig.infn.it:4567/cnafsd/ngx_http_voms_module/nginx-httpg-voms +NGINX_IMAGE_TAG=latest \ No newline at end of file diff --git a/compose/README.md b/compose/README.md new file mode 100644 index 00000000..057b2dc2 --- /dev/null +++ b/compose/README.md @@ -0,0 +1,25 @@ +# Docker compose for StoRM WebDAV + +Run the services with + +``` +$ docker-compose up -d +``` + +The docker-compose contains several services: + +* `trust`: docker image for the GRID CA certificates, mounted in the `/etc/grid-security/certificates` path of the other services. The _igi-test-ca_ used in this deployment is also present in that path +* `storage-setup`: sidecar container, used to allocate proper volumes (i.e. storage areas) owned by _storm_ +* `webdav`: is the main service, also known as StoRM WebDAV. The StoRM WebDAV base URL is https://storm.test.example:8443. It serves the following storage areas: + * `test.vo` for users presenting a proxy issued by a _test.vo_ VO + * `noauth`: which allows read/write mode also to anonymous users + * `fga`: for a fined grained authorization storage area. Its access policies are set in the [application](./assets/etc/storm/webdav/config/application-policies.yml) file + * `oauth-authz`: for users presenting a token issued by the [IAM DEV](https://iam-dev.cloud.cnaf.infn.it) +* `ts`: used for running the StoRM WebDAV testsuite. It shares the storage with the `webdav` service, to run local tests +* `nginx`: is the NGINX service supporting VOMS authentication, used as remote StoRM server for WebDAV calls. It does not forward requests to StoRM WebDAV, but just serves local resources in a separate storage. URL of this service is https://storm-alias.test.example. In the testsuite, the local resources are served by an `oauth-authz` endpoint, that does not require authentication. + +To resolve the hostname of the service, add a line in your `/etc/hosts` file with + +``` +127.0.0.1 storm.test.example storm-alias.test.example +``` \ No newline at end of file diff --git a/compose/assets/certs/hostcert.pem b/compose/assets/certs/hostcert.pem index c97b8a7b..3cbd4774 100644 --- a/compose/assets/certs/hostcert.pem +++ b/compose/assets/certs/hostcert.pem @@ -1,86 +1,85 @@ Certificate: Data: Version: 3 (0x2) - Serial Number: 801 (0x321) + Serial Number: 19 (0x13) Signature Algorithm: sha512WithRSAEncryption Issuer: C=IT, O=IGI, CN=Test CA Validity - Not Before: Oct 15 15:57:05 2018 GMT - Not After : Oct 12 15:57:05 2028 GMT - Subject: C=IT, O=IGI, CN=storm dev + Not Before: Oct 19 08:55:57 2022 GMT + Not After : Oct 16 08:55:57 2032 GMT + Subject: C=IT, O=IGI, CN=*.test.example Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: - 00:cb:98:91:d4:9f:f5:a7:0a:1c:cf:b8:51:7d:2e: - fa:a9:c7:df:74:75:bb:81:1a:52:e0:a5:1e:48:56: - c5:85:39:bf:90:4a:2b:be:c5:ef:83:0a:4a:e0:86: - 84:81:79:14:4f:8e:70:ba:8a:a3:68:07:a3:2c:be: - 76:d6:fc:28:bf:91:31:67:45:eb:2e:b6:ce:31:bd: - 32:d1:f4:a0:88:0c:e9:2f:a0:ee:77:8f:da:c1:1b: - 50:ba:0d:09:05:29:12:b1:4c:98:28:fd:6a:c0:fc: - 9b:d1:40:cd:5c:59:c4:7d:49:bf:c1:0f:a5:3a:42: - 7c:41:0d:1e:25:2e:2e:2e:3d:0c:23:fb:9f:1f:46: - ec:f3:62:aa:a6:ca:85:a9:ea:ec:51:98:26:6e:1a: - bd:cd:0e:eb:22:49:b2:e6:c4:99:2f:6b:3c:ba:82: - 09:46:74:b3:19:a6:dc:b9:a1:83:6d:d5:28:62:43: - ba:1b:f3:e1:1d:61:61:87:b2:cb:1b:14:49:02:de: - d9:10:ca:d7:0c:da:c6:c3:1c:f2:ab:48:27:8d:10: - 17:8b:56:cb:5d:d4:f6:19:65:4c:78:25:cb:3d:be: - a5:93:77:ce:a2:77:97:de:b4:24:8e:aa:3b:dc:c6: - f8:57:d9:a9:ba:42:d9:7a:77:a4:4a:dc:76:07:2b: - 43:c3 + 00:e7:3a:01:a8:93:12:08:f4:a6:c9:89:10:a2:f6: + 6a:6a:d3:93:98:c7:31:c0:e5:8a:3a:44:9b:cf:ef: + b9:3d:05:86:03:61:0e:6e:fc:c6:f9:9a:9e:35:d6: + 3d:38:27:48:cb:77:26:97:15:34:a0:0b:1d:97:31: + dd:18:ec:bf:78:d9:32:9e:00:1a:44:6a:78:15:1f: + ac:7b:3e:bb:ad:b2:b4:32:75:8c:11:d8:31:ec:19: + 7d:bf:ba:5d:1e:70:38:62:10:cf:3a:8a:a4:98:83: + b4:df:e0:50:3b:e5:ec:24:a0:89:14:2c:19:27:48: + 66:c3:d4:1d:74:63:be:63:38:95:3f:64:d0:91:ac: + 95:f7:d9:ca:96:b5:1b:e7:71:70:7b:5f:3b:12:30: + 2c:b8:3a:28:79:84:9c:81:12:db:38:31:6d:2d:2a: + e2:80:05:5c:29:77:53:58:10:19:ee:f9:50:e1:8d: + 3b:2b:e2:c0:0b:d2:9f:3c:a0:95:33:f8:33:17:ce: + 23:0e:31:e8:1e:3d:7e:6a:c9:6d:83:9e:0b:fa:43: + d2:4a:3f:be:d3:19:07:1e:8c:e4:f6:dc:8f:c3:3e: + 3a:8e:66:4a:87:ef:0b:39:db:e8:3e:30:1c:91:9e: + b3:1e:d3:a0:1e:1b:9a:b1:58:99:de:a5:bb:53:3b: + 3b:5d Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: critical CA:FALSE X509v3 Subject Key Identifier: - AF:52:EA:AC:22:88:70:E5:C6:AA:AE:CC:AD:FB:CA:95:EB:17:3B:15 + 60:FA:21:CE:1C:B5:31:8D:9B:01:F6:08:5B:72:4D:59:5A:F8:71:8C X509v3 Key Usage: critical Digital Signature, Non Repudiation, Key Encipherment X509v3 Extended Key Usage: TLS Web Server Authentication, TLS Web Client Authentication, Microsoft Server Gated Crypto, Netscape Server Gated Crypto, E-mail Protection X509v3 Authority Key Identifier: - keyid:91:77:36:7B:2E:B4:69:F3:27:EA:B7:F6:08:8B:4A:23:A2:11:49:C6 + keyid:50:9B:6F:74:01:E3:1A:03:57:AB:D9:D5:7D:15:64:4C:25:F3:F8:F4 X509v3 Subject Alternative Name: - DNS:storm.example, DNS:storm-alias.example, DNS:other.example, DNS:localhost + DNS:*.test.example Signature Algorithm: sha512WithRSAEncryption - b5:36:9a:2d:e4:79:56:1a:1c:d0:34:e4:d8:06:2a:03:94:65: - cc:a7:71:bf:88:c6:f9:1d:bf:20:18:d4:25:6a:8a:a5:5e:97: - 64:8e:23:d2:51:0a:fb:3a:96:68:f6:a3:75:bd:74:6d:3d:4d: - 05:54:1c:b4:43:ee:33:bd:66:80:ee:81:50:f4:9c:ea:38:74: - 22:f3:ab:b1:41:04:7f:f5:64:07:49:78:9e:73:a5:00:0d:8f: - e6:c9:ec:bc:3b:f7:00:7e:9e:09:1a:9b:a4:40:a7:39:90:1c: - fa:ca:ec:31:53:52:27:93:88:db:18:b3:f0:b7:7f:65:4e:06: - c5:f5:b4:9e:6c:af:69:ef:da:ea:4c:e8:50:ed:dc:49:a7:fe: - 69:90:cf:77:69:58:49:0a:1c:50:5e:ab:26:b0:52:31:ca:6f: - 8a:11:78:80:c5:9e:4f:43:40:60:f3:99:46:4d:8d:51:5a:e5: - 04:90:9e:ce:40:4a:c5:35:b1:f1:d1:63:86:8b:42:73:79:7a: - f7:33:d3:69:22:45:a2:82:0c:05:69:7d:00:2b:e5:c9:44:38: - f8:ae:e1:81:71:04:b8:48:bf:51:91:22:4e:90:c6:ad:91:cc: - 30:a5:e8:53:4f:64:b1:3d:7a:c8:cd:ae:b6:b8:7c:dc:c7:98: - 36:eb:a5:e4 + 79:82:f2:54:44:98:96:25:c2:83:c9:0f:19:69:1c:f6:a7:19: + 0d:61:90:f9:96:23:e2:ab:5a:30:db:55:d7:4f:b0:ff:b2:7b: + 41:da:35:97:47:86:e4:85:00:6d:11:64:ee:32:a4:64:ee:fe: + b2:83:a5:24:4a:ce:c3:91:ae:db:3d:5b:af:fa:7e:81:1a:1c: + 69:d0:1a:9e:70:0e:9e:74:85:6b:48:90:6a:1b:62:ff:6e:b3: + 84:30:b7:7f:fa:c0:3e:ee:91:70:0b:f2:13:ea:c8:2c:aa:d8: + cb:3c:60:b1:08:f9:8e:bf:c2:e4:ce:92:6a:7e:0a:41:49:94: + 8f:e5:6e:71:f9:47:04:1a:18:1f:65:47:d6:1c:ea:a9:90:71: + 82:1b:3b:1f:a5:f2:02:ce:5c:d6:2e:5d:1e:05:c4:92:9e:3d: + 8e:ce:fa:00:83:01:d5:c3:c1:cf:e2:e5:fb:08:80:08:f4:6c: + 26:64:96:db:cd:be:4c:e7:bc:8f:af:3d:0e:0c:f7:d2:52:15: + 9c:d5:15:0d:51:b3:95:72:78:1d:8c:ca:37:55:7a:c0:b0:0f: + 18:ae:de:d0:27:6f:1b:e4:5d:1d:4b:f9:4c:5d:44:49:ed:cf: + c2:9e:e7:c6:55:72:ce:2f:43:a7:2f:88:de:b7:da:9f:82:a6: + 54:77:c2:2e -----BEGIN CERTIFICATE----- -MIIDwzCCAqugAwIBAgICAyEwDQYJKoZIhvcNAQENBQAwLTELMAkGA1UEBhMCSVQx -DDAKBgNVBAoMA0lHSTEQMA4GA1UEAwwHVGVzdCBDQTAeFw0xODEwMTUxNTU3MDVa -Fw0yODEwMTIxNTU3MDVaMC8xCzAJBgNVBAYTAklUMQwwCgYDVQQKDANJR0kxEjAQ -BgNVBAMMCXN0b3JtIGRldjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB -AMuYkdSf9acKHM+4UX0u+qnH33R1u4EaUuClHkhWxYU5v5BKK77F74MKSuCGhIF5 -FE+OcLqKo2gHoyy+dtb8KL+RMWdF6y62zjG9MtH0oIgM6S+g7neP2sEbULoNCQUp -ErFMmCj9asD8m9FAzVxZxH1Jv8EPpTpCfEENHiUuLi49DCP7nx9G7PNiqqbKhanq -7FGYJm4avc0O6yJJsubEmS9rPLqCCUZ0sxmm3Lmhg23VKGJDuhvz4R1hYYeyyxsU -SQLe2RDK1wzaxsMc8qtIJ40QF4tWy13U9hllTHglyz2+pZN3zqJ3l960JI6qO9zG -+FfZqbpC2Xp3pErcdgcrQ8MCAwEAAaOB6jCB5zAMBgNVHRMBAf8EAjAAMB0GA1Ud -DgQWBBSvUuqsIohw5caqrsyt+8qV6xc7FTAOBgNVHQ8BAf8EBAMCBeAwPgYDVR0l -BDcwNQYIKwYBBQUHAwEGCCsGAQUFBwMCBgorBgEEAYI3CgMDBglghkgBhvhCBAEG -CCsGAQUFBwMEMB8GA1UdIwQYMBaAFJF3NnsutGnzJ+q39giLSiOiEUnGMEcGA1Ud -EQRAMD6CDXN0b3JtLmV4YW1wbGWCE3N0b3JtLWFsaWFzLmV4YW1wbGWCDW90aGVy -LmV4YW1wbGWCCWxvY2FsaG9zdDANBgkqhkiG9w0BAQ0FAAOCAQEAtTaaLeR5Vhoc -0DTk2AYqA5RlzKdxv4jG+R2/IBjUJWqKpV6XZI4j0lEK+zqWaPajdb10bT1NBVQc -tEPuM71mgO6BUPSc6jh0IvOrsUEEf/VkB0l4nnOlAA2P5snsvDv3AH6eCRqbpECn -OZAc+srsMVNSJ5OI2xiz8Ld/ZU4GxfW0nmyvae/a6kzoUO3cSaf+aZDPd2lYSQoc -UF6rJrBSMcpvihF4gMWeT0NAYPOZRk2NUVrlBJCezkBKxTWx8dFjhotCc3l69zPT -aSJFooIMBWl9ACvlyUQ4+K7hgXEEuEi/UZEiTpDGrZHMMKXoU09ksT16yM2utrh8 -3MeYNuul5A== +MIIDmTCCAoGgAwIBAgIBEzANBgkqhkiG9w0BAQ0FADAtMQswCQYDVQQGEwJJVDEM +MAoGA1UECgwDSUdJMRAwDgYDVQQDDAdUZXN0IENBMB4XDTIyMTAxOTA4NTU1N1oX +DTMyMTAxNjA4NTU1N1owNDELMAkGA1UEBhMCSVQxDDAKBgNVBAoMA0lHSTEXMBUG +A1UEAwwOKi50ZXN0LmV4YW1wbGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQDnOgGokxII9KbJiRCi9mpq05OYxzHA5Yo6RJvP77k9BYYDYQ5u/Mb5mp41 +1j04J0jLdyaXFTSgCx2XMd0Y7L942TKeABpEangVH6x7PrutsrQydYwR2DHsGX2/ +ul0ecDhiEM86iqSYg7Tf4FA75ewkoIkULBknSGbD1B10Y75jOJU/ZNCRrJX32cqW +tRvncXB7XzsSMCy4Oih5hJyBEts4MW0tKuKABVwpd1NYEBnu+VDhjTsr4sAL0p88 +oJUz+DMXziMOMegePX5qyW2Dngv6Q9JKP77TGQcejOT23I/DPjqOZkqH7ws52+g+ +MByRnrMe06AeG5qxWJnepbtTOztdAgMBAAGjgbwwgbkwDAYDVR0TAQH/BAIwADAd +BgNVHQ4EFgQUYPohzhy1MY2bAfYIW3JNWVr4cYwwDgYDVR0PAQH/BAQDAgXgMD4G +A1UdJQQ3MDUGCCsGAQUFBwMBBggrBgEFBQcDAgYKKwYBBAGCNwoDAwYJYIZIAYb4 +QgQBBggrBgEFBQcDBDAfBgNVHSMEGDAWgBRQm290AeMaA1er2dV9FWRMJfP49DAZ +BgNVHREEEjAQgg4qLnRlc3QuZXhhbXBsZTANBgkqhkiG9w0BAQ0FAAOCAQEAeYLy +VESYliXCg8kPGWkc9qcZDWGQ+ZYj4qtaMNtV10+w/7J7Qdo1l0eG5IUAbRFk7jKk +ZO7+soOlJErOw5Gu2z1br/p+gRocadAannAOnnSFa0iQahti/26zhDC3f/rAPu6R +cAvyE+rILKrYyzxgsQj5jr/C5M6San4KQUmUj+VucflHBBoYH2VH1hzqqZBxghs7 +H6XyAs5c1i5dHgXEkp49js76AIMB1cPBz+Ll+wiACPRsJmSW282+TOe8j689Dgz3 +0lIVnNUVDVGzlXJ4HYzKN1V6wLAPGK7e0CdvG+RdHUv5TF1ESe3Pwp7nxlVyzi9D +py+I3rfan4KmVHfCLg== -----END CERTIFICATE----- diff --git a/compose/assets/certs/hostkey.pem b/compose/assets/certs/hostkey.pem index c0c7d06e..610502b8 100644 --- a/compose/assets/certs/hostkey.pem +++ b/compose/assets/certs/hostkey.pem @@ -1,27 +1,27 @@ -----BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEAy5iR1J/1pwocz7hRfS76qcffdHW7gRpS4KUeSFbFhTm/kEor -vsXvgwpK4IaEgXkUT45wuoqjaAejLL521vwov5ExZ0XrLrbOMb0y0fSgiAzpL6Du -d4/awRtQug0JBSkSsUyYKP1qwPyb0UDNXFnEfUm/wQ+lOkJ8QQ0eJS4uLj0MI/uf -H0bs82KqpsqFqersUZgmbhq9zQ7rIkmy5sSZL2s8uoIJRnSzGabcuaGDbdUoYkO6 -G/PhHWFhh7LLGxRJAt7ZEMrXDNrGwxzyq0gnjRAXi1bLXdT2GWVMeCXLPb6lk3fO -oneX3rQkjqo73Mb4V9mpukLZenekStx2BytDwwIDAQABAoIBAHFYwWeEnniekqe6 -T/PHodm/4tGtcfRQOW/DvXY8iL7BBbtI783H2K41nrYdbcu/IuWfwXa5FHwoNFoG -t5a8z9rG9KAwNtzM/UKHuLFW5cCYn4HasKhzuC/mCy1pcGolEbkPkW7QlwxWFlGL -KEmP2GqAEndjRHOI7DAzI2NDsIYgjBARGCWLURcjohr8q5Z1EC9B8ClmzA94f7EZ -RZ61mN3oOZiJtulGRnmn70lIdcJ4sWMlJbrLtKsPK0rHAv8U5Yjs+TSsrz4lYTVa -5sdp9nhr5GpZ3W+JDEq0ZyeiJ5FxyR4krcIj8HVVDVavauW4vRu9CeqnDwDunPl6 -L14O/uECgYEA75RpcfM4bzULJpVbLNHZTkClZtWNyY77rDkfvrOjlsD06QMDBeQh -vFxiNxwO2JqSKoJf1vay8Hn7un9NSm5x8MgRfrMjhsG6MzavycrxESRFtq5Adkdk -3lQyn0WGYsPHFLVs/tx1GtdzCqU5SyBUkeLCqMNaARV1xmD4AjFcaasCgYEA2YzM -ZZ8Z4aAqkv6gJiZTN1gQxMO8nPiCwY8NefI/Mm1U+X6j4ZYRkqTcvdsJzFtnj+ab -rrguS1AOuyDMID2NKjQTrzJLBUhNYzbo7YeMsY2U+k9z0fvM3WGzX0YBvcxtnqXm -BLMKHjbF0YvzEbu0qD1dWj5CZ6e/+DXfK5QlZkkCgYAEAa9hwHeJJJHzKzxDG59O -t7YMajXc0Q9UagAl6EssEj4GR46dYptN0x2xXj7BUJRxMYz4w1dqvh9/lvFr9Tzi -kfX48HX/ou3CPX/jGAnAB6NC0tcxIzCEp1PRZhBBRpTlu8L+4CD1OfUqkGjM4NWJ -OwmWWO4AZqN5ldWP89Nf0QKBgQDUR1RHMNljVRNV/gmtUCZRUaiDJ3ALR17nmjwP -KzdJcG/DSDSHchTRn/cZdvt3ohVK0D5HXccmjAbjx9wG9aiibtBqWsvjaqrAzhq5 -dFPwCPQ+z3p3gpljx+rsY3ZdinXIoZ7yJPYRh2a90y6qthtRMxe9cBUB6iki/QY4 -EsXvqQKBgQCUokN2XeonTeJCIDKU7XKd5JNOuWFWCz/tsBu2lnMSr/2txiL3cCgt -BNJw+rbZ08hMMNeD871lsYKTrPigEXKpMlHlC8RodWK7XEGhTL4nHoZQ/PE8Zq71 -Q6+DM27CV0IU7/78rrWO0YdHii2pE72Fp05i/X16apjTSFi9InL6ZQ== +MIIEowIBAAKCAQEA5zoBqJMSCPSmyYkQovZqatOTmMcxwOWKOkSbz++5PQWGA2EO +bvzG+ZqeNdY9OCdIy3cmlxU0oAsdlzHdGOy/eNkyngAaRGp4FR+sez67rbK0MnWM +Edgx7Bl9v7pdHnA4YhDPOoqkmIO03+BQO+XsJKCJFCwZJ0hmw9QddGO+YziVP2TQ +kayV99nKlrUb53Fwe187EjAsuDooeYScgRLbODFtLSrigAVcKXdTWBAZ7vlQ4Y07 +K+LAC9KfPKCVM/gzF84jDjHoHj1+asltg54L+kPSSj++0xkHHozk9tyPwz46jmZK +h+8LOdvoPjAckZ6zHtOgHhuasViZ3qW7Uzs7XQIDAQABAoIBAAx5xL0jskVpbdZR +3uPsB7Hb2IrVtImD2QFr0jxV4ti4A5MLGYxDdzjgbsjY1lTBSdwwgZSFQGGiN+aA +ej1uCKaskV6VAtXOKMx6+QNtTxMAIVjXnscXsxnaBj7h/0Q1KdWgso2mDVttP8UU +hT+2GBeh0cOU3YaREXpfZ3dwKkWQHbtO/UYwVzu+XVFt8kApPoLMMHoXZfetP6Yp +7YSCuI6id44mwqkP7aY8iGhcUpVTkP3LD7z8nUp4LaG9my6T1Wev8x7hstb/NIsZ +DPiXAzfDUkHWqpMthnoWyOdghGc6JzKGFeJVHqrW4byJ4hNU3WvNIdvZ8tyIEpd1 +56uP/gECgYEA92oUhzHjvw87qfo6tPDai2I8AghXJoPGB6xYYhchlirYMGPx9fU/ +rcVEGbmSBDqXMg9eZUqiXB+E/hukCOrFZJt4kt656Nm/Xy68IDwSifmf5vcUde6q +j0pD6i0vwJFjYWBjjS7gRBK83pr/jHhy8aK1+79lZ0GfbQkLxF/2TxkCgYEA70Af +A387tHDmct7ZH0gAZx9QKYZhtS+WWVCIoZ81028DEeGri0By83KFkU0QZ9RfWKQi +RajBYkB35xJFv4fSX5s4+tcVaTVJKOn7V5YGmUIxrGY3IMuE77+h9SEHd8GY723q +9qgwTF5SQP3cGiVpGFB99M44CBuHbbypFh67iuUCgYEA3Zp6QI2C/AJc4mZqZt7E +IMwgC4IE7U5h9UV89H7banF9qfobIr5EBxUFZjU8f+Uqv3/cgMVUn0bsC94eEo6V +twM5//LWeaVvL4Xgos6rnEGl422zOd5HjohqRDms58JRTUrUYAR4gwB1gr0530uT +SLMAZTiNTusMLNFJZN6+8yECgYAulAY1sRSXmY9T98y/iU4CxZberrnhA2W697HR +/WQGSMuJNK0oDCEVAku8sQsrm64AXNwLQcJ8dV6iju0jT7cGQ/sA4tTZSbV3kK4N +LDkWp0tya+f5q4WzA1Ttm0OP7hHvMzAWW0Ij7A0JeCLcuEHQqQMMoQVJlspz89Hb +a5pJfQKBgFZb6XnLMTSCs/SQe38PQiawIQcA+zXmhG83xkEKspQGm2KqyJL+AdKQ +fXQKoKa/Ubyp7PKRJVZ8raX1/kvtFDQIQ+G3L/hps5rhZgDh5S2n0xd4zlbK/Sw6 +l3RjOUpHSe8oz+X3Jinl/Rwr39I9hrRAW2xj7vkFb84IE98mJu2X -----END RSA PRIVATE KEY----- diff --git a/compose/assets/etc/storm/webdav/README.md b/compose/assets/etc/storm/webdav/README.md index fe79a8f1..a45eefb2 100644 --- a/compose/assets/etc/storm/webdav/README.md +++ b/compose/assets/etc/storm/webdav/README.md @@ -11,22 +11,3 @@ service. Storage area configuration lives in the `sa.d` directory. For more information see the README.md file there. - -## VOMS map files configuration - -VOMS map files contains the list of VO members as obtained by running the -voms-admin list-users command. - -When VOMS mapfiles are enabled, users can authenticate to the StoRM webdav -service using the certificate in their browser and be granted VOMS attributes -if their subject is listed in one of the supported VOMS mapfile. - -For each supported VO, a file having the same name as the VO is put in the -voms-mapfiles directory. - -*Example*: to generate a VOMS mapfile for the `cms` VO, run the following -command - -```bash -voms-admin --host voms.cern.ch --vo cms list-users > cms -``` diff --git a/compose/assets/etc/storm/webdav/config/application-issuers.yml b/compose/assets/etc/storm/webdav/config/application-issuers.yml new file mode 100644 index 00000000..275750b9 --- /dev/null +++ b/compose/assets/etc/storm/webdav/config/application-issuers.yml @@ -0,0 +1,6 @@ +oauth: + issuers: + - name: egi-checkin + issuer: https://egi-checkin.example/ + - name: dev + issuer: https://iam-dev.cloud.cnaf.infn.it/ \ No newline at end of file diff --git a/compose/assets/etc/storm/webdav/config/application-policies.yml b/compose/assets/etc/storm/webdav/config/application-policies.yml new file mode 100644 index 00000000..0ef9a668 --- /dev/null +++ b/compose/assets/etc/storm/webdav/config/application-policies.yml @@ -0,0 +1,54 @@ +storm: + authz: + policies: + - sa: fga + actions: + - all + effect: permit + description: Grant read/write access to test.vo VOMS users + principals: + - type: vo + params: + vo: test.vo + - sa: fga + actions: + - list + - read + effect: permit + description: Grant read access to anyone to the public area + paths: + - /public/** + principals: + - type: anyone + - sa: fga + actions: + - all + effect: permit + description: Grant read/write to the "cms" token (default) group in the cms namespace + paths: + - /cms/** + principals: + - type: jwt-group + params: + iss: https://iam-dev.cloud.cnaf.infn.it/ + group: /cms + - sa: fga + actions: + - all + effect: permit + description: Grant read/write to the "data-manager" token (optional) group + principals: + - type: jwt-group + params: + iss: https://iam-dev.cloud.cnaf.infn.it/ + group: /data-manager + - sa: fga + actions: + - list + - read + effect: permit + description: Grant read access to tokens issued by iam-dev + principals: + - type: jwt-issuer + params: + iss: https://iam-dev.cloud.cnaf.infn.it/ diff --git a/compose/assets/etc/storm/webdav/sa.d/auth.properties b/compose/assets/etc/storm/webdav/sa.d/auth.properties deleted file mode 100644 index a740ef11..00000000 --- a/compose/assets/etc/storm/webdav/sa.d/auth.properties +++ /dev/null @@ -1,24 +0,0 @@ -# -# Copyright (c) Istituto Nazionale di Fisica Nucleare, 2018. -# -# 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. -# - -name=auth -rootPath=/storage/auth -filesystemType=posixfs -accessPoints=/auth -vos=test.vo -authenticatedReadEnabled=true -anonymousReadEnabled=false -voMapGrantsWritePermission=false diff --git a/robot/assets/fixtures/default/sa.d/wlcg.properties b/compose/assets/etc/storm/webdav/sa.d/fga.properties similarity index 61% rename from robot/assets/fixtures/default/sa.d/wlcg.properties rename to compose/assets/etc/storm/webdav/sa.d/fga.properties index 0d635e49..7da65f4d 100644 --- a/robot/assets/fixtures/default/sa.d/wlcg.properties +++ b/compose/assets/etc/storm/webdav/sa.d/fga.properties @@ -1,12 +1,11 @@ -name=wlcg -rootPath=/storage/wlcg +name=fga +rootPath=/storage/fga filesystemType=posixfs -accessPoints=/wlcg -orgs=https://wlcg.cloud.cnaf.infn.it/ +accessPoints=/fga authenticatedReadEnabled=false anonymousReadEnabled=false -voMapGrantsWritePermission=false -wlcgScopeAuthzEnabled=true -fineGrainedAuthzEnabled=true +orgs=https://iam-dev.cloud.cnaf.infn.it/,https://egi-checkin.example/ orgsGrantReadPermission=false orgsGrantWritePermission=false +fineGrainedAuthzEnabled=true +wlcgScopeAuthzEnabled=true diff --git a/compose/assets/etc/storm/webdav/sa.d/oauth_authz.properties b/compose/assets/etc/storm/webdav/sa.d/oauth-authz.properties similarity index 91% rename from compose/assets/etc/storm/webdav/sa.d/oauth_authz.properties rename to compose/assets/etc/storm/webdav/sa.d/oauth-authz.properties index 4985fffa..a09cd12f 100644 --- a/compose/assets/etc/storm/webdav/sa.d/oauth_authz.properties +++ b/compose/assets/etc/storm/webdav/sa.d/oauth-authz.properties @@ -18,7 +18,7 @@ name=oauth-authz rootPath=/storage/oauth-authz filesystemType=posixfs accessPoints=/oauth-authz -orgs=https://iam-test.indigo-datacloud.eu/ +orgs=https://iam-dev.cloud.cnaf.infn.it/,https://egi-checkin.example/ authenticatedReadEnabled=false anonymousReadEnabled=false voMapGrantsWritePermission=false diff --git a/compose/assets/etc/storm/webdav/sa.d/sa.properties.template b/compose/assets/etc/storm/webdav/sa.d/sa.properties.template deleted file mode 100644 index 64cc965c..00000000 --- a/compose/assets/etc/storm/webdav/sa.d/sa.properties.template +++ /dev/null @@ -1,29 +0,0 @@ -# This is an example of StoRM WebDAV storage area configuration - -# Name of the storage area -name=sa - -# Root path for the storage area. Files will be served from this path, which must exist and -# must be accessible from the user that runs the storm webdav service -rootPath=/tmp - -# Comma separated list of storage area access points. -accessPoints=/sa - -# Comma separated list of VOMS VOs supported in this storage area -vos=testers.eu-emi.eu - -# Enables read access to users authenticated with an X.509 certificate issued by -# a trusted CA (users without VOMS credentials). -# Defaults to false, which means that all users need to authenticate with a VOMS credential -# authenticatedReadEnabled=false - -# Enables read access to anonymous users. Defaults to false. -# anonymousReadEnabled=false - -# Enables VO map files for this storage area. Defaults to true. -# voMapEnabled=true - -# VO map normally grants read-only access to storage area files. To grant -# write access set this flag to true. Defaults to false. -# voMapGrantsWriteAccess=false diff --git a/compose/assets/etc/storm/webdav/sa.d/test_vo.properties b/compose/assets/etc/storm/webdav/sa.d/test.vo.properties similarity index 100% rename from compose/assets/etc/storm/webdav/sa.d/test_vo.properties rename to compose/assets/etc/storm/webdav/sa.d/test.vo.properties diff --git a/compose/assets/etc/storm/webdav/vo-mapfiles.d/README.md b/compose/assets/etc/storm/webdav/vo-mapfiles.d/README.md deleted file mode 100644 index 922f3c14..00000000 --- a/compose/assets/etc/storm/webdav/vo-mapfiles.d/README.md +++ /dev/null @@ -1,39 +0,0 @@ -## VO map files configuration -VO map files contains the list of the members of a VOMS-managed Virtual Organization (VO). - -## What are VO map files - -When VO map files are enabled, users can authenticate to the StoRM webdav -service using the certificate in their browser and be granted VOMS attributes -if their subject is listed in one of the supported VO mapfile. - -This mechanism is very similar to the traditional Gridmap file but is just used -to know whether a given user is registered as a member in a VOMS managed VO and -not to map his/her certificate subject to a local unix account. - -### How to enable VO map files - -VO map files support is disabled by default in StoRM WebDAV. - -Set `STORM_WEBDAV_VO_MAP_FILES_ENABLE=true`` in /etc/sysconfig/storm-webdav -to enable VO map file support. - -### How to generate VO map files - -VO map files are generated using the voms-admin list-users command. - -For each supported VO, a file named: - -.vomap - -is put in the voms-mapfiles.d directory. - -*Example*: to generate a VO mapfile for the `cms` VO, run the following -command - -```bash -voms-admin --vo cms list-users > /etc/storm/webdav/vo-mapfiles.d/cms.vomap -``` - -*N.B.:* Ensure that vo map files are readable by the user that runs the StORM -WebDAV service (by default, the `storm` user). diff --git a/compose/assets/nginx/nginx.conf b/compose/assets/nginx/nginx.conf index 5b77bc4f..e92b74b3 100644 --- a/compose/assets/nginx/nginx.conf +++ b/compose/assets/nginx/nginx.conf @@ -1,10 +1,4 @@ -user build; -worker_processes 1; - -env OPENSSL_ALLOW_PROXY_CERTS=1; -env X509_VOMS_DIR=/vomsdir; - -error_log /home/build/local/openresty/nginx/logs/error.log warn; +load_module modules/ngx_http_voms_module.so; events { worker_connections 1024; @@ -12,9 +6,11 @@ events { http { - include /home/build/local/openresty/nginx/conf/mime.types; + include mime.types; default_type application/octet-stream; + resolver 127.0.0.11 ipv6=off; + log_format storm '$time_iso8601 [$request_id] $remote_addr - $remote_user "$request" <$upstream_response_time> ' '$ssl_protocol/$ssl_cipher ' '"$ssl_client_s_dn" ' @@ -22,15 +18,14 @@ http { '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; - access_log /home/build/local/openresty/nginx/logs/access.log storm; + access_log /var/log/nginx/access.log storm; sendfile on; #tcp_nopush on; keepalive_timeout 65; - #gzip on; - client_max_body_size 10000m; + include /etc/nginx/conf.d/*.conf; } diff --git a/compose/assets/nginx/srm.conf b/compose/assets/nginx/srm.conf index 6d58e8b0..69ed1160 100644 --- a/compose/assets/nginx/srm.conf +++ b/compose/assets/nginx/srm.conf @@ -1,27 +1,26 @@ server { - root /tmp/storage; - error_log /home/build/local/openresty/nginx/logs/error.log info; - access_log /home/build/local/openresty/nginx/logs/access.log storm; + root /storage; listen 443 ssl; - server_name storm-alias.example; + server_name storm-alias.test.example; - ssl on; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_certificate /certs/hostcert.pem; ssl_certificate_key /certs/hostkey.pem; - ssl_client_certificate /etc/pki/tls/certs/ca-bundle.crt; + ssl_client_certificate /etc/pki/ca-trust/extracted/pem/tls-ca-bundle-all.pem; ssl_verify_client optional; ssl_verify_depth 100; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; + client_max_body_size 10000m; + location / { autoindex on; - dav_methods PUT DELETE MKCOL; + dav_methods PUT DELETE MKCOL COPY; create_full_put_path on; dav_access group:rw all:r; } diff --git a/compose/assets/oidc-agent/dev-wlcg b/compose/assets/oidc-agent/dev-wlcg new file mode 100644 index 00000000..a833aaea --- /dev/null +++ b/compose/assets/oidc-agent/dev-wlcg @@ -0,0 +1,7 @@ +1010 +1wOf9Pq+mNOxYk3ym2359qDJSF1ovSOG +wIrQxktFUUgtmo0blww69A== +24:16:16:32:1:2:67108864:2 +AkHRbL2GnV8kvBCvd+MMw5YL4nD6XmfUXT9t9GOH/y8jTmULDI7mPtLvm1/jLHA2xCuoQrz0BxPeez/glOuFzAovFeoJYP9GYRfDRNuOUdavH1aTNBCqQ5Mj+uIW7RgkKrS+/hxPcng6QCaYyJVF9RmlpHE6YZd36sFA6T2NO0rSX9xKmD2An/zcdK+6AM6iLc91PKzWEGYiLnBJ5pUem+kiS7ZhHN5wC2s/fEQ6RqCNcs0iQNUixGNU+q+WjHNHXpS2UPs6PEsH/XrFC0sfpaz8rAoDDvEBf3as1rPMI9wyyNVEDBgq33t+5mCH0F+bQfCRNxNXkCik0VQrsb3dK7/wiQ6wIrnqVLO9jQO6esWAwCOqbD+ek4v1wb8NEcP79RO5IVP3srsXTyoteHNepr/fXVYDc4HfOC93wBjppZxISP15t+kVPHoeLHgTidFcOsJ1RrpWaMfrFMUUEbGvf4IcvJDBpPiorUXSI7cpVYM/c0FvWJDyUo7JxBF4H7nX8KLGlbaB5b3Hpv85CqWMaohmSgfRlX2t3xby2OuPaMaQ/rrI34nikn+hUObH3tqOJNIdoJ9AE3Z7P327QkzGZOTRgC3TO7cOLBaJm7p06jsUqMGpu7x10Of9sXsQKA6+pG+QqHx6O/lw2OE0e9hOUbt/PF7mkb/tVn6+uzf4TbLSzLHmML4hupSK4ze1FW3Tp4+rCQSrtGK7WOz/l+Nw3qIa7lUZ+Muglb5s38wMCFUMTaSbToKfcj0zmD4X8c2cUB4e9p8LbsgFcOfBrBHSwvmyVa+emNNxxyXqYLiAr+Obdjyu29LbSz0FyDxeb1natBzFPYdxA0Mag7mdPxgmMzBIVHuFl5A7QrhW/kZxdyVWCcePG+aGGQPv1gJ3yopBcp6PZHzSLS6EOmevDIh3djzIMIn9GKl4nYX8mi7Pmtx4D//Ir3QWYI0HeP/n7cHAD8lCPRds0v0L7KzYjY9/68bmWjjhvsJZgA+IrmHZHiawVvfRt1Jr+r4SgmAcwkrba6/H4YKjPMprDhxrNtYaekxjyGMnkkAs7hNziwtOcp5JDh+ZZ9ZYhTtteYfY8k1XwIAKEqdfS+JRzfb63rc81O8jVTMIbC132+UiFn/J48cjiqoPYAKUUo9PtSWIcfHjhZlqidYv9tRzqRo0GB4Guj8/BZu5dBu3ck04U8u1rrDahfuVUZiiSNwy4PAqv+nYzwDiRQlIuq5i6aGEvz9tMgG9NCMc2w0g7w7UYh8Pd46TsowhraiKHcWjgCNjPX3iooD6zLggzYY4NKZsBpyhssKD0CKmAOJciormsuSkzpmw6MFpwtQ+hfFWzooRfYdP1O8= +/cas1Kw7WNA/XxuxTBfggLhgDYlQGYE/SepMVvBh1y0= +Generated using version: 4.5.1 \ No newline at end of file diff --git a/compose/assets/scripts/ci-run-testsuite.sh b/compose/assets/scripts/ci-run-testsuite.sh new file mode 100755 index 00000000..e79799ff --- /dev/null +++ b/compose/assets/scripts/ci-run-testsuite.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +set -ex + +OIDC_AGENT_ALIAS=${OIDC_AGENT_ALIAS:-dev-wlcg} + +eval $(oidc-agent --no-autoload) +oidc-add --pw-cmd='echo ${OIDC_AGENT_SECRET}' ${OIDC_AGENT_ALIAS} +export IAM_ACCESS_TOKEN=$(oidc-token -s openid ${OIDC_AGENT_ALIAS}) + +/scripts/init-usercerts.sh +echo "pass123" | voms-proxy-init --cert /tmp/usercerts/test0.p12 -voms test.vo --pwstdin + +cp -r /code/robot . + +pushd robot + +sh run-testsuite.sh diff --git a/compose/assets/scripts/init-certs.sh b/compose/assets/scripts/init-certs.sh deleted file mode 100755 index 63f5451c..00000000 --- a/compose/assets/scripts/init-certs.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -set -ex - -CERT_DIR=${CERT_DIR:-/certs} - -cp ${CERT_DIR}/* /etc/grid-security/storm-webdav/ -chown -R storm:storm /etc/grid-security/storm-webdav diff --git a/compose/assets/scripts/init-sa-config.sh b/compose/assets/scripts/init-sa-config.sh deleted file mode 100755 index 876bac16..00000000 --- a/compose/assets/scripts/init-sa-config.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash -set -ex - -SA_CONFIG_DIR=${SA_CONFIG_DIR:-/sa.d} -VOMAP_CONFIG_DIR=${VOMAP_CONFIG_DIR:-/vo-mapfiles.d} - -cp ${SA_CONFIG_DIR}/* /etc/storm/webdav/sa.d -chown -R storm:storm /etc/storm/webdav/sa.d - -cp ${VOMAP_CONFIG_DIR}/* /etc/storm/webdav/vo-mapfiles.d -chown -R storm:storm /etc/storm/webdav/vo-mapfiles.d diff --git a/compose/assets/scripts/init-storage.sh b/compose/assets/scripts/init-storage.sh index 03431990..a0c63ee7 100755 --- a/compose/assets/scripts/init-storage.sh +++ b/compose/assets/scripts/init-storage.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -ex STORAGE_DIR=${STORAGE_DIR:-/storage} diff --git a/compose/assets/scripts/init-usercerts.sh b/compose/assets/scripts/init-usercerts.sh index 00fc19bb..d56dce68 100755 --- a/compose/assets/scripts/init-usercerts.sh +++ b/compose/assets/scripts/init-usercerts.sh @@ -1,8 +1,7 @@ -#!/bin/bash +#!/usr/bin/env bash set -ex USER_CERTS_DIR=${USER_CERTS_DIR:-/usercerts} mkdir -p /tmp/usercerts cp ${USER_CERTS_DIR}/* /tmp/usercerts -chown -R storm:storm /tmp/usercerts chmod 600 /tmp/usercerts/* diff --git a/compose/assets/scripts/run-service.sh b/compose/assets/scripts/run-service.sh deleted file mode 100755 index 944587b6..00000000 --- a/compose/assets/scripts/run-service.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash -set -ex - -JARDIR=/usr/share/java/storm-webdav -JVM_OPTS=${STORM_WEBDAV_JVM_OPTS:-} - -if [ -n "$ENABLE_JREBEL" ]; then - JVM_OPTS="-javaagent:/opt/jrebel/jrebel.jar -Drebel.stats=false -Drebel.usage_reporting=false -Drebel.struts2_plugin=true -Drebel.tiles2_plugin=true -Drebel.license=/home/storm/.jrebel/jrebel.lic $JVM_OPTS" - - mkdir -p /home/storm - cp -r /mnt/.jrebel /home/storm - chown -R storm.storm /home/storm - chmod 755 /home/storm/.jrebel - chmod 644 /home/storm/.jrebel/* -fi - -if [ -z "$DEBUG_PORT" ]; then - DEBUG_PORT=1044 -fi - -if [ -z "$DEBUG_SUSPEND" ]; then - DEBUG_SUSPEND="n" -fi - -if [ ! -z "$DEBUG" ]; then - JVM_OPTS="-Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=$DEBUG_PORT,suspend=$DEBUG_SUSPEND $JVM_OPTS" -fi - -if [ -n "$ENABLE_JMX" ]; then - JVM_OPTS="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=6002 -Dcom.sun.management.jmxremote.rmi.port=6002 -Djava.rmi.server.hostname=dev.local.io -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false $JVM_OPTS" -fi - -if [ -z "${DONT_START_SERVICE}" ]; then - su storm -s /bin/bash -c "java $JVM_OPTS -jar $JARDIR/storm-webdav-server.jar" -fi diff --git a/compose/assets/scripts/run.sh b/compose/assets/scripts/run.sh deleted file mode 100755 index a8934982..00000000 --- a/compose/assets/scripts/run.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -set -ex - -/scripts/unpack-tarball.sh -/scripts/run-service.sh diff --git a/compose/assets/scripts/setup.sh b/compose/assets/scripts/setup.sh deleted file mode 100755 index e1ca14ef..00000000 --- a/compose/assets/scripts/setup.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -set -ex - -/scripts/init-certs.sh -/scripts/init-sa-config.sh -/scripts/init-storage.sh diff --git a/compose/assets/scripts/unpack-tarball.sh b/compose/assets/scripts/unpack-tarball.sh deleted file mode 100755 index cbb73855..00000000 --- a/compose/assets/scripts/unpack-tarball.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -set -ex - -JARDIR=/usr/share/java/storm-webdav - -if [ -z "${DONT_UNPACK_TARBALL}" ]; then - TARFILE="target/storm-webdav-server.tar.gz" - tar -C / --owner=storm --group=storm -xvzf /code/$TARFILE -fi diff --git a/compose/assets/scripts/wait-and-run-testsuite.sh b/compose/assets/scripts/wait-and-run-testsuite.sh deleted file mode 100755 index 8c6c14b2..00000000 --- a/compose/assets/scripts/wait-and-run-testsuite.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -set -ex - -export X509_USER_PROXY=${X509_USER_PROXY:-/tmp/x509up_u$(id -u)} -DAV_HOST=${DAV_HOST:-storm.example} -WAIT_TIMEOUT=${WAIT_TIMEOUT:-30} - -/scripts/init-usercerts.sh -echo "pass" | voms-proxy-init --cert /tmp/usercerts/test0.p12 -voms test.vo --pwstdin - -/scripts/wait-for-it.sh ${DAV_HOST}:8085 --timeout=${WAIT_TIMEOUT} - -rsync -avhu --exclude='.git/' /code/robot . - -pushd robot - - -sh run-testsuite.sh diff --git a/compose/assets/scripts/wait-for-it.sh b/compose/assets/scripts/wait-for-it.sh deleted file mode 100755 index bbe40432..00000000 --- a/compose/assets/scripts/wait-for-it.sh +++ /dev/null @@ -1,177 +0,0 @@ -#!/usr/bin/env bash -# Use this script to test if a given TCP host/port are available - -cmdname=$(basename $0) - -echoerr() { if [[ $QUIET -ne 1 ]]; then echo "$@" 1>&2; fi } - -usage() -{ - cat << USAGE >&2 -Usage: - $cmdname host:port [-s] [-t timeout] [-- command args] - -h HOST | --host=HOST Host or IP under test - -p PORT | --port=PORT TCP port under test - Alternatively, you specify the host and port as host:port - -s | --strict Only execute subcommand if the test succeeds - -q | --quiet Don't output any status messages - -t TIMEOUT | --timeout=TIMEOUT - Timeout in seconds, zero for no timeout - -- COMMAND ARGS Execute command with args after the test finishes -USAGE - exit 1 -} - -wait_for() -{ - if [[ $TIMEOUT -gt 0 ]]; then - echoerr "$cmdname: waiting $TIMEOUT seconds for $HOST:$PORT" - else - echoerr "$cmdname: waiting for $HOST:$PORT without a timeout" - fi - start_ts=$(date +%s) - while : - do - if [[ $ISBUSY -eq 1 ]]; then - nc -z $HOST $PORT - result=$? - else - (echo > /dev/tcp/$HOST/$PORT) >/dev/null 2>&1 - result=$? - fi - if [[ $result -eq 0 ]]; then - end_ts=$(date +%s) - echoerr "$cmdname: $HOST:$PORT is available after $((end_ts - start_ts)) seconds" - break - fi - sleep 1 - done - return $result -} - -wait_for_wrapper() -{ - # In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692 - if [[ $QUIET -eq 1 ]]; then - timeout $BUSYTIMEFLAG $TIMEOUT $0 --quiet --child --host=$HOST --port=$PORT --timeout=$TIMEOUT & - else - timeout $BUSYTIMEFLAG $TIMEOUT $0 --child --host=$HOST --port=$PORT --timeout=$TIMEOUT & - fi - PID=$! - trap "kill -INT -$PID" INT - wait $PID - RESULT=$? - if [[ $RESULT -ne 0 ]]; then - echoerr "$cmdname: timeout occurred after waiting $TIMEOUT seconds for $HOST:$PORT" - fi - return $RESULT -} - -# process arguments -while [[ $# -gt 0 ]] -do - case "$1" in - *:* ) - hostport=(${1//:/ }) - HOST=${hostport[0]} - PORT=${hostport[1]} - shift 1 - ;; - --child) - CHILD=1 - shift 1 - ;; - -q | --quiet) - QUIET=1 - shift 1 - ;; - -s | --strict) - STRICT=1 - shift 1 - ;; - -h) - HOST="$2" - if [[ $HOST == "" ]]; then break; fi - shift 2 - ;; - --host=*) - HOST="${1#*=}" - shift 1 - ;; - -p) - PORT="$2" - if [[ $PORT == "" ]]; then break; fi - shift 2 - ;; - --port=*) - PORT="${1#*=}" - shift 1 - ;; - -t) - TIMEOUT="$2" - if [[ $TIMEOUT == "" ]]; then break; fi - shift 2 - ;; - --timeout=*) - TIMEOUT="${1#*=}" - shift 1 - ;; - --) - shift - CLI=("$@") - break - ;; - --help) - usage - ;; - *) - echoerr "Unknown argument: $1" - usage - ;; - esac -done - -if [[ "$HOST" == "" || "$PORT" == "" ]]; then - echoerr "Error: you need to provide a host and port to test." - usage -fi - -TIMEOUT=${TIMEOUT:-15} -STRICT=${STRICT:-0} -CHILD=${CHILD:-0} -QUIET=${QUIET:-0} - -# check to see if timeout is from busybox? -# check to see if timeout is from busybox? -TIMEOUT_PATH=$(realpath $(which timeout)) -if [[ $TIMEOUT_PATH =~ "busybox" ]]; then - ISBUSY=1 - BUSYTIMEFLAG="-t" -else - ISBUSY=0 - BUSYTIMEFLAG="" -fi - -if [[ $CHILD -gt 0 ]]; then - wait_for - RESULT=$? - exit $RESULT -else - if [[ $TIMEOUT -gt 0 ]]; then - wait_for_wrapper - RESULT=$? - else - wait_for - RESULT=$? - fi -fi - -if [[ $CLI != "" ]]; then - if [[ $RESULT -ne 0 && $STRICT -eq 1 ]]; then - echoerr "$cmdname: strict mode, refusing to execute subprocess" - exit $RESULT - fi - exec "${CLI[@]}" -else - exit $RESULT -fi diff --git a/compose/assets/usercerts/test0.p12 b/compose/assets/usercerts/test0.p12 index 1c97d37226171077c98e89c5d9f23932895fb137..3ce35f01cc03c7a0ab37426fff579a91016b14d7 100644 GIT binary patch delta 2400 zcmV-m37_`m6Xg?-U4L!b{47kiKZODU2mpYB1OO}pm++;TIV#WqNohKu{9)J(RqAcs zIhsnW-I9mZwMTH7oFFN)#2kzb@pHv>0(6acN-ND$1MKMA1OE&R?)mLRk)$yRtG*(g z8&~r;8d%DTV$A8~^9Y2JWl~jw>FkzIJgmFj)jGl1dw=k?ELTgvRqmPK326C( zpjeFrWKB)oiD!R>hYE%_AKU{OEaFk}puW{*2e*gx;HbsOdF4{wL!*5>%NH+J)n42x4 zF&1yA)PE7Gvw!{-*4ul)EDYM^+@Ogc3)|(Q!~-w<)?Z3T?87dH{OSWutQVy<4IHoo zK#6BGLFp>S63e^2&OWl%xBG?g=iL?KU=e}(0}reMbX7HC`w7^m4OdEV>tz;2!Rg1( zdy|2;q`|H~y){I^6|~l2e47*ScB}ekpQ)%F*W*4!&ws`cI0DKnK4)QCC65?js-xSm zK#zb_n9t+om62_vB*^OAaz6EaA`cpfkJQoxnz*!h2iXTA>ZF#Iq*1nT&|ahZw`JW- zWJD&y51-Nwbqk%ee`#(BwkoGx-|wIb`mfxfcO`@9wdj7kHRLrkU<`SXs5lrV#ivXU zDar9!hkxGGuTQwfY+^450$))>Qz$@{(u%&JZKF;;$=l=Rl_&kgI>(6^?Nh3#mNxZ< z%vy*KKV_(p3VUn&zKb~-ruVQH~ZUFZ5-^W&vPgIGnKz&L^jQDuI#qs9hgBuzk! ziQI%|2(*=kvz?jGatS)?dyaI8M8Ffr@8|JqbQ#Bghgfjt0^ zR&r)_D!PY?_fa&el0;v6yrl6#G}d@C8-K4NL*p6F%;T>M$%?stxlu9Dk( z)$DKRr5lLin8mc}I7MvV_iSe#HIzv}$>wLq7dFmsIe+%%%DogwZ-DX6Q$_>yLKs=1 zV*8-jDPMSdZykNMre4U1+BE4?|2dOw1V(>(AJda@b8BS+0tf&Ef&|DSOetg2aKcrf zTuTU=y?z*?qPc5Zf6If-z)EhOQS3Lc`Y#R97Vu(JPQ6x@e);Pwwo-N9I;m5V%s;?% zPF|rSF%zTt`jL5-$yBV|dH1n=h^1hIZJcps;59zt`O~|@$DhgtR}g9(RD_?)9q4~m zjbN_#hW4MST+X`dS%|2nh1!=7-`M=!e4f(+Goa8Bb~*CcP<BEEcjXnmh@JY(Aob_##Wjpr1zZyVlU z6XGYt0X(CO-U*F{2@tmDVTgZ7d$E5GAG;+pn5a#kWV5>4ALdaed->S0DU4$O{@8%_ zS)Lcz)e4AUtU_3a$Q!f91d*xBV4@=jmpjq`f5ehn9>fTUPbNVU@7JLC;ODz_=P8@h zyvYX2BNqg7yRn@5!^hd_fExWUJ-d;g)gL=i;NiEYkAOJx%nIX`AHnyU+z@}ta2R1v zY{%v^V@Iv0=XP2^olKPtV6qHU`8ZRzUvXo01>v5OCGkBjSCLp^@vt1=(WobC=v< zS>B-<_YN4g-6ATL_@$7GqrU6NIdr> z0|>Q`T=kr24FimM@h|3)+X<^;l6EvO7@i-^->1I-W3!_&9#cRn$M?`L)KOS@ql$kIbrKQ*O|C1j+1w4$DysowY_JxB8 zokzH*WXY%uPFx`eo3(#3L?YYdTWMS>PTO*r7OER>s61fbO4mkquq> z6WfUigy%XZ+GF`ZJ8sJ9CyXuC8N8vEjt=tkdWIJkDx!Cw}QWl(>yT&pg8{3*-J*JI62gn!aU48L}E^VgkIrmk=YUz zDFsdT(OdHw#4&$;r`*yE*95~KLl!4t3eDub@}!#kxbt9NE#xfCG8FMeJGONB?im;N zJ(40m9o1{X01D5-YqM=uT2R~>t&aJ$z5N^uKT(Owm9Ob|joD_sJl@JmaHQ1}mJphg z9s9p-xTeJu3~MNYKdNALUYT2cDo}Ca^JatPp+)_(zsrC7qLdmo)A;eFc&e9sb2q!< znn!g&w2p;QD7SJxV^0l^DrSP?7u)9?33@ejyo{psjyACoMnH~kZky+Ni6O%=K6K?k zS#B=V#dM3p&NZdxYb{ylANXK&N7z8@x$0?iF(oh~1_>&LNQUg?E!}d=bp*rFflM8FbM_)D-Ht!8U+9Z6g}%fUWP%u6~C=N6c-;Z%u@|= STLcK!4*iui2lGk-0tf)XqNN4^ delta 2400 zcmV-m37_`m6Xg?-U4Pc93_ynw`)>jQ2mpYB1OR7p1uaz{z9Kp`NmronWuF)Q23{73 zi%dP3u(gZ_t%uP^r*GowDOWmshRK)|YbDP_``kP*8k`+OM8sc%sbv1c^C+y=G}WF} zATwTT<9dqFK216&YnG{%52f)VC;rN{@J*A0z?>R?+QfDkrh*Q2=D)_p=+dmB)fu^X%&x@tNj#rn1vZAzA+2Rl0yx_k!$|Bw1u(cv8 zT)Ei-zAj!jj#MBwJ@KjWE5cDq?g9KToqE(Zl=l6TR)15o?Jr*L1&e6_*vv{{R&_WN zlrN0tpLQPFjqE22Q11Y@3rO?#Dai+=mw_5%W)>LyUWl;edaOr zcnQD(BY*Qrw{1rfVrfCbTdkRG;%Bcz`#83h3P^1+G6l6c%E0~+RoJl~QERQI2fWTpxEI=_5v9xd%gwcp8Gi--2am{?l_Y^YA*;F^gnrS+WQmWp znXwr;F$<%SR^z5NeNorZRO}xZ3@vmFG&Eq5+p(;G#aqvVu!+K_z3(>IaSs4X)}=+~ zaQGs9)hGUxHERzik@v3;{rlA`{dGOieH*HrP_g4u977^@9446?6C|Z4c|;KzLYT3R z`G0|mF%cU?f|JUGa0yQPhxg3i)^>9sp*x4A9ek&Ao+L$#=i$iU4>xBV-8CuD5#tlD zd2OVrO2DnjDFbCK%KCXRzGAYq?`#H`uZ8_$dQw2L@3 zQ_QCPIpiHwJN%LnQ|+;L(r~L>K}% zY$07~T?Vw+PY*QW%*EwZV8#5nlB<(#1V(=tnmhnsQl&`(0tf&Ef&|F3yV`ta%ldv; z!;GY8B&P-?D}HdLXzuDG>soIYo?GkgSxT1rC9rum(@z&?2NdHZetzgYpNycN{(X1# z-BKl7h41xgi}o}UG8v4pnal-hxIi`+Jl)aKUWu6$P*P@(gIx;!HR=)uX5B?U#<>Xt38N=4J&p3Id|_Beb*>UG~$*i{4M3pqs0$ z6-b3@v0w5k&aM@%Vih{-?hunipu8nldh(IRYbAm#b~@y02P2cKG_J|=Z=8F~Uef#y z+Z2c_6;g~}w_9oc0qzM8OV)^q{Vm@dR>s7DQ~~#z1Kzlqy~~>B zVW3|WT$yvFYin~wUz~Dixm$YSVj^L(5M+;-Djkl280a3BQaX7 z>iaZ1pl_A18r|7bbcgk@+%8z7%(O-7igSk9xOvzj0v*G=O5#-J1R7xc?;=2V&^~9E&$=`SwS|v>95yNXs>1jqkN`GB zu}s5_X<$D+Y$^JNlw*L#WQCu@Uge8;Fkk@O0a+g!qL)_E=0y$$XR_r-H%VJef57AAa4T+$MAO>sS)om zyCCu5$dRm3!BuLbU~Xb9OH-tSCorQhK*!=KIyv0?`F^>WY9!z%M_2BD&_Qrw=W(L9 z+mf#k&LCP_CQfjN6?p{Y6h$}8(l37|1ti{54l7b}&4UU#okN}> zyDEDdj~(zhzZhPO#Wg@(mdqenxF(oh~1_>&LNQUFflM8FbM_)D-Ht!8U+9Z6unjn*yx^bNUn +export OIDC_AGENT_SECRET= +``` + +Set the minimum arguments required by the testsuite to run against the two StoRM WebDAV +servers deployed into the compose: + +``` +export ROBOT_ARGS="--variable dav.host:storm.test.example --variable remote.dav.host:storm-alias.test.example --variable remote.davs.port:443" +``` + +Now you can run the test suite with + +``` +/scripts/ci-run-testsuite.sh +``` + +The default path for the test suite report is `/home/test/robot/reports`; +in case you want to copy it locally, run + +``` +docker cp storm-webdav-ts-1:/home/test/robot/reports . +``` + +### Testsuite parameters + +| Parameter name | Description | Default value | +| -------------- | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------- | +| `dav.host` | Hostname of the WebDAV server considered as running locally | localhost | +| `dav.port` | Schema of the WebDAV server considered as running locally | 8085 | +| `davs.port` | Schema of the WebDAV server considered as running locally with HTTPS | 8443 | +| `remote.dav.host` | Hostname of the WebDAV server considered as running remotely | localhost | +| `remote.dav.port` | Schema of the WebDAV server considered as running remotely | 8085 | +| `remote.davs.port` | Schema of the WebDAV server considered as running remotely with HTTPS | 8443 | +| `token.endpoint` | WebDAV endpoint for the locally issued tokens | https://localhost:8443/oauth/token | +| `cred.oauth.env_var_name` | Environment variable for an OAuth access token | IAM_ACCESS_TOKEN | +| `cred.voms.use_os` | Use `/tmp/x509up_u` as proxy path | True | +| `oidc-agent.alias` | Alias for the oidc-agent client | dev-wlcg | +| `oauth.group.claim` | Claim for the token group | wlcg.groups | +| `oauth.optional.group.claim` | Claim for the optional token group. In IAM, optional groups appears in the token only if explicitly requested | wlcg.groups:/data-manager | + +For other parameters, see the [variables file](./test/variables.robot). + + +### Enable custom token issuers + +In order for authorization tests being executed with custom token issuers, one needs to modify +the StoRM WebDAV configuration as follow: + +* append the custom token issuer among the `orgs` comma separated list, in the [fga.property](../compose/assets/etc/storm/webdav/sa.d/fga.properties) and [oauth-authz.properties](../compose/assets/etc/storm/webdav/sa.d/oauth-authz.properties) files +* include the custom token issuer in the [application-issuers.yml](../compose/assets/etc/storm/webdav/config/application-issuers.yml) file +* write down authorization policies for the `fga` storage area indicating your token issuer in the [application-policies.yml](../compose/assets/etc/storm/webdav/config/application-policies.yml) file. The default behavior is: + * users presenting a VOMS proxy released by a `test.vo` can read/write in the SA + * anyone can read in the `/public` folder and sub-folders + * users presenting a JWT token which embeds the `/cms` group have read/write access in the `/cms` folder and sub-folders + * users presenting a JWT token which embeds the `/data-manager` group have read/write access in the SA. + +In case the group claim in your token is not `wlcg.groups`, please append among the `ROBOT_ARGS` + +``` +--variable oauth.group.claim: --variable oauth.optional.group.claim: +``` + +Remember to set the proper oidc-agent alias appending also + +``` +--variable oidc-agent.alias: +``` \ No newline at end of file diff --git a/robot/assets/README.md b/robot/assets/README.md deleted file mode 100644 index 80998f3d..00000000 --- a/robot/assets/README.md +++ /dev/null @@ -1 +0,0 @@ -This folder contains assets useful for the execution of the storm-webdav suite diff --git a/robot/assets/fixtures/default/config/application-hackathon.yml b/robot/assets/fixtures/default/config/application-hackathon.yml deleted file mode 100644 index 54fa7ab9..00000000 --- a/robot/assets/fixtures/default/config/application-hackathon.yml +++ /dev/null @@ -1,70 +0,0 @@ -spring: - profiles: - active: oidc - -oauth: - enable-oidc: true - - issuers: - - name: iam-test - issuer: https://iam-test.indigo-datacloud.eu/ - - - name: wlcg - issuer: https://wlcg.cloud.cnaf.infn.it/ - - - name: tf-local - issuer: http://localhost:8080 - - - name: tf - issuer: https://tf.cloud.cnaf.infn.it - -storm: - authz: - policies: - - sa: tf - description: Grant read access to the SA to tf members - actions: - - list - - read - effect: permit - principals: - - type: jwt-issuer - params: - iss: https://tf.cloud.cnaf.infn.it - - sa: wlcg - description: Grant read access to the SA to wlcg members - actions: - - list - - read - effect: permit - principals: - - type: vo - params: - vo: wlcg - - type: jwt-issuer - params: - iss: https://wlcg.cloud.cnaf.infn.it/ - - sa: wlcg - description: Grant all access to /wlcg/protected to /wlcg/test members - actions: - - all - paths: - - protected/** - effect: permit - principals: - - type: fqan - params: - fqan: /wlcg/Role=test - - type: jwt-group - params: - iss: https://wlcg.cloud.cnaf.infn.it/ - group: /wlcg/test - - sa: wlcg - description: Grant all access to /wlcg VOMS members - actions: - - all - effect: permit - principals: - - type: vo - params: - vo: wlcg diff --git a/robot/assets/fixtures/default/config/application-oidc.yml b/robot/assets/fixtures/default/config/application-oidc.yml deleted file mode 100644 index 5351068b..00000000 --- a/robot/assets/fixtures/default/config/application-oidc.yml +++ /dev/null @@ -1,24 +0,0 @@ -spring: - security: - oauth2: - client: - registration: - iam-test: - provider: indigo - authorization-grant-type: authorization_code - client-name: INDIGO IAM test instance - client-id: ${IAM_TEST_CLIENT_ID} - client-secret: ${IAM_TEST_CLIENT_SECRET} - scope: - - openid - - profile - wlcg: - provider: wlcg - authorization-grant-type: authorization_code - client-name: WLCG IAM - client-id: ${WLCG_CLIENT_ID} - client-secret: ${WLCG_CLIENT_SECRET} - scope: - - openid - - profile - - wlcg.groups diff --git a/robot/assets/fixtures/default/config/application.yml b/robot/assets/fixtures/default/config/application.yml deleted file mode 100644 index 6f190f59..00000000 --- a/robot/assets/fixtures/default/config/application.yml +++ /dev/null @@ -1,62 +0,0 @@ -spring: - profiles: - active: oidc - -oauth: - enable-oidc: true - - issuers: - - name: iam-test - issuer: https://iam-test.indigo-datacloud.eu/ - - - name: wlcg - issuer: https://wlcg.cloud.cnaf.infn.it/ - - - name: tf-local - issuer: http://localhost:8080 - - - name: tf - issuer: https://tf.cloud.cnaf.infn.it - -storm: - authz: - policies: - - sa: fga - actions: - - list - - read - effect: permit - description: Grant read access to anyone to the public area - paths: - - /public/** - principals: - - type: anyone - - sa: fga - actions: - - all - effect: permit - description: Grant read/write access to test.vo and WLCG VOMS vo users - principals: - - type: vo - params: - vo: test.vo - - type: vo - params: - vo: wlcg - - type: jwt-group - params: - iss: https://wlcg.cloud.cnaf.infn.it/ - group: /wlcg - - sa: wlcg - actions: - - all - effect: permit - description: Grant read/write access to WLCG VOMS vo users - principals: - - type: vo - params: - vo: wlcg - - type: jwt-group - params: - iss: https://wlcg.cloud.cnaf.infn.it/ - group: /wlcg/xfers diff --git a/robot/assets/fixtures/default/sa.d/README.md b/robot/assets/fixtures/default/sa.d/README.md deleted file mode 100644 index d158bf4f..00000000 --- a/robot/assets/fixtures/default/sa.d/README.md +++ /dev/null @@ -1,2 +0,0 @@ -This folder contains the storage area configuration required for a default execution -of the storm-webdav testsuite diff --git a/robot/assets/fixtures/default/sa.d/auth.properties b/robot/assets/fixtures/default/sa.d/auth.properties deleted file mode 100644 index a0fe26f7..00000000 --- a/robot/assets/fixtures/default/sa.d/auth.properties +++ /dev/null @@ -1,7 +0,0 @@ -name=auth -rootPath=/storage/auth -filesystemType=posixfs -accessPoints=/auth -authenticatedReadEnabled=true -anonymousReadEnabled=false -voMapGrantsWritePermission=false diff --git a/robot/assets/fixtures/default/sa.d/fga.properties b/robot/assets/fixtures/default/sa.d/fga.properties deleted file mode 100644 index afda19f1..00000000 --- a/robot/assets/fixtures/default/sa.d/fga.properties +++ /dev/null @@ -1,5 +0,0 @@ -name=fga -rootPath=/storage/fga -filesystemType=posixfs -accessPoints=/fga -fineGrainedAuthzEnabled=true diff --git a/robot/assets/fixtures/default/sa.d/noauth.properties b/robot/assets/fixtures/default/sa.d/noauth.properties deleted file mode 100644 index 7af45b9c..00000000 --- a/robot/assets/fixtures/default/sa.d/noauth.properties +++ /dev/null @@ -1,7 +0,0 @@ -name=noauth -rootPath=/storage/noauth -filesystemType=posixfs -accessPoints=/noauth -authenticatedReadEnabled=true -anonymousReadEnabled=true -voMapGrantsWritePermission=false diff --git a/robot/assets/fixtures/default/sa.d/oauth_authz.properties b/robot/assets/fixtures/default/sa.d/oauth_authz.properties deleted file mode 100644 index 81171037..00000000 --- a/robot/assets/fixtures/default/sa.d/oauth_authz.properties +++ /dev/null @@ -1,8 +0,0 @@ -name=oauth-authz -rootPath=/storage/oauth-authz -filesystemType=posixfs -accessPoints=/oauth-authz -orgs=https://iam-test.indigo-datacloud.eu/ -authenticatedReadEnabled=false -anonymousReadEnabled=false -voMapGrantsWritePermission=false diff --git a/robot/assets/fixtures/default/sa.d/test_vo.properties b/robot/assets/fixtures/default/sa.d/test_vo.properties deleted file mode 100644 index ee96ca81..00000000 --- a/robot/assets/fixtures/default/sa.d/test_vo.properties +++ /dev/null @@ -1,9 +0,0 @@ -name=test.vo -rootPath=/storage/test.vo -filesystemType=posixfs -accessPoints=/test.vo,/ciccio -vos=test.vo,local -orgs=https://iam-test.indigo-datacloud.eu/ -authenticatedReadEnabled=false -anonymousReadEnabled=false -voMapGrantsWritePermission=false diff --git a/robot/assets/fixtures/default/sa.d/tf.properties b/robot/assets/fixtures/default/sa.d/tf.properties deleted file mode 100644 index fed2e0b6..00000000 --- a/robot/assets/fixtures/default/sa.d/tf.properties +++ /dev/null @@ -1,12 +0,0 @@ -name=tf -rootPath=/storage/tf -filesystemType=posixfs -accessPoints=/tf -orgs=http://localhost:8080,https://tf.cloud.cnaf.infn.it -authenticatedReadEnabled=false -anonymousReadEnabled=false -voMapGrantsWritePermission=false -wlcgScopeAuthzEnabled=true -fineGrainedAuthzEnabled=false -orgsGrantReadPermission=false -orgsGrantWritePermission=false diff --git a/robot/assets/fixtures/redirector/config/application-oidc.yml b/robot/assets/fixtures/redirector/config/application-oidc.yml deleted file mode 100644 index 5351068b..00000000 --- a/robot/assets/fixtures/redirector/config/application-oidc.yml +++ /dev/null @@ -1,24 +0,0 @@ -spring: - security: - oauth2: - client: - registration: - iam-test: - provider: indigo - authorization-grant-type: authorization_code - client-name: INDIGO IAM test instance - client-id: ${IAM_TEST_CLIENT_ID} - client-secret: ${IAM_TEST_CLIENT_SECRET} - scope: - - openid - - profile - wlcg: - provider: wlcg - authorization-grant-type: authorization_code - client-name: WLCG IAM - client-id: ${WLCG_CLIENT_ID} - client-secret: ${WLCG_CLIENT_SECRET} - scope: - - openid - - profile - - wlcg.groups diff --git a/robot/assets/fixtures/redirector/config/application.yml b/robot/assets/fixtures/redirector/config/application.yml deleted file mode 100644 index 6a75854e..00000000 --- a/robot/assets/fixtures/redirector/config/application.yml +++ /dev/null @@ -1,70 +0,0 @@ -spring: - profiles: - active: oidc - -oauth: - enable-oidc: true - - issuers: - - name: iam-test - issuer: https://iam-test.indigo-datacloud.eu/ - - - name: wlcg - issuer: https://wlcg.cloud.cnaf.infn.it/ - - - name: tf-local - issuer: http://localhost:8080 - - - name: tf - issuer: https://tf.cloud.cnaf.infn.it - -storm: - - redirector: - enabled: true - max-token-lifetime-secs: 600 - pool: - endpoints: - - endpoint: http://storm.example:8085 - - authz: - policies: - - sa: fga - actions: - - list - - read - effect: permit - description: Grant read access to anyone to the public area - paths: - - /public/** - principals: - - type: anyone - - sa: fga - actions: - - all - effect: permit - description: Grant read/write access to test.vo and WLCG VOMS vo users - principals: - - type: vo - params: - vo: test.vo - - type: vo - params: - vo: wlcg - - type: jwt-group - params: - iss: https://wlcg.cloud.cnaf.infn.it/ - group: /wlcg - - sa: wlcg - actions: - - all - effect: permit - description: Grant read/write access to WLCG VOMS vo users - principals: - - type: vo - params: - vo: wlcg - - type: jwt-group - params: - iss: https://wlcg.cloud.cnaf.infn.it/ - group: /wlcg/xfers diff --git a/robot/assets/fixtures/redirector/sa.d/README.md b/robot/assets/fixtures/redirector/sa.d/README.md deleted file mode 100644 index d158bf4f..00000000 --- a/robot/assets/fixtures/redirector/sa.d/README.md +++ /dev/null @@ -1,2 +0,0 @@ -This folder contains the storage area configuration required for a default execution -of the storm-webdav testsuite diff --git a/robot/assets/fixtures/redirector/sa.d/auth.properties b/robot/assets/fixtures/redirector/sa.d/auth.properties deleted file mode 100644 index a0fe26f7..00000000 --- a/robot/assets/fixtures/redirector/sa.d/auth.properties +++ /dev/null @@ -1,7 +0,0 @@ -name=auth -rootPath=/storage/auth -filesystemType=posixfs -accessPoints=/auth -authenticatedReadEnabled=true -anonymousReadEnabled=false -voMapGrantsWritePermission=false diff --git a/robot/assets/fixtures/redirector/sa.d/fga.properties b/robot/assets/fixtures/redirector/sa.d/fga.properties deleted file mode 100644 index afda19f1..00000000 --- a/robot/assets/fixtures/redirector/sa.d/fga.properties +++ /dev/null @@ -1,5 +0,0 @@ -name=fga -rootPath=/storage/fga -filesystemType=posixfs -accessPoints=/fga -fineGrainedAuthzEnabled=true diff --git a/robot/assets/fixtures/redirector/sa.d/noauth.properties b/robot/assets/fixtures/redirector/sa.d/noauth.properties deleted file mode 100644 index 7af45b9c..00000000 --- a/robot/assets/fixtures/redirector/sa.d/noauth.properties +++ /dev/null @@ -1,7 +0,0 @@ -name=noauth -rootPath=/storage/noauth -filesystemType=posixfs -accessPoints=/noauth -authenticatedReadEnabled=true -anonymousReadEnabled=true -voMapGrantsWritePermission=false diff --git a/robot/assets/fixtures/redirector/sa.d/oauth_authz.properties b/robot/assets/fixtures/redirector/sa.d/oauth_authz.properties deleted file mode 100644 index 81171037..00000000 --- a/robot/assets/fixtures/redirector/sa.d/oauth_authz.properties +++ /dev/null @@ -1,8 +0,0 @@ -name=oauth-authz -rootPath=/storage/oauth-authz -filesystemType=posixfs -accessPoints=/oauth-authz -orgs=https://iam-test.indigo-datacloud.eu/ -authenticatedReadEnabled=false -anonymousReadEnabled=false -voMapGrantsWritePermission=false diff --git a/robot/assets/fixtures/redirector/sa.d/test_vo.properties b/robot/assets/fixtures/redirector/sa.d/test_vo.properties deleted file mode 100644 index ee96ca81..00000000 --- a/robot/assets/fixtures/redirector/sa.d/test_vo.properties +++ /dev/null @@ -1,9 +0,0 @@ -name=test.vo -rootPath=/storage/test.vo -filesystemType=posixfs -accessPoints=/test.vo,/ciccio -vos=test.vo,local -orgs=https://iam-test.indigo-datacloud.eu/ -authenticatedReadEnabled=false -anonymousReadEnabled=false -voMapGrantsWritePermission=false diff --git a/robot/assets/fixtures/redirector/sa.d/tf.properties b/robot/assets/fixtures/redirector/sa.d/tf.properties deleted file mode 100644 index fed2e0b6..00000000 --- a/robot/assets/fixtures/redirector/sa.d/tf.properties +++ /dev/null @@ -1,12 +0,0 @@ -name=tf -rootPath=/storage/tf -filesystemType=posixfs -accessPoints=/tf -orgs=http://localhost:8080,https://tf.cloud.cnaf.infn.it -authenticatedReadEnabled=false -anonymousReadEnabled=false -voMapGrantsWritePermission=false -wlcgScopeAuthzEnabled=true -fineGrainedAuthzEnabled=false -orgsGrantReadPermission=false -orgsGrantWritePermission=false diff --git a/robot/assets/fixtures/redirector/sa.d/wlcg.properties b/robot/assets/fixtures/redirector/sa.d/wlcg.properties deleted file mode 100644 index 0d635e49..00000000 --- a/robot/assets/fixtures/redirector/sa.d/wlcg.properties +++ /dev/null @@ -1,12 +0,0 @@ -name=wlcg -rootPath=/storage/wlcg -filesystemType=posixfs -accessPoints=/wlcg -orgs=https://wlcg.cloud.cnaf.infn.it/ -authenticatedReadEnabled=false -anonymousReadEnabled=false -voMapGrantsWritePermission=false -wlcgScopeAuthzEnabled=true -fineGrainedAuthzEnabled=true -orgsGrantReadPermission=false -orgsGrantWritePermission=false diff --git a/robot/common/credentials.robot b/robot/common/credentials.robot index 2e53a682..80ad3f06 100644 --- a/robot/common/credentials.robot +++ b/robot/common/credentials.robot @@ -4,34 +4,14 @@ Library VOMSHelperLibrary *** Variables *** -${cred.voms.use_os} True - ## Where the testsuite should look for an OAuth ## access token ${cred.oauth.env_var_name} IAM_ACCESS_TOKEN -## Embedded VOMS proxies -${cred.voms.1} assets/certs/voms.1 -${cred.voms.2} assets/certs/voms.2 - -${cred.voms.default} ${cred.voms.1} - -## Embedded GRID proxies -${cred.grid.1} assets/certs/grid.1 -${cred.grid.default} ${cred.grid.1} - -## Embedded X.509 certs -${cred.cert.1.p12} assets/certs/test0.p12 -${cred.cert.1.cert} assets/certs/test0.pem -${cred.cert.1.password} pass - -${cred.cert.2.p12} assets/certs/test1.p12 -${cred.cert.2.cert} assets/certs/test1.pem -${cred.cert.2.password} pass +${cred.voms.use_os} True -${cred.cert.default.p12} ${cred.cert.1.p12} -${cred.cert.default.cert} ${cred.cert.1.cert} -${cred.cert.default.password} ${cred.cert.1.password} +## Embedded VOMS proxies +${cred.voms.default} assets/certs/voms.1 *** Keywords *** Default Proxy Path diff --git a/robot/common/curl.robot b/robot/common/curl.robot index e527b422..7a464572 100644 --- a/robot/common/curl.robot +++ b/robot/common/curl.robot @@ -10,7 +10,10 @@ ${x509.trustdir} /etc/grid-security/certificates *** Keywords *** Curl [Arguments] ${url} ${opts}=${curl.opts.default} - ${rc} ${out} Run and Return RC And Output curl ${url} ${opts} + ${cmd} Set Variable curl ${url} ${opts} + Log ${cmd} level=debug + ${rc} ${out} Run and Return RC And Output ${cmd} + Log ${out} level=debug [Return] ${rc} ${out} Curl Success [Arguments] ${url} ${opts}=${curl.opts.default} @@ -32,18 +35,36 @@ Curl Voms HEAD Success [Arguments] ${url} ${opts}=${curl.opts.default} ${rc} ${out} Curl Success ${url} ${all_opts} [Return] ${rc} ${out} +Curl Voms HEAD Failure [Arguments] ${url} ${opts}=${curl.opts.default} + ${voms_opts} Get Curl Voms Proxy Options + ${all_opts} Set variable --HEAD ${opts} ${voms_opts} + ${rc} ${out} Curl Error ${url} ${all_opts} + [Return] ${rc} ${out} + Curl Voms Get Success [Arguments] ${url} ${opts}=${curl.opts.default} ${voms_opts} Get Curl Voms Proxy Options ${all_opts} Set variable -X GET ${opts} ${voms_opts} ${rc} ${out} Curl Success ${url} ${all_opts} [Return] ${rc} ${out} +Curl Voms Get Failure [Arguments] ${url} ${opts}=${curl.opts.default} + ${voms_opts} Get Curl Voms Proxy Options + ${all_opts} Set variable -X GET ${opts} ${voms_opts} + ${rc} ${out} Curl Error ${url} ${all_opts} + [Return] ${rc} ${out} + Curl Voms MKCOL Success [Arguments] ${url} ${opts}=${curl.opts.default} ${voms_opts} Get Curl Voms Proxy Options ${all_opts} Set variable -X MKCOL ${opts} ${voms_opts} ${rc} ${out} Curl Success ${url} ${all_opts} [Return] ${rc} ${out} +Curl Voms MKCOL Failure [Arguments] ${url} ${opts}=${curl.opts.default} + ${voms_opts} Get Curl Voms Proxy Options + ${all_opts} Set variable -X MKCOL ${opts} ${voms_opts} + ${rc} ${out} Curl Error ${url} ${all_opts} + [Return] ${rc} ${out} + Curl Voms Pull COPY Success [Arguments] ${dest} ${source} ${opts}=${curl.opts.default} ${voms_opts} Get Curl Voms Proxy Options ${all_opts} Set variable -X COPY -H "Source: ${source}" ${opts} ${voms_opts} @@ -80,6 +101,12 @@ Curl Voms PUT Success [Arguments] ${file} ${url} ${opts}=${curl.opts.default ${rc} ${out} Curl Success ${url} ${all_opts} [Return] ${rc} ${out} +Curl Voms PUT Failure [Arguments] ${file} ${url} ${opts}=${curl.opts.default} + ${voms_opts} Get Curl Voms Proxy Options + ${all_opts} Set variable -X PUT -T ${file} ${opts} ${voms_opts} + ${rc} ${out} Curl Error ${url} ${all_opts} + [Return] ${rc} ${out} + Curl Voms POST Success [Arguments] ${url} ${opts}=${curl.opts.default} ${voms_opts} Get Curl Voms Proxy Options ${all_opts} Set variable -X POST ${opts} ${voms_opts} @@ -92,18 +119,42 @@ Curl Voms POST Failure [Arguments] ${url} ${opts}=${curl.opts.default} ${rc} ${out} Curl Error ${url} ${all_opts} [Return] ${rc} ${out} +Curl Voms DELETE Success [Arguments] ${url} ${opts}=${curl.opts.default} + ${voms_opts} Get Curl Voms Proxy Options + ${all_opts} Set variable -X DELETE ${opts} ${voms_opts} + ${rc} ${out} Curl Success ${url} ${all_opts} + [Return] ${rc} ${out} + +Curl Voms DELETE Failure [Arguments] ${url} ${opts}=${curl.opts.default} + ${voms_opts} Get Curl Voms Proxy Options + ${all_opts} Set variable -X DELETE ${opts} ${voms_opts} + ${rc} ${out} Curl Error ${url} ${all_opts} + [Return] ${rc} ${out} + Curl Voms MOVE Success [Arguments] ${dest} ${source} ${opts}=${curl.opts.default} ${voms_opts} Get Curl Voms Proxy Options ${all_opts} Set variable -X MOVE -H "Destination: ${dest}" ${opts} ${voms_opts} ${rc} ${out} Curl Success ${source} ${all_opts} [Return] ${rc} ${out} +Curl Voms MOVE Failure [Arguments] ${dest} ${source} ${opts}=${curl.opts.default} + ${voms_opts} Get Curl Voms Proxy Options + ${all_opts} Set variable -X MOVE -H "Destination: ${dest}" ${opts} ${voms_opts} + ${rc} ${out} Curl Error ${source} ${all_opts} + [Return] ${rc} ${out} + Curl Voms MOVE [Arguments] ${dest} ${source} ${opts}=-s -L -i ${voms_opts} Get Curl Voms Proxy Options ${all_opts} Set variable -X MOVE -H "Destination: ${dest}" ${opts} ${voms_opts} ${rc} ${out} Curl ${source} ${all_opts} [Return] ${rc} ${out} +Curl Voms OPTIONS [Arguments] ${url} ${opts}=-s -L -i + ${voms_opts} Get Curl Voms Proxy Options + ${all_opts} Set variable -X OPTIONS ${voms_opts} + ${rc} ${out} Curl ${url} ${all_opts} + [Return] ${rc} ${out} + Curl pull COPY Success [Arguments] ${dest} ${source} ${opts}=${curl.opts.default} ${all_opts} Set variable -X COPY -H "Source: ${source}" ${opts} ${rc} ${out} Curl Success ${dest} ${all_opts} @@ -112,4 +163,10 @@ Curl pull COPY Success [Arguments] ${dest} ${source} ${opts}=${curl.opts.def Curl push COPY Success [Arguments] ${dest} ${source} ${opts}=${curl.opts.default} ${all_opts} Set variable -X COPY -H "Destination: ${dest}" ${opts} ${rc} ${out} Curl Success ${source} ${all_opts} + [Return] ${rc} ${out} + +Curl Voms PROPFIND [Arguments] ${url} ${body} ${opts}=${curl.opts.default} + ${voms_opts} Get Curl Voms Proxy Options + ${all_opts} Set variable -X PROPFIND ${opts} ${voms_opts} --data ${body} + ${rc} ${out} Curl ${url} ${all_opts} [Return] ${rc} ${out} \ No newline at end of file diff --git a/robot/common/oidc-agent.robot b/robot/common/oidc-agent.robot new file mode 100644 index 00000000..ea126e51 --- /dev/null +++ b/robot/common/oidc-agent.robot @@ -0,0 +1,16 @@ +*** Settings *** + +Resource common/credentials.robot + +*** Variables *** + +${oidc-agent.scope.default} -s openid +${oidc-agent.alias} dev-wlcg + + +*** Keywords *** + +Get token [Arguments] ${scope}=${oidc-agent.scope.default} ${issuer}=${oidc-agent.alias} ${opts}=${EMPTY} + ${rc} ${out} Execute and Check Success oidc-token ${scope} ${opts} ${issuer} + Set Environment Variable ${cred.oauth.env_var_name} ${out} + [Return] ${out} \ No newline at end of file diff --git a/robot/common/setup_and_teardown.robot b/robot/common/setup_and_teardown.robot new file mode 100644 index 00000000..e579684a --- /dev/null +++ b/robot/common/setup_and_teardown.robot @@ -0,0 +1,30 @@ +*** Keywords *** + +Default Setup + Default VOMS credential + +Default Teardown + Unset VOMS credential + +Setup file [Arguments] ${file_name} ${content}=Hello World! + Default Setup + Create Test File ${file_name} ${content} + +Setup directory [Arguments] ${dir_name} + Default Setup + Create Test Directory ${dir_name} + +Teardown file [Arguments] ${file_name} + Default Teardown + Remove Test File ${file_name} + Remove Test File ${file_name}.dest + +Teardown file cross sa [Arguments] ${file_name} + Default Teardown + Remove Test File ${file_name} + Remove Test File ${file_name}.dest sa=${sa.oauth} + +Teardown directory [Arguments] ${dir_name} + Default Teardown + Remove Test Directory ${dir_name} + Remove Test Directory ${dir_name}.dest \ No newline at end of file diff --git a/robot/common/storage_areas.robot b/robot/common/storage_areas.robot index 0455379d..1ba23ed6 100644 --- a/robot/common/storage_areas.robot +++ b/robot/common/storage_areas.robot @@ -6,11 +6,9 @@ Resource common/utils.robot *** Variables *** ${sa.default} test.vo -${sa.auth} auth ${sa.noauth} noauth ${sa.fga} fga ${sa.oauth} oauth-authz -${sa.wlcg} wlcg ${storage.root} /storage @@ -29,11 +27,16 @@ Create 1MB Test File [Arguments] ${file} ${sa}=${sa.default} ${path}= Normalize Path ${storage.root}/${sa}/${file} File Should Not Exist ${path} ${rc} ${out} Execute and Check Success dd if=/dev/zero of=${path} bs=1 count=0 seek=1048576 + +Create Test Directory [Arguments] ${directory} ${sa}=${sa.default} + ${path}= Normalize Path ${storage.root}/${sa}/${directory} + Directory Should Not Exist ${path} + Create Directory ${path} Remove Test File [Arguments] ${file} ${sa}=${sa.default} ${path}= Normalize Path ${storage.root}/${sa}/${file} Remove file ${path} -Remove Test Directory [Arguments] ${file} ${sa}=${sa.default} - ${path}= Normalize Path ${storage.root}/${sa}/${file} - Remove Directory ${path} \ No newline at end of file +Remove Test Directory [Arguments] ${directory} ${sa}=${sa.default} + ${path}= Normalize Path ${storage.root}/${sa}/${directory} + Remove Directory ${path} recursive=true \ No newline at end of file diff --git a/robot/common/utils.robot b/robot/common/utils.robot index 6e07c0c9..40d141ef 100644 --- a/robot/common/utils.robot +++ b/robot/common/utils.robot @@ -1,12 +1,16 @@ *** Keywords *** Execute and Check Success [Arguments] ${cmd} + Log ${cmd} level=debug ${rc} ${output} Run and Return RC And Output ${cmd} + Log ${output} level=debug Should Be Equal As Integers ${rc} 0 ${cmd} exited with status ${rc} != 0 : ${output} False [Return] ${rc} ${output} Execute and Check Failure [Arguments] ${cmd} + Log ${cmd} level=debug ${rc} ${output} Run and Return RC And Output ${cmd} + Log ${output} level=debug Should Not Be Equal As Integers ${rc} 0 ${cmd} exited with 0 : ${output} False [Return] ${rc} ${output} diff --git a/robot/reports/.gitignore b/robot/reports/.gitignore deleted file mode 100644 index ced4d9da..00000000 --- a/robot/reports/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -./log.html -./output.xml -./report.html diff --git a/robot/run-testsuite.sh b/robot/run-testsuite.sh index 42fb8c8f..b8472441 100755 --- a/robot/run-testsuite.sh +++ b/robot/run-testsuite.sh @@ -16,15 +16,11 @@ # set -ex -DAV_HOST=${DAV_HOST:-localhost} - -REMOTE_DAV_HOST=${REMOTE_DAV_HOST:-${DAV_HOST:-localhost}} - REPORTS_DIR=${REPORTS_DIR:-reports} ROBOT_ARGS=${ROBOT_ARGS:-} -DEFAULT_ARGS="--pythonpath .:common --variable dav.host:${DAV_HOST} --variable remote.dav.host:${REMOTE_DAV_HOST} -d ${REPORTS_DIR}" +DEFAULT_ARGS="--pythonpath .:common -d ${REPORTS_DIR}" ARGS=${DEFAULT_ARGS} diff --git a/robot/test/authorization.robot b/robot/test/authorization.robot new file mode 100644 index 00000000..4cdd367f --- /dev/null +++ b/robot/test/authorization.robot @@ -0,0 +1,223 @@ +*** Settings *** +Resource common/storage_areas.robot +Resource common/credentials.robot +Resource common/davix.robot +Resource common/curl.robot +Resource common/setup_and_teardown.robot +Resource common/oidc-agent.robot +Resource test/variables.robot + +Test Setup Get token +Test Teardown Get token + +*** Variables *** + +${oauth.group.claim} wlcg.groups +${oauth.group.value} cms + +${oauth.optional.group.claim} ${oauth.group.claim}:/${oauth.optional.group.value} +${oauth.optional.group.value} data-manager + + +*** Keywords *** + +Setup directory fga [Arguments] ${dir_name} ${file_name}=test_file + Create Test Directory ${dir_name} ${sa.fga} + Create Test File ${dir_name}/${file_name} Hello world! ${sa.fga} + +Teardown file fga [Arguments] ${file_name} + Remove Test File ${file_name} ${sa.fga} + +Teardown directory fga [Arguments] ${dir_name} + Remove Test Directory ${dir_name} ${sa.fga} + + +*** Test cases *** + +Read access allowed to anyone to the public area + [Tags] fga get + [Setup] Setup directory fga public + ${url} DAVS URL public/test_file ${sa.fga} + ${rc} ${out} Curl Success ${url} ${curl.opts.default} + Should Contain ${out} Hello world! + [Teardown] Teardown directory fga public + +List access allowed to anyone to the public area + [Tags] fga propfind + [Setup] Setup directory fga public + ${url} DAVS URL public ${sa.fga} + ${rc} ${out} Curl Success ${url} -X PROPFIND ${curl.opts.default} + Should Contain ${out} test_file + [Teardown] Teardown directory fga public + +Anonymous put not allowed to the public area + [Tags] fga put + [Setup] Run Keywords Create Temporary File put_not_allowed 123456789 + ... AND Setup directory fga public + ${url} DAVS URL public/put_not_allowed ${sa.fga} + ${rc} ${out} Curl Error ${url} -X PUT -T ${TEMPDIR}/put_not_allowed ${curl.opts.default} + Should Match Regexp ${out} 401|403 + [Teardown] Run Keywords Remove Temporary File put_not_allowed + ... AND Teardown directory fga public + +Anonymous mkcol not allowed to the public area + [Tags] fga mkcol + [Setup] Setup directory fga public + ${url} DAVS URL public/mkcol_not_allowed ${sa.fga} + ${rc} ${out} Curl Error ${url} -X MKCOL ${curl.opts.default} + Should Match Regexp ${out} 401|403 + [Teardown] Teardown directory fga public + +Anonymous read not allowed outside the public area + [Tags] fga get + [Setup] Setup directory fga anonymous + ${url} DAVS URL anonymous/test_file ${sa.fga} + ${rc} ${out} Curl Error ${url} ${curl.opts.default} + Should Match Regexp ${out} 401|403 + [Teardown] Teardown directory fga anonymous + +Anonymous list not allowed outside the public area + [Tags] fga propfind + [Setup] Setup directory fga anonymous + ${url} DAVS URL anonymous ${sa.fga} + ${rc} ${out} Curl Error ${url} -X PROPFIND ${curl.opts.default} + Should Match Regexp ${out} 401|403 + [Teardown] Teardown directory fga anonymous + +Read access allowed to trusted issued tokens + [Tags] fga get oauth + [Setup] Setup directory fga trusted_issuer + ${token} Get token scope=-s openid + ${curl.opts.oauth} Set Variable -H "Authorization: Bearer %{${cred.oauth.env_var_name}}" + ${url} DAVS URL trusted_issuer/test_file ${sa.fga} + ${rc} ${out} Curl Success ${url} ${curl.opts.oauth} ${curl.opts.default} + Should Contain ${out} Hello world! + [Teardown] Teardown directory fga trusted_issuer + +List access allowed to trusted issued tokens + [Tags] fga propfind oauth + [Setup] Setup directory fga trusted_issuer + ${token} Get token scope=-s openid + ${curl.opts.oauth} Set Variable -H "Authorization: Bearer %{${cred.oauth.env_var_name}}" + ${url} DAVS URL trusted_issuer ${sa.fga} + ${rc} ${out} Curl Success ${url} -X PROPFIND ${curl.opts.oauth} ${curl.opts.default} + Should Contain ${out} test_file + [Teardown] Teardown directory fga trusted_issuer + +Put not allowed to the trusted issued tokens + [Tags] fga put oauth + [Setup] Create Temporary File trusted_issuer 123456789 + ${token} Get token scope=-s openid + ${curl.opts.oauth} Set Variable -H "Authorization: Bearer %{${cred.oauth.env_var_name}}" + ${url} DAVS URL trusted_issuer ${sa.fga} + ${rc} ${out} Curl Error ${url} -X PUT -T ${TEMPDIR}/trusted_issuer ${curl.opts.oauth} ${curl.opts.default} + Should Match Regexp ${out} 401|403 + [Teardown] Remove Temporary File trusted_issuer + +Mkcol not allowed to the trusted issued tokens + [Tags] fga mkcol oauth + ${token} Get token scope=-s openid + ${curl.opts.oauth} Set Variable -H "Authorization: Bearer %{${cred.oauth.env_var_name}}" + ${url} DAVS URL trusted_issuer ${sa.fga} + ${rc} ${out} Curl Error ${url} -X MKCOL ${curl.opts.oauth} ${curl.opts.default} + Should Match Regexp ${out} 401|403 + +Read access allowed to the cms group in the namespace + [Tags] fga get oauth + [Setup] Setup directory fga cms + ${token} Get token scope=-s ${oauth.group.claim} + ${curl.opts.oauth} Set Variable -H "Authorization: Bearer %{${cred.oauth.env_var_name}}" + ${url} DAVS URL cms/test_file ${sa.fga} + ${rc} ${out} Curl Success ${url} ${curl.opts.oauth} ${curl.opts.default} + Should Contain ${out} Hello world! + [Teardown] Teardown directory fga cms + +List access allowed to the cms group in the namespace + [Tags] fga propfind oaut + [Setup] Setup directory fga cms + ${token} Get token scope=-s ${oauth.group.claim} + ${curl.opts.oauth} Set Variable -H "Authorization: Bearer %{${cred.oauth.env_var_name}}" + ${url} DAVS URL cms ${sa.fga} + ${rc} ${out} Curl Success ${url} -X PROPFIND ${curl.opts.oauth} ${curl.opts.default} + Should Contain ${out} test_file + [Teardown] Teardown directory fga cms + +Put allowed to the cms group in the namespace + [Tags] fga put oauth + [Setup] Run Keywords Create Temporary File cms_group 123456789 + ... AND Setup directory fga cms + ${token} Get token scope=-s ${oauth.group.claim} + ${curl.opts.oauth} Set Variable -H "Authorization: Bearer %{${cred.oauth.env_var_name}}" + ${url} DAVS URL cms/cms_group ${sa.fga} + ${rc} ${out} Curl Success ${url} -X PUT -T ${TEMPDIR}/cms_group ${curl.opts.oauth} ${curl.opts.default} + Should Contain ${out} 201 Created + [Teardown] Run Keywords Remove Temporary File cms_group + ... AND Teardown directory fga cms + +Mkcol allowed to the cms group in the namespace + [Tags] fga mkcol oauth + [Setup] Setup directory fga cms + ${token} Get token scope=-s ${oauth.group.claim} + ${curl.opts.oauth} Set Variable -H "Authorization: Bearer %{${cred.oauth.env_var_name}}" + ${url} DAVS URL cms/cms_group ${sa.fga} + Curl Success ${url} -X MKCOL ${curl.opts.oauth} ${curl.opts.default} + [Teardown] Teardown directory fga cms + +Put denied to the cms group outside the namespace + [Tags] fga put oauth + [Setup] Run Keywords Create Temporary File denied 123456789 + ... AND Setup directory fga denied + ${token} Get token scope=-s ${oauth.group.claim} + ${curl.opts.oauth} Set Variable -H "Authorization: Bearer %{${cred.oauth.env_var_name}}" + ${url} DAVS URL denied/denied ${sa.fga} + ${rc} ${out} Curl Error ${url} -X PUT -T ${TEMPDIR}/denied ${curl.opts.oauth} ${curl.opts.default} + Should Match Regexp ${out} 401|403 + [Teardown] Run Keywords Remove Temporary File denied + ... AND Teardown directory fga denied + +Mkcol denied to the cms group outside the namespace + [Tags] fga mkcol oauth + ${token} Get token scope=-s ${oauth.group.claim} + ${curl.opts.oauth} Set Variable -H "Authorization: Bearer %{${cred.oauth.env_var_name}}" + ${url} DAVS URL denied ${sa.fga} + ${rc} ${out} Curl Error ${url} -X MKCOL ${curl.opts.oauth} ${curl.opts.default} + Should Match Regexp ${out} 401|403 + +Read access allowed to data-manager group + [Tags] fga get oauth + [Setup] Setup directory fga data-manager + ${token} Get token scope=-s ${oauth.optional.group.claim} + ${curl.opts.oauth} Set Variable -H "Authorization: Bearer %{${cred.oauth.env_var_name}}" + ${url} DAVS URL data-manager/test_file ${sa.fga} + ${rc} ${out} Curl Success ${url} ${curl.opts.oauth} ${curl.opts.default} + Should Contain ${out} Hello world! + [Teardown] Teardown directory fga data-manager + +List access allowed to data-manager group + [Tags] fga propfind oauth + [Setup] Setup directory fga data-manager + ${token} Get token scope=-s ${oauth.optional.group.claim} + ${curl.opts.oauth} Set Variable -H "Authorization: Bearer %{${cred.oauth.env_var_name}}" + ${url} DAVS URL data-manager ${sa.fga} + ${rc} ${out} Curl Success ${url} -X PROPFIND ${curl.opts.oauth} ${curl.opts.default} + Should Contain ${out} test_file + [Teardown] Teardown directory fga data-manager + +Put allowed to data-manager group + [Tags] fga put oauth + [Setup] Create Temporary File data-manager 123456789 + ${token} Get token scope=-s ${oauth.optional.group.claim} + ${curl.opts.oauth} Set Variable -H "Authorization: Bearer %{${cred.oauth.env_var_name}}" + ${url} DAVS URL data-manager ${sa.fga} + ${rc} ${out} Curl Success ${url} -X PUT -T ${TEMPDIR}/data-manager ${curl.opts.oauth} ${curl.opts.default} + Should Contain ${out} 201 Created + [Teardown] Run Keywords Remove Temporary File data-manager + ... AND Teardown file fga data-manager + +Mkcol allowed to data-manager group + [Tags] fga mkcol oauth + ${token} Get token scope=-s ${oauth.optional.group.claim} + ${curl.opts.oauth} Set Variable -H "Authorization: Bearer %{${cred.oauth.env_var_name}}" + ${url} DAVS URL data-manager ${sa.fga} + ${rc} ${out} Curl Success ${url} -X MKCOL ${curl.opts.oauth} ${curl.opts.default} + [Teardown] Teardown directory fga data-manager \ No newline at end of file diff --git a/robot/test/basic_tests.robot b/robot/test/basic_tests.robot index 5ecfa8af..6af1dc68 100644 --- a/robot/test/basic_tests.robot +++ b/robot/test/basic_tests.robot @@ -3,149 +3,39 @@ Resource common/storage_areas.robot Resource common/credentials.robot Resource common/davix.robot Resource common/curl.robot +Resource common/setup_and_teardown.robot Resource test/variables.robot Test Setup Default Setup Test Teardown Default Teardown -*** Keywords *** -Default Setup - Default VOMS credential - -Default Teardown - Unset VOMS credential - -Get works Setup - Default Setup - Create Test File get_test - -Get works Teardown - Default Teardown - Remove Test File get_test - -Put works Setup - Default Setup - Create Temporary File put_test 123456789 - -Put works Teardown - Default Teardown - Remove Temporary File put_test - -Rm works Setup - Default Setup - Create Test File rm_test - -Rm works Teardown - Default Setup - Remove Test File rm_test - -Mkdir works Teardown - Default Teardown - Remove Test Directory mkdir_test - -Partial Get Works Setup - Default Setup - Create Test File pget_test 1x2y456789 - -Partial Get Works Teardown - Default Setup - Remove Test File pget_test - -Partial Put Works Setup - Default Setup - Create Temporary File pput0_test 0000000000 - Create Temporary File pput1_test 1111111111 - -Partial Put Works Teardown - Default Teardown - Remove Test File pput_test - Remove Temporary File pput0_test - Remove Temporary File pput1_test - -Single Test File Setup [Arguments] ${file_name} - Default Setup - Create Test File ${file_name} - -Single Test File Teardown [Arguments] ${file_name} - Default Teardown - Remove Test File ${file_name} - -Head works on large files setup [Arguments] ${file_name} - Default setup - Create Test File With Size ${file_name} 2g - -Head works on large files teardown [Arguments] ${file_name} - Default Teardown - Remove Test File ${file_name} - *** Test cases *** -Get works - [Tags] voms get - [Setup] Get works Setup - Davix Get Success ${davs.endpoint}/${sa.default}/get_test - [Teardown] Get works Teardown - -Get returns 404 for file that does not exist - [Tags] voms get - ${rc} ${out} Davix Get Failure ${davs.endpoint}/${sa.default}/does_not_exist - Should Contain ${out} 404 - ${rc} ${out} Davix Get Failure ${davs.endpoint}/${sa.default}/does_not_exist/also - Should Contain ${out} 404 - -Put works - [Tags] voms put - [Setup] Put works Setup - Davix Put Success ${TEMPDIR}/put_test ${davs.endpoint}/${sa.default}/put_test - Davix Get Success ${davs.endpoint}/${sa.default}/put_test - Remove File put_test - [Teardown] Put works Teardown - -Rm works - [Tags] voms rm - [Setup] Rm works Setup - Davix Get Success ${davs.endpoint}/${sa.default}/rm_test - Davix Rm Success ${davs.endpoint}/${sa.default}/rm_test - ${rc} ${out} Davix Get Failure ${davs.endpoint}/${sa.default}/rm_test - Should Contain ${out} 404 - [Teardown] Rm works teardown - -Mkdir works - [Tags] voms Mkdir - ## There's a bug in Davix which returns 0 even if the mkdir call fails - ## Davix Mkdir Success ${davs.endpoint}/${sa.default}/mkdir_test - ${rc} ${out} Curl Voms MKCOL Success ${davs.endpoint}/${sa.default}/mkdir_test - [Teardown] Mkdir works teardown - -Partial Get works - [Tags] voms get partial - [Setup] Partial Get Works Setup - ${opts} Set Variable -H "Range: 0-3" ${curl.opts.default} - ${rc} ${out} Curl Voms Get Success ${davs.endpoint}/${sa.default}/pget_test ${opts} - Should Contain ${out} 1x2y - Should Contain ${out} ength: 4 - [Teardown] Partial Get Works Teardown - -Partial Put works - [Tags] voms put partial - [Setup] Partial Put Works Setup - ${opts} Set Variable -H "Content-Range: bytes=0-3/*" ${curl.opts.default} - ${dest} DAVS Url pput_test - ${rc} ${out} Curl Voms Put Success ${TEMPDIR}/pput0_test ${dest} - ${rc} ${out} Curl Voms Put Success ${TEMPDIR}/pput1_test ${dest} ${opts} - [Teardown] Partial Put Works Teardown - Post not allowed on content [Tags] voms post - [Setup] Single Test File Setup test_post_not_allowed + [Setup] Setup file test_post_not_allowed ${url} DAVS Url test_post_not_allowed ${rc} ${out} Curl Voms Post Failure ${url} - Should Contain ${out} 405 - [Teardown] Single Test File Teardown test_post_not_allowed - -Head works on large files - [Tags] voms head - [Setup] Head works on large files setup hwlf - ${rc} ${out} Curl Voms HEAD Success ${davs.endpoint}/${sa.default}/hwlf - Should Contain ${out} ength: 2147483648 - [Teardown] Head works on large files teardown hwlf \ No newline at end of file + Should Contain ${out} 405 Method Not Allowed + [Teardown] Teardown file test_post_not_allowed + +Rename file with missing parent + [Tags] voms + [Setup] Setup file rename-me + ${source} DAVS URL rename-me + ${dest} DAVS URL /parent-dir/child-dir/rename-me + ${rc} ${out} Curl Voms GET Success ${source} + Should Contain ${out} Hello World! + ${rc} ${out} Curl Voms HEAD Failure ${dest} + Should Contain ${out} 404 + ${rc} ${out} Curl Voms HEAD Failure ${davs.endpoint}/${sa.default}/parent-dir/child-dir + Should Contain ${out} 404 + ${rc} ${out} Curl Voms HEAD Failure ${davs.endpoint}/${sa.default}/parent-dir + Should Contain ${out} 404 + Curl Voms MKCOL Success ${davs.endpoint}/${sa.default}/parent-dir + Curl Voms MKCOL Success ${davs.endpoint}/${sa.default}/parent-dir/child-dir + ${rc} ${out} Curl Voms MOVE Success ${dest} ${source} + Davix Get Success ${dest} ${davix.opts.voms} + [Teardown] Run Keywords Default Teardown + ... AND Remove Test File rename-me + ... AND Remove Test Directory parent-dir \ No newline at end of file diff --git a/robot/test/checksum.robot b/robot/test/checksum.robot index 1007a32f..0a0cdd01 100644 --- a/robot/test/checksum.robot +++ b/robot/test/checksum.robot @@ -3,38 +3,42 @@ Resource common/storage_areas.robot Resource common/credentials.robot Resource common/davix.robot Resource common/curl.robot +Resource common/setup_and_teardown.robot Resource test/variables.robot Test Setup Default Setup Test Teardown Default Teardown -*** Keywords *** -Default Setup - Default VOMS credential - -Default Teardown - Unset VOMS credential - -Set extended attr [Arguments] ${file} ${attr} ${attr_value} +Default Tags checksum -Set checksum attr [Arguments] ${file} ${checksum} +*** Keywords *** -Get checksum works setup +Setup file for checksum [Arguments] ${file_name} ${content}=Hello World! Default Setup - Create Temporary File checksum_test 123456789 + Create Temporary File ${file_name} ${content} -Get checksum works Teardown +Teardown file for checksum [Arguments] ${file_name} Default Teardown - Remove Test File checksum_test - Remove Temporary File checksum_test + Teardown file ${file_name} + Remove Temporary File ${file_name} + *** Test cases *** Get checksum works - [Setup] Get checksum works setup - [Tags] voms checksum put - ${dst} DAVS Url checksum_test - Davix Put Success ${TEMPDIR}/checksum_test ${dst} - ${rc} ${out} Curl Voms Get Success ${dst} - Should Contain ${out} Digest: adler32=91e01de - [Teardown] Get checksum works Teardown \ No newline at end of file + [Setup] Setup file for checksum checksum_works 123456789 + [Tags] voms get + ${url} DAVS URL checksum_works + Davix Put Success ${TEMPDIR}/checksum_works ${url} + ${rc} ${out} Curl Voms GET Success ${url} + Should Contain ${out} Digest: adler32=091e01de + [Teardown] Teardown file for checksum checksum_works + +Head checksum works + [Setup] Setup file for checksum checksum_works test123456789 + [Tags] voms put + ${url} DAVS URL checksum_works + Davix Put Success ${TEMPDIR}/checksum_works ${url} + ${rc} ${out} Curl Voms HEAD Success ${url} + Should Contain ${out} Digest: adler32=1d3b039e + [Teardown] Teardown file for checksum checksum_works \ No newline at end of file diff --git a/robot/test/copy.robot b/robot/test/copy.robot index e8068752..2456bc34 100644 --- a/robot/test/copy.robot +++ b/robot/test/copy.robot @@ -3,6 +3,7 @@ Resource common/storage_areas.robot Resource common/credentials.robot Resource common/davix.robot Resource common/curl.robot +Resource common/setup_and_teardown.robot Resource test/variables.robot Test Setup Default Setup @@ -10,45 +11,83 @@ Test Teardown Default Teardown Default Tags copy -*** Keywords *** +*** Test cases *** -Default Setup - Default VOMS credential +Copy works + [Tags] voms + [Setup] Setup file copy_works + ${dest} DAVS URL copy_works.dest + ${source} DAVS URL copy_works + ${rc} ${out} Curl Voms Push COPY Success ${dest} ${source} + Davix Get Success ${dest} ${davix.opts.voms} + [Teardown] Teardown file copy_works -Default Teardown - Unset VOMS credential +Copy directory works + [Tags] voms + [Setup] Setup directory copy_works + ${dest} DAVS URL copy_works.dest + ${source} DAVS URL copy_works + ${rc} ${out} Curl Voms Push COPY Success ${dest} ${source} + Davix Get Success ${dest} ${davix.opts.voms} + [Teardown] Teardown directory copy_works -Setup copy file [Arguments] ${file_name} - Default Setup - Create Test File ${file_name} content=Hello World! +Copy not empty directory works + [Tags] voms + [Setup] Run Keywords Setup directory copy_works + ... AND Create Test File copy_works/file_copy_works + ${dest} DAVS URL copy_works.dest + ${source} DAVS URL copy_works + ${rc} ${out} Curl Voms Push COPY Success ${dest} ${source} + Davix Get Success ${dest} ${davix.opts.voms} + [Teardown] Teardown directory copy_works -Teardown copy file [Arguments] ${file_name} - Default Teardown - Remove Test File ${file_name} - Remove Test File ${file_name}.copied +Copy override works + [Tags] voms + [Setup] Setup file copy_works + ${dest} DAVS URL copy_works.dest + ${source} DAVS URL copy_works + Curl Voms Push COPY Success ${dest} ${source} + ${overwriteHeader} Set variable --header "Overwrite: T" + ${rc} ${out} Curl Voms Push COPY Success ${dest} ${source} ${curl.opts.default} ${overwriteHeader} + Davix Get Success ${dest} ${davix.opts.voms} + [Teardown] Teardown file copy_works -Teardown copy file cross sa [Arguments] ${file_name} - Default Teardown - Remove Test File ${file_name} - Remove Test File ${file_name}.copied sa=${sa.oauth} +Copy override fails + [Tags] voms + [Setup] Setup file copy_works + ${dest} DAVS URL copy_works.dest + ${source} DAVS URL copy_works + Curl Voms Push COPY Success ${dest} ${source} + ${overwriteHeader} Set variable --header "Overwrite: F" + ${rc} ${out} Curl Voms Push COPY Failure ${dest} ${source} ${curl.opts.default} ${overwriteHeader} + Should Contain ${out} 412 Precondition Failed + [Teardown] Teardown file copy_works -*** Test cases *** +Copy not existent resource + [Tags] voms + [Setup] Default Setup + ${dest} DAVS URL copy_works.dest + ${source} DAVS URL copy_works + ${rc} ${out} Curl Voms Push COPY Failure ${dest} ${source} + Should Contain ${out} 404 Not Found + [Teardown] Default Teardown -Local copy works +Copy with destination equal to source [Tags] voms - [Setup] Setup copy file copy_works - ${dest} DAVS URL copy_works.copied + [Setup] Setup file copy_works + ${dest} DAVS URL copy_works ${source} DAVS URL copy_works - ${rc} ${out} Curl Voms Push COPY Success ${dest} ${source} - Davix Get Success ${dest} ${davix.opts.voms} - [Teardown] Teardown copy file copy_works + ${overwriteHeader} Set variable --header "Overwrite: T" + ${rc} ${out} Curl Voms Push COPY Failure ${dest} ${source} ${curl.opts.default} ${overwriteHeader} + Should Contain ${out} 403 + [Teardown] Teardown file copy_works -Local copy across storage areas fails +Copy across storage areas fails [Tags] voms - [Setup] Setup copy file copy_x_sa_works - ${dest} DAVS URL copy_x_sa_works.copied sa=${sa.oauth} + [Setup] Setup file copy_x_sa_works + ${dest} DAVS URL copy_x_sa_works.dest sa=${sa.oauth} ${source} DAVS URL copy_x_sa_works ${rc} ${out} Curl Voms Push COPY ${dest} ${source} Should Contain ${out} 400 Should Contain ${out} Local copy across storage areas is not supported - [Teardown] Teardown copy file cross sa copy_x_sa_works + [Teardown] Teardown file cross sa copy_x_sa_works \ No newline at end of file diff --git a/robot/test/delete.robot b/robot/test/delete.robot new file mode 100644 index 00000000..de0111cd --- /dev/null +++ b/robot/test/delete.robot @@ -0,0 +1,51 @@ +*** Settings *** +Resource common/storage_areas.robot +Resource common/credentials.robot +Resource common/davix.robot +Resource common/curl.robot +Resource common/setup_and_teardown.robot +Resource test/variables.robot + +Test Setup Default Setup +Test Teardown Default Teardown + +Default Tags delete + + +*** Test cases *** + +Delete works + [Tags] voms + [Setup] Setup file delete_works + ${url} DAVS URL delete_works + ${rc} ${out} Curl Voms DELETE Success ${url} + ${rc} ${out} Davix Get Failure ${url} ${davix.opts.voms} + Should Contain ${out} 404 + [Teardown] Teardown file delete_works + +Delete directory works + [Tags] voms + [Setup] Setup directory delete_works + ${url} DAVS URL delete_works + ${rc} ${out} Curl Voms DELETE Success ${url} + ${rc} ${out} Davix Get Failure ${url} ${davix.opts.voms} + Should Contain ${out} 404 + [Teardown] Teardown directory delete_works + +Delete not empty directory fails + [Documentation] Since v1.3.1 removing not empty directories is not allowed + [Tags] voms + [Setup] Run Keywords Setup directory delete_works + ... AND Create Test File delete_works/file_delete_works + ${url} DAVS URL delete_works + ${rc} ${out} Curl Voms DELETE Failure ${url} + Should Contain ${out} 412 Precondition Failed + [Teardown] Teardown directory delete_works + +Delete not existent resource + [Tags] voms + [Setup] Default Setup + ${url} DAVS URL delete_works + ${rc} ${out} Curl Voms DELETE Failure ${url} + Should Contain ${out} 404 Not Found + [Teardown] Default Teardown \ No newline at end of file diff --git a/robot/test/get.robot b/robot/test/get.robot new file mode 100644 index 00000000..e489d78c --- /dev/null +++ b/robot/test/get.robot @@ -0,0 +1,56 @@ +*** Settings *** +Resource common/storage_areas.robot +Resource common/credentials.robot +Resource common/davix.robot +Resource common/curl.robot +Resource common/setup_and_teardown.robot +Resource test/variables.robot + +Test Setup Default Setup +Test Teardown Default Teardown + +Default Tags get + + +*** Test cases *** + +Get works + [Tags] voms + [Setup] Setup file get_works + ${url} DAVS URL get_works + ${rc} ${out} Curl Voms GET Success ${url} + Should Contain ${out} Hello World! + Davix Get Success ${url} ${davix.opts.voms} + [Teardown] Teardown file get_works + +Get directory works + [Tags] voms + [Setup] Setup directory get_works + ${url} DAVS URL get_works + ${rc} ${out} Curl Voms GET Success ${url} + Davix Get Success ${url} ${davix.opts.voms} + [Teardown] Teardown directory get_works + +Get not empty directory works + [Tags] voms + [Setup] Run Keywords Setup directory get_works + ... AND Create Test File get_works/file_get_works + ${url} DAVS URL get_works + ${rc} ${out} Curl Voms GET Success ${url} + Davix Get Success ${url} ${davix.opts.voms} + [Teardown] Teardown directory get_works + +Get root directory works + [Tags] voms + [Setup] Default Setup + ${url} Set Variable ${davs.endpoint}/${sa.default} + ${rc} ${out} Curl Voms GET Success ${url} + Davix Get Success ${url} ${davix.opts.voms} + [Teardown] Default Teardown + +Get not existent resource + [Tags] voms + ${rc} ${out} Davix Get Failure ${davs.endpoint}/${sa.default}/does_not_exist + Should Contain ${out} 404 + ${rc} ${out} Davix Get Failure ${davs.endpoint}/${sa.default}/does_not_exist/also + Should Contain ${out} 404 \ No newline at end of file diff --git a/robot/test/head.robot b/robot/test/head.robot new file mode 100644 index 00000000..f1a0d8e5 --- /dev/null +++ b/robot/test/head.robot @@ -0,0 +1,65 @@ +*** Settings *** +Resource common/storage_areas.robot +Resource common/credentials.robot +Resource common/davix.robot +Resource common/curl.robot +Resource common/setup_and_teardown.robot +Resource test/variables.robot + +Test Setup Default Setup +Test Teardown Default Teardown + +Default Tags head + + +*** Test cases *** + +Head works + [Tags] voms + [Setup] Setup file head_works + ${url} DAVS URL head_works + ${rc} ${out} Curl Voms HEAD Success ${url} + Should Contain ${out} Content-Length: 12 + [Teardown] Teardown file head_works + +Head directory works + [Tags] voms + [Setup] Setup directory head_works + ${url} DAVS URL head_works + ${rc} ${out} Curl Voms HEAD Success ${url} + Should Contain ${out} Content-Length: 4096 + [Teardown] Teardown directory head_works + +Head not empty directory works + [Tags] voms + [Setup] Run Keywords Setup directory head_works + ... AND Create Test File head_works/file_head_works some-text + ${url} DAVS URL head_works + ${rc} ${out} Curl Voms HEAD Success ${url} + Should Contain ${out} Content-Length: 4096 + ${rc} ${out} Curl Voms HEAD Success ${url}/file_head_works + Should Contain ${out} Content-Length: 9 + [Teardown] Teardown directory head_works + +Head root directory works + [Tags] voms + [Setup] Default Setup + ${url} Set Variable ${davs.endpoint}/${sa.default} + ${rc} ${out} Curl Voms HEAD Success ${url} + Should Contain ${out} Content-Length: 4096 + [Teardown] Default Teardown + +Head not existent resource + [Tags] voms + ${rc} ${out} Curl Voms HEAD Failure ${davs.endpoint}/${sa.default}/does_not_exist + Should Contain ${out} 404 + ${rc} ${out} Curl Voms HEAD Failure ${davs.endpoint}/${sa.default}/does_not_exist/also + Should Contain ${out} 404 + +Head works on large files + [Tags] voms + [Setup] Run Keywords Default setup + ... AND Create Test File With Size hwlf 2G + ${rc} ${out} Curl Voms HEAD Success ${davs.endpoint}/${sa.default}/hwlf + Should Contain ${out} ength: 2147483648 + [Teardown] Teardown file hwlf \ No newline at end of file diff --git a/robot/test/mkcol.robot b/robot/test/mkcol.robot new file mode 100644 index 00000000..bf744f52 --- /dev/null +++ b/robot/test/mkcol.robot @@ -0,0 +1,38 @@ +*** Settings *** +Resource common/storage_areas.robot +Resource common/credentials.robot +Resource common/davix.robot +Resource common/curl.robot +Resource common/setup_and_teardown.robot +Resource test/variables.robot + +Test Setup Default Setup +Test Teardown Default Teardown + +Default Tags mkcol + + +*** Test cases *** + +Mkcol works + [Tags] voms + ${url} DAVS URL mkcol_works + Curl Voms MKCOL Success ${url} + ${rc} ${out} Curl Voms HEAD Success ${url} + Should Contain ${out} Content-Length: 4096 + [Teardown] Teardown directory mkcol_works + +Mkcol with missing parent + [Tags] voms + ${url} DAVS URL missing-dir/mkcol_works + ${rc} ${out} Curl Voms MKCOL Failure ${url} + Should Contain ${out} 409 Conflict + [Teardown] Teardown directory missing-dir + +Mkcol on existent resource + [Tags] voms + [Setup] Setup directory mkcol_works + ${url} DAVS URL mkcol_works + ${rc} ${out} Curl Voms MKCOL Failure ${url} + Should Contain ${out} 405 Method Not Allowed + [Teardown] Teardown directory mkcol_works \ No newline at end of file diff --git a/robot/test/move.robot b/robot/test/move.robot index 9cf2f989..de7a2b94 100644 --- a/robot/test/move.robot +++ b/robot/test/move.robot @@ -3,6 +3,7 @@ Resource common/storage_areas.robot Resource common/credentials.robot Resource common/davix.robot Resource common/curl.robot +Resource common/setup_and_teardown.robot Resource test/variables.robot Test Setup Default Setup @@ -10,45 +11,84 @@ Test Teardown Default Teardown Default Tags move -*** Keywords *** -Default Setup - Default VOMS credential +*** Test cases *** + +Move works + [Tags] voms + [Setup] Setup file move_works + ${dest} DAVS URL move_works.dest + ${source} DAVS URL move_works + ${rc} ${out} Curl Voms MOVE Success ${dest} ${source} + Davix Get Success ${dest} ${davix.opts.voms} + [Teardown] Teardown file move_works -Default Teardown - Unset VOMS credential +Move directory works + [Tags] voms + [Setup] Setup directory move_works + ${dest} DAVS URL move_works.dest + ${source} DAVS URL move_works + ${rc} ${out} Curl Voms MOVE Success ${dest} ${source} + Davix Get Success ${dest} ${davix.opts.voms} + [Teardown] Teardown directory move_works -Setup move file [Arguments] ${file_name} - Default Setup - Create Test File ${file_name} content=Hello World! +Move not empty directory works + [Tags] voms + [Setup] Run Keywords Setup directory move_works + ... AND Create Test File move_works/file_move_works + ${dest} DAVS URL move_works.dest + ${source} DAVS URL move_works + ${rc} ${out} Curl Voms MOVE Success ${dest} ${source} + Davix Get Success ${dest} ${davix.opts.voms} + [Teardown] Teardown directory move_works -Teardown move file [Arguments] ${file_name} - Default Teardown - Remove Test File ${file_name} - Remove Test File ${file_name}.moved +Move override works + [Tags] voms + [Setup] Setup file move_works + ${dest} DAVS URL move_works.dest + ${source} DAVS URL move_works + Curl Voms Push COPY Success ${dest} ${source} + ${overwriteHeader} Set variable --header "Overwrite: T" + ${rc} ${out} Curl Voms MOVE Success ${dest} ${source} ${curl.opts.default} ${overwriteHeader} + Davix Get Success ${dest} ${davix.opts.voms} + [Teardown] Teardown file move_works -Teardown move file cross sa [Arguments] ${file_name} - Default Teardown - Remove Test File ${file_name} - Remove Test File ${file_name}.moved sa=${sa.oauth} +Move override fails + [Tags] voms + [Setup] Setup file move_works + ${dest} DAVS URL move_works.dest + ${source} DAVS URL move_works + Curl Voms Push COPY Success ${dest} ${source} + ${overwriteHeader} Set variable --header "Overwrite: F" + ${rc} ${out} Curl Voms MOVE Failure ${dest} ${source} ${curl.opts.default} ${overwriteHeader} + Should Contain ${out} 412 Precondition Failed + [Teardown] Teardown file move_works -*** Test cases *** +Move not existent resource + [Tags] voms + [Setup] Default Setup + ${dest} DAVS URL move_works.dest + ${source} DAVS URL move_works + ${rc} ${out} Curl Voms MOVE Failure ${dest} ${source} + Should Contain ${out} 404 Not Found + [Teardown] Default Teardown -Move works - [Tags] voms move - [Setup] Setup move file move_works - ${dest} DAVS URL move_works.moved +Move with destination equal to source + [Tags] voms + [Setup] Setup file move_works + ${dest} DAVS URL move_works ${source} DAVS URL move_works - ${rc} ${out} Curl Voms MOVE Success ${dest} ${source} - Davix Get Success ${dest} ${davix.opts.voms} - [Teardown] Teardown move file move_works + ${overwriteHeader} Set variable --header "Overwrite: T" + ${rc} ${out} Curl Voms MOVE Failure ${dest} ${source} ${curl.opts.default} ${overwriteHeader} + Should Contain ${out} 403 + [Teardown] Teardown file move_works Move across storage areas fails - [Tags] voms move - [Setup] Setup move file move_x_sa_works - ${dest} DAVS URL move_x_sa_works.moved sa=${sa.oauth} + [Tags] voms + [Setup] Setup file move_x_sa_works + ${dest} DAVS URL move_x_sa_works.dest sa=${sa.oauth} ${source} DAVS URL move_works ${rc} ${out} Curl Voms MOVE ${dest} ${source} Should Contain ${out} 400 Should Contain ${out} Move across storage areas is not supported - [Teardown] Teardown move file cross sa move_x_sa_works + [Teardown] Teardown file cross sa move_x_sa_works diff --git a/robot/test/oauth.robot b/robot/test/oauth.robot index ef64b75c..a3dd9553 100644 --- a/robot/test/oauth.robot +++ b/robot/test/oauth.robot @@ -8,9 +8,6 @@ Resource test/variables.robot Test Setup Default Setup Test Teardown Default Teardown -*** Keywords *** -OAuth Get Works Setup - *** Test cases *** @@ -27,5 +24,5 @@ OAuth Put works ${url} DAVS URL oauth_put_test ${sa.oauth} Davix Put Success ${TEMPDIR}/oauth_put_test ${url} ${davix.opts.oauth} Davix Get Success ${url} ${davix.opts.oauth} - Remove File oauth_put_test + Remove Test File oauth_put_test ${sa.oauth} [Teardown] Remove Temporary File oauth_put_test \ No newline at end of file diff --git a/robot/test/options.robot b/robot/test/options.robot new file mode 100644 index 00000000..b984c409 --- /dev/null +++ b/robot/test/options.robot @@ -0,0 +1,39 @@ +*** Settings *** +Resource common/storage_areas.robot +Resource common/credentials.robot +Resource common/davix.robot +Resource common/curl.robot +Resource common/setup_and_teardown.robot +Resource test/variables.robot + +Test Setup Default Setup +Test Teardown Default Teardown + +Default Tags options + + +*** Test cases *** + +Options on storage area root works + [Tags] voms + [Setup] Default Setup + ${url} Set Variable ${davs.endpoint}/${sa.default} + ${rc} ${out} Curl Voms OPTIONS ${url} + Should Be Equal As Integers ${rc} 0 + [Teardown] Default Teardown + +Options on file works + [Tags] voms + [Setup] Setup file option_works + ${url} DAVS URL option_works + ${rc} ${out} Curl Voms OPTIONS ${url} + Should Be Equal As Integers ${rc} 0 + [Teardown] Teardown file option_works + +Options on directory works + [Tags] voms + [Setup] Setup directory option_works + ${url} DAVS URL option_works + ${rc} ${out} Curl Voms OPTIONS ${url} + Should Be Equal As Integers ${rc} 0 + [Teardown] Teardown directory option_works \ No newline at end of file diff --git a/robot/test/partial_transfer.robot b/robot/test/partial_transfer.robot new file mode 100644 index 00000000..0f1642ce --- /dev/null +++ b/robot/test/partial_transfer.robot @@ -0,0 +1,93 @@ +*** Settings *** +Resource common/storage_areas.robot +Resource common/credentials.robot +Resource common/davix.robot +Resource common/curl.robot +Resource common/setup_and_teardown.robot +Resource test/variables.robot + +Test Setup Default Setup +Test Teardown Default Teardown + +Default Tags partial + +*** Keywords *** + +Partial Put Setup + Default Setup + Create Temporary File pput0_test 0000000000 + Create Temporary File pput1_test 1111111111 + +Partial Put Teardown + Default Teardown + Remove Test File pput_test + Remove Temporary File pput0_test + Remove Temporary File pput1_test + +*** Test cases *** + +Partial Get works + [Tags] voms get + [Setup] Setup file partial_works test123456789 + ${url} DAVS URL partial_works + ${rc} ${out} Curl Voms Get Success ${url} ${curl.opts.default} -H "Range: 0-3" + Should Contain ${out} test + Should Not Contain ${out} 123456789 + Should Contain ${out} Content-Length: 4 + ${rc} ${out} Curl Voms Get Success ${url} ${curl.opts.default} -H "Range: 4-7" + Should Contain ${out} 1234 + Should Contain ${out} Content-Length: 4 + ${rc} ${out} Curl Voms Get Success ${url} ${curl.opts.default} -H "Range: 9-12" + Should Contain ${out} 6789 + Should Contain ${out} Content-Length: 4 + [Teardown] Teardown file partial_works + +Partial Get with multiple range + [Tags] voms get + [Setup] Setup file partial_works test123456789 + ${url} DAVS URL partial_works + ${rc} ${out} Curl Voms Get Success ${url} ${curl.opts.default} -H "Range: 1-3,5-7,10-11" + Should Contain ${out} Content-Range: bytes 1-3/13 + Should Contain ${out} est + Should Contain ${out} Content-Range: bytes 5-7/13 + Should Contain ${out} 234 + Should Contain ${out} Content-Range: bytes 10-11/13 + Should Contain ${out} 78 + [Teardown] Teardown file partial_works + +Partial Get not entirely on range + [Tags] voms get + [Setup] Setup file partial_works test123456789 + ${url} DAVS URL partial_works + ${rc} ${out} Curl Voms Get Success ${url} ${curl.opts.default} -H "Range: 11-13" + Should Contain ${out} Content-Range: bytes 11-12/13 + Should Contain ${out} 89 + Should Contain ${out} Content-Length: 2 + [Teardown] Teardown file partial_works + +Partial Get out of range + [Tags] voms get + [Setup] Setup file partial_works test123456789 + ${url} DAVS URL partial_works + ${rc} ${out} Curl Voms Get Failure ${url} ${curl.opts.default} -H "Range: 20-24" + Should Match Regexp ${out} 416 Requested Range Not Satisfiable|416 Range Not Satisfiable + [Teardown] Teardown file partial_works + +Partial Get out in one of multiple range + [Tags] voms get + [Setup] Setup file partial_works test123456789 + ${url} DAVS URL partial_works + ${rc} ${out} Curl Voms Get Success ${url} ${curl.opts.default} -H "Range: 1-3,20-24" + Should Contain ${out} Content-Range: bytes 1-3/13 + Should Contain ${out} est + Should Contain ${out} Content-Length: 3 + [Teardown] Teardown file partial_works + +Partial Put works + [Tags] voms put + [Setup] Partial Put Setup + ${opts} Set Variable -H "Content-Range: bytes=0-3/*" ${curl.opts.default} + ${dest} DAVS Url pput_test + ${rc} ${out} Curl Voms Put Success ${TEMPDIR}/pput0_test ${dest} + ${rc} ${out} Curl Voms Put Success ${TEMPDIR}/pput1_test ${dest} ${opts} + [Teardown] Partial Put Teardown \ No newline at end of file diff --git a/robot/test/propfind.robot b/robot/test/propfind.robot new file mode 100644 index 00000000..113aad9a --- /dev/null +++ b/robot/test/propfind.robot @@ -0,0 +1,74 @@ +*** Settings *** +Resource common/storage_areas.robot +Resource common/credentials.robot +Resource common/davix.robot +Resource common/curl.robot +Resource common/setup_and_teardown.robot +Resource test/variables.robot + +Test Setup Default Setup +Test Teardown Default Teardown + +Default Tags propfind + +*** Keywords *** + +Get PROPFIND ALLPROP body + ${output} Set variable "" + [Return] ${output} + +Get PROPFIND PROPNAME body + ${output} Set variable "" + [Return] ${output} + +Get PROPFIND PROP body [Arguments] ${propname} + ${output} Set variable "" + [Return] ${output} + + +*** Test cases *** + +Propfind allprop works + [Tags] voms + [Setup] Setup file propfind_works + ${url} DAVS URL propfind_works + ${body} Get PROPFIND ALLPROP body + ${rc} ${out} Curl Voms PROPFIND ${url} ${body} + Should Contain ${out} + Should Contain ${out} FALSE + Should Contain ${out} propfind_works + Should Contain ${out} HTTP/1.1 200 OK + Should Contain ${out} 12 + [Teardown] Teardown file propfind_works + +Propfind allprop not empty directory works + [Tags] voms + [Setup] Run Keywords Setup directory propfind_works + ... AND Create Test File propfind_works/file_propfind_works + ${url} DAVS URL propfind_works + ${body} Get PROPFIND ALLPROP body + ${rc} ${out} Curl Voms PROPFIND ${url} ${body} + Should Contain ${out} + Should Contain ${out} FALSEfile_propfind_works + Should Contain ${out} TRUEpropfind_works + Should Contain ${out} HTTP/1.1 200 OK + Should Contain ${out} 0 + [Teardown] Teardown directory propfind_works + +Propfind propname works + [Tags] voms + [Setup] Setup file propfind_works + ${url} DAVS URL propfind_works + ${body} Get PROPFIND PROPNAME body + ${rc} ${out} Curl Voms PROPFIND ${url} ${body} + Should Contain ${out} propfind_works + [Teardown] Teardown file propfind_works + +Propfind status property works + [Tags] voms + [Setup] Setup file propfind_works + ${url} DAVS URL propfind_works + ${body} Get PROPFIND PROP body status + ${rc} ${out} Curl Voms PROPFIND ${url} ${body} + Should Contain ${out} HTTP/1.1 200 OK + [Teardown] Teardown file propfind_works \ No newline at end of file diff --git a/robot/test/put.robot b/robot/test/put.robot new file mode 100644 index 00000000..b04cbb39 --- /dev/null +++ b/robot/test/put.robot @@ -0,0 +1,67 @@ +*** Settings *** +Resource common/storage_areas.robot +Resource common/credentials.robot +Resource common/davix.robot +Resource common/curl.robot +Resource common/setup_and_teardown.robot +Resource test/variables.robot + +Test Setup Default Setup +Test Teardown Default Teardown + +Default Tags put + +*** Keywords *** + +Put Setup [Arguments] ${file_name} + Default Setup + Create Temporary File ${file_name} 123456789 + +Put Teardown [Arguments] ${file_name} + Default Teardown + Remove Temporary File ${file_name} + Remove Test File ${file_name} + +Put directory Teardown [Arguments] ${file_name} ${directory_name}=${file_name} + Default Teardown + Remove Temporary File ${file_name} + Remove Test Directory ${directory_name} + +*** Test cases *** + +Put works + [Tags] voms + [Setup] Put Setup put_works + ${url} DAVS URL put_works + ${rc} ${out} Curl Voms PUT Success ${TEMPDIR}/put_works ${url} + Should Contain ${out} 201 Created + ${rc} ${out} Curl Voms Get Success ${url} + Should Contain ${out} 123456789 + [Teardown] Put Teardown put_works + +Put override works + [Tags] voms + [Setup] Put Setup put_works + ${url} DAVS URL put_works + Curl Voms PUT Success ${TEMPDIR}/put_works ${url} + ${rc} ${out} Curl Voms PUT Success ${TEMPDIR}/put_works ${url} + Should Contain ${out} 204 No Content + [Teardown] Put Teardown put_works + +Put with missing parent works + [Tags] voms + [Setup] Put Setup put_works + ${url} DAVS URL put-directory/put_works + ${rc} ${out} Curl Voms PUT Success ${TEMPDIR}/put_works ${url} + Should Contain ${out} 201 Created + [Teardown] Run Keywords Put Teardown put_works + ... AND Remove Test Directory put-directory + +Put over directory not allowed + [Tags] voms known-issue + [Setup] Run Keywords Setup directory put_works + ... AND Put Setup put_works + ${url} DAVS URL put_works + ${rc} ${out} Curl Voms PUT Failure ${TEMPDIR}/put_works ${url} + Should Contain ${out} 405 Method not allowed + [Teardown] Put directory Teardown put_works \ No newline at end of file diff --git a/robot/test/token_request.robot b/robot/test/token_request.robot index 269416cf..c3d15d31 100644 --- a/robot/test/token_request.robot +++ b/robot/test/token_request.robot @@ -77,7 +77,7 @@ Put works with locally issued token ${opts} Set variable -H "Authorization: Bearer ${token}" --capath /etc/grid-security/certificates Davix Put Success ${TEMPDIR}/token_put_test ${url} ${opts} Davix Get Success ${url} ${opts} - Remove file token_put_test + Remove Test File token_put_test [Teardown] Put works with locally issued token Teardown Get works with locally issued token fga @@ -97,5 +97,5 @@ Put works with locally issued token fga ${opts} Set variable -H "Authorization: Bearer ${token}" --capath /etc/grid-security/certificates Davix Put Success ${TEMPDIR}/token_put_test ${url} ${opts} Davix Get Success ${url} ${opts} - Remove file token_put_test + Remove Test File token_put_test ${sa.fga} [Teardown] Put works with locally issued token fga Teardown diff --git a/robot/test/tpc.robot b/robot/test/tpc.robot index 601f94fe..658b56ce 100644 --- a/robot/test/tpc.robot +++ b/robot/test/tpc.robot @@ -90,7 +90,7 @@ Pull copy works [Teardown] Pull copy works Teardown Pull copy works https - [Tags] voms tpc dbg + [Tags] voms tpc [Setup] Pull copy works https Setup ${dest} DAVS URL tpc_test_https ${src} Remote DAVS URL tpc_test_https sa=${sa.noauth} @@ -120,7 +120,7 @@ Pull copy works oauth and https [Teardown] Pull copy works oauth and https Teardown Push copy works - [Tags] voms oauth tpc push kk + [Tags] voms oauth tpc push [Setup] Push copy works Setup ${dst} Remote DAVS URL tpc_test_push sa=${sa.oauth} ${src} DAVS URL tpc_test_push @@ -131,7 +131,7 @@ Push copy works [Teardown] Push copy works Teardown Oauth pull copy works - [Tags] oauth tpc pull maghe987 + [Tags] oauth tpc pull [Setup] Oauth pull copy works Setup ${src} Remote DAVS URL oauth_pull_copy_works sa=${sa.oauth} ${dst} DAVS URL oauth_pull_copy_works.copy sa=${sa.oauth} From 5e5cef11689a700d0fcc7487cbe744166944f82e Mon Sep 17 00:00:00 2001 From: Federica Agostini Date: Tue, 19 Nov 2024 11:53:09 +0100 Subject: [PATCH 14/22] Use RT with infinite lifetime --- compose/assets/oidc-agent/dev-wlcg | 10 +++++----- compose/assets/oidc-agent/issuer.config | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) create mode 100644 compose/assets/oidc-agent/issuer.config diff --git a/compose/assets/oidc-agent/dev-wlcg b/compose/assets/oidc-agent/dev-wlcg index a833aaea..9d79eca1 100644 --- a/compose/assets/oidc-agent/dev-wlcg +++ b/compose/assets/oidc-agent/dev-wlcg @@ -1,7 +1,7 @@ -1010 -1wOf9Pq+mNOxYk3ym2359qDJSF1ovSOG -wIrQxktFUUgtmo0blww69A== +988 +jKnjHytEdw2+IC/vyCA8vFb4TOsCzU5I +V548LMRkzXbmeRhZrXwdkg== 24:16:16:32:1:2:67108864:2 -AkHRbL2GnV8kvBCvd+MMw5YL4nD6XmfUXT9t9GOH/y8jTmULDI7mPtLvm1/jLHA2xCuoQrz0BxPeez/glOuFzAovFeoJYP9GYRfDRNuOUdavH1aTNBCqQ5Mj+uIW7RgkKrS+/hxPcng6QCaYyJVF9RmlpHE6YZd36sFA6T2NO0rSX9xKmD2An/zcdK+6AM6iLc91PKzWEGYiLnBJ5pUem+kiS7ZhHN5wC2s/fEQ6RqCNcs0iQNUixGNU+q+WjHNHXpS2UPs6PEsH/XrFC0sfpaz8rAoDDvEBf3as1rPMI9wyyNVEDBgq33t+5mCH0F+bQfCRNxNXkCik0VQrsb3dK7/wiQ6wIrnqVLO9jQO6esWAwCOqbD+ek4v1wb8NEcP79RO5IVP3srsXTyoteHNepr/fXVYDc4HfOC93wBjppZxISP15t+kVPHoeLHgTidFcOsJ1RrpWaMfrFMUUEbGvf4IcvJDBpPiorUXSI7cpVYM/c0FvWJDyUo7JxBF4H7nX8KLGlbaB5b3Hpv85CqWMaohmSgfRlX2t3xby2OuPaMaQ/rrI34nikn+hUObH3tqOJNIdoJ9AE3Z7P327QkzGZOTRgC3TO7cOLBaJm7p06jsUqMGpu7x10Of9sXsQKA6+pG+QqHx6O/lw2OE0e9hOUbt/PF7mkb/tVn6+uzf4TbLSzLHmML4hupSK4ze1FW3Tp4+rCQSrtGK7WOz/l+Nw3qIa7lUZ+Muglb5s38wMCFUMTaSbToKfcj0zmD4X8c2cUB4e9p8LbsgFcOfBrBHSwvmyVa+emNNxxyXqYLiAr+Obdjyu29LbSz0FyDxeb1natBzFPYdxA0Mag7mdPxgmMzBIVHuFl5A7QrhW/kZxdyVWCcePG+aGGQPv1gJ3yopBcp6PZHzSLS6EOmevDIh3djzIMIn9GKl4nYX8mi7Pmtx4D//Ir3QWYI0HeP/n7cHAD8lCPRds0v0L7KzYjY9/68bmWjjhvsJZgA+IrmHZHiawVvfRt1Jr+r4SgmAcwkrba6/H4YKjPMprDhxrNtYaekxjyGMnkkAs7hNziwtOcp5JDh+ZZ9ZYhTtteYfY8k1XwIAKEqdfS+JRzfb63rc81O8jVTMIbC132+UiFn/J48cjiqoPYAKUUo9PtSWIcfHjhZlqidYv9tRzqRo0GB4Guj8/BZu5dBu3ck04U8u1rrDahfuVUZiiSNwy4PAqv+nYzwDiRQlIuq5i6aGEvz9tMgG9NCMc2w0g7w7UYh8Pd46TsowhraiKHcWjgCNjPX3iooD6zLggzYY4NKZsBpyhssKD0CKmAOJciormsuSkzpmw6MFpwtQ+hfFWzooRfYdP1O8= -/cas1Kw7WNA/XxuxTBfggLhgDYlQGYE/SepMVvBh1y0= +YAf68/yXEOBM53CwmPXXyR7f2ExOcFQ/gXvW+BKnKJ6S05SzAr3qrYmRBDi33t4IEgHgx83+OyF0W+p+fanl1veg0Wfegr++8BY/c6sToV+xKUMRYZe3dtjJTkcd1x0e7fwxQgsQn2AXb0pMp7AadVaiqW2xPOkR6EiYuHA1gcKc1lekshbMh1IbMS+BH7cmlOqJDlGbPbZpR70HaP3Pd/xx3Rbcyx0RRCLNUSmwdPcud/uLaBLGkjXmz6vq2uf50nyJRABOchWpSnZBf73reb5aMVwXOrnJLSSXvm8caMKRRum7IDEt4bmwa0KAx8xnblYd4AhRtEu9Dy+KVv0C60FxQqV43kOSvjvR4V6QOwcIiCv0fmAavY1BTO3yxgcHsKEx75psbjmsMvjmqTrgoRW+74vv01vgtjHyOsioGTpTl6RhECvy1RCcOt9D9SWCmqiHtSPsrB/6YN/ba2NLOqQotsEwPJnZlgMaOeBQeUb2XYNCV5pCDKige3vPEv69EUhi2ArWNxEryMK7RcGRK4mSNTmb2fAj5SuWsgrRucbzEsjeypIWhaSmdgwbmElAaDY5b0kEOE6jzR1tYIGQ/RfteHJE4n217C9XqN1nnLdIgu+wrxR0cHFgAd8T6qC7hD3TuTRzFZ9p/H2k8DSLZl4qrj+AgVOCVqKSXma0gakpLL0+MdtUxHCaf7G0D4EWmz0c3qDHiIEaJOkeHJ4kJt4sGuqd3zFW9bTAu4sKH9FqXAKi/oKE5jz/joEGKQw66+LWdLFyqSJcUHAG0DWUUKxzOmC1O0GuD3KkfcsPvbjNjNeySfn+e8Y/puSUyyEbvWztHi83MyJ0jF3da9dE9dpfakfezY0RlN8HplIPNPfSdxompoTkzv76j/rKE+FAkVZyQaASYaruaWjmBxDU5HxLbHUrvhhJXN3BG9nQwqE49NmDfghAsVaZmXrft3wgnvT/Qtb9ZjJY8CZ6EJbMmnuZm3V9u4q4o78LLLwyfH83npAfBiGx0KAKLosDV7sGahIXh77hrnOstLU2EoS8EeypCNnp8miKNW0mXYgru9OfFQQAS1osQc/Eqtf/WzGONf2/WsC7N69QWhgxxbMYsEQGpgpCp5pr96FNNciB0U4gqFuVfi2gGMoXahHVCGWYaIiG9i1CTZ6BJ6qZOu1uZLIiSRovS2dK2WVpvJfm1xdAZanMkFKvW4Qzk5siLLGjLbkWlitWlvwEQhemLdeP400MwpFZIWFsVKV48vOVcu5O2iw5xr+q4K+N8Zr8eFXFVVGTAFN2Ir+doZiinnezUDE3zRntEbAzWFxbIQ== +Xlhm5mu9d9cbcXxWzn83HGEx8HjT/535x22xWkP1o70= Generated using version: 4.5.1 \ No newline at end of file diff --git a/compose/assets/oidc-agent/issuer.config b/compose/assets/oidc-agent/issuer.config new file mode 100644 index 00000000..c935cc99 --- /dev/null +++ b/compose/assets/oidc-agent/issuer.config @@ -0,0 +1 @@ +https://iam-dev.cloud.cnaf.infn.it/ dev-wlcg \ No newline at end of file From be653fa94c53e06af9b6b649a020e8d6ebc59255 Mon Sep 17 00:00:00 2001 From: Luca Bassi Date: Tue, 19 Nov 2024 12:37:32 +0100 Subject: [PATCH 15/22] Fix file move: replace destination if exists The implementation was changed in aee7266b058c7cb2ad3ec39bf3e9113f61338b38 from com.google.common.io.Files which implicitly overwrites to java.nio.file.Files which does not. --- .../storm/webdav/fs/DefaultFSStrategy.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/italiangrid/storm/webdav/fs/DefaultFSStrategy.java b/src/main/java/org/italiangrid/storm/webdav/fs/DefaultFSStrategy.java index bb59126e..9ca90799 100644 --- a/src/main/java/org/italiangrid/storm/webdav/fs/DefaultFSStrategy.java +++ b/src/main/java/org/italiangrid/storm/webdav/fs/DefaultFSStrategy.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; +import java.nio.file.StandardCopyOption; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; @@ -70,15 +71,15 @@ public void mv(File source, File dest) { LOG.debug("mv: source={}, dest={}", source.getAbsolutePath(), dest.getAbsolutePath()); - + try { if (source.getCanonicalPath().equals(dest.getCanonicalPath())) { throw new SameFileError("Source and destination files are the same"); } - - // Overwrites the destination, if it exists - Files.move(source.toPath(), dest.toPath()); + + // Overwrites the destination, if it exists + Files.move(source.toPath(), dest.toPath(), StandardCopyOption.REPLACE_EXISTING); } catch (IOException e) { throw new StoRMWebDAVError(e.getMessage(), e); @@ -100,7 +101,7 @@ public void cp(File source, File dest) { dest.getAbsolutePath()); try { - + if (source.getCanonicalPath().equals(dest.getCanonicalPath())) { throw new SameFileError("Source and destination files are the same"); } From fdddb641f93a3bdb9143285ffc690caf6f9292dd Mon Sep 17 00:00:00 2001 From: Federica Agostini Date: Tue, 19 Nov 2024 18:14:00 +0100 Subject: [PATCH 16/22] Fail the workflow in case of tests failure Always collect test reports --- .github/workflows/run-testsuite.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run-testsuite.yml b/.github/workflows/run-testsuite.yml index fd97bcaa..bad5c3ad 100644 --- a/.github/workflows/run-testsuite.yml +++ b/.github/workflows/run-testsuite.yml @@ -27,17 +27,18 @@ jobs: run: docker compose up --build -d storage-setup webdav nginx - name: Run testsuite + if: ${{ always() }} working-directory: compose run: | docker compose up -d ts docker compose exec -T ts bash -c '/scripts/ci-run-testsuite.sh' - continue-on-error: true - name: Create artifacts directory if: ${{ always() }} run: mkdir -p ${ARTIFACTS} - name: Collect test reports + if: ${{ always() }} run: docker cp storm-webdav-ts-1:/home/test/robot/reports ${ARTIFACTS} - name: Collect service log From a81305a5304b0c11653e0e5b72cb81bf763d249f Mon Sep 17 00:00:00 2001 From: Federica Agostini Date: Wed, 30 Oct 2024 12:22:44 +0100 Subject: [PATCH 17/22] Add jwt-client as principal type --- .../config/FineGrainedAuthzPolicyParser.java | 4 + .../FineGrainedAuthzPolicyProperties.java | 1 + .../config/validation/PrincipalValidator.java | 3 + .../oauth/StormJwtAuthoritiesConverter.java | 10 +- .../oauth/authority/JwtClientAuthority.java | 56 +++++++++ .../oidc/OidcGrantedAuthoritiesMapper.java | 14 ++- src/main/resources/application-fga.yml | 51 +++++++++ .../AuthorizationIntegrationTests.java | 108 +++++++++++++++++- .../webdav/test/authz/pdp/AuthzPdpTests.java | 41 +++++++ src/test/resources/application-authz-test.yml | 13 ++- src/test/resources/conf/sa.d/fga.properties | 27 +++++ src/test/resources/storage/fga/file | 1 + 12 files changed, 314 insertions(+), 15 deletions(-) create mode 100644 src/main/java/org/italiangrid/storm/webdav/oauth/authority/JwtClientAuthority.java create mode 100644 src/main/resources/application-fga.yml create mode 100644 src/test/resources/conf/sa.d/fga.properties create mode 100644 src/test/resources/storage/fga/file diff --git a/src/main/java/org/italiangrid/storm/webdav/config/FineGrainedAuthzPolicyParser.java b/src/main/java/org/italiangrid/storm/webdav/config/FineGrainedAuthzPolicyParser.java index 715a61e1..1e545b06 100644 --- a/src/main/java/org/italiangrid/storm/webdav/config/FineGrainedAuthzPolicyParser.java +++ b/src/main/java/org/italiangrid/storm/webdav/config/FineGrainedAuthzPolicyParser.java @@ -40,6 +40,7 @@ import org.italiangrid.storm.webdav.config.FineGrainedAuthzPolicyProperties.Action; import org.italiangrid.storm.webdav.config.FineGrainedAuthzPolicyProperties.PrincipalProperties; import org.italiangrid.storm.webdav.config.FineGrainedAuthzPolicyProperties.PrincipalProperties.PrincipalType; +import org.italiangrid.storm.webdav.oauth.authority.JwtClientAuthority; import org.italiangrid.storm.webdav.oauth.authority.JwtGroupAuthority; import org.italiangrid.storm.webdav.oauth.authority.JwtIssuerAuthority; import org.italiangrid.storm.webdav.oauth.authority.JwtScopeAuthority; @@ -137,6 +138,9 @@ PrincipalMatcher parsePrincipal(PrincipalProperties p) { } else if (PrincipalType.JWT_SUBJECT.equals(p.getType())) { matcher = AuthorityHolder.fromAuthority( new JwtSubjectAuthority(p.getParams().get("iss"), p.getParams().get("sub"))); + } else if (PrincipalType.JWT_CLIENT.equals(p.getType())) { + matcher = AuthorityHolder.fromAuthority( + new JwtClientAuthority(p.getParams().get("iss"), p.getParams().get("id"))); } else if (PrincipalType.VO.equals(p.getType())) { matcher = AuthorityHolder.fromAuthority(new VOMSVOAuthority(p.getParams().get("vo"))); } else if (PrincipalType.VO_MAP.equals(p.getType())) { diff --git a/src/main/java/org/italiangrid/storm/webdav/config/FineGrainedAuthzPolicyProperties.java b/src/main/java/org/italiangrid/storm/webdav/config/FineGrainedAuthzPolicyProperties.java index 29be9fb0..dc3d35c8 100644 --- a/src/main/java/org/italiangrid/storm/webdav/config/FineGrainedAuthzPolicyProperties.java +++ b/src/main/java/org/italiangrid/storm/webdav/config/FineGrainedAuthzPolicyProperties.java @@ -45,6 +45,7 @@ public enum PrincipalType { JWT_SCOPE, JWT_ISSUER, JWT_SUBJECT, + JWT_CLIENT, VO, FQAN, VO_MAP, diff --git a/src/main/java/org/italiangrid/storm/webdav/config/validation/PrincipalValidator.java b/src/main/java/org/italiangrid/storm/webdav/config/validation/PrincipalValidator.java index fc7d4dc0..dab3fe78 100644 --- a/src/main/java/org/italiangrid/storm/webdav/config/validation/PrincipalValidator.java +++ b/src/main/java/org/italiangrid/storm/webdav/config/validation/PrincipalValidator.java @@ -18,6 +18,7 @@ import static com.google.common.base.Strings.isNullOrEmpty; import static java.lang.String.format; import static org.italiangrid.storm.webdav.config.FineGrainedAuthzPolicyProperties.PrincipalProperties.PrincipalType.FQAN; +import static org.italiangrid.storm.webdav.config.FineGrainedAuthzPolicyProperties.PrincipalProperties.PrincipalType.JWT_CLIENT; import static org.italiangrid.storm.webdav.config.FineGrainedAuthzPolicyProperties.PrincipalProperties.PrincipalType.JWT_GROUP; import static org.italiangrid.storm.webdav.config.FineGrainedAuthzPolicyProperties.PrincipalProperties.PrincipalType.JWT_SCOPE; import static org.italiangrid.storm.webdav.config.FineGrainedAuthzPolicyProperties.PrincipalProperties.PrincipalType.JWT_ISSUER; @@ -50,6 +51,8 @@ public class PrincipalValidator implements .put(JWT_SUBJECT, "iss") .put(JWT_SUBJECT, "sub") .put(JWT_ISSUER, "iss") + .put(JWT_CLIENT, "iss") + .put(JWT_CLIENT, "id") .put(VO, "vo") .put(VO_MAP, "vo") .put(X509_SUBJECT, "subject") diff --git a/src/main/java/org/italiangrid/storm/webdav/oauth/StormJwtAuthoritiesConverter.java b/src/main/java/org/italiangrid/storm/webdav/oauth/StormJwtAuthoritiesConverter.java index a9f9160d..6a68bd03 100644 --- a/src/main/java/org/italiangrid/storm/webdav/oauth/StormJwtAuthoritiesConverter.java +++ b/src/main/java/org/italiangrid/storm/webdav/oauth/StormJwtAuthoritiesConverter.java @@ -24,11 +24,11 @@ import org.italiangrid.storm.webdav.authz.SAPermission; import org.italiangrid.storm.webdav.config.ServiceConfigurationProperties; import org.italiangrid.storm.webdav.config.StorageAreaConfiguration; +import org.italiangrid.storm.webdav.oauth.authority.JwtClientAuthority; import org.italiangrid.storm.webdav.oauth.authority.JwtGroupAuthority; import org.italiangrid.storm.webdav.oauth.authority.JwtIssuerAuthority; import org.italiangrid.storm.webdav.oauth.authority.JwtScopeAuthority; import org.italiangrid.storm.webdav.oauth.authority.JwtSubjectAuthority; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.convert.converter.Converter; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.oauth2.jwt.Jwt; @@ -41,7 +41,6 @@ public class StormJwtAuthoritiesConverter extends GrantedAuthoritiesMapperSupport implements Converter> { - @Autowired public StormJwtAuthoritiesConverter(StorageAreaConfiguration conf, ServiceConfigurationProperties props) { super(conf, props); @@ -108,8 +107,11 @@ protected Collection extractAuthorities(Jwt jwt) { authorities.addAll(extractOauthGroupAuthorities(jwt)); authorities.addAll(extractOauthScopeAuthorities(jwt)); - authorities.add(new JwtIssuerAuthority(jwt.getIssuer().toString())); - authorities.add(new JwtSubjectAuthority(jwt.getIssuer().toString(), jwt.getSubject())); + authorities.add(new JwtIssuerAuthority(issuer)); + authorities.add(new JwtSubjectAuthority(issuer, jwt.getSubject())); + if (jwt.getClaim("client_id") != null) { + authorities.add(new JwtClientAuthority(issuer, jwt.getClaim("client_id"))); + } return authorities; } diff --git a/src/main/java/org/italiangrid/storm/webdav/oauth/authority/JwtClientAuthority.java b/src/main/java/org/italiangrid/storm/webdav/oauth/authority/JwtClientAuthority.java new file mode 100644 index 00000000..c36fb310 --- /dev/null +++ b/src/main/java/org/italiangrid/storm/webdav/oauth/authority/JwtClientAuthority.java @@ -0,0 +1,56 @@ +/** + * 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.oauth.authority; + +public class JwtClientAuthority extends JwtAuthority { + + private static final long serialVersionUID = 1L; + + public static final String AUTH_TEMPLATE = "O_client(%s,%s)"; + + private final String clientId; + + public JwtClientAuthority(String issuer, String clientId) { + super(issuer, String.format(AUTH_TEMPLATE, issuer, clientId)); + this.clientId = clientId; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + ((clientId == null) ? 0 : clientId.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + JwtClientAuthority other = (JwtClientAuthority) obj; + if (clientId == null) { + if (other.clientId != null) + return false; + } else if (!clientId.equals(other.clientId)) + return false; + return true; + } + +} diff --git a/src/main/java/org/italiangrid/storm/webdav/oidc/OidcGrantedAuthoritiesMapper.java b/src/main/java/org/italiangrid/storm/webdav/oidc/OidcGrantedAuthoritiesMapper.java index d30036ba..72a0e6fd 100644 --- a/src/main/java/org/italiangrid/storm/webdav/oidc/OidcGrantedAuthoritiesMapper.java +++ b/src/main/java/org/italiangrid/storm/webdav/oidc/OidcGrantedAuthoritiesMapper.java @@ -17,15 +17,16 @@ import java.util.Collection; import java.util.List; +import java.util.Optional; import java.util.Set; import org.italiangrid.storm.webdav.config.ServiceConfigurationProperties; import org.italiangrid.storm.webdav.config.StorageAreaConfiguration; import org.italiangrid.storm.webdav.oauth.GrantedAuthoritiesMapperSupport; +import org.italiangrid.storm.webdav.oauth.authority.JwtClientAuthority; import org.italiangrid.storm.webdav.oauth.authority.JwtGroupAuthority; import org.italiangrid.storm.webdav.oauth.authority.JwtIssuerAuthority; import org.italiangrid.storm.webdav.oauth.authority.JwtSubjectAuthority; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; import org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority; @@ -37,7 +38,6 @@ public class OidcGrantedAuthoritiesMapper extends GrantedAuthoritiesMapperSupport implements GrantedAuthoritiesMapper { - @Autowired public OidcGrantedAuthoritiesMapper(StorageAreaConfiguration conf, ServiceConfigurationProperties props) { super(conf, props); @@ -66,9 +66,13 @@ protected Collection mapAuthorities(OidcUserAuthority userAuth authorities.addAll(authzMap.get(idTokenIssuer)); authorities.addAll(grantGroupAuthorities(userAuthority)); - authorities.add(new JwtIssuerAuthority(userAuthority.getIdToken().getIssuer().toString())); - authorities.add(new JwtSubjectAuthority(userAuthority.getIdToken().getIssuer().toString(), - userAuthority.getIdToken().getSubject())); + authorities.add(new JwtIssuerAuthority(idTokenIssuer)); + authorities.add(new JwtSubjectAuthority(idTokenIssuer, userAuthority.getIdToken().getSubject())); + Optional clientIdClaim = + Optional.ofNullable(userAuthority.getIdToken().getClaim("client_id")); + if (clientIdClaim.isPresent()) { + authorities.add(new JwtClientAuthority(idTokenIssuer, clientIdClaim.get())); + } return authorities; } diff --git a/src/main/resources/application-fga.yml b/src/main/resources/application-fga.yml new file mode 100644 index 00000000..7a207fb3 --- /dev/null +++ b/src/main/resources/application-fga.yml @@ -0,0 +1,51 @@ +server: + jetty: + accesslog: + enabled: false + +management: + endpoints: + web: + exposure: + include: env + +oauth: + enable-oidc: false + issuers: + - name: iam-dev + issuer: https://iam-dev.cloud.cnaf.infn.it/ + +storm: + connector: + port: 8086 + securePort: 9443 + sa: + config-dir: src/test/resources/conf/sa.d + tls: + trust-anchors-dir: src/test/resources/trust-anchors + certificate-path: src/test/resources/hostcert/hostcert.pem + private-key-path: src/test/resources/hostcert/hostkey.pem + authz-server: + enabled: true + voms: + trust-store: + dir: src/test/resources/vomsdir + tape: + well-known: + source: src/test/resources/well-known/wlcg-tape-rest-api.json + + authz: + policies: + - sa: fga + actions: + - all + effect: permit + description: Grant read/write access to a specific client + paths: + - /** + principals: + - type: jwt-client + params: + iss: https://iam-dev.cloud.cnaf.infn.it/ + id: 42999a63-7449-43fb-952e-42f2d75b865b + diff --git a/src/test/java/org/italiangrid/storm/webdav/test/authz/integration/AuthorizationIntegrationTests.java b/src/test/java/org/italiangrid/storm/webdav/test/authz/integration/AuthorizationIntegrationTests.java index 2bf656b5..db4c60c4 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/authz/integration/AuthorizationIntegrationTests.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/authz/integration/AuthorizationIntegrationTests.java @@ -57,6 +57,9 @@ public class AuthorizationIntegrationTests { public static final String UNKNOWN_ISSUER = "https://unknown.example"; public static final String EXAMPLE_ISSUER = "https://issuer.example"; + public static final String AUTHORIZED_JWT_CLIENT_ID = "1234"; + public static final String UNAUTHORIZED_JWT_CLIENT_ID = "5678"; + @Autowired private MockMvc mvc; @@ -127,11 +130,8 @@ void issuerChecksAreEnforcedForWlcgScopeBasedAuthz() throws Exception { @Test void getAccessAsJwtUserWithoutScopeLeadsToAccessDenied() throws Exception { - Jwt token = Jwt.withTokenValue("test") - .header("kid", "rsa1") - .issuer(WLCG_ISSUER) - .subject("123") - .build(); + Jwt token = + Jwt.withTokenValue("test").header("kid", "rsa1").issuer(WLCG_ISSUER).subject("123").build(); mvc.perform(get(SLASH_WLCG_SLASH_FILE).with(jwt().jwt(token))) .andExpect(status().isForbidden()); @@ -180,6 +180,104 @@ void getAccessAsJwtWithWriteCapabilityResultsInAccessDenied() throws Exception { } + @Test + void readAccessAsJwtWithAllowedClient() throws Exception { + Jwt token = Jwt.withTokenValue("test") + .header("kid", "rsa1") + .issuer(EXAMPLE_ISSUER) + .claim("client_id", AUTHORIZED_JWT_CLIENT_ID) + .build(); + + mvc.perform(get(SLASH_WLCG_SLASH_FILE).with(jwt().jwt(token).authorities(authConverter))) + .andExpect(status().isNotFound()); + + } + + @Test + void readAccessWithoutMatchedJWTIsDenied() throws Exception { + Jwt token = Jwt.withTokenValue("test") + .header("kid", "rsa1") + .issuer(EXAMPLE_ISSUER) + .claim("client_id", UNAUTHORIZED_JWT_CLIENT_ID) + .build(); + + mvc.perform(get(SLASH_WLCG_SLASH_FILE).with(jwt().jwt(token).authorities(authConverter))) + .andExpect(status().isForbidden()); + + token = Jwt.withTokenValue("test") + .header("kid", "rsa1") + .issuer(UNKNOWN_ISSUER) + .claim("client_id", AUTHORIZED_JWT_CLIENT_ID) + .build(); + + mvc.perform(get(SLASH_WLCG_SLASH_FILE).with(jwt().jwt(token).authorities(authConverter))) + .andExpect(status().isForbidden()); + + token = Jwt.withTokenValue("test") + .header("kid", "rsa1") + .issuer(UNKNOWN_ISSUER) + .claim("client_id", UNAUTHORIZED_JWT_CLIENT_ID) + .build(); + + mvc.perform(get(SLASH_WLCG_SLASH_FILE).with(jwt().jwt(token).authorities(authConverter))) + .andExpect(status().isForbidden()); + + token = Jwt.withTokenValue("test").header("kid", "rsa1").issuer(UNKNOWN_ISSUER).build(); + + mvc.perform(get(SLASH_WLCG_SLASH_FILE).with(jwt().jwt(token).authorities(authConverter))) + .andExpect(status().isForbidden()); + + } + + @Test + void writeAccessAsJwtWithAllowedClient() throws Exception { + Jwt token = Jwt.withTokenValue("test") + .header("kid", "rsa1") + .issuer(EXAMPLE_ISSUER) + .claim("client_id", AUTHORIZED_JWT_CLIENT_ID) + .build(); + + mvc.perform(put(SLASH_WLCG_SLASH_FILE).with(jwt().jwt(token).authorities(authConverter))) + .andExpect(status().isOk()); + + } + + @Test + void writeAccessWithoutMatchedJWTIsDenied() throws Exception { + Jwt token = Jwt.withTokenValue("test") + .header("kid", "rsa1") + .issuer(EXAMPLE_ISSUER) + .claim("client_id", UNAUTHORIZED_JWT_CLIENT_ID) + .build(); + + mvc.perform(put(SLASH_WLCG_SLASH_FILE).with(jwt().jwt(token).authorities(authConverter))) + .andExpect(status().isForbidden()); + + token = Jwt.withTokenValue("test") + .header("kid", "rsa1") + .issuer(UNKNOWN_ISSUER) + .claim("client_id", AUTHORIZED_JWT_CLIENT_ID) + .build(); + + mvc.perform(put(SLASH_WLCG_SLASH_FILE).with(jwt().jwt(token).authorities(authConverter))) + .andExpect(status().isForbidden()); + + token = Jwt.withTokenValue("test") + .header("kid", "rsa1") + .issuer(UNKNOWN_ISSUER) + .claim("client_id", UNAUTHORIZED_JWT_CLIENT_ID) + .build(); + + mvc.perform(put(SLASH_WLCG_SLASH_FILE).with(jwt().jwt(token).authorities(authConverter))) + .andExpect(status().isForbidden()); + + token = Jwt.withTokenValue("test").header("kid", "rsa1").issuer(UNKNOWN_ISSUER).build(); + + mvc.perform(put(SLASH_WLCG_SLASH_FILE).with(jwt().jwt(token).authorities(authConverter))) + .andExpect(status().isForbidden()); + + } + @WithMockVOMSUser(vos = "wlcg", saReadPermissions = {"wlcg"}) @Test void localVomsCopyRequiresWithReadPermissionsGetsAccessDenied() throws Exception { diff --git a/src/test/java/org/italiangrid/storm/webdav/test/authz/pdp/AuthzPdpTests.java b/src/test/java/org/italiangrid/storm/webdav/test/authz/pdp/AuthzPdpTests.java index e7bdf69e..342326fc 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/authz/pdp/AuthzPdpTests.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/authz/pdp/AuthzPdpTests.java @@ -37,6 +37,7 @@ import org.italiangrid.storm.webdav.authz.pdp.PathAuthorizationResult; import org.italiangrid.storm.webdav.authz.pdp.principal.Anyone; import org.italiangrid.storm.webdav.authz.pdp.principal.AuthorityHolder; +import org.italiangrid.storm.webdav.oauth.authority.JwtClientAuthority; import org.italiangrid.storm.webdav.oauth.authority.JwtGroupAuthority; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.BeforeEach; @@ -57,6 +58,8 @@ public class AuthzPdpTests { public static final String TEST_ISSUER = "https://test.example"; public static final String TEST2_ISSUER = "https://test2.example"; + public static final String AUTHORIZED_JWT_CLIENT_ID = "1234"; + public static final String UNAUTHORIZED_JWT_CLIENT_ID = "5678"; @Mock PathAuthorizationPolicyRepository repo; @@ -258,6 +261,44 @@ void oauthGroupHolderPolicyApplied() { assertThat(result.getPolicy().isPresent(), is(true)); assertThat(result.getPolicy().get(), is(oauthTestPolicy)); } + + @Test + void oauthClientHolderPolicyApplied() { + + when(request.getServletPath()).thenReturn("/test/file0"); + when(request.getMethod()).thenReturn("GET"); + + when(authentication.getAuthorities()) + .thenReturn(authorities(new JwtClientAuthority(TEST_ISSUER, AUTHORIZED_JWT_CLIENT_ID))); + + PathAuthorizationPolicy oauthTestPolicy = PathAuthorizationPolicy.builder() + .withPermit() + .withPrincipalMatcher(AuthorityHolder + .fromAuthority(new JwtClientAuthority(TEST_ISSUER, AUTHORIZED_JWT_CLIENT_ID))) + .withRequestMatcher(new AntPathRequestMatcher("/test/**", "GET")) + .build(); + + PathAuthorizationPolicy denyAllPolicy = PathAuthorizationPolicy.builder() + .withDeny() + .withPrincipalMatcher(new Anyone()) + .withRequestMatcher(new AntPathRequestMatcher("/**")) + .build(); + + List policies = Lists.newArrayList(oauthTestPolicy, denyAllPolicy); + when(repo.getPolicies()).thenReturn(policies); + + PathAuthorizationResult result = + pdp.authorizeRequest(newAuthorizationRequest(request, authentication)); + assertThat(result.getDecision(), is(PERMIT)); + assertThat(result.getPolicy().isPresent(), is(true)); + assertThat(result.getPolicy().get(), is(oauthTestPolicy)); + + when(authentication.getAuthorities()) + .thenReturn(authorities(new JwtClientAuthority(TEST_ISSUER, UNAUTHORIZED_JWT_CLIENT_ID))); + + result = pdp.authorizeRequest(newAuthorizationRequest(request, authentication)); + assertThat(result.getDecision(), is(DENY)); + } @Test void multiplePrincipalMatchersWorkAsExpected() { diff --git a/src/test/resources/application-authz-test.yml b/src/test/resources/application-authz-test.yml index df719d4f..9d38c178 100644 --- a/src/test/resources/application-authz-test.yml +++ b/src/test/resources/application-authz-test.yml @@ -55,4 +55,15 @@ storm: params: iss: https://issuer.example group: /example/admins - + - sa: wlcg + actions: + - all + effect: permit + description: Grant read/write access to a specific JWT client ID + paths: + - /** + principals: + - type: jwt-client + params: + iss: https://issuer.example + id: 1234 \ No newline at end of file diff --git a/src/test/resources/conf/sa.d/fga.properties b/src/test/resources/conf/sa.d/fga.properties new file mode 100644 index 00000000..e9f0a67d --- /dev/null +++ b/src/test/resources/conf/sa.d/fga.properties @@ -0,0 +1,27 @@ +# +# Copyright (c) Istituto Nazionale di Fisica Nucleare, 2018. +# +# 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. +# + +name=fga +rootPath=src/test/resources/storage/fga +filesystemType=posixfs +accessPoints=/fga +authenticatedReadEnabled=false +anonymousReadEnabled=false +orgs=https://iam-dev.cloud.cnaf.infn.it/ +orgsGrantReadPermission=false +orgsGrantWritePermission=false +fineGrainedAuthzEnabled=true +wlcgScopeAuthzEnabled=true diff --git a/src/test/resources/storage/fga/file b/src/test/resources/storage/fga/file new file mode 100644 index 00000000..af218bce --- /dev/null +++ b/src/test/resources/storage/fga/file @@ -0,0 +1 @@ +Testing the fine-grained authorization From 5a357abfee7a1915a882f2576fb86a3784c66896 Mon Sep 17 00:00:00 2001 From: Enrico Vianello Date: Thu, 5 Dec 2024 15:03:17 +0100 Subject: [PATCH 18/22] Bump version to 1.5.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 98940045..cf3874ed 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.italiangrid storm-webdav-server - 1.4.3 + 1.5.0 jar storm-webdav-server From c9f19ae702e9d106c438198b5e6b261b53d3fdae Mon Sep 17 00:00:00 2001 From: Enrico Vianello Date: Mon, 9 Dec 2024 18:31:20 +0100 Subject: [PATCH 19/22] Skip invalid DN into the VO map file (#74) An invalid DN can lead to ignore the succeeding entries Fix for https://issues.infn.it/jira/browse/STOR-1399 --- .../vomap/MapfileVOMembershipSource.java | 12 ++++--- .../webdav/test/authz/vomap/VOMSMapTests.java | 36 ++++++++++++------- src/test/resources/vomsmap/testers.map | 4 ++- 3 files changed, 33 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/italiangrid/storm/webdav/authz/vomap/MapfileVOMembershipSource.java b/src/main/java/org/italiangrid/storm/webdav/authz/vomap/MapfileVOMembershipSource.java index 54a3dce3..132eca24 100644 --- a/src/main/java/org/italiangrid/storm/webdav/authz/vomap/MapfileVOMembershipSource.java +++ b/src/main/java/org/italiangrid/storm/webdav/authz/vomap/MapfileVOMembershipSource.java @@ -22,7 +22,6 @@ import java.io.File; import java.io.FileReader; import java.io.IOException; -import java.util.HashSet; import java.util.List; import java.util.Set; @@ -32,6 +31,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.collect.Sets; + import eu.emi.security.authn.x509.impl.OpensslNameUtils; public class MapfileVOMembershipSource implements VOMembershipSource { @@ -64,12 +65,12 @@ private CSVParser getParser() { private boolean isValidCSVRecord(CSVRecord r) { if (r.size() > 3) { - logger.debug("Invalid CSVRecord: {}. Illegal size: {}", r, r.size()); + logger.warn("Invalid CSVRecord: {}. Illegal size: {}", r, r.size()); return false; } if (!r.get(0).startsWith("/")) { - logger.debug("Invalid CSVRecord: {}. Subject does not start with / : {}", + logger.warn("Invalid CSVRecord: {}. Subject does not start with / : {}", r, r.get(0)); return false; } @@ -82,7 +83,7 @@ public Set getVOMembers() { long startTime = System.currentTimeMillis(); - Set subjects = new HashSet(); + Set subjects = Sets.newHashSet(); CSVParser parser = getParser(); @@ -97,7 +98,8 @@ public Set getVOMembers() { } if (!isValidCSVRecord(r)) { - break; + /* Fix https://issues.infn.it/jira/browse/STOR-1399 */ + continue; } String subject = r.get(0); diff --git a/src/test/java/org/italiangrid/storm/webdav/test/authz/vomap/VOMSMapTests.java b/src/test/java/org/italiangrid/storm/webdav/test/authz/vomap/VOMSMapTests.java index e52d3974..e9f1d369 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/authz/vomap/VOMSMapTests.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/authz/vomap/VOMSMapTests.java @@ -24,18 +24,28 @@ public class VOMSMapTests { - public static final String MY_SUBJECT = "CN=Andrea Ceccanti,L=CNAF,OU=Personal Certificate,O=INFN,C=IT"; - @Test - void VOMapParserTest() { - - MapfileVOMembershipSource m = new MapfileVOMembershipSource("testers", - new File("src/test/resources/vomsmap/testers.map")); - - Assert.assertEquals("testers",m.getVOName()); - Assert.assertTrue(m.getVOMembers().contains(MY_SUBJECT)); - - Assert.assertFalse(m.getVOMembers().contains("CN=I am not Real, L=CNAF")); - - } + public static final String AC_SUBJECT = + "CN=Andrea Ceccanti,L=CNAF,OU=Personal Certificate,O=INFN,C=IT"; + public static final String EV_SUBJECT = + "CN=Enrico Vianello,L=CNAF,OU=Personal Certificate,O=INFN,C=IT"; + public static final String COMMA_SUBJECT = + "CN=Federica Agostini,L=CNAF,Bologna,OU=Personal Certificate,O=INFN,C=IT"; + public static final String RM_SUBJECT = + "CN=Roberta Miccoli,L=CNAF,OU=Personal Certificate,O=INFN,C=IT"; + + @Test + void VOMapParserTest() { + + MapfileVOMembershipSource m = new MapfileVOMembershipSource("testers", + new File("src/test/resources/vomsmap/testers.map")); + + Assert.assertEquals("testers", m.getVOName()); + Assert.assertTrue(m.getVOMembers().contains(AC_SUBJECT)); + Assert.assertFalse(m.getVOMembers().contains(EV_SUBJECT)); + Assert.assertFalse(m.getVOMembers().contains(COMMA_SUBJECT)); + Assert.assertTrue(m.getVOMembers().contains(RM_SUBJECT)); + Assert.assertFalse(m.getVOMembers().contains("CN=I am not Real, L=CNAF")); + + } } diff --git a/src/test/resources/vomsmap/testers.map b/src/test/resources/vomsmap/testers.map index 9eaa5fed..b5f13a28 100644 --- a/src/test/resources/vomsmap/testers.map +++ b/src/test/resources/vomsmap/testers.map @@ -1,2 +1,4 @@ /C=IT/O=INFN/OU=Personal Certificate/L=CNAF/CN=Andrea Ceccanti,/C=IT/O=INFN/CN=INFN CA,andrea.ceccanti@cnaf.infn.it -/C=IT/O=INFN/OU=Personal Certificate/L=CNAF/CN=Enrico Vianello,/C=IT/O=INFN/CN=INFN CA,enrico.vianello@cnaf.infn.it \ No newline at end of file +C=IT/O=INFN/OU=Personal Certificate/L=CNAF/CN=Enrico Vianello,/C=IT/O=INFN/CN=INFN CA,enrico.vianello@cnaf.infn.it +/C=IT/O=INFN/OU=Personal Certificate/L=CNAF,Bologna/CN=Federica Agostini,/C=IT/O=INFN/CN=INFN CA,federica.agostini@cnaf.infn.it +/C=IT/O=INFN/OU=Personal Certificate/L=CNAF/CN=Roberta Miccoli,/C=IT/O=INFN/CN=INFN CA,roberta.miccoli@cnaf.infn.it \ No newline at end of file From df507cbb1c538da60b5417a8116c653521927a5e Mon Sep 17 00:00:00 2001 From: Federica Agostini Date: Tue, 10 Dec 2024 15:30:18 +0100 Subject: [PATCH 20/22] Support for the 'entitlements' JWT claim (#59) --- .../GrantedAuthoritiesMapperSupport.java | 4 ++-- .../AuthorizationIntegrationTests.java | 21 ++++++++++++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/italiangrid/storm/webdav/oauth/GrantedAuthoritiesMapperSupport.java b/src/main/java/org/italiangrid/storm/webdav/oauth/GrantedAuthoritiesMapperSupport.java index e09483aa..6ff8e74a 100644 --- a/src/main/java/org/italiangrid/storm/webdav/oauth/GrantedAuthoritiesMapperSupport.java +++ b/src/main/java/org/italiangrid/storm/webdav/oauth/GrantedAuthoritiesMapperSupport.java @@ -34,8 +34,8 @@ public class GrantedAuthoritiesMapperSupport { protected final Multimap authzMap = ArrayListMultimap.create(); protected final AuthorizationServerProperties authzServerProperties; - public static final String[] OAUTH_GROUP_CLAIM_NAMES = {"groups", "wlcg.groups"}; - public static final String SCOPE_CLAIM_NAME = "scope"; + protected static final String[] OAUTH_GROUP_CLAIM_NAMES = {"groups", "wlcg.groups", "entitlements"}; + protected static final String SCOPE_CLAIM_NAME = "scope"; protected final Set anonymousGrantedAuthorities = Sets.newHashSet(); diff --git a/src/test/java/org/italiangrid/storm/webdav/test/authz/integration/AuthorizationIntegrationTests.java b/src/test/java/org/italiangrid/storm/webdav/test/authz/integration/AuthorizationIntegrationTests.java index db4c60c4..eddc1680 100644 --- a/src/test/java/org/italiangrid/storm/webdav/test/authz/integration/AuthorizationIntegrationTests.java +++ b/src/test/java/org/italiangrid/storm/webdav/test/authz/integration/AuthorizationIntegrationTests.java @@ -228,7 +228,7 @@ void readAccessWithoutMatchedJWTIsDenied() throws Exception { .andExpect(status().isForbidden()); } - + @Test void writeAccessAsJwtWithAllowedClient() throws Exception { Jwt token = Jwt.withTokenValue("test") @@ -275,7 +275,26 @@ void writeAccessWithoutMatchedJWTIsDenied() throws Exception { mvc.perform(put(SLASH_WLCG_SLASH_FILE).with(jwt().jwt(token).authorities(authConverter))) .andExpect(status().isForbidden()); + } + + @Test + void readWriteAccessAsJwtWithAllowedGroup() throws Exception { + + final String[] OAUTH_GROUP_CLAIM_NAMES = {"groups", "wlcg.groups", "entitlements"}; + + for (String groupClaim : OAUTH_GROUP_CLAIM_NAMES) { + Jwt token = Jwt.withTokenValue("test") + .header("kid", "rsa1") + .issuer(EXAMPLE_ISSUER) + .claim(groupClaim, "/example/admins") + .build(); + + mvc.perform(get(SLASH_WLCG_SLASH_FILE).with(jwt().jwt(token).authorities(authConverter))) + .andExpect(status().isNotFound()); + mvc.perform(put(SLASH_WLCG_SLASH_FILE).with(jwt().jwt(token).authorities(authConverter))) + .andExpect(status().isOk()); + } } @WithMockVOMSUser(vos = "wlcg", saReadPermissions = {"wlcg"}) From 2c973ccbc77febb622f888d3a68994ba568f3d5a Mon Sep 17 00:00:00 2001 From: Paul Millar Date: Wed, 27 Nov 2024 10:20:27 +0100 Subject: [PATCH 21/22] update milton version from v2.7.1.7 to v2.8.0.3 Motivation: The current version of Milton is ancient. The git commit that updated the version to v2.7.1.7 (f50f6c3e3) was made 2016-07-01. At this time, the Milton release process was rather haphazard, with no git tags and no jar files uploaded to Maven central. This makes building StoRM WebDAV difficult. Milton v2.8.0.3 was released 2020-09-23 and is the first version with a reasonable release process: git tags and jar files uploaded to Maven central. The online Milton release notes also start with this version. Although v2.8.0.3 is now over four years old, updating to this version fixes build problems while limiting the risk (of breaking changes) from the library upgrade. Modification: Update pom.xml to use the newer version. Result: No user- or admin observable changes. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cf3874ed..ae2ae52c 100644 --- a/pom.xml +++ b/pom.xml @@ -39,7 +39,7 @@ https://sonarcloud.io 3.3.3 - 2.7.1.7 + 2.8.0.3 2.3 1.2 From e54fb0775416d4d8d604b8b993db3819225f3e0c Mon Sep 17 00:00:00 2001 From: Luca Bassi Date: Mon, 9 Dec 2024 08:30:18 +0100 Subject: [PATCH 22/22] Do not proxy all Maven requests through Nexus --- .github/workflows/maven.yml | 4 +-- .github/workflows/sonar.yml | 2 +- Dockerfile | 7 +++--- cnaf-mirror-settings.xml | 46 ---------------------------------- maven/cnaf-mirror-settings.xml | 46 ---------------------------------- pom.xml | 1 - travis/build.sh | 24 ------------------ travis/install-deps.sh | 25 ------------------ 8 files changed, 6 insertions(+), 149 deletions(-) delete mode 100644 cnaf-mirror-settings.xml delete mode 100644 maven/cnaf-mirror-settings.xml delete mode 100644 travis/build.sh delete mode 100644 travis/install-deps.sh diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 048fb743..ce3d6572 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -38,6 +38,6 @@ jobs: key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} restore-keys: ${{ runner.os }}-m2 - name: Build with Maven - run: mvn -B clean package -s maven/cnaf-mirror-settings.xml + run: mvn -B clean package - name: Checkstyle with Maven - run: mvn clean compile -s maven/cnaf-mirror-settings.xml -U -Dmaven.test.failure.ignore -DfailIfNoTests=false checkstyle:check -Dcheckstyle.config.location=google_checks.xml + run: mvn clean compile -U -Dmaven.test.failure.ignore -DfailIfNoTests=false checkstyle:check -Dcheckstyle.config.location=google_checks.xml diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml index 6f4f5747..d231dc4d 100644 --- a/.github/workflows/sonar.yml +++ b/.github/workflows/sonar.yml @@ -34,4 +34,4 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - run: mvn -s maven/cnaf-mirror-settings.xml -B -U install org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=italiangrid_storm-webdav + run: mvn -B -U install org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=italiangrid_storm-webdav diff --git a/Dockerfile b/Dockerfile index 21fd5b1d..9d3b7ce3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,13 +3,12 @@ FROM eclipse-temurin:11-jdk-alpine as build WORKDIR /workspace/app RUN apk add maven COPY pom.xml . -COPY maven maven -RUN mvn dependency:resolve -s maven/cnaf-mirror-settings.xml -RUN mvn dependency:resolve-plugins -s maven/cnaf-mirror-settings.xml +RUN mvn dependency:resolve +RUN mvn dependency:resolve-plugins COPY .git .git COPY etc etc COPY src src -RUN mvn package -s maven/cnaf-mirror-settings.xml -Dmaven.test.skip +RUN mvn package -Dmaven.test.skip RUN mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar) FROM eclipse-temurin:11-centos7 diff --git a/cnaf-mirror-settings.xml b/cnaf-mirror-settings.xml deleted file mode 100644 index 3280e381..00000000 --- a/cnaf-mirror-settings.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - - false - - - nexus - CNAF maven mirror - https://repo.cloud.cnaf.infn.it/repository/maven-public - * - - - - - nexus - - - central - http://central - true - true - - - - - - nexus - - diff --git a/maven/cnaf-mirror-settings.xml b/maven/cnaf-mirror-settings.xml deleted file mode 100644 index 3280e381..00000000 --- a/maven/cnaf-mirror-settings.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - - false - - - nexus - CNAF maven mirror - https://repo.cloud.cnaf.infn.it/repository/maven-public - * - - - - - nexus - - - central - http://central - true - true - - - - - - nexus - - diff --git a/pom.xml b/pom.xml index ae2ae52c..71a7e51d 100644 --- a/pom.xml +++ b/pom.xml @@ -125,7 +125,6 @@ src/main/resources/static/** src/main/resources/META-INF/spring.factories maven/** - cnaf-mirror-settings.xml sonar-project.properties true diff --git a/travis/build.sh b/travis/build.sh deleted file mode 100644 index f51cf154..00000000 --- a/travis/build.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash -# -# 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. -# - -set -e - -mvn -B clean compile -echo "storm-webdav build completed succesfully" - -mvn -B clean test -echo "storm-webdav tests completed succesfully" diff --git a/travis/install-deps.sh b/travis/install-deps.sh deleted file mode 100644 index 68846397..00000000 --- a/travis/install-deps.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash -# -# 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. -# - -set -ex - -pwd - -sudo apt-get install -y wget -wget https://raw.githubusercontent.com/italiangrid/build-settings/master/maven/cnaf-mirror-settings.xml - -mv cnaf-mirror-settings.xml ~/.m2/settings.xml