diff --git a/postgres.docker-compose.yml b/postgres.docker-compose.yml index 61337cff..c6ffcec0 100644 --- a/postgres.docker-compose.yml +++ b/postgres.docker-compose.yml @@ -51,8 +51,8 @@ services: POSTGRES_USER: db_user POSTGRES_PASSWORD: db_user@123 POSTGRES_DB: sts - volumes: - - "./.docker/sts-db/postgres:/var/lib/postgresql/data" +# volumes: +# - "./.docker/sts-db/postgres:/var/lib/postgresql/data" ports: - 5432:5432 # networks: diff --git a/sts-persistence-jpa/src/main/java/de/adorsys/sts/persistence/jpa/entity/JpaSecret.java b/sts-persistence-jpa/src/main/java/de/adorsys/sts/persistence/jpa/entity/JpaSecret.java index a5473015..45bc9452 100644 --- a/sts-persistence-jpa/src/main/java/de/adorsys/sts/persistence/jpa/entity/JpaSecret.java +++ b/sts-persistence-jpa/src/main/java/de/adorsys/sts/persistence/jpa/entity/JpaSecret.java @@ -12,8 +12,8 @@ public class JpaSecret { @Id - @GeneratedValue(strategy = GenerationType.AUTO) - private int id; + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; private String subject; diff --git a/sts-secret-server/src/test/java/de/adorsys/sts/secretserver/SecretServerApplicationIT.java b/sts-secret-server/src/test/java/de/adorsys/sts/secretserver/SecretServerApplicationIT.java index f4d1fe89..9fdb663c 100644 --- a/sts-secret-server/src/test/java/de/adorsys/sts/secretserver/SecretServerApplicationIT.java +++ b/sts-secret-server/src/test/java/de/adorsys/sts/secretserver/SecretServerApplicationIT.java @@ -15,7 +15,6 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.TestConfiguration; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.http.HttpStatus; import org.springframework.test.annotation.DirtiesContext; @@ -23,8 +22,6 @@ import org.springframework.web.client.DefaultResponseErrorHandler; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestTemplate; -import org.testcontainers.containers.PostgreSQLContainer; -import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import java.util.Arrays; @@ -95,7 +92,7 @@ void setup() { } -// @Test + @Test void shouldReturnTheSameSecretForSameUser() { String firstSecret = getDecryptedSecret(USERNAME_ONE, PASSWORD_ONE); String secondSecret = getDecryptedSecret(USERNAME_ONE, PASSWORD_ONE); @@ -103,7 +100,7 @@ void shouldReturnTheSameSecretForSameUser() { assertThat(firstSecret, is(equalTo(secondSecret))); } - // @Test + @Test void shouldReturnDifferentSecretsForDifferentUsers() throws Exception { String firstSecret = getDecryptedSecret(USERNAME_ONE, PASSWORD_ONE); String secondSecret = getDecryptedSecret(USERNAME_TWO, PASSWORD_TWO); @@ -111,7 +108,7 @@ void shouldReturnDifferentSecretsForDifferentUsers() throws Exception { assertThat(firstSecret, is(not(equalTo(secondSecret)))); } - // @Test + @Test void shouldNotReturnTheSameTokenForSameUser() throws Exception { TokenResponse firstTokenResponse = getSecretServerToken(USERNAME_ONE, PASSWORD_ONE); assertThat(firstTokenResponse.getAccess_token(), is(notNullValue())); @@ -122,7 +119,7 @@ void shouldNotReturnTheSameTokenForSameUser() throws Exception { assertThat(firstTokenResponse, is(not(equalTo(secondTokenResponse)))); } - // @Test + @Test void shouldNotGetSecretForInvalidAccessToken() throws Exception { final String invalidAccessToken = "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJvVjU2Uk9namthbTVzUmVqdjF6b1JVNmY" + "1R3YtUGRTdjN2b1ZfRVY5MmxnIn0.eyJqdGkiOiI5NWY2MzQ4NC04MTk2LTQ2NzYtYjI4Ni1lYjY4YTFmOTZmYTAiLCJleHAiOjE1N" + @@ -158,7 +155,7 @@ void shouldNotGetSecretForFakeAccessToken() throws Exception { assertThat(((HttpClientErrorException) caughtException).getStatusCode(), is(equalTo(HttpStatus.FORBIDDEN))); } - // @Test + @Test void shouldGetEmptySecretsForUnknownAudience() throws Exception { Authentication.AuthenticationToken authToken = authentication.login(USERNAME_ONE, PASSWORD_ONE); diff --git a/sts-spring/src/main/java/de/adorsys/sts/token/authentication/ConfigurationPropertiesAuthServerProvider.java b/sts-spring/src/main/java/de/adorsys/sts/token/authentication/ConfigurationPropertiesAuthServerProvider.java index c57cfc2c..e50074f5 100644 --- a/sts-spring/src/main/java/de/adorsys/sts/token/authentication/ConfigurationPropertiesAuthServerProvider.java +++ b/sts-spring/src/main/java/de/adorsys/sts/token/authentication/ConfigurationPropertiesAuthServerProvider.java @@ -55,8 +55,7 @@ private AuthServer mapFromProperties(AuthServerConfigurationProperties.AuthServe properties.getName(), properties.getIssUrl(), properties.getJwksUrl(), - properties.getRefreshIntervalSeconds(), - objectMapper + properties.getRefreshIntervalSeconds() ); } } diff --git a/sts-spring/src/main/java/de/adorsys/sts/token/authentication/LoggingAuthServer.java b/sts-spring/src/main/java/de/adorsys/sts/token/authentication/LoggingAuthServer.java index 8295ebc3..6aeebf19 100644 --- a/sts-spring/src/main/java/de/adorsys/sts/token/authentication/LoggingAuthServer.java +++ b/sts-spring/src/main/java/de/adorsys/sts/token/authentication/LoggingAuthServer.java @@ -10,17 +10,9 @@ import java.util.List; public class LoggingAuthServer extends AuthServer { - private static final Logger LOG = LoggerFactory.getLogger(LoggingBearerTokenValidator.class); - private final ObjectMapper objectMapper; - public LoggingAuthServer(String name, String issUrl, String jwksUrl, ObjectMapper objectMapper) { - super(name, issUrl, jwksUrl); - this.objectMapper = objectMapper; - } - - public LoggingAuthServer(String name, String issUrl, String jwksUrl, int refreshIntervalSeconds, ObjectMapper objectMapper) { + public LoggingAuthServer(String name, String issUrl, String jwksUrl, int refreshIntervalSeconds) { super(name, issUrl, jwksUrl, refreshIntervalSeconds); - this.objectMapper = objectMapper; } @Override diff --git a/sts-token-auth/pom.xml b/sts-token-auth/pom.xml index caa20b48..c922839f 100644 --- a/sts-token-auth/pom.xml +++ b/sts-token-auth/pom.xml @@ -38,5 +38,17 @@ backport-util-concurrent 3.1 + + org.junit.jupiter + junit-jupiter-api + 5.10.1 + test + + + + org.mockito + mockito-core + test + diff --git a/sts-token-auth/src/main/java/de/adorsys/sts/tokenauth/AuthServer.java b/sts-token-auth/src/main/java/de/adorsys/sts/tokenauth/AuthServer.java index aa023767..f250344e 100644 --- a/sts-token-auth/src/main/java/de/adorsys/sts/tokenauth/AuthServer.java +++ b/sts-token-auth/src/main/java/de/adorsys/sts/tokenauth/AuthServer.java @@ -3,9 +3,12 @@ import com.nimbusds.jose.JOSEException; import com.nimbusds.jose.jwk.*; import com.nimbusds.jose.jwk.source.JWKSource; -import com.nimbusds.jose.jwk.source.RemoteJWKSet; +import com.nimbusds.jose.jwk.source.JWKSourceBuilder; import com.nimbusds.jose.proc.SecurityContext; import lombok.Getter; +import lombok.Setter; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; import java.net.URL; import java.security.Key; @@ -13,35 +16,40 @@ import java.util.List; import java.util.concurrent.ConcurrentHashMap; +@Slf4j public class AuthServer { + @Setter @Getter private String name; @Getter private final String issUrl; private final String jwksUrl; - private int refreshIntervalSeconds = 600; + private final int refreshIntervalSeconds; - private final ConcurrentHashMap jwkCache = new ConcurrentHashMap<>(); - private long lastCacheUpdate = 0; + @Setter + JWKSource jwkSource; + + final ConcurrentHashMap jwkCache = new ConcurrentHashMap<>(); + long lastCacheUpdate = 0; public AuthServer(String name, String issUrl, String jwksUrl) { - super(); - this.name = name; - this.issUrl = issUrl; - this.jwksUrl = jwksUrl; + this(name, issUrl, jwksUrl, 600); } + @SneakyThrows public AuthServer(String name, String issUrl, String jwksUrl, int refreshIntervalSeconds) { super(); this.name = name; this.issUrl = issUrl; this.jwksUrl = jwksUrl; this.refreshIntervalSeconds = refreshIntervalSeconds; + + jwkSource = JWKSourceBuilder.create(new URL(this.jwksUrl)).build(); } private void updateJwkCache() throws JsonWebKeyRetrievalException { try { - JWKSource jwkSource = new RemoteJWKSet<>(new URL(this.jwksUrl)); + List jwks = jwkSource.get(new JWKSelector(new JWKMatcher.Builder().build()), null); onJsonWebKeySetRetrieved(jwks); @@ -83,11 +91,8 @@ public Key getJWK(String keyID) throws JsonWebKeyRetrievalException { } } - public void setName(String name) { - this.name = name; - } - protected void onJsonWebKeySetRetrieved(List jwks) { + log.info("Retrieved {} keys from {}", jwks.size(), jwksUrl); } public static class JsonWebKeyRetrievalException extends RuntimeException { diff --git a/sts-token-auth/src/test/java/de/adorsys/sts/tokenauth/AuthServerTest.java b/sts-token-auth/src/test/java/de/adorsys/sts/tokenauth/AuthServerTest.java new file mode 100644 index 00000000..f0a91f1f --- /dev/null +++ b/sts-token-auth/src/test/java/de/adorsys/sts/tokenauth/AuthServerTest.java @@ -0,0 +1,103 @@ +package de.adorsys.sts.tokenauth; + +import com.nimbusds.jose.RemoteKeySourceException; +import com.nimbusds.jose.jwk.JWK; +import com.nimbusds.jose.jwk.JWKSelector; +import com.nimbusds.jose.jwk.RSAKey; +import com.nimbusds.jose.jwk.source.JWKSource; +import com.nimbusds.jose.jwk.source.RemoteJWKSet; +import com.nimbusds.jose.proc.SecurityContext; +import com.nimbusds.jose.util.Base64URL; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import java.security.Key; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.interfaces.RSAPublicKey; +import java.util.Collections; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +class AuthServerTest { + + private AuthServer authServer; + private RemoteJWKSet mockRemoteJWKSet; + + @BeforeEach + void setUp() throws Exception { + authServer = new AuthServer("TestServer", "https://example.com/iss", "https://example.com/jwks", 10); + mockRemoteJWKSet = Mockito.mock(RemoteJWKSet.class); + JWK jwk = new RSAKey.Builder(new Base64URL("n"), new Base64URL("e")).keyID("testKey").build(); + when(mockRemoteJWKSet.get(any(JWKSelector.class), any(SecurityContext.class))).thenReturn(Collections.singletonList(jwk)); + } + + @Test + void testCacheInitialization() { + assertTrue(authServer.jwkCache.isEmpty(), "Cache should be initially empty"); + } + + @Test + void testCacheUpdateAfterInterval() throws Exception { + // Simulieren, dass die letzte Aktualisierung lange zurĆ¼ckliegt + JWKSource mockJwkSource = Mockito.mock(JWKSource.class); + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(512); // 512-bit RSA key pair + KeyPair keyPair = keyPairGenerator.generateKeyPair(); + + // Create a mock RSAKey from the generated key pair + RSAKey rsaKey = new RSAKey.Builder((RSAPublicKey) keyPair.getPublic()).keyID("testKey").build(); + + // Configure your AuthServer instance + AuthServer authServer = new AuthServer("TestServer", "https://example.com/iss", "https://example.com/jwks"); + authServer.setJwkSource(mockJwkSource); // Inject the mock + + // Mock the JWKSource and configure it to return the mock RSAKey + Mockito.when(mockJwkSource.get(any(), any())).thenReturn(Collections.singletonList(rsaKey)); + authServer.lastCacheUpdate = 0; + authServer.getJWK("testKey"); + + assertFalse(authServer.jwkCache.isEmpty(), "Cache should be updated after interval"); + } + + @Test + void testCacheUpdateOnNonExistingKey() { + assertThrows(AuthServer.JsonWebKeyRetrievalException.class, () -> authServer.getJWK("nonExistingKey")); + } + + @Test + void testValidKeyRetrieval() throws Exception { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(512); // 512-bit RSA key pair + KeyPair keyPair = keyPairGenerator.generateKeyPair(); + + // Create a mock RSAKey from the generated key pair + RSAKey rsaKey = new RSAKey.Builder((RSAPublicKey) keyPair.getPublic()).keyID("testKey").build(); + + // Mock the JWKSource and configure it to return the mock RSAKey + JWKSource mockJwkSource = Mockito.mock(JWKSource.class); + Mockito.when(mockJwkSource.get(any(), any())).thenReturn(Collections.singletonList(rsaKey)); + + // Inject the mock JWKSource into your AuthServer + AuthServer authServer = new AuthServer("TestServer", "https://example.com/iss", "https://example.com/jwks"); + // Assuming you have a method to set the JWKSource + authServer.setJwkSource(mockJwkSource); + // Now you can test your method + Key key = authServer.getJWK("testKey"); + + assertNotNull(key, "Should return a valid key for a valid keyID"); + } + + @Test + void testExceptionHandling() throws RemoteKeySourceException { + // Konfigurieren Sie das Mock-Objekt, um eine Ausnahme zu werfen + when(mockRemoteJWKSet.get(any(JWKSelector.class), any(SecurityContext.class))).thenThrow(new RuntimeException("Test Exception")); + + assertThrows(AuthServer.JsonWebKeyRetrievalException.class, () -> authServer.getJWK("testKey")); + } + + +} \ No newline at end of file diff --git a/sts-token-auth/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/sts-token-auth/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker new file mode 100644 index 00000000..1f0955d4 --- /dev/null +++ b/sts-token-auth/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker @@ -0,0 +1 @@ +mock-maker-inline