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 c2c029055 KNOX-3120 - Add a specialized use API for API KEY based on 
KNOXTOKEN API (#1019)
c2c029055 is described below

commit c2c029055c5a8253dd766c655b836ca551afa7c2
Author: lmccay <[email protected]>
AuthorDate: Thu Apr 10 09:17:28 2025 -0400

    KNOX-3120 - Add a specialized use API for API KEY based on KNOXTOKEN API 
(#1019)
---
 .../gateway/service/knoxtoken/APIKeyResource.java  | 89 ++++++++++++++++++++++
 .../knoxtoken/ClientCredentialsResource.java       | 71 ++---------------
 ...esource.java => PasscodeTokenResourceBase.java} | 69 ++---------------
 .../deploy/APIKeyServiceDeploymentContributor.java | 45 +++++++++++
 ...nox.gateway.deploy.ServiceDeploymentContributor |  3 +-
 .../knoxtoken/TokenServiceResourceTest.java        | 36 +++++++--
 .../services/security/token/TokenMetadataType.java |  2 +-
 7 files changed, 177 insertions(+), 138 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
new file mode 100644
index 000000000..b29750890
--- /dev/null
+++ 
b/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/APIKeyResource.java
@@ -0,0 +1,89 @@
+/*
+ * 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.knox.gateway.service.knoxtoken;
+
+import org.apache.knox.gateway.services.security.token.TokenMetadata;
+import org.apache.knox.gateway.services.security.token.TokenMetadataType;
+import org.apache.knox.gateway.util.JsonUtils;
+
+import javax.inject.Singleton;
+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.util.HashMap;
+
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+import static javax.ws.rs.core.MediaType.APPLICATION_XML;
+
+@Path(APIKeyResource.RESOURCE_PATH)
+@Singleton
+public class APIKeyResource extends PasscodeTokenResourceBase {
+    private static final String TYPE = "type";
+    public static final String RESOURCE_PATH = "apikey/api/v1/auth/key";
+    public static final String API_KEY = "api_key";
+    public static final String KEY_ID = "key_id";
+
+    @Override
+    @GET
+    @Produces({ APPLICATION_JSON, APPLICATION_XML })
+    public Response doGet() {
+        return super.doGet();
+    }
+
+    @Override
+    @POST
+    @Produces({ APPLICATION_JSON, APPLICATION_XML })
+    public Response doPost() {
+        return super.doPost();
+    }
+
+    @Override
+    protected void addArbitraryTokenMetadata(TokenMetadata tokenMetadata) {
+        tokenMetadata.add(TYPE, TokenMetadataType.API_KEY.name());
+        super.addArbitraryTokenMetadata(tokenMetadata);
+    }
+
+    @Override
+    public Response getAuthenticationToken() {
+        UserContext context = buildUserContext(request);
+        Response response = checkForInvalidRequestResponse(context);
+        if (response != null) {
+            return response;
+        }
+
+        TokenResponseContext resp = getTokenResponse (context);
+        if (resp.responseMap != null) {
+            String passcode = (String) resp.responseMap.map.get(PASSCODE);
+            String tokenId = resp.responseMap.tokenId;
+
+            final HashMap<String, Object> map = new HashMap<>();
+            map.put(KEY_ID, tokenId);
+            map.put(API_KEY, passcode);
+            String jsonResponse = JsonUtils.renderAsJsonString(map);
+            return resp.responseBuilder.entity(jsonResponse).build();
+        }
+
+        if (resp.responseStr != null) {
+            return resp.responseBuilder.entity(resp.responseStr).build();
+        } else {
+            return resp.responseBuilder.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 6def33dff..63bfd7f4d 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
@@ -17,84 +17,29 @@
  */
 package org.apache.knox.gateway.service.knoxtoken;
 
-import com.nimbusds.jose.JOSEException;
-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.services.GatewayServices;
-import org.apache.knox.gateway.services.ServiceType;
-import org.apache.knox.gateway.services.security.AliasService;
-import org.apache.knox.gateway.services.security.AliasServiceException;
 import org.apache.knox.gateway.services.security.token.TokenMetadata;
 import org.apache.knox.gateway.services.security.token.TokenMetadataType;
-import org.apache.knox.gateway.services.security.token.TokenStateService;
-import org.apache.knox.gateway.services.security.token.impl.TokenMAC;
 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.util.HashMap;
-import java.util.UUID;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 import static javax.ws.rs.core.MediaType.APPLICATION_XML;
 
 @Path(ClientCredentialsResource.RESOURCE_PATH)
 @Singleton
-public class ClientCredentialsResource extends TokenResource {
+public class ClientCredentialsResource extends PasscodeTokenResourceBase {
     private static final String TYPE = "type";
     public static final String RESOURCE_PATH = 
"clientid/api/v1/oauth/credentials";
     public static final String CLIENT_ID = "client_id";
     public static final String CLIENT_SECRET = "client_secret";
 
-    private GatewayServices services;
-
-    @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");
-        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);
-        gatewayConfig.getKnoxTokenHashAlgorithm();
-        final AliasService aliasService = 
services.getService(ServiceType.ALIAS_SERVICE);
-        char[] hashkey = new char[0];
-        try {
-            hashkey = 
aliasService.getPasswordFromAliasForGateway(TokenMAC.KNOX_TOKEN_HASH_KEY_ALIAS_NAME);
-        } catch (AliasServiceException e) {
-            throw new ServletException(e);
-        }
-        if (hashkey == null) {
-            generateAndStoreHMACKeyAlias();
-        }
-        return wrapperContext;
-    }
-
-    private void generateAndStoreHMACKeyAlias() {
-        final int keyLength = 
Integer.parseInt(JWSAlgorithm.HS256.getName().substring(2));
-        String jwkAsText = null;
-        try {
-            final OctetSequenceKey jwk = new 
OctetSequenceKeyGenerator(keyLength).keyID(UUID.randomUUID().toString())
-                    .algorithm(JWSAlgorithm.HS256).generate();
-            jwkAsText = jwk.getKeyValue().toJSONString().replace("\"", "");
-            getAliasService().addAliasForCluster("__gateway", 
TokenMAC.KNOX_TOKEN_HASH_KEY_ALIAS_NAME, jwkAsText);
-        } catch (JOSEException | AliasServiceException e) {
-            throw new RuntimeException("Error while generating " + keyLength + 
" bits JWK secret", e);
-        }
-    }
-
-    protected AliasService getAliasService() {
-        return services.getService(ServiceType.ALIAS_SERVICE);
-    }
-
     @Override
     @GET
     @Produces({ APPLICATION_JSON, APPLICATION_XML })
@@ -117,17 +62,11 @@ public class ClientCredentialsResource extends 
TokenResource {
 
     @Override
     public Response getAuthenticationToken() {
-        Response response = enforceClientCertIfRequired();
-        if (response != null) { return response; }
-
-        response = onlyAllowGroupsToBeAddedWhenEnabled();
-        if (response != null) { return response; }
-
         UserContext context = buildUserContext(request);
-
-        response = enforceTokenLimitsAsRequired(context.userName);
-        if (response != null) { return response; }
-
+        Response response = checkForInvalidRequestResponse(context);
+        if (response != null) {
+            return response;
+        }
         TokenResponseContext resp = getTokenResponse(context);
         if (resp.responseMap != null) {
             String passcode = (String) resp.responseMap.map.get(PASSCODE);
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/PasscodeTokenResourceBase.java
similarity index 63%
copy from 
gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/ClientCredentialsResource.java
copy to 
gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/PasscodeTokenResourceBase.java
index 6def33dff..6804fbf24 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/PasscodeTokenResourceBase.java
@@ -26,36 +26,16 @@ import org.apache.knox.gateway.services.GatewayServices;
 import org.apache.knox.gateway.services.ServiceType;
 import org.apache.knox.gateway.services.security.AliasService;
 import org.apache.knox.gateway.services.security.AliasServiceException;
-import org.apache.knox.gateway.services.security.token.TokenMetadata;
-import org.apache.knox.gateway.services.security.token.TokenMetadataType;
 import org.apache.knox.gateway.services.security.token.TokenStateService;
 import org.apache.knox.gateway.services.security.token.impl.TokenMAC;
-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.util.HashMap;
 import java.util.UUID;
 
-import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
-import static javax.ws.rs.core.MediaType.APPLICATION_XML;
-
-@Path(ClientCredentialsResource.RESOURCE_PATH)
-@Singleton
-public class ClientCredentialsResource extends TokenResource {
-    private static final String TYPE = "type";
-    public static final String RESOURCE_PATH = 
"clientid/api/v1/oauth/credentials";
-    public static final String CLIENT_ID = "client_id";
-    public static final String CLIENT_SECRET = "client_secret";
-
-    private GatewayServices services;
-
+public class PasscodeTokenResourceBase extends TokenResource {
+    protected GatewayServices services;
     @Override
     protected ServletContext wrapContextForDefaultParams(ServletContext 
context) throws ServletException {
         ServletContext wrapperContext = new ServletContextWrapper(context);
@@ -66,7 +46,7 @@ public class ClientCredentialsResource extends TokenResource {
         final GatewayConfig gatewayConfig = (GatewayConfig) 
wrapperContext.getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE);
         gatewayConfig.getKnoxTokenHashAlgorithm();
         final AliasService aliasService = 
services.getService(ServiceType.ALIAS_SERVICE);
-        char[] hashkey = new char[0];
+        char[] hashkey;
         try {
             hashkey = 
aliasService.getPasswordFromAliasForGateway(TokenMAC.KNOX_TOKEN_HASH_KEY_ALIAS_NAME);
         } catch (AliasServiceException e) {
@@ -95,55 +75,16 @@ public class ClientCredentialsResource extends 
TokenResource {
         return services.getService(ServiceType.ALIAS_SERVICE);
     }
 
-    @Override
-    @GET
-    @Produces({ APPLICATION_JSON, APPLICATION_XML })
-    public Response doGet() {
-        return super.doGet();
-    }
-
-    @Override
-    @POST
-    @Produces({ APPLICATION_JSON, APPLICATION_XML })
-    public Response doPost() {
-        return super.doPost();
-    }
-
-    @Override
-    protected void addArbitraryTokenMetadata(TokenMetadata tokenMetadata) {
-        tokenMetadata.add(TYPE, TokenMetadataType.CLIENT_ID.name());
-        super.addArbitraryTokenMetadata(tokenMetadata);
-    }
-
-    @Override
-    public Response getAuthenticationToken() {
+    protected Response checkForInvalidRequestResponse(UserContext context) {
         Response response = enforceClientCertIfRequired();
         if (response != null) { return response; }
 
         response = onlyAllowGroupsToBeAddedWhenEnabled();
         if (response != null) { return response; }
 
-        UserContext context = buildUserContext(request);
-
         response = enforceTokenLimitsAsRequired(context.userName);
         if (response != null) { return response; }
 
-        TokenResponseContext resp = getTokenResponse(context);
-        if (resp.responseMap != null) {
-            String passcode = (String) resp.responseMap.map.get(PASSCODE);
-            String tokenId = resp.responseMap.tokenId;
-
-            final HashMap<String, Object> map = new HashMap<>();
-            map.put(CLIENT_ID, tokenId);
-            map.put(CLIENT_SECRET, passcode);
-            String jsonResponse = JsonUtils.renderAsJsonString(map);
-            return resp.responseBuilder.entity(jsonResponse).build();
-        }
-
-        if (resp.responseStr != null) {
-            return resp.responseBuilder.entity(resp.responseStr).build();
-        } else {
-            return resp.responseBuilder.build();
-        }
+        return response;
     }
 }
diff --git 
a/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/deploy/APIKeyServiceDeploymentContributor.java
 
b/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/deploy/APIKeyServiceDeploymentContributor.java
new file mode 100644
index 000000000..ebfafa7a3
--- /dev/null
+++ 
b/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/deploy/APIKeyServiceDeploymentContributor.java
@@ -0,0 +1,45 @@
+/*
+ * 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.knox.gateway.service.knoxtoken.deploy;
+
+import org.apache.knox.gateway.jersey.JerseyServiceDeploymentContributorBase;
+
+public class APIKeyServiceDeploymentContributor extends 
JerseyServiceDeploymentContributorBase {
+
+    public static final String ROLE = "APIKEY";
+
+    @Override
+    public String getRole() {
+        return ROLE;
+    }
+
+    @Override
+    public String getName() {
+        return "APIKeyService";
+    }
+
+    @Override
+    protected String[] getPackages() {
+      return new String[]{ "org.apache.knox.gateway.service.knoxtoken" };
+    }
+
+    @Override
+    protected String[] getPatterns() {
+       return new String[]{ "apikey/api/**?**" };
+    }
+}
diff --git 
a/gateway-service-knoxtoken/src/main/resources/META-INF/services/org.apache.knox.gateway.deploy.ServiceDeploymentContributor
 
b/gateway-service-knoxtoken/src/main/resources/META-INF/services/org.apache.knox.gateway.deploy.ServiceDeploymentContributor
index 5d15627dd..2151d84f2 100644
--- 
a/gateway-service-knoxtoken/src/main/resources/META-INF/services/org.apache.knox.gateway.deploy.ServiceDeploymentContributor
+++ 
b/gateway-service-knoxtoken/src/main/resources/META-INF/services/org.apache.knox.gateway.deploy.ServiceDeploymentContributor
@@ -17,4 +17,5 @@
 ##########################################################################
 
 
org.apache.knox.gateway.service.knoxtoken.deploy.TokenServiceDeploymentContributor
-org.apache.knox.gateway.service.knoxtoken.deploy.ClientCredentialsServiceDeploymentContributor
\ No newline at end of file
+org.apache.knox.gateway.service.knoxtoken.deploy.ClientCredentialsServiceDeploymentContributor
+org.apache.knox.gateway.service.knoxtoken.deploy.APIKeyServiceDeploymentContributor
\ No newline at end of file
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 ef35b082f..9613b28b9 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
@@ -1319,6 +1319,30 @@ public class TokenServiceResourceTest {
     testPasscodeToken(false, false, false);
   }
 
+  @Test
+  public void testClientCredentialsResponse() throws Exception {
+    Map<String, String> contextExpectations = new HashMap<>();
+    try {
+      tss = new PersistentTestTokenStateService();
+      configureCommonExpectations(contextExpectations, Boolean.TRUE);
+
+      ClientCredentialsResource ccr = new ClientCredentialsResource();
+      ccr.request = request;
+      ccr.context = context;
+      ccr.init();
+
+      Response response = ccr.doPost();
+      assertEquals(200, response.getStatus());
+
+      String clientId = getTagValue(response.getEntity().toString(), 
ClientCredentialsResource.CLIENT_ID);
+      assertNotNull(clientId);
+      String clientSecret = getTagValue(response.getEntity().toString(), 
ClientCredentialsResource.CLIENT_SECRET);
+      assertNotNull(clientSecret);
+    } finally {
+      tss = new TestTokenStateService();
+    }
+  }
+
   @Test
   public void 
passcodeShouldNotBeInResponseIfTokenStateServiceIsNotPersistent() throws 
Exception {
     testPasscodeToken(true, false, false);
@@ -1396,13 +1420,13 @@ public class TokenServiceResourceTest {
   }
 
   @Test
-  public void testClientCredentialsResponse() throws Exception {
+  public void testAPIKeyResponse() throws Exception {
     Map<String, String> contextExpectations = new HashMap<>();
     try {
       tss = new PersistentTestTokenStateService();
       configureCommonExpectations(contextExpectations, Boolean.TRUE);
 
-      ClientCredentialsResource ccr = new ClientCredentialsResource();
+      APIKeyResource ccr = new APIKeyResource();
       ccr.request = request;
       ccr.context = context;
       ccr.init();
@@ -1410,10 +1434,10 @@ public class TokenServiceResourceTest {
       Response response = ccr.doPost();
       assertEquals(200, response.getStatus());
 
-      String clientId = getTagValue(response.getEntity().toString(), 
ClientCredentialsResource.CLIENT_ID);
-      assertNotNull(clientId);
-      String clientSecret = getTagValue(response.getEntity().toString(), 
ClientCredentialsResource.CLIENT_SECRET);
-      assertNotNull(clientSecret);
+      String keyId = getTagValue(response.getEntity().toString(), 
APIKeyResource.KEY_ID);
+      assertNotNull(keyId);
+      String apikey = getTagValue(response.getEntity().toString(), 
APIKeyResource.API_KEY);
+      assertNotNull(apikey);
     } finally {
       tss = new TestTokenStateService();
     }
diff --git 
a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/TokenMetadataType.java
 
b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/TokenMetadataType.java
index a4c27140b..17e82e0af 100644
--- 
a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/TokenMetadataType.java
+++ 
b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/TokenMetadataType.java
@@ -18,6 +18,6 @@ package org.apache.knox.gateway.services.security.token;
 
 public enum TokenMetadataType {
 
-  JWT, KNOXSSO_COOKIE, CLIENT_ID;
+  JWT, KNOXSSO_COOKIE, CLIENT_ID, API_KEY;
 
 }

Reply via email to