This is an automated email from the ASF dual-hosted git repository.
lmccay pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/knox.git
The following commit(s) were added to refs/heads/master by this push:
new ccfb00c3c KNOX-3129 - Add expiry to APIKEY and CLIENTID API Responses
(#1022)
ccfb00c3c is described below
commit ccfb00c3ce337b801ffa111c2a16c3381d82e591
Author: lmccay <[email protected]>
AuthorDate: Mon Apr 14 18:48:58 2025 -0400
KNOX-3129 - Add expiry to APIKEY and CLIENTID API Responses (#1022)
---
.../gateway/service/knoxtoken/APIKeyResource.java | 1 +
.../knoxtoken/ClientCredentialsResource.java | 1 +
.../gateway/service/knoxtoken/OAuthResource.java | 43 +++++--------------
.../knoxtoken/PasscodeTokenResourceBase.java | 50 ++++++++++++++++++++--
.../knoxtoken/TokenServiceResourceTest.java | 32 +++++++++++++-
5 files changed, 90 insertions(+), 37 deletions(-)
diff --git
a/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/APIKeyResource.java
b/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/APIKeyResource.java
index b29750890..eb8312013 100644
---
a/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/APIKeyResource.java
+++
b/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/APIKeyResource.java
@@ -76,6 +76,7 @@ public class APIKeyResource extends PasscodeTokenResourceBase
{
final HashMap<String, Object> map = new HashMap<>();
map.put(KEY_ID, tokenId);
map.put(API_KEY, passcode);
+ addExpiryIfNotNever(map);
String jsonResponse = JsonUtils.renderAsJsonString(map);
return resp.responseBuilder.entity(jsonResponse).build();
}
diff --git
a/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/ClientCredentialsResource.java
b/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/ClientCredentialsResource.java
index 63bfd7f4d..9b7e98d57 100644
---
a/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/ClientCredentialsResource.java
+++
b/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/ClientCredentialsResource.java
@@ -75,6 +75,7 @@ public class ClientCredentialsResource extends
PasscodeTokenResourceBase {
final HashMap<String, Object> map = new HashMap<>();
map.put(CLIENT_ID, tokenId);
map.put(CLIENT_SECRET, passcode);
+ addExpiryIfNotNever(map);
String jsonResponse = JsonUtils.renderAsJsonString(map);
return resp.responseBuilder.entity(jsonResponse).build();
}
diff --git
a/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/OAuthResource.java
b/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/OAuthResource.java
index 4064092b5..e2f7239a4 100644
---
a/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/OAuthResource.java
+++
b/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/OAuthResource.java
@@ -17,18 +17,16 @@
*/
package org.apache.knox.gateway.service.knoxtoken;
-import org.apache.knox.gateway.i18n.messages.MessagesFactory;
import org.apache.knox.gateway.util.JsonUtils;
import javax.inject.Singleton;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;
-
-import java.time.Duration;
-import java.time.format.DateTimeParseException;
import java.util.HashMap;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
@@ -36,8 +34,7 @@ import static javax.ws.rs.core.MediaType.APPLICATION_XML;
@Singleton
@Path(OAuthResource.RESOURCE_PATH)
-public class OAuthResource extends TokenResource {
- private static TokenServiceMessages log =
MessagesFactory.get(TokenServiceMessages.class);
+public class OAuthResource extends PasscodeTokenResourceBase {
static final String RESOURCE_PATH =
"/{serviceName:.*}/v1/{oauthSegment:(oauth|token)}{path:(/tokens)?}";
public static final String ISSUED_TOKEN_TYPE = "issued_token_type";
public static final String REFRESH_TOKEN = "refresh_token";
@@ -57,6 +54,13 @@ public class OAuthResource extends TokenResource {
return super.doPost();
}
+ @Override
+ protected ServletContext wrapContextForDefaultParams(ServletContext
context) throws ServletException {
+ ServletContext wrapperContext = new ServletContextWrapper(context);
+ setupTokenStateService(wrapperContext);
+ return wrapperContext;
+ }
+
@Override
public Response getAuthenticationToken() {
@@ -101,31 +105,4 @@ public class OAuthResource extends TokenResource {
return resp.responseBuilder.build();
}
}
-
- private long getTokenLifetimeInSeconds() {
- long secs = tokenTTL/1000;
-
- String lifetimeStr = request.getParameter(LIFESPAN);
- if (lifetimeStr == null || lifetimeStr.isEmpty()) {
- if (tokenTTL == -1) {
- return -1;
- }
- }
- else {
- try {
- long lifetime = Duration.parse(lifetimeStr).toMillis()/1000;
- if (tokenTTL == -1) {
- // if TTL is set to -1 the topology owner grants unlimited
lifetime therefore no additional check is needed on lifespan
- secs = lifetime;
- } else if (lifetime <= tokenTTL/1000) {
- //this is expected due to security reasons: the configured
TTL acts as an upper limit regardless of the supplied lifespan
- secs = lifetime;
- }
- }
- catch (DateTimeParseException e) {
- log.invalidLifetimeValue(lifetimeStr);
- }
- }
- return secs;
- }
}
diff --git
a/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/PasscodeTokenResourceBase.java
b/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/PasscodeTokenResourceBase.java
index 6804fbf24..63b43661c 100644
---
a/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/PasscodeTokenResourceBase.java
+++
b/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/PasscodeTokenResourceBase.java
@@ -22,6 +22,7 @@ import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.jwk.OctetSequenceKey;
import com.nimbusds.jose.jwk.gen.OctetSequenceKeyGenerator;
import org.apache.knox.gateway.config.GatewayConfig;
+import org.apache.knox.gateway.i18n.messages.MessagesFactory;
import org.apache.knox.gateway.services.GatewayServices;
import org.apache.knox.gateway.services.ServiceType;
import org.apache.knox.gateway.services.security.AliasService;
@@ -32,15 +33,32 @@ import
org.apache.knox.gateway.services.security.token.impl.TokenMAC;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.ws.rs.core.Response;
+import java.time.Duration;
+import java.time.format.DateTimeParseException;
+import java.util.Map;
import java.util.UUID;
public class PasscodeTokenResourceBase extends TokenResource {
protected GatewayServices services;
+ private static TokenServiceMessages log =
MessagesFactory.get(TokenServiceMessages.class);
+
+ protected void addExpiryIfNotNever(Map<String, Object> map) {
+ long expiresIn = getTokenLifetimeInSeconds();
+ if (expiresIn != -1) {
+ map.put(EXPIRES_IN, expiresIn);
+ }
+ }
+
@Override
protected ServletContext wrapContextForDefaultParams(ServletContext
context) throws ServletException {
ServletContext wrapperContext = new ServletContextWrapper(context);
-
wrapperContext.setInitParameter(TokenStateService.CONFIG_SERVER_MANAGED,
"true");
wrapperContext.setInitParameter(TokenResource.TOKEN_TTL_PARAM, "-1");
+ setupTokenStateService(wrapperContext);
+ return wrapperContext;
+ }
+
+ protected void setupTokenStateService(ServletContext wrapperContext)
throws ServletException {
+
wrapperContext.setInitParameter(TokenStateService.CONFIG_SERVER_MANAGED,
"true");
services = (GatewayServices)
wrapperContext.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE);
tokenStateService =
services.getService(ServiceType.TOKEN_STATE_SERVICE);
final GatewayConfig gatewayConfig = (GatewayConfig)
wrapperContext.getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE);
@@ -55,10 +73,9 @@ public class PasscodeTokenResourceBase extends TokenResource
{
if (hashkey == null) {
generateAndStoreHMACKeyAlias();
}
- return wrapperContext;
}
- private void generateAndStoreHMACKeyAlias() {
+ protected void generateAndStoreHMACKeyAlias() {
final int keyLength =
Integer.parseInt(JWSAlgorithm.HS256.getName().substring(2));
String jwkAsText = null;
try {
@@ -87,4 +104,31 @@ public class PasscodeTokenResourceBase extends
TokenResource {
return response;
}
+
+ protected long getTokenLifetimeInSeconds() {
+ long secs = tokenTTL/1000;
+
+ String lifetimeStr = request.getParameter(LIFESPAN);
+ if (lifetimeStr == null || lifetimeStr.isEmpty()) {
+ if (tokenTTL == -1) {
+ return -1;
+ }
+ }
+ else {
+ try {
+ long lifetime = Duration.parse(lifetimeStr).toMillis()/1000;
+ if (tokenTTL == -1) {
+ // if TTL is set to -1 the topology owner grants unlimited
lifetime therefore no additional check is needed on lifespan
+ secs = lifetime;
+ } else if (lifetime <= tokenTTL/1000) {
+ //this is expected due to security reasons: the configured
TTL acts as an upper limit regardless of the supplied lifespan
+ secs = lifetime;
+ }
+ }
+ catch (DateTimeParseException e) {
+ log.invalidLifetimeValue(lifetimeStr);
+ }
+ }
+ return secs;
+ }
}
diff --git
a/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java
b/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java
index 9613b28b9..2214d365d 100644
---
a/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java
+++
b/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java
@@ -1321,11 +1321,18 @@ public class TokenServiceResourceTest {
@Test
public void testClientCredentialsResponse() throws Exception {
+ tryClientCredentialsResource(null);
+ tryClientCredentialsResource("30000");
+ }
+
+ public void tryClientCredentialsResource(String expiryInMs) throws Exception
{
Map<String, String> contextExpectations = new HashMap<>();
try {
tss = new PersistentTestTokenStateService();
+ if (expiryInMs != null) {
+ contextExpectations.put("knox.token.ttl", expiryInMs);
+ }
configureCommonExpectations(contextExpectations, Boolean.TRUE);
-
ClientCredentialsResource ccr = new ClientCredentialsResource();
ccr.request = request;
ccr.context = context;
@@ -1338,6 +1345,13 @@ public class TokenServiceResourceTest {
assertNotNull(clientId);
String clientSecret = getTagValue(response.getEntity().toString(),
ClientCredentialsResource.CLIENT_SECRET);
assertNotNull(clientSecret);
+ String expiresIn = getTagValue(response.getEntity().toString(),
APIKeyResource.EXPIRES_IN);
+ if (expiryInMs == null) {
+ assertNull(expiresIn);
+ } else {
+ assertNotNull(expiresIn);
+ assertEquals(30, Integer.parseInt(expiresIn));
+ }
} finally {
tss = new TestTokenStateService();
}
@@ -1421,9 +1435,18 @@ public class TokenServiceResourceTest {
@Test
public void testAPIKeyResponse() throws Exception {
+ tryAPIKeyResponse(null);
+ tryAPIKeyResponse("30000");
+ }
+
+ public void tryAPIKeyResponse(String expiryInMs) throws Exception {
Map<String, String> contextExpectations = new HashMap<>();
try {
tss = new PersistentTestTokenStateService();
+ // let the default built into
+ if (expiryInMs != null) {
+ contextExpectations.put("knox.token.ttl", expiryInMs);
+ }
configureCommonExpectations(contextExpectations, Boolean.TRUE);
APIKeyResource ccr = new APIKeyResource();
@@ -1438,6 +1461,13 @@ public class TokenServiceResourceTest {
assertNotNull(keyId);
String apikey = getTagValue(response.getEntity().toString(),
APIKeyResource.API_KEY);
assertNotNull(apikey);
+ String expiresIn = getTagValue(response.getEntity().toString(),
APIKeyResource.EXPIRES_IN);
+ if (expiryInMs == null) {
+ assertNull(expiresIn);
+ } else {
+ assertNotNull(expiresIn);
+ assertEquals(30, Integer.parseInt(expiresIn));
+ }
} finally {
tss = new TestTokenStateService();
}