This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to branch issue/memory-token-store-for-tests in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-auth-oauth-client.git
commit e20215730bfad497236a833b17982ab03808afea Author: Robert Munteanu <[email protected]> AuthorDate: Mon Dec 9 14:42:47 2024 +0100 test: extract a test-only InMemoryOAuthTokenStore --- .../auth/oauth_client/InMemoryOAuthTokenStore.java | 95 ++++++++++++++++++++++ .../impl/InMemoryOAuthTokenStoreTest.java | 36 ++++++++ .../impl/OAuthCallbackServletTest.java | 49 ++--------- 3 files changed, 137 insertions(+), 43 deletions(-) diff --git a/src/test/java/org/apache/sling/auth/oauth_client/InMemoryOAuthTokenStore.java b/src/test/java/org/apache/sling/auth/oauth_client/InMemoryOAuthTokenStore.java new file mode 100644 index 0000000..cfdc16f --- /dev/null +++ b/src/test/java/org/apache/sling/auth/oauth_client/InMemoryOAuthTokenStore.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.sling.auth.oauth_client; + +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; + +import org.apache.sling.api.resource.ResourceResolver; + +/** + * In-memory, volatile token store implementation + * + * <p>This implementation exists for testing purposes only</p> + */ +public class InMemoryOAuthTokenStore implements OAuthTokenStore { + + record Key(String connectionName, String userId) {} + + record Value(OAuthTokens tokens, Instant expires) { + + public Value(OAuthTokens tokens) { + this(tokens, tokens.expiresAt() != 0 ? Instant.now().plusSeconds(tokens.expiresAt()) : null); + } + + public boolean isValid() { + return expires == null || expires.isAfter(Instant.now()); + } + + } + + private final Map<Key, Value> storage = new HashMap<>(); + + @Override + public void persistTokens(ClientConnection connection, ResourceResolver resolver, OAuthTokens tokens) + throws OAuthException { + storage.put(new Key(connection.name(), resolver.getUserID()), new Value(tokens)); + } + + @Override + public OAuthToken getRefreshToken(ClientConnection connection, ResourceResolver resolver) throws OAuthException { + Value value = storage.get(new Key(connection.name(), resolver.getUserID())); + if (value == null || value.tokens == null || value.tokens.refreshToken() == null) + return new OAuthToken(TokenState.MISSING, null); + + return new OAuthToken(TokenState.VALID, value.tokens.refreshToken()); + } + + @Override + public OAuthToken getAccessToken(ClientConnection connection, ResourceResolver resolver) throws OAuthException { + Value value = storage.get(new Key(connection.name(), resolver.getUserID())); + if (value == null || value.tokens == null || value.tokens.accessToken() == null ) + return new OAuthToken(TokenState.MISSING, null); + + if (!value.isValid()) + return new OAuthToken(TokenState.EXPIRED, value.tokens.accessToken()); + + return new OAuthToken(TokenState.VALID, value.tokens.accessToken()); + + } + + @Override + public void clearAccessToken(ClientConnection connection, ResourceResolver resolver) throws OAuthException { + Key key = new Key(connection.name(), resolver.getUserID()); + Value value = storage.get(key); + + // preserve the refresh token is present + if ( value != null && value.tokens != null && value.tokens.refreshToken() != null ) { + OAuthTokens newTokens = new OAuthTokens(null, 0, value.tokens.refreshToken()); + storage.put(key, new Value(newTokens)); + // remover all tokens if only the access token is present + } else if ( value != null ) { + storage.remove(key); + } + } + + public Stream<OAuthTokens> allTokens() { + return storage.values().stream().map(Value::tokens); + } +} \ No newline at end of file diff --git a/src/test/java/org/apache/sling/auth/oauth_client/impl/InMemoryOAuthTokenStoreTest.java b/src/test/java/org/apache/sling/auth/oauth_client/impl/InMemoryOAuthTokenStoreTest.java new file mode 100644 index 0000000..3f6a7af --- /dev/null +++ b/src/test/java/org/apache/sling/auth/oauth_client/impl/InMemoryOAuthTokenStoreTest.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.sling.auth.oauth_client.impl; + +import org.apache.sling.auth.oauth_client.InMemoryOAuthTokenStore; +import org.apache.sling.testing.mock.sling.ResourceResolverType; +import org.apache.sling.testing.mock.sling.junit5.SlingContext; +import org.testcontainers.junit.jupiter.Testcontainers; + +@Testcontainers +public class InMemoryOAuthTokenStoreTest extends TokenStoreTestSupport<InMemoryOAuthTokenStore> { + + + InMemoryOAuthTokenStoreTest() { + super(MockOidcConnection.DEFAULT_CONNECTION, new SlingContext(ResourceResolverType.JCR_MOCK)); + } + + @Override + InMemoryOAuthTokenStore createTokenStore() { + return new InMemoryOAuthTokenStore(); + } +} diff --git a/src/test/java/org/apache/sling/auth/oauth_client/impl/OAuthCallbackServletTest.java b/src/test/java/org/apache/sling/auth/oauth_client/impl/OAuthCallbackServletTest.java index 807b423..f603fdc 100644 --- a/src/test/java/org/apache/sling/auth/oauth_client/impl/OAuthCallbackServletTest.java +++ b/src/test/java/org/apache/sling/auth/oauth_client/impl/OAuthCallbackServletTest.java @@ -32,15 +32,9 @@ import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletResponse; -import org.apache.sling.api.resource.ResourceResolver; import org.apache.sling.auth.oauth_client.ClientConnection; -import org.apache.sling.auth.oauth_client.OAuthException; -import org.apache.sling.auth.oauth_client.OAuthToken; -import org.apache.sling.auth.oauth_client.OAuthTokenStore; +import org.apache.sling.auth.oauth_client.InMemoryOAuthTokenStore; import org.apache.sling.auth.oauth_client.OAuthTokens; -import org.apache.sling.auth.oauth_client.impl.OAuthCallbackException; -import org.apache.sling.auth.oauth_client.impl.OAuthCallbackServlet; -import org.apache.sling.auth.oauth_client.impl.OAuthStateManager; import org.apache.sling.testing.mock.sling.junit5.SlingContext; import org.apache.sling.testing.mock.sling.junit5.SlingContextExtension; import org.junit.jupiter.api.AfterEach; @@ -52,39 +46,6 @@ import com.sun.net.httpserver.HttpServer; @ExtendWith(SlingContextExtension.class) class OAuthCallbackServletTest { - - static final class StubOAuthTokenStore implements OAuthTokenStore { - - private OAuthTokens tokens; - - @Override - public void persistTokens(ClientConnection connection, ResourceResolver resolver, OAuthTokens tokens) - throws OAuthException { - if ( this.tokens != null ) - throw new IllegalStateException("Tokens already set once"); - this.tokens = tokens; - - } - - @Override - public OAuthToken getRefreshToken(ClientConnection connection, ResourceResolver resolver) throws OAuthException { - throw new IllegalStateException("Not implemented"); - } - - @Override - public OAuthToken getAccessToken(ClientConnection connection, ResourceResolver resolver) throws OAuthException { - throw new IllegalStateException("Not implemented"); - } - - @Override - public void clearAccessToken(ClientConnection connection, ResourceResolver resolver) throws OAuthException { - throw new IllegalStateException("Not implemented"); - } - - public OAuthTokens getTokens() { - return tokens; - } - } private static final String MOCK_OIDC_PARAM = "mock-oidc-param"; @@ -94,7 +55,7 @@ class OAuthCallbackServletTest { private HttpServer tokenEndpointServer; - private StubOAuthTokenStore tokenStore; + private InMemoryOAuthTokenStore tokenStore; private OAuthCallbackServlet servlet; @@ -113,7 +74,7 @@ class OAuthCallbackServletTest { ) ); - tokenStore = new StubOAuthTokenStore(); + tokenStore = new InMemoryOAuthTokenStore(); servlet = new OAuthCallbackServlet(connections, tokenStore, new StubOAuthStateManager()); } @@ -255,7 +216,9 @@ class OAuthCallbackServletTest { servlet.service(context.request(), context.response()); - assertThat(tokenStore.getTokens()) + assertThat(tokenStore.allTokens()) + .hasSize(1) + .element(0) .isNotNull() .extracting( OAuthTokens::accessToken ) .isEqualTo("Token");
