Skip to content

Commit

Permalink
Add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
enricovianello committed Mar 13, 2024
1 parent 3f82e5e commit f8d435a
Show file tree
Hide file tree
Showing 7 changed files with 258 additions and 36 deletions.
7 changes: 7 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@

<!-- Keep this aligned with the parent project version! -->
<spring-boot.version>2.7.18</spring-boot.version>
<spring-security-oauth2.version>2.5.2.RELEASE</spring-security-oauth2.version>

<!-- Sonarcloud.io properties -->
<sonar.projectKey>italiangrid_storm-webdav</sonar.projectKey>
Expand Down Expand Up @@ -264,6 +265,12 @@
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>${spring-security-oauth2.version}</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
Expand Down
61 changes: 55 additions & 6 deletions src/main/resources/application-test.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,58 @@
spring:
main:
banner-mode: "off"
security:
oauth2:
client:
provider:
wlcg:
issuer-uri: https://wlcg.cloud.cnaf.infn.it/
registration:
wlcg:
provider: wlcg
authorization-grant-type: authorization_code
client-name: WLCG IAM
client-id: xfer.cr.cnaf.infn.it
client-secret: ANR7a3cYRFfXV3r8CXHVzA7xYSXFA7gQ6kEcQBKEESvJrdBXYZMaSaLXAlp3RXd5dfWAs1b1K4EGxbEJWTH4coU
scope:
- openid
- profile
- wlcg.groups

session:
store-type: none

server:
jetty:
accesslog:
enabled: false
oauth:

enable-oidc: true
refresh-period-minutes: 1
issuers:
- name: iam-test
issuer: https://iam-test.indigo-datacloud.eu/
- name: iam-wlcg
- name: wlcg
issuer: https://wlcg.cloud.cnaf.infn.it/
- name: iam-local
issuer: https://iam.local.io/
enforce-audience-checks: true
audiences:
- https://wlcg.cern.ch/jwt/v1/any
- https://xfer.cr.cnaf.infn.it:8443

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

2 changes: 2 additions & 0 deletions src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,5 @@ logging.level.org.italiangrid.storm=INFO
#logging.level.org.eclipse.jetty.io=ERROR
#logging.level.org.eclipse.jetty.server=DEBUG
#logging.level.org.eclipse.jetty.server.HttpOutput=ERROR

logging.level.org.springframework.security.oauth2.jwt.NimbusJwtDecoder=DEBUG
Original file line number Diff line number Diff line change
Expand Up @@ -15,61 +15,122 @@
*/
package org.italiangrid.storm.webdav.test.oauth.jwk;

import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.lenient;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.jwt;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.ParseException;
import java.net.URI;
import java.nio.file.Path;
import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.Map;

import org.italiangrid.storm.webdav.config.OAuthProperties;
import org.italiangrid.storm.webdav.oauth.CompositeJwtDecoder;
import org.apache.commons.io.FileUtils;
import org.italiangrid.storm.webdav.authz.VOMSAuthenticationFilter;
import org.italiangrid.storm.webdav.fs.attrs.ExtendedAttributesHelper;
import org.italiangrid.storm.webdav.oauth.StormJwtAuthoritiesConverter;
import org.italiangrid.storm.webdav.oauth.utils.OidcConfigurationFetcher;
import org.italiangrid.storm.webdav.oauth.utils.TrustedJwtDecoderCacheLoader;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.italiangrid.storm.webdav.server.servlet.MiltonFilter;
import org.italiangrid.storm.webdav.test.utils.oauth.WithMockOAuthUser;
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.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.internal.verification.VerificationModeFactory;
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.mock.mockito.MockBean;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder;
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.google.common.cache.CacheBuilder;
import com.google.common.cache.LoadingCache;
import com.nimbusds.jose.jwk.JWKSet;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.nimbusds.jose.RemoteKeySourceException;

@ExtendWith(MockitoExtension.class)
import io.lettuce.core.GetExArgs.Builder;
import io.milton.http.HttpManager;

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles("jwk-test")
public class JWKCachingTests {

public static final Instant NOW = Instant.parse("2018-01-01T00:00:00.00Z");
public static final String SLASH_WLCG_SLASH_FILE = "/wlcg/example";

public static final String ISSUER = "https://wlcg.cloud.cnaf.infn.it/";
public static final URI JWKS_URI = URI.create("https://wlcg.cloud.cnaf.infn.it/jwk");

Clock fixedClock = Clock.fixed(NOW, ZoneId.systemDefault());
ObjectMapper mapper = new ObjectMapper();

@Mock
TrustedJwtDecoderCacheLoader loader;
@Autowired
private MockMvc mvc;

@Mock
@MockBean
OidcConfigurationFetcher fetcher;

@Mock
ExecutorService executor;
@MockBean
JwtDecoder decoder;

@MockBean
ExtendedAttributesHelper helper;

@Autowired
private VOMSAuthenticationFilter filter;

@Mock
OAuthProperties oauthProperties;
@Autowired
private FilterRegistrationBean<MiltonFilter> miltonFilter;

@SuppressWarnings("unchecked")
@BeforeEach
public void setup() throws IOException, RemoteKeySourceException {

private JWKSet getTestJWKSet() throws IOException, ParseException {
ClassLoader classLoader = getClass().getClassLoader();
String jwks = FileUtils.readFileToString(new File(classLoader.getResource("jwk-test/well-known/jwk/keystore.jwks").getFile()), "UTF-8");
String oidcConfiguration = FileUtils.readFileToString(new File(classLoader.getResource("jwk-test/well-known/opeind-configuration").getFile()), "UTF-8");
Map<String, Object> configurationMap = (Map<String, Object>) mapper.readValue(oidcConfiguration, Map.class);
configurationMap.entrySet().forEach(e -> System.out.println(e.getKey() + "-" + e.getValue()));
lenient().when(fetcher.loadConfigurationForIssuer(eq(ISSUER))).thenReturn(configurationMap);
lenient().when(fetcher.loadJWKSourceForURL(eq(JWKS_URI))).thenReturn(jwks);

lenient().when(helper.getChecksumAttribute(Mockito.any(File.class))).thenReturn("");
lenient().when(helper.getChecksumAttribute(Mockito.any(Path.class))).thenReturn("");

String data =
new String(getClass().getResourceAsStream("jwk/test-keystore.jwks").readAllBytes());
return JWKSet.parse(data);
filter.setCheckForPrincipalChanges(false);

HttpManager httpManager = Mockito.mock(HttpManager.class);
miltonFilter.getFilter().setMiltonHTTPManager(httpManager);
}

public void testRefreshPeriodExpired() {
@Test
public void testRefreshPeriodExpired() throws Exception {

Jwt token = Jwt.withTokenValue("test")
.header("kid", "rsa1")
.issuer(ISSUER)
.subject("123")
.claim("scope", "storage.read:/ storage.modify:/")
.build();


Mockito.verify(fetcher, VerificationModeFactory.times(1)).loadConfigurationForIssuer(ISSUER);
mvc.perform(put("/wlcg/test").with(jwt().jwt(token))).andExpect(status().isOk());
Mockito.verify(fetcher, VerificationModeFactory.times(1)).loadJWKSourceForURL(JWKS_URI);
Thread.sleep(61000); // 61 seconds
mvc.perform(get(SLASH_WLCG_SLASH_FILE)).andExpect(status().isOk());
Mockito.verify(fetcher, VerificationModeFactory.times(2)).loadJWKSourceForURL(JWKS_URI);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/**
* 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.utils;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.italiangrid.storm.webdav.server.util.ThreadPoolBuilder.DEFAULT_IDLE_TIMEOUT;
import static org.italiangrid.storm.webdav.server.util.ThreadPoolBuilder.DEFAULT_MAX_THREADS;
import static org.italiangrid.storm.webdav.server.util.ThreadPoolBuilder.DEFAULT_MIN_THREADS;
import static org.italiangrid.storm.webdav.server.util.ThreadPoolBuilder.DEFAULT_THREAD_POOL_METRIC_NAME;
import static org.junit.Assert.assertNotNull;

import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.util.thread.ThreadPool;
import org.italiangrid.storm.webdav.server.util.ThreadPoolBuilder;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.jetty9.InstrumentedQueuedThreadPool;

@ExtendWith(MockitoExtension.class)
public class ThreadPoolBuilderTests {

final static String PREFIX = "iam";
final static String NAME = "queue";
final static int IDLE_TIMEOUT = 6;
final static int MIN_THREADS = 10;
final static int MAX_THREADS = 20;
final static int MAX_QUEUE_SIZE = 40;

@Mock
MetricRegistry registry;

@Test
public void threadPoolBuilderTests() {

ThreadPool tp = ThreadPoolBuilder.instance()
.withMinThreads(MIN_THREADS)
.withMaxThreads(MAX_THREADS)
.withIdleTimeoutMsec(IDLE_TIMEOUT)
.withMaxRequestQueueSize(MAX_QUEUE_SIZE)
.withName(NAME)
.withPrefix(PREFIX)
.build();
assertNotNull(tp);
assertThat(tp.getClass(), is(QueuedThreadPool.class));
QueuedThreadPool qtp = (QueuedThreadPool) tp;
assertThat(qtp.getMinThreads(), is(MIN_THREADS));
assertThat(qtp.getMaxThreads(), is(MAX_THREADS));
assertThat(qtp.getIdleTimeout(), is(IDLE_TIMEOUT));
assertThat(qtp.getMaxAvailableThreads(), is(MAX_THREADS));
assertThat(qtp.getName(), is(NAME));
}

@Test
public void threadPoolBuilderWithDefaultValuesTests() {

ThreadPool tp = ThreadPoolBuilder.instance().build();
assertNotNull(tp);
assertThat(tp.getClass(), is(QueuedThreadPool.class));
QueuedThreadPool qtp = (QueuedThreadPool) tp;
assertThat(qtp.getMinThreads(), is(DEFAULT_MIN_THREADS));
assertThat(qtp.getMaxThreads(), is(DEFAULT_MAX_THREADS));
assertThat(qtp.getIdleTimeout(), is(DEFAULT_IDLE_TIMEOUT));
assertThat(qtp.getMaxAvailableThreads(), is(DEFAULT_MAX_THREADS));
assertThat(qtp.getName(), is(DEFAULT_THREAD_POOL_METRIC_NAME));
}

@Test
public void threadPoolBuilderWithRegistryTests() {

ThreadPool tp = ThreadPoolBuilder.instance().registry(registry).build();
assertNotNull(tp);
assertThat(tp.getClass(), is(InstrumentedQueuedThreadPool.class));
InstrumentedQueuedThreadPool qtp = (InstrumentedQueuedThreadPool) tp;
assertThat(qtp.getMinThreads(), is(DEFAULT_MIN_THREADS));
assertThat(qtp.getMaxThreads(), is(DEFAULT_MAX_THREADS));
assertThat(qtp.getIdleTimeout(), is(DEFAULT_IDLE_TIMEOUT));
assertThat(qtp.getMaxAvailableThreads(), is(DEFAULT_MAX_THREADS));
assertThat(qtp.getName(), is(DEFAULT_THREAD_POOL_METRIC_NAME));
}
}
4 changes: 4 additions & 0 deletions src/test/resources/application-authz-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ server:
enabled: false
oauth:
enable-oidc: false
refresh-period-minutes: 1
issuers:
- name: iam-dev
issuer: https://iam-dev.cloud.cnaf.infn.it/

storm:
sa:
Expand Down
3 changes: 2 additions & 1 deletion src/test/resources/conf/sa.d/test.properties
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ name=test
rootPath=src/test/resources/storage/test
filesystemType=posixfs
accessPoints=/test
vos=test.vo
vos=wlcg
orgs=https://wlcg.cloud.cnaf.infn.it/
authenticatedReadEnabled=true
anonymousReadEnabled=false
voMapGrantsWritePermission=false

0 comments on commit f8d435a

Please sign in to comment.