You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2024/09/19 14:54:03 UTC
(sling-whiteboard) branch master updated (ac030337 -> 866f4b66)
This is an automated email from the ASF dual-hosted git repository.
rombert pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/sling-whiteboard.git
from ac030337 oidc-rp: tweaks around bundle name and description
new fdb443d1 oidc-rp: add a redis token store
new 866f4b66 oidc-rp: return latest access token after a refresh
The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
org.apache.sling.servlets.oidc-rp/pom.xml | 7 ++
.../src/main/features/main.json | 16 +++
.../impl/JcrUserHomeOAuthTokenStore.java | 5 +-
.../oauth_client/impl/RedisOAuthTokenStore.java | 116 +++++++++++++++++++++
.../support/OAuthEnabledSlingServlet.java | 2 +-
.../servlets/oidc_rp/AuthorizationCodeFlowIT.java | 16 ++-
6 files changed, 158 insertions(+), 4 deletions(-)
create mode 100644 org.apache.sling.servlets.oidc-rp/src/main/java/org/apache/sling/extensions/oauth_client/impl/RedisOAuthTokenStore.java
(sling-whiteboard) 01/02: oidc-rp: add a redis token store
Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
rombert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-whiteboard.git
commit fdb443d1c5f0621a7f650715b38ed17b4a742fff
Author: Robert Munteanu <ro...@apache.org>
AuthorDate: Thu Sep 19 16:49:42 2024 +0200
oidc-rp: add a redis token store
Also require a configuration for token stores to make sure only one is configured.
---
org.apache.sling.servlets.oidc-rp/pom.xml | 7 ++
.../src/main/features/main.json | 16 +++
.../impl/JcrUserHomeOAuthTokenStore.java | 5 +-
.../oauth_client/impl/RedisOAuthTokenStore.java | 116 +++++++++++++++++++++
.../servlets/oidc_rp/AuthorizationCodeFlowIT.java | 16 ++-
5 files changed, 157 insertions(+), 3 deletions(-)
diff --git a/org.apache.sling.servlets.oidc-rp/pom.xml b/org.apache.sling.servlets.oidc-rp/pom.xml
index 03bc0e73..ee60ff7b 100644
--- a/org.apache.sling.servlets.oidc-rp/pom.xml
+++ b/org.apache.sling.servlets.oidc-rp/pom.xml
@@ -293,6 +293,13 @@
<version>9.35</version>
<scope>provided</scope>
</dependency>
+ <!-- Redis persistence support-->
+ <dependency>
+ <groupId>redis.clients</groupId>
+ <artifactId>jedis</artifactId>
+ <version>5.0.0</version>
+ <scope>provided</scope>
+ </dependency>
<!-- test dependencies -->
<dependency>
diff --git a/org.apache.sling.servlets.oidc-rp/src/main/features/main.json b/org.apache.sling.servlets.oidc-rp/src/main/features/main.json
index 2b7ba41c..0113b82c 100644
--- a/org.apache.sling.servlets.oidc-rp/src/main/features/main.json
+++ b/org.apache.sling.servlets.oidc-rp/src/main/features/main.json
@@ -27,6 +27,22 @@
{
"id":"net.minidev:accessors-smart:2.4.8",
"start-order": 24
+ },
+ {
+ "id":"redis.clients:jedis:5.1.5",
+ "start-order": 24
+ },
+ {
+ "id": "org.apache.commons:commons-pool2:2.12.0",
+ "start-order": 24
+ },
+ {
+ "id": "com.google.code.gson:gson:2.11.0",
+ "start-order": 24
+ },
+ {
+ "id": "org.json:json:20231013",
+ "start-order": 24
}
]
}
\ No newline at end of file
diff --git a/org.apache.sling.servlets.oidc-rp/src/main/java/org/apache/sling/extensions/oauth_client/impl/JcrUserHomeOAuthTokenStore.java b/org.apache.sling.servlets.oidc-rp/src/main/java/org/apache/sling/extensions/oauth_client/impl/JcrUserHomeOAuthTokenStore.java
index 0f4d4e11..30ec0ba1 100644
--- a/org.apache.sling.servlets.oidc-rp/src/main/java/org/apache/sling/extensions/oauth_client/impl/JcrUserHomeOAuthTokenStore.java
+++ b/org.apache.sling.servlets.oidc-rp/src/main/java/org/apache/sling/extensions/oauth_client/impl/JcrUserHomeOAuthTokenStore.java
@@ -16,6 +16,8 @@
*/
package org.apache.sling.extensions.oauth_client.impl;
+import static org.osgi.service.component.annotations.ConfigurationPolicy.REQUIRE;
+
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.GregorianCalendar;
@@ -37,7 +39,8 @@ import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-@Component(service = { OAuthTokenStore.class } )
+// a config class is intentionally not defined, but a config is required to select an implementation
+@Component(configurationPolicy = REQUIRE)
public class JcrUserHomeOAuthTokenStore implements OAuthTokenStore {
private static final String PROPERTY_NAME_EXPIRES_AT = "expires_at";
diff --git a/org.apache.sling.servlets.oidc-rp/src/main/java/org/apache/sling/extensions/oauth_client/impl/RedisOAuthTokenStore.java b/org.apache.sling.servlets.oidc-rp/src/main/java/org/apache/sling/extensions/oauth_client/impl/RedisOAuthTokenStore.java
new file mode 100644
index 00000000..882becd7
--- /dev/null
+++ b/org.apache.sling.servlets.oidc-rp/src/main/java/org/apache/sling/extensions/oauth_client/impl/RedisOAuthTokenStore.java
@@ -0,0 +1,116 @@
+/*
+ * 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.extensions.oauth_client.impl;
+
+import static org.osgi.service.component.annotations.ConfigurationPolicy.REQUIRE;
+
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.extensions.oauth_client.OAuthException;
+import org.apache.sling.extensions.oauth_client.OAuthToken;
+import org.apache.sling.extensions.oauth_client.OAuthTokenStore;
+import org.apache.sling.extensions.oauth_client.OAuthTokens;
+import org.apache.sling.extensions.oauth_client.OidcConnection;
+import org.apache.sling.extensions.oauth_client.TokenState;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.Designate;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+
+import redis.clients.jedis.Jedis;
+import redis.clients.jedis.JedisPool;
+
+@Component(configurationPolicy = REQUIRE)
+@Designate(ocd = RedisOAuthTokenStore.Config.class)
+public class RedisOAuthTokenStore implements OAuthTokenStore {
+
+ @ObjectClassDefinition(name = "Redis OAuth Token Store")
+ static @interface Config {
+ @AttributeDefinition(name = "Redis URL")
+ String redisUrl();
+ }
+
+ private static final String KEY_PREFIX = "sling.oauth.tokens";
+
+ private static final String KEY_SEGMENT_ACCESS_TOKEN = "access_token";
+ private static final String KEY_SEGMENT_REFRESH_TOKEN = "refresh_token";
+
+ private final JedisPool pool;
+
+ @Activate
+ public RedisOAuthTokenStore(Config cfg) {
+ pool = new JedisPool(cfg.redisUrl());
+ }
+
+ public void deactivate() {
+ pool.close();
+ }
+
+ @Override
+ public OAuthToken getAccessToken(OidcConnection connection, ResourceResolver resolver) throws OAuthException {
+ String userId = resolver.getUserID();
+
+ try ( Jedis jedis = pool.getResource()) {
+ String accessToken = jedis.get(keyFor(userId, KEY_SEGMENT_ACCESS_TOKEN));
+ if ( accessToken != null ) {
+ return new OAuthToken(TokenState.VALID , accessToken);
+ }
+
+ String refreshToken = jedis.get(keyFor(userId, KEY_SEGMENT_REFRESH_TOKEN));
+ if ( refreshToken != null ) {
+ return new OAuthToken(TokenState.EXPIRED, null);
+ }
+
+ return new OAuthToken(TokenState.MISSING, null);
+ }
+ }
+
+ @Override
+ public OAuthToken getRefreshToken(OidcConnection connection, ResourceResolver resolver) throws OAuthException {
+ String userId = resolver.getUserID();
+
+ try (Jedis jedis = pool.getResource()) {
+ String refreshToken = jedis.get(keyFor(userId, KEY_SEGMENT_REFRESH_TOKEN));
+ if (refreshToken != null) {
+ return new OAuthToken(TokenState.VALID, refreshToken);
+ }
+
+ return new OAuthToken(TokenState.MISSING, null);
+ }
+ }
+
+ @Override
+ public void persistTokens(OidcConnection connection, ResourceResolver resolver, OAuthTokens tokens)
+ throws OAuthException {
+ String userId = resolver.getUserID();
+
+ try (Jedis jedis = pool.getResource()) {
+ setWithExpiry(jedis, keyFor(userId, KEY_SEGMENT_ACCESS_TOKEN), tokens.accessToken(), tokens.expiresAt() );
+ if ( tokens.refreshToken() != null )
+ jedis.set(keyFor(userId, KEY_SEGMENT_REFRESH_TOKEN), tokens.refreshToken());
+ }
+ }
+
+ private void setWithExpiry(Jedis jedis, String key, String value, long expiry) {
+ jedis.set(key, value);
+ jedis.expire(key, expiry);
+ }
+
+ private String keyFor(String principal, String tokenType) {
+ return KEY_PREFIX + "." + principal + "." + tokenType;
+ }
+}
diff --git a/org.apache.sling.servlets.oidc-rp/src/test/java/org/apache/sling/servlets/oidc_rp/AuthorizationCodeFlowIT.java b/org.apache.sling.servlets.oidc-rp/src/test/java/org/apache/sling/servlets/oidc_rp/AuthorizationCodeFlowIT.java
index e5554386..20bb06bc 100644
--- a/org.apache.sling.servlets.oidc-rp/src/test/java/org/apache/sling/servlets/oidc_rp/AuthorizationCodeFlowIT.java
+++ b/org.apache.sling.servlets.oidc-rp/src/test/java/org/apache/sling/servlets/oidc_rp/AuthorizationCodeFlowIT.java
@@ -37,11 +37,11 @@ import java.util.stream.Stream;
import org.apache.http.Header;
import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
+import org.apache.sling.extensions.oauth_client.impl.JcrUserHomeOAuthTokenStore;
import org.apache.sling.extensions.oauth_client.impl.OidcConnectionImpl;
import org.apache.sling.testing.clients.ClientException;
import org.apache.sling.testing.clients.SlingClient;
import org.apache.sling.testing.clients.SlingHttpResponse;
-import org.apache.sling.testing.clients.exceptions.TestingValidationException;
import org.apache.sling.testing.clients.osgi.OsgiConsoleClient;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
@@ -64,6 +64,8 @@ class AuthorizationCodeFlowIT {
private String oidcOsgiConfigPid;
+ private String tokenStoreOsgiConfigPid;
+
@BeforeEach
@SuppressWarnings("resource")
void initKeycloak() {
@@ -98,17 +100,26 @@ class AuthorizationCodeFlowIT {
}
@AfterEach
- void cleanupSlingOidcConfig() throws TestingValidationException, ClientException {
+ void cleanupOsgiConfigs() throws ClientException {
// the Sling testing clients do not offer a way of listing configurations, as assigned PIDs
// are not predictable. So instead of running deleting test configs when the test starts
// we fall back to cleaning after, which is hopefully reliable enough
if ( oidcOsgiConfigPid != null )
sling.adaptTo(OsgiConsoleClient.class).deleteConfiguration(oidcOsgiConfigPid);
+ if ( tokenStoreOsgiConfigPid != null )
+ sling.adaptTo(OsgiConsoleClient.class).deleteConfiguration(tokenStoreOsgiConfigPid);
}
@Test
void accessTokenIsPresentOnSuccessfulLogin() throws Exception {
+
+ // configure token store
+ tokenStoreOsgiConfigPid = sling.adaptTo(OsgiConsoleClient.class).editConfiguration(JcrUserHomeOAuthTokenStore.class.getName(), null,
+ Map.of(
+ "unused", "unused"
+ )
+ );
String oidcConnectionName = "keycloak";
@@ -122,6 +133,7 @@ class AuthorizationCodeFlowIT {
"scopes", "openid"
)
);
+
// clean up any existing tokens
String userPath = getUserPath(sling, sling.getUser());
(sling-whiteboard) 02/02: oidc-rp: return latest access token after a refresh
Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
rombert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-whiteboard.git
commit 866f4b662b457a6a78fe3e1223f8c3d835629e4c
Author: Robert Munteanu <ro...@apache.org>
AuthorDate: Thu Sep 19 16:53:31 2024 +0200
oidc-rp: return latest access token after a refresh
---
.../sling/extensions/oauth_client/support/OAuthEnabledSlingServlet.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/org.apache.sling.servlets.oidc-rp/src/main/java/org/apache/sling/extensions/oauth_client/support/OAuthEnabledSlingServlet.java b/org.apache.sling.servlets.oidc-rp/src/main/java/org/apache/sling/extensions/oauth_client/support/OAuthEnabledSlingServlet.java
index 06f1b8ee..33fe1030 100644
--- a/org.apache.sling.servlets.oidc-rp/src/main/java/org/apache/sling/extensions/oauth_client/support/OAuthEnabledSlingServlet.java
+++ b/org.apache.sling.servlets.oidc-rp/src/main/java/org/apache/sling/extensions/oauth_client/support/OAuthEnabledSlingServlet.java
@@ -86,7 +86,7 @@ public abstract class OAuthEnabledSlingServlet extends SlingSafeMethodsServlet {
OAuthTokens oidcTokens = oidcClient.refreshTokens(connection, refreshToken.getValue());
tokenStore.persistTokens(connection, request.getResourceResolver(), oidcTokens);
- doGetWithToken(request, response, tokenResponse);
+ doGetWithToken(request, response, new OAuthToken(TokenState.VALID, oidcTokens.accessToken()));
break;
}
}