expose multiple resource path: best practice?

Hello, 

I have to protect with Apigee an application with thousands of resource path, all with the same flow.

Should I have in the XML proxy a <Condition> tag with thousands of "or" clause?
For example: 

<Condition>(proxy.pathsuffix MatchesPath "/products") or (proxy.pathsuffix MatchesPath “/contracts”) or (proxy.pathsuffix MatchesPath “/documents”)  etc....</Condition>

I'm afraid it's neither easy to maintain nor fast to execute. Are there best practice?

I cannot use wildcards! 🙂 

Thanks in advance for the help

 

Solved Solved
0 4 361
1 ACCEPTED SOLUTION

I'm afraid it's neither easy to maintain nor fast to execute.

I agree with you, it does not seem that it would be "easy" to maintain. Lots of copy-pasta in your proxy endpoint. I don't think I would want to maintain that. I am unsure how fast it would be.

Are there best practice?

Hmmm. It's a bit unusual. Are the LEFT MOST path segments all common? or? Are you saying that /products and /contracts etc are examples of the left-most segments in the path?

I mean does it look like this?

https://endpoint/v1/products
https://endpoint/v1/contracts
https://endpoint/v1/documents

or does it look like this

https://endpoint/contracts
https://endpoint/products
https://endpoint/documents

 

Something you might try: In the proxyendpoint itself, don't use a conditional flow. Or use Conditional flows to EXCLUDE verbs you know you don't want to allow (PUT or PATCH for example, possibly). Then, in the Request PostFlow, attach a JavaScript step that checks the 1000 or more paths in a loop.

 

var validResourcePaths = /* something here */
var pathsuffix = context.getVariable('proxy.pathsuffix');

function pathMatcher(path) {
  return path.startsWith(pathsuffix);
}

if (!validResourcePaths.find(pathMatcher))  {
  // The find method invokes the pathMatcher function once for each item 
  // in the list until a match is found. 
  // If control reaches here, the provided path is not in the list.
  throw new Error('unknown request');
  // This will throw a Fault and result in an error sent back to the client. 
  // Use FaultRules in the ProxyEndpoint to "handle" the fault and 
  // send a curated error message. 
}

// If control reaches here, the pathsuffix is valid.  Do nothing further.

 

Some notes

  1. I have no idea how fast this would be. Probably it would be slower than using a single, very very long Condition statement. But, there is a limit to the length of the condition statement, and the I guess the limit probably would not allow for 1000 or more tests. Even if it did, the better maintainability of a JS step would probably be preferable. My personal preference is to favor maintainability over absolute performance.

  2. This JS approach still leaves open the question of .. where do you store the list of valid paths? In Apigee X and hybrid there is a nifty feature called "propertysets" that might allow it, except I think there is a line limit in the propertyset file too. (Try it and see). If you are not using Apigee X or hybrid, then you need something else. I could suggest a separate JS file which stores the data as a JS array. Something like this:

    var list = [
     '/products', '/contracts', '/documents', ...
    ];

    And then in the JS step you would have to use the IncludeURL element, like this:

    <Javascript name='JS-CheckPath' >
      <IncludeURL>jsc://list-of-valid-paths.js</IncludeURL>
      <ResourceURL>jsc://checker-script.js</ResourceURL>
    </Javascript>
    

    Another option: you could store the set of valid paths externally, and use a ServiceCallout to retrieve them, and then cache that data, and retrieve it from cache using LookupCache. In that case the top of your checker-script.js would do something like:

    var list = JSON.parse(context.getVariable('serviceCallloutResponse.content')); 
    

View solution in original post

4 REPLIES 4

I'm afraid it's neither easy to maintain nor fast to execute.

I agree with you, it does not seem that it would be "easy" to maintain. Lots of copy-pasta in your proxy endpoint. I don't think I would want to maintain that. I am unsure how fast it would be.

Are there best practice?

Hmmm. It's a bit unusual. Are the LEFT MOST path segments all common? or? Are you saying that /products and /contracts etc are examples of the left-most segments in the path?

I mean does it look like this?

https://endpoint/v1/products
https://endpoint/v1/contracts
https://endpoint/v1/documents

or does it look like this

https://endpoint/contracts
https://endpoint/products
https://endpoint/documents

 

Something you might try: In the proxyendpoint itself, don't use a conditional flow. Or use Conditional flows to EXCLUDE verbs you know you don't want to allow (PUT or PATCH for example, possibly). Then, in the Request PostFlow, attach a JavaScript step that checks the 1000 or more paths in a loop.

 

var validResourcePaths = /* something here */
var pathsuffix = context.getVariable('proxy.pathsuffix');

function pathMatcher(path) {
  return path.startsWith(pathsuffix);
}

if (!validResourcePaths.find(pathMatcher))  {
  // The find method invokes the pathMatcher function once for each item 
  // in the list until a match is found. 
  // If control reaches here, the provided path is not in the list.
  throw new Error('unknown request');
  // This will throw a Fault and result in an error sent back to the client. 
  // Use FaultRules in the ProxyEndpoint to "handle" the fault and 
  // send a curated error message. 
}

// If control reaches here, the pathsuffix is valid.  Do nothing further.

 

Some notes

  1. I have no idea how fast this would be. Probably it would be slower than using a single, very very long Condition statement. But, there is a limit to the length of the condition statement, and the I guess the limit probably would not allow for 1000 or more tests. Even if it did, the better maintainability of a JS step would probably be preferable. My personal preference is to favor maintainability over absolute performance.

  2. This JS approach still leaves open the question of .. where do you store the list of valid paths? In Apigee X and hybrid there is a nifty feature called "propertysets" that might allow it, except I think there is a line limit in the propertyset file too. (Try it and see). If you are not using Apigee X or hybrid, then you need something else. I could suggest a separate JS file which stores the data as a JS array. Something like this:

    var list = [
     '/products', '/contracts', '/documents', ...
    ];

    And then in the JS step you would have to use the IncludeURL element, like this:

    <Javascript name='JS-CheckPath' >
      <IncludeURL>jsc://list-of-valid-paths.js</IncludeURL>
      <ResourceURL>jsc://checker-script.js</ResourceURL>
    </Javascript>
    

    Another option: you could store the set of valid paths externally, and use a ServiceCallout to retrieve them, and then cache that data, and retrieve it from cache using LookupCache. In that case the top of your checker-script.js would do something like:

    var list = JSON.parse(context.getVariable('serviceCallloutResponse.content')); 
    

Hi Dino,
thanks for the support.

The backend wraps those resources in a base path, call it /project .
Consequently the Apigee proxy has /project base-path.
The backend exposes the path example.com/project/products , etc ...

The desired is to prevent external clients from calling non-existent paths, for example /project/animals, these calls must not arrive at the backend for security reasons.

 

Hmmm. It's a bit unusual. 

What scenario is Apigee best suited to?

At this point I imagine a simple proxy that routes all the traffic (authorized by the security policies in the flow) to the backend, without guaranteeing only the exposure of the existing resources ...

What scenario is Apigee best suited to?

Apigee is well suited to acting as an API Gateway. The unusual part is the 1000's of paths that all must be screened, without wildcards. Usually the number of path segments is o(10), not o(1000).

Did you try what I suggested? Did you evaluate it? 

Hi Dino,
at the moment we are in the analysis phase, I have not tested but I think the best solution is the one proposed by you.

Unfortunately at the moment we are migrating from another technology where it was necessary to specify each path, and the customer would like to keep the same paradigm.

Like you, too, I think that as a reverse proxy Apigee must route all traffic.

There were no topics about this, I think it will be useful to others too.