Skip to content

Commit 467c286

Browse files
Fix incorrect behavior when the token is revoked by OAuth backend and add connection timeout configuration (#12583)
* Fix incorrect behavior when the token is revoked by OAuth backend and add connection timeout configurations * Removed unnecessary lines from unit tests
1 parent cf27866 commit 467c286

File tree

34 files changed

+581
-58
lines changed

34 files changed

+581
-58
lines changed

components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/EndpointSecurity.java

+36-1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ public class EndpointSecurity {
4848

4949
private Map additionalProperties = new HashMap();
5050

51+
private int connectionTimeoutDuration = -1;
52+
53+
private int connectionRequestTimeoutDuration = -1;
54+
55+
private int socketTimeoutDuration = -1;
56+
5157
public EndpointSecurity(EndpointSecurity endpointSecurity) {
5258

5359
this.uniqueIdentifier = endpointSecurity.uniqueIdentifier;
@@ -61,7 +67,9 @@ public EndpointSecurity(EndpointSecurity endpointSecurity) {
6167
this.clientSecret = endpointSecurity.clientSecret;
6268
this.customParameters = endpointSecurity.customParameters;
6369
this.additionalProperties = endpointSecurity.additionalProperties;
64-
70+
this.connectionTimeoutDuration = endpointSecurity.connectionTimeoutDuration;
71+
this.connectionRequestTimeoutDuration = endpointSecurity.connectionRequestTimeoutDuration;
72+
this.socketTimeoutDuration = endpointSecurity.socketTimeoutDuration;
6573
}
6674

6775
public EndpointSecurity() {
@@ -197,6 +205,30 @@ public void setApiKeyValue(String apiKeyValue) {
197205
this.apiKeyValue = apiKeyValue;
198206
}
199207

208+
public int getConnectionTimeoutDuration() {
209+
return connectionTimeoutDuration;
210+
}
211+
212+
public void setConnectionTimeoutDuration(int connectionTimeoutDuration) {
213+
this.connectionTimeoutDuration = connectionTimeoutDuration;
214+
}
215+
216+
public int getConnectionRequestTimeoutDuration() {
217+
return connectionRequestTimeoutDuration;
218+
}
219+
220+
public void setConnectionRequestTimeoutDuration(int connectionRequestTimeoutDuration) {
221+
this.connectionRequestTimeoutDuration = connectionRequestTimeoutDuration;
222+
}
223+
224+
public int getSocketTimeoutDuration() {
225+
return socketTimeoutDuration;
226+
}
227+
228+
public void setSocketTimeoutDuration(int socketTimeoutDuration) {
229+
this.socketTimeoutDuration = socketTimeoutDuration;
230+
}
231+
200232
@Override
201233
public String toString() {
202234

@@ -214,6 +246,9 @@ public String toString() {
214246
", apiKeyValue='" + apiKeyValue + '\'' +
215247
", customParameters='" + customParameters + '\'' +
216248
", additionalProperties=" + additionalProperties +
249+
", connectionTimeoutDuration=" + connectionTimeoutDuration +
250+
", connectionRequestTimeoutDuration=" + connectionRequestTimeoutDuration +
251+
", socketTimeoutDuration=" + socketTimeoutDuration +
217252
'}';
218253
}
219254
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/*
2+
* Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com/).
3+
*
4+
* WSO2 LLC. licenses this file to you under the Apache License,
5+
* Version 2.0 (the "License"); you may not use this file except
6+
* in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing,
12+
* software distributed under the License is distributed on an
13+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
* KIND, either express or implied. See the License for the
15+
* specific language governing permissions and limitations
16+
* under the License.
17+
*/
18+
19+
package org.wso2.carbon.apimgt.gateway.mediators.oauth;
20+
21+
import org.apache.synapse.endpoints.auth.oauth.TokenCacheProvider;
22+
import org.wso2.carbon.apimgt.gateway.internal.ServiceReferenceHolder;
23+
import org.wso2.carbon.apimgt.gateway.utils.redis.RedisCacheUtils;
24+
25+
import java.util.Set;
26+
27+
/**
28+
* Singleton class implementing TokenCacheProvider for caching OAuth tokens using Redis.
29+
* This class allows storing, retrieving, and removing tokens from Redis via RedisCacheUtils
30+
* and is utilized by OAuthHandler in Synapse for OAuth token caching.
31+
*/
32+
public class RedisTokenCache implements TokenCacheProvider {
33+
34+
// Singleton instance of RedisTokenCache
35+
private static final RedisTokenCache instance = new RedisTokenCache();
36+
private static final RedisCacheUtils redisCacheUtils = new RedisCacheUtils(ServiceReferenceHolder.getInstance().
37+
getRedisPool());
38+
39+
private RedisTokenCache() {
40+
}
41+
42+
/**
43+
* Provides the singleton instance of RedisTokenCache.
44+
* If no instance exists, a new one is created.
45+
*
46+
* @return the singleton instance of RedisTokenCache
47+
*/
48+
public static RedisTokenCache getInstance() {
49+
return instance;
50+
}
51+
52+
/**
53+
* Stores a token in the Redis cache with the given identifier as the key.
54+
*
55+
* @param id the identifier (key) for the token in the cache
56+
* @param token the token to be cached
57+
*/
58+
@Override
59+
public void putToken(String id, String token) {
60+
redisCacheUtils.setValue(id, token);
61+
}
62+
63+
/**
64+
* Retrieves a token from the Redis cache for the given identifier.
65+
*
66+
* @param id the identifier (key) of the token in the cache
67+
* @return the token associated with the given identifier, or null if not found
68+
*/
69+
@Override
70+
public String getToken(String id) {
71+
return redisCacheUtils.getValue(id);
72+
}
73+
74+
/**
75+
* Removes the token associated with the given identifier from the Redis cache.
76+
*
77+
* @param id the identifier (key) of the token to be removed
78+
*/
79+
@Override
80+
public void removeToken(String id) {
81+
redisCacheUtils.deleteKey(id);
82+
}
83+
84+
/**
85+
* This method is called to remove all tokens from the Redis cache when the endpoint is destroyed that are
86+
* associated with a specific OAuth handler. The keys of the tokens that need to be removed should start with the
87+
* provided oauthHandlerId.
88+
*
89+
* @param oauthHandlerId the ID of the OAuth handler whose tokens should be removed
90+
*/
91+
@Override
92+
public void removeTokens(String oauthHandlerId) {
93+
if (redisCacheUtils.isRedisCacheSessionActive()) {
94+
// Retrieve all keys that match the given pattern
95+
Set<String> keys = redisCacheUtils.getKeys(oauthHandlerId + "*");
96+
97+
// Remove all tokens with keys that match the pattern
98+
for (String key : keys) {
99+
redisCacheUtils.deleteKey(key);
100+
}
101+
}
102+
}
103+
}

components/apimgt/org.wso2.carbon.apimgt.gateway/src/main/java/org/wso2/carbon/apimgt/gateway/utils/redis/RedisCacheUtils.java

+21
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
import java.io.IOException;
3131
import java.util.Map;
32+
import java.util.Set;
3233

3334
/**
3435
* Utility class singleton to connect to Redis Server, and perform general operations
@@ -244,4 +245,24 @@ public Object getObject(String key, Class objectType) {
244245
return null;
245246
}
246247

248+
/**
249+
* Retrieves a set of keys from Redis that match the specified pattern.
250+
*
251+
* @param pattern the pattern to match keys (e.g., "oauth_*" to match all keys starting with "oauth_")
252+
* @return a set of matching keys from Redis, or an empty set if no keys match
253+
*/
254+
public Set<String> getKeys(String pattern) {
255+
try (Jedis jedis = jedisPool.getResource()) {
256+
return jedis.keys(pattern);
257+
}
258+
}
259+
260+
/**
261+
* Checks if the Redis cache session is active.
262+
*
263+
* @return true if the Redis cache session (Jedis pool) is initialized and open, false otherwise.
264+
*/
265+
public boolean isRedisCacheSessionActive() {
266+
return jedisPool != null && !jedisPool.isClosed();
267+
}
247268
}

components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIConstants.java

+2
Original file line numberDiff line numberDiff line change
@@ -1476,6 +1476,7 @@ public static class OAuthConstants {
14761476
public static final String TOKEN_TYPE = "token_type";
14771477
public static final String EXPIRES_IN = "expires_in";
14781478
public static final String EXPIRES_IN_CONFIG = "ExpiresIn";
1479+
public static final String ENABLE_RETRY_CALL_WITH_NEW_TOKEN = "EnableRetryCallWithNewToken";
14791480

14801481
// Properties in Endpoint Config
14811482
public static final String ENDPOINT_SECURITY_PRODUCTION = "production";
@@ -1817,6 +1818,7 @@ private ConfigParameters() {
18171818
public static final String ENDPOINT_SPECIFIC_CONFIG = "config";
18181819
public static final String ENDPOINT_CONFIG_ACTION_DURATION = "actionDuration";
18191820
public static final String ENDPOINT_TYPE_GRAPHQL = "graphql";
1821+
public static final String ENABLE_RETRY_CALL_WITH_NEW_OAUTH_TOKEN = "enableRetryCallWithNewOauthToken";
18201822

18211823
public static final String API_ENDPOINT_CONFIG_TIMEOUT = "timeout";
18221824
public static final String API_ENDPOINT_CONFIG_PROTOCOL_TYPE = "endpoint_type";

components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/APIUtil.java

+13
Original file line numberDiff line numberDiff line change
@@ -2072,6 +2072,19 @@ public static boolean isPortalConfigurationOnlyModeEnabled() {
20722072
return false;
20732073
}
20742074

2075+
/**
2076+
* Check if Retry Call With New OAuth Token is enabled
2077+
*
2078+
* @return True if Retry Call With New OAuth Token is enabled
2079+
*/
2080+
public static boolean isRetryCallWithNewOAuthTokenEnabled() {
2081+
String property = ServiceReferenceHolder.getInstance().getAPIManagerConfigurationService().
2082+
getAPIManagerConfiguration().getFirstProperty(APIConstants.MEDIATOR_CONFIG + APIConstants.
2083+
OAuthConstants.OAUTH_MEDIATION_CONFIG + APIConstants.OAuthConstants.
2084+
ENABLE_RETRY_CALL_WITH_NEW_TOKEN);
2085+
return Boolean.parseBoolean(property);
2086+
}
2087+
20752088
/**
20762089
* Check if an issuer for internal keys has been defined
20772090
*

components/apimgt/org.wso2.carbon.apimgt.rest.api.common/src/main/resources/publisher-api.yaml

+6
Original file line numberDiff line numberDiff line change
@@ -13183,6 +13183,12 @@ components:
1318313183
Is Portal Configuration Only Mode enabled
1318413184
example: false
1318513185
default: false
13186+
retryCallWithNewOAuthTokenEnabled:
13187+
type: boolean
13188+
description: |
13189+
Is Retry Call With New OAuth Token Enabled
13190+
example: true
13191+
default: true
1318613192
crossTenantSubscriptionEnabled:
1318713193
type: boolean
1318813194
description: |

components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/pom.xml

+2
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,8 @@
224224
${project.artifactId}
225225
</Bundle-SymbolicName>
226226
<Import-Package>
227+
org.osgi.framework.*;version="${imp.package.version.osgi.framework}",
228+
org.osgi.service.*;version="${imp.package.version.osgi.service}",
227229
org.apache.commons.logging.*; version="${import.package.version.commons.logging}",
228230
javax.validation.*; version="1.1.0.Final",
229231
*;resolution:=optional

components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/dto/SettingsDTO.java

+22-1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public class SettingsDTO {
3636
private Boolean externalStoresEnabled = null;
3737
private Boolean docVisibilityEnabled = null;
3838
private Boolean portalConfigurationOnlyModeEnabled = false;
39+
private Boolean retryCallWithNewOAuthTokenEnabled = true;
3940
private Boolean crossTenantSubscriptionEnabled = false;
4041
private String defaultAdvancePolicy = null;
4142
private String defaultSubscriptionPolicy = null;
@@ -222,6 +223,24 @@ public void setPortalConfigurationOnlyModeEnabled(Boolean portalConfigurationOnl
222223
this.portalConfigurationOnlyModeEnabled = portalConfigurationOnlyModeEnabled;
223224
}
224225

226+
/**
227+
* Is Retry Call With New OAuth Token Enabled
228+
**/
229+
public SettingsDTO retryCallWithNewOAuthTokenEnabled(Boolean retryCallWithNewOAuthTokenEnabled) {
230+
this.retryCallWithNewOAuthTokenEnabled = retryCallWithNewOAuthTokenEnabled;
231+
return this;
232+
}
233+
234+
235+
@ApiModelProperty(example = "true", value = "Is Retry Call With New OAuth Token Enabled ")
236+
@JsonProperty("retryCallWithNewOAuthTokenEnabled")
237+
public Boolean isRetryCallWithNewOAuthTokenEnabled() {
238+
return retryCallWithNewOAuthTokenEnabled;
239+
}
240+
public void setRetryCallWithNewOAuthTokenEnabled(Boolean retryCallWithNewOAuthTokenEnabled) {
241+
this.retryCallWithNewOAuthTokenEnabled = retryCallWithNewOAuthTokenEnabled;
242+
}
243+
225244
/**
226245
* Is Cross Tenant Subscriptions Enabled
227246
**/
@@ -367,6 +386,7 @@ public boolean equals(java.lang.Object o) {
367386
Objects.equals(externalStoresEnabled, settings.externalStoresEnabled) &&
368387
Objects.equals(docVisibilityEnabled, settings.docVisibilityEnabled) &&
369388
Objects.equals(portalConfigurationOnlyModeEnabled, settings.portalConfigurationOnlyModeEnabled) &&
389+
Objects.equals(retryCallWithNewOAuthTokenEnabled, settings.retryCallWithNewOAuthTokenEnabled) &&
370390
Objects.equals(crossTenantSubscriptionEnabled, settings.crossTenantSubscriptionEnabled) &&
371391
Objects.equals(defaultAdvancePolicy, settings.defaultAdvancePolicy) &&
372392
Objects.equals(defaultSubscriptionPolicy, settings.defaultSubscriptionPolicy) &&
@@ -378,7 +398,7 @@ public boolean equals(java.lang.Object o) {
378398

379399
@Override
380400
public int hashCode() {
381-
return Objects.hash(devportalUrl, environment, gatewayTypes, scopes, monetizationAttributes, subscriberContactAttributes, securityAuditProperties, externalStoresEnabled, docVisibilityEnabled, portalConfigurationOnlyModeEnabled, crossTenantSubscriptionEnabled, defaultAdvancePolicy, defaultSubscriptionPolicy, authorizationHeader, isJWTEnabledForLoginTokens, allowSubscriptionValidationDisabling, customProperties);
401+
return Objects.hash(devportalUrl, environment, gatewayTypes, scopes, monetizationAttributes, subscriberContactAttributes, securityAuditProperties, externalStoresEnabled, docVisibilityEnabled, portalConfigurationOnlyModeEnabled, retryCallWithNewOAuthTokenEnabled, crossTenantSubscriptionEnabled, defaultAdvancePolicy, defaultSubscriptionPolicy, authorizationHeader, isJWTEnabledForLoginTokens, allowSubscriptionValidationDisabling, customProperties);
382402
}
383403

384404
@Override
@@ -396,6 +416,7 @@ public String toString() {
396416
sb.append(" externalStoresEnabled: ").append(toIndentedString(externalStoresEnabled)).append("\n");
397417
sb.append(" docVisibilityEnabled: ").append(toIndentedString(docVisibilityEnabled)).append("\n");
398418
sb.append(" portalConfigurationOnlyModeEnabled: ").append(toIndentedString(portalConfigurationOnlyModeEnabled)).append("\n");
419+
sb.append(" retryCallWithNewOAuthTokenEnabled: ").append(toIndentedString(retryCallWithNewOAuthTokenEnabled)).append("\n");
399420
sb.append(" crossTenantSubscriptionEnabled: ").append(toIndentedString(crossTenantSubscriptionEnabled)).append("\n");
400421
sb.append(" defaultAdvancePolicy: ").append(toIndentedString(defaultAdvancePolicy)).append("\n");
401422
sb.append(" defaultSubscriptionPolicy: ").append(toIndentedString(defaultSubscriptionPolicy)).append("\n");

components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/common/TemplateBuilderUtil.java

+3-4
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,12 @@
5656
import org.wso2.carbon.apimgt.impl.definitions.GraphQLSchemaDefinition;
5757
import org.wso2.carbon.apimgt.impl.dto.SoapToRestMediationDto;
5858
import org.wso2.carbon.apimgt.impl.importexport.ImportExportConstants;
59-
import org.wso2.carbon.apimgt.impl.internal.ServiceReferenceHolder;
6059
import org.wso2.carbon.apimgt.impl.template.APITemplateBuilder;
6160
import org.wso2.carbon.apimgt.impl.template.APITemplateException;
6261
import org.wso2.carbon.apimgt.impl.utils.APIUtil;
6362
import org.wso2.carbon.apimgt.impl.utils.CertificateMgtUtils;
6463
import org.wso2.carbon.apimgt.impl.utils.GatewayUtils;
64+
import org.wso2.carbon.apimgt.rest.api.publisher.v1.common.internal.ServiceReferenceHolder;
6565
import org.wso2.carbon.apimgt.rest.api.publisher.v1.common.mappings.APIMappingUtil;
6666
import org.wso2.carbon.apimgt.rest.api.publisher.v1.common.mappings.ImportUtils;
6767
import org.wso2.carbon.apimgt.rest.api.publisher.v1.common.template.APITemplateBuilderImpl;
@@ -1270,9 +1270,8 @@ private static String getEndpointName(String endpointConfig) throws XMLStreamExc
12701270

12711271
private static void setSecureVaultPropertyToBeAdded(String prefix, API api, GatewayAPIDTO gatewayAPIDTO) {
12721272

1273-
boolean isSecureVaultEnabled =
1274-
Boolean.parseBoolean(ServiceReferenceHolder.getInstance().getAPIManagerConfigurationService().
1275-
getAPIManagerConfiguration().getFirstProperty(APIConstants.API_SECUREVAULT_ENABLE));
1273+
boolean isSecureVaultEnabled = Boolean.parseBoolean(ServiceReferenceHolder.getInstance().
1274+
getAPIManagerConfiguration().getFirstProperty(APIConstants.API_SECUREVAULT_ENABLE));
12761275

12771276
if (isSecureVaultEnabled) {
12781277
org.json.JSONObject endpointConfig = new org.json.JSONObject(api.getEndpointConfig());

0 commit comments

Comments
 (0)