adding uri escaped xml as queryparam - apigee

Hello and thanks for any support.
So I have an API that is expecting an escaped xml as part of the query params. so I tried a couple of things.
<Set>
<QueryParams>
<QueryParam name="xml">%3CcreateSession%3E%3CapiKey+type%3D%22integer%22%3123123123123123123%3C%2FapiKey%3E%3C%2FcreateSession%3E</QueryParam>
</QueryParams>
</Set>
this actually gets extra escaped and we end up with an invalid xml
<Set>
<QueryParams>
<QueryParam name="xml">
<createSession><apiKey type=\"integer\">123123123123</apiKey>
</createSession>
</QueryParam>
</QueryParams>
</Set>
this one is not even saved.
<Payload variablePrefix="#" variableSuffix="#" contentType="application/json">
{ 'xml': '<createSession><apiKey type=\"integer\">123123123123</apiKey></createSession>'}
</Payload>
this is not helping me either.
so what would be the best way to add an xml to the params without getting extra escaped or escaped only once

Did you try
<Set>
<QueryParams>
<QueryParam name="xml">
<createSession><apiKey type=\"integer\">123123123123</apiKey>
</createSession>
</QueryParam>
</QueryParams>
</Set>

Related

ERP Operation not found error in WSO2EI DSS

I have created a REST - data service in WSO2EI an tried accessing through postman client tool, but I am getting this error.
End Point: http://LAPTOP-T4F1HOAM:8280/services/getStudRecNo?user_id=test8
The endpoint reference (EPR) for the Operation not found is /services/getStudRecNo?user_id=test8 and the WSA Action = null. If this EPR was previously reachable, please contact the server administrator.
Can anyone please help.
Thanks
dss code.
<data name="getStudRecNo" transports="http https local">
<description>get student unique id</description>
<config enableOData="false" id="mySchoolDB">
<property name="driverClassName">org.postgresql.Driver</property>
<property name="url">jdbc:postgresql://localhost:5432/mySchool</property>
<property name="username">admin</property>
<property name="password">admin</property>
</config>
<query id="GetStudentRecordNo" useConfig="mySchoolDB">
<sql>SELECT getstudentid FROM getstudentid(?)</sql>
<result outputType="json">{"entries": {"entry": [ { "getstudentid": "$getstudentid"} ]}}</result>
<param defaultValue="TEST" name="user_id" sqlType="STRING"/>
</query>
<operation name="getsrno">
<call-query href="GetStudentRecordNo">
<with-param name="user_id" query-param="user_id"/>
</call-query>
</operation>
<resource method="GET" path="getsrno">
<call-query href="GetStudentRecordNo">
<with-param name="user_id" query-param="user_id"/>
</call-query>
</resource>
</data>
You have to append the resource path ("getsrno" in your case) to the URL. Then the URL would look like,
http://LAPTOP-T4F1HOAM:8280/services/getStudRecNo/getsrno?user_id=test8
Further, since the resource's HTTP method is GET, the request should also be a GET request.

Path Variables in Web Security Expressions and set /users/{userId:\\d+}

spring security 4.1.3.RELEASE intercept-url request-matcher="mvc"
I try to use spring-security.xml to config my web security with request-matche="mvc", but the pattern "/users/{userId:id}" dosen't work.
My config just look like the following code:
<http entry-point-ref="myAuthenticationEntryPoint" auto-config="true" use-expressions="true" create-session="stateless" request-matcher="mvc"
access-decision-manager-ref="myAccessDecisionManager">
<intercept-url pattern="/users/{userId:\\d+}" method="GET" access="#webSecurity.isMe(authentication, #userId) or hasAnyRole('ADMIN')"/>
<intercept-url pattern="/users/management" method="GET" access="hasAnyRole('ADMIN')"/>
</http>
I expecte that the request for /users/12345 will match the pattern /users/{userId:\d+} which will match 12345 to the userId, and the request for /users/management will match the pattern /users/management. However, in fact, the pattern /users/{userId:\d+} dosen't work, it never match the request /users/12345. and if I change the setting to the following code:
<http entry-point-ref="myAuthenticationEntryPoint" auto-config="true" use-expressions="true" create-session="stateless" request-matcher="mvc"
access-decision-manager-ref="myAccessDecisionManager">
<intercept-url pattern="/users/{userId}" method="GET" access="#webSecurity.isMe(authentication, #userId) or hasAnyRole('ADMIN')"/>
<intercept-url pattern="/users/management" method="GET" access="hasAnyRole('ADMIN')"/>
</http>
Remove the :\d+ from /users/{userId:\d+}, it will match the request for /users/12345. But it will match the request for /users/management as well, which makes the value of userId equals to unexpected "management". I have try ant-matcher and regex-matcher, and I can't find the userId in a perfect solution.
I have solved the problem by myself. It's a stupid mistake. To solve the problem, I just need to replace /users/{userId:\\d+} with /users/{userId:\d+} or /users/{userId:[0-9]+} and use AntPathMatcher instead of using MvcPathMatcher. Configuring the security with spring-security.xml is defferent with using javaConfig or using annotation. In javaConfig or annotation #RequestMapping, the content in "" is String Object, so we need an annother \ to escape the \d+. But in xml, it's unnecessary. If using \\d+ here, spring security will append it to \\\\d+, which will never match number type string. I find this solution when I set a breakpoint in org.springframework.util.AntPathMatcher.matchStrings(String str, Map<String, String> uriTemplateVariables) and watch the request.

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>

Resources