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

Use endpoint URL and OAuth credentials when caching token #2131

Merged
merged 2 commits into from
Feb 1, 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 @@ -140,7 +140,18 @@ private String decodeString(String value) {
}
}

private void processUrlTemplate(MessageContext synCtx) throws VariableExpansionException {
protected void processUrlTemplate(MessageContext synCtx) throws VariableExpansionException {

String evaluatedUri = resolveUrlTemplate(synCtx);
if (evaluatedUri != null) {
synCtx.setTo(new EndpointReference(evaluatedUri));
if (super.getDefinition() != null) {
synCtx.setProperty(EndpointDefinition.DYNAMIC_URL_VALUE, evaluatedUri);
}
}
}

protected String resolveUrlTemplate(MessageContext synCtx) throws VariableExpansionException {
Map<String, Object> variables = new HashMap<String, Object>();

/*The properties with uri.var.* are only considered for Outbound REST Endpoints*/
Expand All @@ -162,10 +173,10 @@ private void processUrlTemplate(MessageContext synCtx) throws VariableExpansionE
if (objProperty != null) {
if (objProperty instanceof String) {
variables.put(propertyKey.toString(),
decodeString((String) synCtx.getProperty(propertyKey.toString())));
decodeString((String) synCtx.getProperty(propertyKey.toString())));
} else {
variables.put(propertyKey.toString(),
decodeString(String.valueOf(synCtx.getProperty(propertyKey.toString()))));
decodeString(String.valueOf(synCtx.getProperty(propertyKey.toString()))));
}
}
}
Expand Down Expand Up @@ -224,14 +235,14 @@ private void processUrlTemplate(MessageContext synCtx) throws VariableExpansionE
(propertyKey.toString().startsWith(RESTConstants.REST_URI_VARIABLE_PREFIX)
|| propertyKey.toString().startsWith(RESTConstants.REST_QUERY_PARAM_PREFIX))) {
Object objProperty =
synCtx.getProperty(propertyKey.toString());
synCtx.getProperty(propertyKey.toString());
if (objProperty != null) {
if (objProperty instanceof String) {
variables.put(propertyKey.toString(),
(String) synCtx.getProperty(propertyKey.toString()));
(String) synCtx.getProperty(propertyKey.toString()));
} else {
variables.put(propertyKey.toString(),
(String) String.valueOf(synCtx.getProperty(propertyKey.toString())));
(String) String.valueOf(synCtx.getProperty(propertyKey.toString())));
}
}
}
Expand Down Expand Up @@ -282,14 +293,7 @@ private void processUrlTemplate(MessageContext synCtx) throws VariableExpansionE
}
}
}


if (evaluatedUri != null) {
synCtx.setTo(new EndpointReference(evaluatedUri));
if (super.getDefinition() != null) {
synCtx.setProperty(EndpointDefinition.DYNAMIC_URL_VALUE, evaluatedUri);
}
}
return evaluatedUri;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@

package org.apache.synapse.endpoints;

import com.damnhandy.uri.template.VariableExpansionException;
import org.apache.axis2.AxisFault;
import org.apache.axis2.addressing.EndpointReference;
import org.apache.synapse.MessageContext;
import org.apache.synapse.SynapseConstants;
import org.apache.synapse.endpoints.auth.AuthHandler;
Expand Down Expand Up @@ -46,6 +48,7 @@ public OAuthConfiguredHTTPEndpoint(AuthHandler authHandler) {
public void send(MessageContext synCtx) {

try {
setResolvedUrlTemplate(synCtx);
oAuthHandler.setAuthHeader(synCtx);

// If this a blocking call, add 401 as a non error http status code
Expand Down Expand Up @@ -77,17 +80,22 @@ public void send(MessageContext synCtx) {
*/
public MessageContext retryCallWithNewToken(MessageContext synCtx) {
// remove the existing token from the cache so that a new token is generated
oAuthHandler.removeTokenFromCache();
// set RETRIED_ON_OAUTH_FAILURE property to true
synCtx.setProperty(AuthConstants.RETRIED_ON_OAUTH_FAILURE, true);
send(synCtx);
try {
// set RETRIED_ON_OAUTH_FAILURE property to true
synCtx.setProperty(AuthConstants.RETRIED_ON_OAUTH_FAILURE, true);
oAuthHandler.removeTokenFromCache(synCtx);
send(synCtx);
} catch (AuthException e) {
handleError(synCtx,
"Error removing access token for oauth configured http endpoint " + this.getName(), e);
}
return synCtx;
}

@Override
public void destroy() {

oAuthHandler.removeTokenFromCache();
oAuthHandler.removeTokensFromCache();
super.destroy();
}

Expand All @@ -109,4 +117,21 @@ private void handleError(MessageContext synCtx, String message, Exception except
log.error(errorMsg);
informFailure(synCtx, SynapseConstants.ENDPOINT_AUTH_FAILURE, errorMsg);
}

private void setResolvedUrlTemplate(MessageContext messageContext) {
String resolvedUrl = resolveUrlTemplate(messageContext);
if (resolvedUrl != null) {
messageContext.setTo(new EndpointReference(resolvedUrl));
if (super.getDefinition() != null) {
messageContext.setProperty(EndpointDefinition.DYNAMIC_URL_VALUE, resolvedUrl);
}
}
}

@Override
protected void processUrlTemplate(MessageContext synCtx) throws VariableExpansionException {
// Since we set the resolved URL in the OAuthConfiguredHTTPEndpoint.send method
// return the processUrlTemplate to skip re-resolving at HTTPEndpoint.
return;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import org.apache.synapse.endpoints.auth.AuthConstants;
import org.apache.synapse.endpoints.auth.AuthException;

import java.util.Objects;

/**
* This class is used to handle Authorization code grant oauth.
*/
Expand Down Expand Up @@ -74,6 +76,15 @@ protected OMElement serializeSpecificOAuthConfigs(OMFactory omFactory) {
return authCode;
}

@Override
protected int getHash(MessageContext messageContext) throws AuthException {
return Objects.hash(messageContext.getTo().getAddress(), OAuthUtils.resolveExpression(getTokenUrl(), messageContext),
OAuthUtils.resolveExpression(getClientId(), messageContext), OAuthUtils.resolveExpression(getClientSecret(),
messageContext), OAuthUtils.resolveExpression(getRefreshToken(), messageContext),
getRequestParametersAsString(messageContext), getResolvedCustomHeadersMap(getCustomHeadersMap(),
messageContext));
}

/**
* Return the refresh token secret relevant to the Authorization Code Handler.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import org.apache.synapse.endpoints.auth.AuthConstants;
import org.apache.synapse.endpoints.auth.AuthException;

import java.util.Objects;

/**
* This class is used to handle Client Credentials grant oauth.
*/
Expand Down Expand Up @@ -60,4 +62,12 @@ protected OMElement serializeSpecificOAuthConfigs(OMFactory omFactory) {

return omFactory.createOMElement(AuthConstants.CLIENT_CREDENTIALS, SynapseConstants.SYNAPSE_OMNAMESPACE);
}

@Override
protected int getHash(MessageContext messageContext) throws AuthException {
return Objects.hash(messageContext.getTo().getAddress(), OAuthUtils.resolveExpression(getTokenUrl(), messageContext),
OAuthUtils.resolveExpression(getClientId(), messageContext), OAuthUtils.resolveExpression(getClientSecret(),
messageContext), getRequestParametersAsString(messageContext),
getResolvedCustomHeadersMap(getCustomHeadersMap(), messageContext));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@
public abstract class OAuthHandler implements AuthHandler {

private final String id;

private final String tokenApiUrl;
private final String clientId;
private final String clientSecret;
Expand Down Expand Up @@ -89,7 +88,7 @@ public void setAuthHeader(MessageContext messageContext) throws AuthException {
private String getToken(final MessageContext messageContext) throws AuthException {

try {
return TokenCache.getInstance().getToken(id, new Callable<String>() {
return TokenCache.getInstance().getToken(getId(messageContext), new Callable<String>() {
@Override
public String call() throws AuthException, IOException {
return OAuthClient.generateToken(OAuthUtils.resolveExpression(tokenApiUrl, messageContext),
Expand Down Expand Up @@ -129,12 +128,20 @@ public int compare(String o1, String o2) {
}
}

/**
* Method to remove the token from the cache when the token is invalid.
*/
public void removeTokenFromCache(MessageContext messageContext) throws AuthException {

TokenCache.getInstance().removeToken(getId(messageContext));
}

/**
* Method to remove the token from the cache when the endpoint is destroyed.
*/
public void removeTokenFromCache() {
public void removeTokensFromCache() {

TokenCache.getInstance().removeToken(id);
TokenCache.getInstance().removeTokens(id.concat("_"));
}

/**
Expand Down Expand Up @@ -313,7 +320,7 @@ public void setCustomHeaders(Map<String, String> customHeadersMap) {
* @param messageContext Message Context of the request which will be used to resolve dynamic expressions
* @return Map<String, String> Resolved custom headers
*/
private Map<String, String> getResolvedCustomHeadersMap(Map<String, String> customHeadersMap,
protected Map<String, String> getResolvedCustomHeadersMap(Map<String, String> customHeadersMap,
MessageContext messageContext) throws AuthException {

Map<String, String> resolvedCustomHeadersMap = null;
Expand All @@ -339,4 +346,9 @@ public int getSocketTimeout() {
return socketTimeout;
}

protected abstract int getHash(MessageContext messageContext) throws AuthException;

private String getId(MessageContext messageContext) throws AuthException {
return id.concat("_").concat(String.valueOf(getHash(messageContext)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import org.apache.synapse.endpoints.auth.AuthConstants;
import org.apache.synapse.endpoints.auth.AuthException;

import java.util.Objects;

/**
* This class is used to handle Password Credentials grant oauth.
*/
Expand Down Expand Up @@ -78,6 +80,16 @@ protected OMElement serializeSpecificOAuthConfigs(OMFactory omFactory) {
return passwordCredentials;
}

@Override
protected int getHash(MessageContext messageContext) throws AuthException {
return Objects.hash(messageContext.getTo().getAddress(), OAuthUtils.resolveExpression(getTokenUrl(), messageContext),
OAuthUtils.resolveExpression(getClientId(), messageContext), OAuthUtils.resolveExpression(getClientSecret(),
messageContext), OAuthUtils.resolveExpression(getUsername(), messageContext),
OAuthUtils.resolveExpression(getPassword(), messageContext),
getRequestParametersAsString(messageContext), getResolvedCustomHeadersMap(getCustomHeadersMap(),
messageContext));
}

public String getUsername() {

return username;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public String getToken(String id, Callable<String> callable) throws ExecutionExc
}

/**
* This method is called to remove the token from the cache when the endpoint is destroyed
* This method is called to remove the token from the cache when the token is invalid
*
* @param id id of the endpoint
*/
Expand All @@ -91,4 +91,12 @@ public void removeToken(String id) {
tokenMap.invalidate(id);
}

/**
* This method is called to remove the tokens from the cache when the endpoint is destroyed
*
* @param oauthHandlerId id of the OAuth handler bounded to the endpoint
*/
public void removeTokens(String oauthHandlerId) {
tokenMap.asMap().entrySet().removeIf(entry -> entry.getKey().startsWith(oauthHandlerId));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,27 @@ public void testOAuthConfiguredEPWithDynamicValues() throws Exception {
HttpResponse response = client.doGet("http://127.0.0.1:8280/foodapi/list/dynamicValues");
assertEquals(HttpStatus.SC_OK, response.getStatus());
}

public void testOAuthConfiguredDynamicURLEP() throws Exception {

String payload1 = "<request>\n" +
"\t<ep_url>http://localhost:9000/foodservice/food</ep_url>\n" +
"\t<token_ep>http://localhost:9000/foodservice/token1</token_ep>\n" +
"</request>";

String payload2 = "<request>\n" +
"\t<ep_url>http://localhost:9000/foodservice/apple</ep_url>\n" +
"\t<token_ep>http://localhost:9000/foodservice/token2</token_ep>\n" +
"</request>";

BasicHttpClient client = new BasicHttpClient();
HttpResponse response = client.doPost("http://127.0.0.1:8280/foodapi/list/dynamicURL", payload1.getBytes(),
"text/xml");
assertEquals(HttpStatus.SC_OK, response.getStatus());

HttpResponse response2 = client.doPost("http://127.0.0.1:8280/foodapi/list/dynamicURL", payload2.getBytes(),
"text/xml");
assertEquals(HttpStatus.SC_OK, response2.getStatus());
assertEquals("1", response2.getBodyAsString());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public class Constants {

static final String refreshToken = "wxyz#9876";
static final String accessToken = "abcd@1234";
static final String accessToken2 = "jklm#6789";
static final String expiresIn = "3600";
static final String tokenType = "Bearer";
static final String clientId = "my_client_id";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public class FoodService {

private int unauthorizedReqCount = 0;
private int tokenReqCount = 0;
private int appleServiceRequests = 0;

@POST
@Path("/token")
Expand All @@ -57,6 +58,38 @@ public Response getAccessToken(@Context HttpHeaders httpHeaders,
return Response.status(Response.Status.UNAUTHORIZED).entity("Invalid Credentials").build();
}

@POST
@Path("/token1")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces(MediaType.APPLICATION_JSON)
public Response getAccessToken1(@Context HttpHeaders httpHeaders,
MultivaluedMap<String, String> tokenRequestParams) {

String basicHeader = httpHeaders.getHeaderString("Authorization");

if (validateBasicAuthHeader(basicHeader)) {
return Response.status(Response.Status.OK).entity(new Token(Constants.accessToken, Constants.expiresIn,
Constants.tokenType)).build();
}
return Response.status(Response.Status.UNAUTHORIZED).entity("Invalid Credentials").build();
}

@POST
@Path("/token2")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces(MediaType.APPLICATION_JSON)
public Response getAccessToken2(@Context HttpHeaders httpHeaders,
MultivaluedMap<String, String> tokenRequestParams) {

String basicHeader = httpHeaders.getHeaderString("Authorization");

if (validateBasicAuthHeader(basicHeader)) {
return Response.status(Response.Status.OK).entity(new Token(Constants.accessToken2, Constants.expiresIn,
Constants.tokenType)).build();
}
return Response.status(Response.Status.UNAUTHORIZED).entity("Invalid Credentials").build();
}

@POST
@Path("/custom-token")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
Expand Down Expand Up @@ -105,6 +138,22 @@ public Response getFoodItem(@Context HttpHeaders httpHeaders) {
return Response.status(Response.Status.UNAUTHORIZED).build();
}

@GET
@Path("/apple")
@Produces(MediaType.APPLICATION_JSON)
public Response getFoodItem2(@Context HttpHeaders httpHeaders) {

appleServiceRequests++;
String authorizationHeader = httpHeaders.getHeaderString("Authorization");
if (authorizationHeader != null) {
String token = authorizationHeader.split(" ")[1];
if (token.equals(Constants.accessToken2)) {
return Response.status(Response.Status.OK).entity(appleServiceRequests).build();
}
}
return Response.status(Response.Status.UNAUTHORIZED).build();
}

@GET
@Path("/unauthorized")
@Produces(MediaType.APPLICATION_JSON)
Expand Down
Loading
Loading