Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add component ID to API key identifier #3615

Merged
merged 1 commit into from
Nov 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -109,22 +109,27 @@ protected String retrieveTokenFromRequestCtx(RequestContext requestContext) thro
APISecurityConstants.API_AUTH_INVALID_CREDENTIALS_MESSAGE);
}
String keyHash = APIKeyUtils.generateAPIKeyHash(apiKey);
Object cachedJWT = CacheProvider.getGatewayAPIKeyJWTCache().getIfPresent(keyHash);
String componentId = requestContext.getMatchedAPI().getChoreoComponentInfo().getComponentID();
if (componentId == null) {
componentId = "";
}
String apiKeyId = keyHash + APIKeyConstants.API_KEY_ID_SEPARATOR + componentId;
Object cachedJWT = CacheProvider.getGatewayAPIKeyJWTCache().getIfPresent(apiKeyId);
if (cachedJWT != null && !APIKeyUtils.isJWTExpired((String) cachedJWT)) {
if (log.isDebugEnabled()) {
log.debug("Token retrieved from the cache. Token: " + FilterUtils.getMaskedToken(keyHash));
}
return (String) cachedJWT;
}
// Exchange the API Key to a JWT token.
Optional<String> jwt = APIKeyUtils.exchangeAPIKeyToJWT(keyHash);
Optional<String> jwt = APIKeyUtils.exchangeAPIKeyToJWT(apiKeyId);
if (jwt.isEmpty()) {
throw new APISecurityException(APIConstants.StatusCodes.UNAUTHENTICATED.getCode(),
APISecurityConstants.API_AUTH_INVALID_CREDENTIALS,
APISecurityConstants.API_AUTH_INVALID_CREDENTIALS_MESSAGE);
}
// Cache the JWT token.
CacheProvider.getGatewayAPIKeyJWTCache().put(keyHash, jwt.get());
CacheProvider.getGatewayAPIKeyJWTCache().put(apiKeyId, jwt.get());
return jwt.get();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ public class APIKeyConstants {

public static final String API_KEY_JSON_KEY = "key";

public static final String API_KEY_ID_SEPARATOR = "#";

public static final String PAT_EXCHANGE_ENDPOINT = "/internal/pat";
public static final String API_KEY_EXCHANGE_ENDPOINT = "/internal/apiKey/token";
}
Original file line number Diff line number Diff line change
Expand Up @@ -142,12 +142,12 @@ public static Optional<String> exchangePATToJWT(String patHash) {
}

/**
* Exchange a given API key hash to a JWT token.
* Exchange a given API key ID to a JWT token.
*
* @param apiKeyHash API Key Hash
* @param apiKeyId API Key Hash + "#" + Target component ID.
* @return JWT corresponding to given API Key.
*/
public static Optional<String> exchangeAPIKeyToJWT(String apiKeyHash) {
public static Optional<String> exchangeAPIKeyToJWT(String apiKeyId) {

URL url = null;
try {
Expand All @@ -162,7 +162,7 @@ public static Optional<String> exchangeAPIKeyToJWT(String apiKeyHash) {
// Create a request to exchange API key to JWT.
HttpPost exchangeRequest = new HttpPost(url.toURI());
exchangeRequest.addHeader("Content-Type", ContentType.APPLICATION_JSON.toString());
exchangeRequest.setEntity(new StringEntity(createKeyHashExchangeRequest(apiKeyHash)));
exchangeRequest.setEntity(new StringEntity(createKeyHashExchangeRequest(apiKeyId)));
try (CloseableHttpResponse response = httpClient.execute(exchangeRequest)) {
if (response.getStatusLine().getStatusCode() == 200) {
HttpEntity entity = response.getEntity();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.wso2.carbon.apimgt.common.gateway.dto.JWTConfigurationDto;
import org.wso2.choreo.connect.enforcer.common.CacheProvider;
import org.wso2.choreo.connect.enforcer.commons.model.APIConfig;
import org.wso2.choreo.connect.enforcer.commons.model.ChoreoComponentInfo;
import org.wso2.choreo.connect.enforcer.commons.model.RequestContext;
import org.wso2.choreo.connect.enforcer.config.ConfigHolder;
import org.wso2.choreo.connect.enforcer.config.EnforcerConfig;
Expand Down Expand Up @@ -68,9 +69,13 @@ public void setup() {
public void retrieveTokenFromRequestCtxTest_invalidKey() {

RequestContext.Builder requestContextBuilder = new RequestContext.Builder("/api-key");
ChoreoComponentInfo choreoComponentInfo = new ChoreoComponentInfo();
choreoComponentInfo.setComponentID("component_id");
requestContextBuilder.matchedAPI(new APIConfig.Builder("Petstore")
.basePath("/test")
.uuid("6003a3b7-af0f-4fb3-853e-a6562b2345f2")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.uuid("6003a3b7-af0f-4fb3-853e-a6562b2345f2")
.uuid("64c33f14c2cf3616f59ecd8f")

.apiType("REST")
.choreoComponentInfo(choreoComponentInfo)
.build());
Map<String, String> headersMap = new HashMap<>();
headersMap.put("choreo-api-key",
Expand All @@ -97,9 +102,13 @@ public void retrieveTokenFromRequestCtxTest_cached_validKey() throws APISecurity
PowerMockito.when(CacheProvider.getGatewayAPIKeyJWTCache()).thenReturn(gatewayAPIKeyJWTCache);
PowerMockito.when(gatewayAPIKeyJWTCache.getIfPresent(Mockito.anyString())).thenReturn(mockJWT);

ChoreoComponentInfo choreoComponentInfo = new ChoreoComponentInfo();
choreoComponentInfo.setComponentID("component_id");
RequestContext.Builder requestContextBuilder = new RequestContext.Builder("/api-key");
requestContextBuilder.matchedAPI(new APIConfig.Builder("Petstore")
.basePath("/test")
.uuid("6003a3b7-af0f-4fb3-853e-a6562b2345f2")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not a uuid4 string

Suggested change
.uuid("6003a3b7-af0f-4fb3-853e-a6562b2345f2")
.uuid("64c33f14c2cf3616f59ecd8f")

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not used in the tests. Will remove these in a separate PR

.choreoComponentInfo(choreoComponentInfo)
.apiType("REST")
.build());
Map<String, String> headersMap = new HashMap<>();
Expand Down Expand Up @@ -128,10 +137,14 @@ public void retrieveTokenFromRequestCtxTest_validKey() throws APISecurityExcepti
PowerMockito.when(CacheProvider.getGatewayAPIKeyJWTCache()).thenReturn(gatewayAPIKeyJWTCache);
PowerMockito.when(gatewayAPIKeyJWTCache.getIfPresent(Mockito.anyString())).thenReturn(null);

ChoreoComponentInfo choreoComponentInfo = new ChoreoComponentInfo();
choreoComponentInfo.setComponentID("component_id");
RequestContext.Builder requestContextBuilder = new RequestContext.Builder("/api-key");
requestContextBuilder.matchedAPI(new APIConfig.Builder("Petstore")
.basePath("/test")
.uuid("6003a3b7-af0f-4fb3-853e-a6562b2345f2")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.uuid("6003a3b7-af0f-4fb3-853e-a6562b2345f2")
.uuid("64c33f14c2cf3616f59ecd8f")

.apiType("REST")
.choreoComponentInfo(choreoComponentInfo)
.build());
Map<String, String> headersMap = new HashMap<>();
headersMap.put("choreo-api-key",
Expand Down
Loading