I wish to restrict access to certain methods in my RESTful API. I am trying to do this via products for simplicity allowing access to the resource /athletes* but I don't see a way of any finer control i.e. I wish to only allow GET requests and not POST and DELETE. Is there a syntax for expressing this in the custom resource path section of a product or do I need to handle this via a conditional flow e.g. to check for the product name to see if they can access?
I do this with Scopes but I had to do one little kludge at the end. This assumes you're using at least client_credentials grants for access_tokens (VerifyAPIKey policy doesn't directly support Scopes).
First create an API Proxy for, say, /v1/content (base path /v1, path suffix /content). Then create two resources.
<Flows>
<Flow name="Content Read">
<Description/>
<Request>
<Step>
<FaultRules/>
<Name>RegEx-Check-Scope-READ-Content</Name>
</Step>
</Request>
<Response />
<Condition>(proxy.pathsuffix MatchesPath "/content") and (request.verb = "GET")</Condition>
</Flow>
<Flow name="Content Create">
<Description/>
<Request>
<Step>
<FaultRules/>
<Name>RegEx-Check-Scope-POST-Content</Name>
</Step>
</Request>
<Response />
<Condition>(proxy.pathsuffix MatchesPath "/content") and (request.verb = "POST")</Condition>
</Flow>
Then create a product with scopes for CONTENT-READ and CONTENT-WRITE something like this:
Now, you could either create a second product with only CONTENT-READ to restrict some apps to Read only or you could generate your access_token with scopes like this:
<OAuthV2 name="GenerateAccessTokenClient">
<Operation>GenerateAccessToken</Operation>
<ExpiresIn>3600000</ExpiresIn>
<SupportedGrantTypes>
<GrantType>client_credentials</GrantType>
</SupportedGrantTypes>
<GrantType>request.formparam.grant_type</GrantType>
<Scope>request.formparam.scope</Scope>
<GenerateResponse/>
</OAuthV2>
Probably easier to enforce the App level using two products, but passing scopes also let the User is a 3-legged Oauth add restrictions. Regardless, when you generated the access_token the policy will create an Apigee variable called "scope" which will include the scopes you included when you generated the access_token. If you don't specify scopes when you generate the access_token you will get ALL scopes from the product included in the scopes variable like this:
scope: CONTENT-READ CONTENT-WRITE
It's just one long string, separated by spaces. And there didn't seem to be an easy way to put this into the Condition for the flow, so I added a RegEx policy to check if the allowed scope is in the scope variable, like this:
<RegularExpressionProtection async="false" continueOnError="false" enabled="true" name="RegEx-Check-Scope-POST-Content">
<DisplayName>RegEx Check Scope POST-Content</DisplayName>
<FaultRules/>
<Properties/>
<IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>>
<Variable name="scope">
<Pattern>^((?!CONTENT-WRITE).)*$</Pattern>
</Variable>
<Source>request</Source>
</RegularExpressionProtection>
Where the regex returns true if CONTENT-WRITE is not in the scope string. And if the regex returns true, then it raises a fault and stops processing so the App doesn't get to POST.
Let me know if that makes sense (it's a few steps...)
On the API proxies page (not the Product page), you can add resources. Therein, you can indicate the method (or request verb) you allow. This is the same as using conditions on your Flows based on the request verb. For instance, if you add <Condition>(request.verb = "POST")</Condition> to your API flow in your bundle, that means that particular flow will execute only when your request verb is POST. All other verbs for the base path of that bundle will be ignored.
This way, you only allow specific request verbs for your API bundle.
If you're trying to have fine control of resource + verb using API Products, you can set the variable flow.resource.name within the API proxy code to be:
"/" + request.verb + "/" + proxy.pathsuffix
This will leave you with something like /GET/products/1234-567. The variable flow.resource.name is the one that's validated against the API Resource Paths configured for the API products, when using the VerifyAPIKey policy.
Related
When trying to perform a StructureFareRulesRQ using Sabre SOAP API i keep getting the below response. Is this because the airline doesn't support structured fare rules or something else? thank you
<StructureFareRulesRS xmlns="http://webservices.sabre.com/sabreXML/2003/07" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" Version="1.0.4">
<Errors>
<Error ErrorCode="005106" ErrorMessage="FORMAT FARE BASIS NOT AVAILABLE"/>
</Errors>
</StructureFareRulesRS>
This is mostly related to the way the service is built. Remember that fare components o not necessarily reflect segments, and check the directionality of the fare. If you are using BFM, the service will return fare directionality and the actual cities you have to use. Sometimes fares are loaded as full in ATPCO, not only by segments, so that means that the city pair you will have to use in your request will be what you can see in FareComponentBeginAirport and FareComponentEndAirport in BFM's response. I would advise to check with the helpdesk, as you can send them your full payloads and they can check in your PCC as well.
I want to configure my proxy in the following fashion.
All requests by default get passed through to http://main.api.com
Any request that has has a URI path that start /admin gets routed to http://admin.api.com
<RouteRule name="mocker">
<Condition>(proxy.pathsuffix JavaRegex "^/admin/?.*$")</Condition>
<TargetEndpoint>MockTarget</TargetEndpoint>
</RouteRule>
<RouteRule name="default">
<TargetEndpoint>default</TargetEndpoint>
</RouteRule>
This partially works in that it does indeed route to a different host. However, it maintains the path; so requesting /admin/foo/bar gets redirected to http://admin.api.com/admin/foo/bar, but I wanted it to go to http://admin.api.com/foo/bar
How can I achieve that?
You will have to set target.copy.pathsuffix to false in the Target PreFlow. This will ensure that the path from Proxy is not copied to the Target.
You can easily do this by adding a Javascript policy on the Target Preflow with the code
context.setVariable("target.copy.pathsuffix", false);
I created a key/value map using the platform API defined in this doc. I used the API-scoped url to create a key/value map named countrymap.
https://api.enterprise.apigee.com/v1/o/{org_name}/apis/{api_name}/keyvaluemaps
and plugged in the sample weather api I made while following the tutorials.
However I could not seem to refer to this map when I added a KeyValueMapOperations policy inside the API proxy. I tried adding a mapIdentifier="countrymap" to the definition (based on the samples) but it still does not see it.
<KeyValueMapOperations async="false" continueOnError="false" enabled="true" mapIdentifier="countrymap" name="keyvaluemapoperations-2">
<DisplayName>KeyValueMapOperations-2</DisplayName>
<FaultRules/>
<Properties/>
<ExclusiveCache>false</ExclusiveCache>
<ExpiryTimeInSecs>-1</ExpiryTimeInSecs>
<Get assignTo="my.country" index="1">
<Key>
<Parameter ref="apigee.countryCode"></Parameter>
</Key>
</Get>
<Scope>organization</Scope>
</KeyValueMapOperations>
The policy works if I add an <InitialEntries> definition to the code above.
According to the Key/Value Maps docs:
Key/ValueMaps provide an API for storing arbitrary name/value pairs
that can be access at runtime by custom policies, or for other custom
runtime requirements such as protocol support.
Do custom policies include the KeyValueMapOperations policy? What am I missing here?
As you mentioned, you put the KeyValueMap in the scope of the api. To access that KVM, you'll need to use:
<Scope>apiproxy</Scope>
See the details for configuring the policy here.
Step 1: Verify if teh keyvalumap is correctly created:
/v1/o/{org_name}/apis/{api_name}/keyvaluemaps" --> this should list the countrymap KVM
Step 2: Populate the KVM before trying to do a GET call on it.You can populate it either by using API Calls (apigee.com/docs/api/api_methods/62-update-keyvaluemap) , or by Using PUT operation of KeyValueMapOperations as shown here (apigee.com/docs/api-services/content/persist-data-using-keyvaluemap)
Step 3: Check the entries of KVM
/v1/o/{org_name}/apis/{api_name}/keyvaluemaps/countrymap
Step 4: You can use the same KeyValueMapOperations Polices as given in example to do a get on the KVM
I was also facing this issue and after making below change in KeyValueMapOperations, it started working for me.
<Scope>apiproxy</Scope>
<Parameter>apigee.countryCode</Parameter>
This question is of two parts
Is it possible to provide Path Variables for Spring Web Flow?
Is it possible to hide the execution key in the URL
The current URL is as follows: http://localhost/bugs/ticket/?execution=e2s1
Here, the associated Web Flow location pattern is
<flow:flow-location-pattern value="/**/flow.xml" />
where the Folder Structure is: bugs/ticket/flow.xml
Is it possible to provide the ticket ID in the URL itself and point to the appropriate flow - i.e. the URL should be http://localhost/bugs/ticket/102?execution=e2s1, but still the flow is taken from bugs/ticket/flow.xml
I tried with the following patterns - value="/**/*/flow.xml", flow-path id="bugs/ticket/*" etc, but to no avail.
Also, is it possible to hide the execution key also in the URL? Is it possible to send it via say HTTP header which can be pulled in at Spring Web Flow ?
This does not answer all your questions
This is how I send parameters into the flow
External Page
Enter Flow
Flow
<view-state>
<on-entry>
<set name="variable2.field" value="requestParameters.uid"/>
</on-entry>
</view-state>
I've got a app.config configuration for Rebus and it works:
<configuration>
<configSections>
<section name="rebus" type="Rebus.Configuration.RebusConfigurationSection, Rebus" />
</configSections>
<rebus address="192.168.10.100" inputQueue="a.messages" errorQueue="a.error" workers="1" maxRetries="10">
<endpoints>
<add messages="ESB_Model" endpoint="a.messages#MyRemoteMachine" />
</endpoints>
</rebus>
</configuration>
After now I want to set the address and endpoint in code instead of configfile:
_adapter = new BuiltinContainerAdapter();
_bus = Configure.With(_adapter)
.Logging(l => l.None())
.Transport(t => t.UseMsmq("a.messages#MyRemoteMachine", "a.error"))
.MessageOwnership(d => d.FromRebusConfigurationSection())
.CreateBus()
.Start();
This is not accepted and I'm not sure how to set the IP-address. Any help would be welcome!
I can see a couple of issues with your code, first one being that the two configurations are not equivalent.
The issue with the second configuration is that Rebus does not allow you to use a remote queue as your endpoint's input queue. Therefore, the #-syntax should not be used when defining the input queue.
Also, it seems you're mixing something up - a.messages seems to be your input queue, but it also seems that you want to use it (granted: on another machine) as the owner of all messages from ESB_Model. It might be correct, but it seems you're mixing it up a little bit.
Usually, each endpoint should have its own unique input queue, and endpoints should always receive their messages from a local queue. And you should probably never have two endpoints receiving messages from the same queue.
Lastly: Unless you really know what you're doing, please don't explicitly specify the IP address of your endpoint - Rebus will automatically use the input queue in combination with the machine name as the address - e.g. if an endpoint a.messages running on SomeMachine sends a message to another endpoint, the return address will automatically be set as a.messages#SomeMachine.
If you'd like some more inspiration, you can check out the samples: https://github.com/mookid8000/Rebus/tree/master/samples/Rebus.Samples - the pub/sub sample has some simple configurations that work fine and don't over-specify.
Hope that clears it up a little bit - please let me know if you need more help :)