What is the best practice to transform the path between proxy and target in apigee? - apigee

I am making a request /Patient/1 at the proxy endpoint, and I want to transform this into target endpoint /openemr/api. I tried solutions given in https://community.apigee.com/articles/2793/how-to-geo-locate-calls-to-target-endpoint.html, but none of these worked for me. Please suggest an optimal way for achieving this.
Thanks in advance.

It depends on how many reroutes you have to implement. So in case of 5+, use a javascript policy, such as:
var reqPathsuffix = context.getVariable("proxy.pathsuffix");
var reqVerb = context.getVariable("request.verb");
if ((reqPathsuffix === "/contact") && (reqVerb === "GET"))
{
endPointURL = "https://yourendpointurl/
}
context.setVariable("target.url",endPointURL);
else maybe use a 'AssignMessage' policy.
<AssignMessage async="false" continueOnError="false" enabled="true" name="Assign-Message-1">
<AssignTo createNew="false" transport="http" type="request"/>
<AssignVariable>
<Name>target.url</Name>
<Value>http://google.nl</Value>
</AssignVariable>
Don't forget to do this on the 'target endpoint preflow request' flow. Else it won't work.

Related

ASP.NET MVC CORS issue

I have an ASP.NET MVC application (.NET 4.6) where on one occasion I need to redirect to a different domain.
So the code is like this (only authenticated users can come to the controller):
public ActionResult UploadForDesign(int parcelId)
{
......
string url = "differentdomain/parcelId" ;
return ReDirect(url);
}
I am getting an error:
has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin'.....
Failed to load resource: net::ERR_FAILED
I did the following in the control before redirect call
HttpContext.Response.AddHeader("Access-Control-Allow-Origin", "*");
and I still get the same error.
Also I tried the following in web.config, but didn't help much.
<httpProtocol>
<customHeaders>
<clear />
<add name="Access-Control-Allow-Origin" value="*" />
</customHeaders>
</httpProtocol>
Now I had another test bare ASP.NET MVC app (.NET 4.5.2) with no authentication, and the above code works fine. I checked all the code and it looks the same except authentication in place and the framework version.
Any idea what to do?
You can try to use the WithOrigins method on your CORS policy and add whatever domain you need:
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder =>
.WithOrigins("http://localhost:4200/")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials()
);
});
Ahhh sorry. I believe you should be able to use something like:
var p = new CorsPolicy();
p.AllowAnyHeader = true;
p.AllowAnyMethod = true;
p.AllowAnyOrigin = false;
p.Origins.Add("http://localhost:4200/");

How to omit null values in JSON AssignMessage payload in APIGEE?

I built a proxy that basically expects a different JSON input object than the one the final endpoint is expecting to receive. So, in order to bridge the request object from one to the other I'm using an AssingMessage policy to transform the json input.
I'm doing something like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<AssignMessage async="false" continueOnError="false" enabled="true" name="Assign-Message-Sample">
<DisplayName>Assign Message-Sample</DisplayName>
<Remove>
<Headers>
<Header name="login_id"/>
<Header name="Authorization"/>
</Headers>
<Payload>true</Payload>
</Remove>
<Set>
<Payload contentType="application/json">
{
"valueA": "{clientrequest.valueA}",
"valueB": "{clientrequest.valueB}",
"valueC": "{clientrequest.valueC}",
"valueD": "{clientrequest.valueD}",
"valueE": "{clientrequest.valueE}",
"valueF": "{clientrequest.valueF}",
}
</Payload>
<Verb>POST</Verb>
</Set>
<IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
<AssignTo createNew="false" transport="http" type="request"/>
</AssignMessage>
The problem comes when some of the values are empty. The destination server does not handle properly any empty values (escapes from my control).
My question is: how can I skip entirely a parameter if value is empty?
I'm looking for something like this (or better alternative):
<Payload contentType="application/json">
{
<skip-if-empty name="clientrequest.valueA">
"valueA": "{clientrequest.valueA}",
</skip-if-empty>
"valueB": "{clientrequest.valueB}",
...
}
</Payload>
For what I have found from my research, it seems this is a job for a Javascript Policy.
How is this done?
You basically need to place a javascript policy right before the AssignMessage execution. In the javascript policy you have the freedom to apply all the logic to omit certain parameters if values are not provided.
So for example, say we have already extracted the request values to variables using an ExtractVariables policy. Then, in the Javascript policy we can validate those values and build the resulting JSON object to later store it in another variable that will be picked up by the AssingMessage policy.
javascript policy:
var valueA = context.getVariable("clientrequest.valueA"),
valueB = context.getVariable("clientrequest.valueB"),
valueC = context.getVariable("clientrequest.valueC"),
...
var result = {};
if(valueB) {
result.b = valueB;
}
if(valueA) {
result.a = valueA;
}
if(valueC) {
result.c = valueC;
}
...
context.setVariable("newInput", JSON.stringify(result));
Then our AssignMessage will just pick up the variable we just stored: "newInput" that will contain the complete JSON object string:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<AssignMessage async="false" continueOnError="false" enabled="true" name="Assign-Message-Sample">
<DisplayName>Assign Message-Sample</DisplayName>
<Remove>
<Headers>
<Header name="login_id"/>
<Header name="Authorization"/>
</Headers>
<Payload>true</Payload>
</Remove>
<Set>
<Payload contentType="application/json">
{newInput}
</Payload>
<Verb>POST</Verb>
</Set>
<IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
<AssignTo createNew="false" transport="http" type="request"/>
</AssignMessage>
This solution worked fine for me. I hope someone else finds it helpful.

Using AssignMessage policy to generate a formatted string in Apigee

I am doing a URL rewrite in my API End point. So, the querystring is not passed by default to the backend service.
I want to create a variable with name "querystring" and format that string with the input query params. How can I do that?
For eg: apigee api url = https://myapigee-prod.agigee.net/v1/test/resource1?p1=abc&p2=123
I want to create the querystring variable as querystring = param1=abc&param2=123
I tried
<AssignVariable>
<Name>myqs</Name>
<Value>param1={request.queryparam.p1}&param2={request.queryparam.p2}</Value>
</AssignVariable>
<Ref> tag can be used instead of <Value> tag only if I am getting value of one parameter. To form a string, it wouldn't work.
Unfortunately you can't use the curly braces method of variable expansion within an AssignVariable.
You can do this via a JavaScript policy. Here is the JavaScript code:
var myqs = "param1=" + context.getVariable("request.queryparam.p1") +
"&param2=" + context.getVariable("request.queryparam.p2");
context.setVariable("myqs", myqs);
Also, you can use an AssignMessage policy to do this, on the Proxy request or Target request flow.
http://apigee.com/docs/api-services/content/generate-or-modify-messages-using-assignmessage
e.g.
<AssignMessage name="GenerateRequest">
<AssignTo createNew="false" type="request">Request</AssignTo>
<Set>
<QueryParams>
<QueryParam name="p1">{request.queryparam.param1}</QueryParam>
<QueryParam name="p2">{request.queryparam.param2}</QueryParam>
</QueryParams>
</Set>
</AssignMessage>

How to add basic auth to a service callout policy

This is the service callout policy:
<ServiceCallout name="GeoCodeClient">
<Request clearPayload="false" variable="GeocodingRequest" />
<Response>GeocodingResponse</Response>
<Timeout>30000</Timeout>
<HTTPTargetConnection>
<URL>http://maps.googleapis.com/maps/api/geocode/json</URL>
</HTTPTargetConnection>
</ServiceCallout>
Let us say I have to access a resource that is username/password protected. How do I add that basic authorization to this policy to enable me to do that?
In our project a KeyValueMaps are used to store the basic auth info at org level. The authorisation information is retrieved using the KeyValueMap policy and added as the basic auth header to the request message.
See if this approach works for you.
To add Basic Authentication header for your service callout, you can use an 'AssignMessage' policy that sets the 'Authorization' header in the 'GeocodingRequest' as follows:
<AssignMessage enabled="true" continueOnError="true" async="false" name="AssignAuthorizationHeaderPolicy">
<IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
<AssignTo createNew="true" transport="http" type="request">GeocodingRequest</AssignTo>
<Add>
<Headers>
<Header name="Authorization">Basic YourAuthenticationHeader</Header>
</Headers>
</Add>
</AssignMessage>
Once you have created this policy, you will need to attach it in the request flow before the serviceCallout in the proxy.xml as flows:
<Step>
<FaultRules/>
<Name>AssignAuthorizationHeaderPolicy</Name>
</Step>
<Step>
<FaultRules/>
<Name>GeoCodeClient</Name>
</Step>
to add to what's already been said, if you need base64 encoding (and you probably will if you're using Basic Authorization), you'll need to do script callout. For instance, you can use the following Python:
import base64
if (client_secret is not None):
data = client_id + ":" + client_secret
header_value = base64.b64encode(data)
header_value = "Basic " + header_value
flow.setVariable("request.header.Authorization", header_value)
JS will be a little trickier since you need to include appropriate libraries, but I'm sure SO has plenty of more examples to follow for that.
Using Key Value Map to store sensitive data in a secure way
Step 1)Use below API to Create/Update the key Value maphttps://api.enterprise.apigee.com/v1/o/{orgname}/environments/{env}/keyvaluemaps Body:-{
"entry" : [ {
"name" : "basic_auth_system1",
"value" : "Basic XXXXXXXXXXX"
} ],
"name" : "system1_credentials"
}
Step 2) Policy used to lookup The key Value map
<KeyValueMapOperations enabled="true" continueOnError="false" async="false" name="keymap_get_credentials" mapIdentifier="system1_credentials">
<DisplayName>keymap_get_credentials</DisplayName>
<FaultRules/>
<Properties/>
<ExpiryTimeInSecs>-1</ExpiryTimeInSecs>
<Get assignTo="basic_auth_system1">
<Key>
<Parameter>basic_auth_system1</Parameter>
</Key>
</Get>
<Scope>environment</Scope>
</KeyValueMapOperations>

Does anyone know if the crossdomain policy file at salesforce.com has changed?

Suddenly my Flex Apps can no longer connect to salesforce.com via its API, I am getting a security sandbox violation. Login credentials are correct, I have tried them via a different means, and I have obfuscated them below.
This was working fine earlier today and I have not been coding since then.
Anyone else come across this or know what's going on?
Here is the exception returned to my app
Method name is: login
'A997F86A-36E9-DDDC-EC6B-BBEE23101466' producer connected.
'A997F86A-36E9-DDDC-EC6B-BBEE23101466' producer sending message 'B89E5879-D7F7-E91E-2082-BBEE231054DD'
'direct_http_channel' channel sending message:
(mx.messaging.messages::HTTPRequestMessage)#0
body = "<se:Envelope xmlns:se="http://schemas.xmlsoap.org/soap/envelope/"><se:Header xmlns:sfns="urn:partner.soap.sforce.com"/><se:Body><login xmlns="urn:partner.soap.sforce.com" xmlns:ns1="sobject.partner.soap.sforce.com"><username>simon.palmer#***.com</username><password>***</password></login></se:Body></se:Envelope>"
clientId = (null)
contentType = "text/xml; charset=UTF-8"
destination = "DefaultHTTPS"
headers = (Object)#1
httpHeaders = (Object)#2
Accept = "text/xml"
SOAPAction = """"
X-Salesforce-No-500-SC = "true"
messageId = "B89E5879-D7F7-E91E-2082-BBEE231054DD"
method = "POST"
recordHeaders = false
timestamp = 0
timeToLive = 0
url = "https://www.salesforce.com/services/Soap/u/11.0"
Method name is: login
*** Security Sandbox Violation ***
Connection to https://www.salesforce.com/services/Soap/u/11.0 halted - not permitted from https://localhost/pm_server/pm/pm-debug.swf
'A997F86A-36E9-DDDC-EC6B-BBEE23101466' producer acknowledge of 'B89E5879-D7F7-E91E-2082-BBEE231054DD'.
'A997F86A-36E9-DDDC-EC6B-BBEE23101466' producer fault for 'B89E5879-D7F7-E91E-2082-BBEE231054DD'.
Comunication Error : Channel.Security.Error : Security error accessing url : Destination: DefaultHTTPS
Error: Request for resource at https://www.salesforce.com/services/Soap/u/11.0 by requestor from https://localhost/pm_server/pm/pm-debug.swf is denied due to lack of policy file permissions.
You have to make sure to load the policy from the /services tree, the default policy at the root won't help you. You need to load this policy https://www.salesforce.com/services/crossdomain.xml
The solution to this problem was to set the server protocol and url as follows:
apex = new Connection();
apex.serverUrl = "https://na3.salesforce.com/services/Soap/u/14.0";
apex.protocol = "https";
However, this seems to create a secondary issue of users being locked out, so the issue of non-connectivity remains.
Update: salesforce.com have acknowledged a bug. See my other related post.
Did you recently upgrade to flash player 10? Flash player 10 changes the way policy files work to some degree, and the crossdomain.xml file needs to be updated to address this. In short, Salesforce.com probably isn't prepared for users upgrading to Flash Player 10 yet.
I resolve this issue accessing to the Flash Player Configuration Panel(I just recommend it in a development environment), in the "Global Security" tab, select Always Allow.
Regards.
I am uploading a file from flex to Google docs. Everything is working in the local file however, when we upload the SWF file as S-controls in Salesforce (sandbox), an error appears upon connecting to Google. Please see error below:
Error:[FaultEvent fault=[RPC Fault faultString="Security error accessing url"
faultCode="Channel.Security.Error" faultDetail="Destination: DefaultHTTPS"]
messageId="1F812836-1318-B845-AC01-F51AB1D11518" type="fault" bubbles=false
cancelable=true eventPhase=2]
We tried the following solutions below but nothing seems to work:
FLEX:
- Add the crossdomain.xml in the bin-debug folder: below is the content of the cross domain policy.
<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
<allow-access-from domain="*" secure="false" />
<allow-http-request-headers-from domain="*" headers="*" secure="false" />
</cross-domain-policy>
Used flash.system.security.allowinsecuredomain/allowdomain(“*”) in the initialization.
Also tried in the connection.protocol set to http
Salesforce:
Disabled the protocol security in the remote site settings
o Setup -> Administration Setup -> Security Controls -> Remote Site Settings
 URL: http://www.google.com.ph
There’s no problem in connection to Salesforce but upon initialization of the uploading page the security error will appear specifically in the onErrorFault function. Below are code snippets:
<?xml version="1.0" encoding="utf-8"?>
<mx:TitleWindow xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" width="534" height="462" verticalScrollPolicy="off" horizontalScrollPolicy="off"
creationComplete="init()" showCloseButton="true" close="{this.closeWindow(event)}" roundedBottomCorners="true">
<mx:Script>
<![CDATA[
private function init():void{
Security.allowInsecureDomain("*");
//<salesforce:Connection id="apex" sendRequest="sendRequestListener(event)" serverUrl="http://www.salesforce.com/services/Soap/u/10.0" protocol="http"/>
RESTProxyTest();
send_data();
arrAddedFiles = new Array();
this.uploadGrid.dataProvider= this.acFiles;
this.title = "Attachment: "+this.selectedTimeSheetDetail.Project.label;
}
public function RESTProxyTest():void
{
_conn = new NetConnection();
_conn.addEventListener(AsyncErrorEvent.ASYNC_ERROR, doAsyncError);
_conn.addEventListener(IOErrorEvent.IO_ERROR, doIOError);
_conn.addEventListener(SecurityErrorEvent.SECURITY_ERROR, doSecurityError);
_conn.addEventListener(NetStatusEvent.NET_STATUS, doNetStatus);
_conn.objectEncoding = ObjectEncoding.AMF3;
_conn.connect(_url);
_responder = new Responder(onResult, onFault);
}
private function send_data():void {
userRequest.url = getLoginURL();
userRequest.addEventListener(ResultEvent.RESULT, httpResult);
userRequest.addEventListener(FaultEvent.FAULT, onErrorFault);
userRequest.send();
}
private function onErrorFault(obj:FaultEvent):void
{
Alert.show("Error:"+obj.toString());
}
private function httpResult(obj:ResultEvent):void
{
trace(obj.toString());
var result:String = obj.result as String;
var pos:int = result.lastIndexOf("Auth=");
var auth:String = result.substr(pos + 5);
txtAuth.text = StringUtil.trim(auth);
placeCall();
}
protected function placeCall():void
{
trace("placeCall");
var headers:Array = ["Authorization: " + "GoogleLogin auth=" + StringUtil.trim(txtAuth.text)];
var postVars:Array = [];
var uri:String = "http://docs.google.com/feeds/documents/private/full?showfolders=true";
_conn.call("RESTProxy.request", _responder, uri, "get", new Array(), postVars, headers);
}
private function getLoginURL():String
{
var url:String = 'https://www.google.com/accounts/ClientLogin?accountType=HOSTED_OR_GOOGLE&' +
'Email=' + this.session.config.gmail + '&' +
'Passwd=' + this.session.config.password + '&service=writely';
return url;
}
]]>
</mx:Script>
<mx:HTTPService id="userRequest" useProxy="false" method="POST" contentType="application/x-www-form-urlencoded" showBusyCursor="true"/>

Resources