I am trying to fetch a pdf document from Companies House API - api

I am trying to fetch a pdf document from API using ColdFusion and I receive this error:
<?xml version="1.0" encoding="UTF-8"?> <Error><Code>InvalidArgument</Code> <Message>Only one auth mechanism allowed; only the X-Amz-Algorithm query parameter, Signature query string parameter or the Authorization header should be specified</Message><ArgumentName>Authorization</ArgumentName> <ArgumentValue>#key#</ArgumentValue> <RequestId>Some requestid</RequestId> <HostId>some host id</HostId>
400 Bad Request
Here is my code:
<cfset urlD="https://document-api.company-information.service.gov.uk/document/#documentID#/content">
<cfhttp urlD="#Url#" method="GET" result="takeDoc" resolveurl="Yes" timeout="999">
<cfhttpparam type="HEADER" name="Accept" value="application/pdf">
<cfhttpparam type="HEADER" name="content-length" value=93295 />
<cfhttpparam type="HEADER" name="Authorization" value="#key#"/>
</cfhttp>
<cfdump var="#takeDoc#">
Any ideas how to solve this final step?

I appears you are transmitting the value #key# as your Authorization instead of the value for the variable key. try putting cfoutput tags around your cfhttp tags.

Related

Using ColdFusion REST API, how do I return a response in onError handler?

I'm trying to set up a REST Web Services with ColdFusion 10, and if I have an onError handler in Application.cfc to handle incorrect parameter error.
My Download API Service:
<cfcomponent rest="true" restpath="downloadStuff">
<cffunction name="downloadStuff" access="remote" returntype="String" httpmethod="GET">
<!--- parameters --->
<cfargument name="id" required="true" type="string" restargsource="query"/>
<cfargument name="account" required="true" type="string" restargsource="query"/>
<!--- download stuff logic --->
...
<cfreturn response>
</cffunction>
</cfcomponent>
It is expecting 2 parameters and user get a 500 error if parameters are not there. I don't want user to see the 500 error, I want user to see a normal response message asking user to add the missing parameter.
This is my onError handler in application.cfc
<cffunction name="onError" returntype="String" output="true">
<cfargument name="exception" required="true">
<cfargument name="eventname" type="string" required="true">
<!--- error logging here --->
<cfset response = "Please add missing parameters">
<cfreturn response>
When user enter incorrect parameters to API, I am getting 500 error with "cannot convert the value "Please add missing parameters" to a boolean".
How can I pass back a response message in onError? Is there a better way to handle incorrect parameters error and return a message to user?

ADLS SAS token is truncated when rewriting in API Management

I have an ADLS with images that I want to display on my website.
I want to expose them through APIM. I am sending the image name and SAS token in the request which I re-write in the actual backend request with the right folder structure.
The policy -
<policies>
<inbound>
<set-variable name="BlobName" value="#(context.Request.Url.Query.GetValueOrDefault("BlobName"))" />
<set-variable name="sasToken" value="#(System.Net.WebUtility.UrlDecode(context.Request.Url.Query.GetValueOrDefault("sasToken")))" />
<base />
<set-backend-service base-url="#{
string blobName = context.Variables.GetValueOrDefault<string>("BlobName");
string sasToken = context.Variables.GetValueOrDefault<string>("sasToken");
return String.Format("https://myadls.blob.core.windows.net/UserImages/Images/{0}?{1}",blobName,sasToken);
}" />
<authentication-managed-identity resource="https://storage.azure.com/" />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
The SAS token - sv=2020-08-04&st=2022-02-24T04%3A17%3A53Z&se=2022-02-24T06%3A17%3A53Z&sr=c&sp=r&sig=5B6IUrj9VSh7oZSHAOKQK7fsWLun%2B%2BL7v0o1gQJHxvU%3D
Since the SAS token and '&' sign, the sasToken string is getting truncated to 'sv=2020-08-04'
As you can see in the policy I tried to encode the SAS in c# as
System.Net.WebUtility.UrlEncode(dataLakeSasBuilder.ToSasQueryParameters(sharedKeyCredential).ToString());
But, the System.Net.WebUtility.UrlDecode did not decode the value.
Thanks in advance.
I found that encoding it in the code and decoding the string in the policy is solving this
Encoding.UTF8.GetString(Convert.FromBase64String(context.Request.Url.Query.GetValueOrDefault("sasToken")))

How to consume API in ColdFusion 10

Can you please help me? I am trying to consume response in my ColdFusion application. Just wanted to try with this fake API before proceeding to the actual one.
I have created a component with two functions inside it. My cfc looks like this:
photoUploadNew.cfc
<cfcomponent displayname="test" hint="testing.." output="yes">
<cfsetting enablecfoutputonly="true" showdebugoutput="true">
<cffunction name="start" access="public" output="no" returntype="any" description="initialize the component">
<cfset variables.testUrl = "https://jsonplaceholder.typicode.com/posts">
<cfreturn this>
</cffunction>
<cffunction access="public" output="false" name="testGetReq" displayname="TestGetReq" description="testing" returntype="any">
<cfset variables.testUrl = "https://jsonplaceholder.typicode.com/posts">
<cfhttp
result="httpResponsetest"
url="#variables.testUrl#"
timeout="30"
method="get"
>
<cfhttpparam
type="header"
name="Content-Type"
value="application/json"
/>
</cfhttp>
</cfhttp>
<cfreturn httpResponsetest>
</cffunction>
</cfcomponent>
In my cfm page. I am trying to instantiate this component and print whatever I am getting as a response but I am not able to print anything out there.
<cfset testObj = CreateObject("component","usedGear_admin.cfc.photoUploadNew").testGetReq()>
<cfoutput >
#testObj#
</cfoutput>
Any help would be greatly appreciated.
I think you are using cfhttp result wrong here. When we do a cfhttp call,
<cfhttp
method="get"
result="httpResponsetest"
url="https://jsonplaceholder.typicode.com/posts"
timeout="30"
>
</cfhttp>
They try the following, you will see httpResponsetest has multiple keys. The data provided by API will be present in httpResponsetest.fileContent. Also most of the time there is Mimetype,Responseheader,Statuscode etc.
<cfdump var="#httpResponsetest.fileContent#">
Here you can see the data is in JSON format. That means you'll need to deserialize them to be able to use it.
<cfdump var="#deserializeJSON(httpResponsetest.fileContent)#">
You can deserialize it and return from the function. Along with that you'll need to handle the case where API responds with am error.
Demo

how can set header mediator value with runtime value in WSO2 APIM 2.6?

I get a token authorization parameter from a Login API. I need to use this token in my header parameters I gave with In Flow mediator in another API.so I try use CORS configuration and get the token parameter as type header and fill it when invoking the API.
I gave this token value in mediator with static value and It worked. But this value is dynamically changed by invoking Login API so I have to give run time value.
my mediator is :
<?xml version="1.0" encoding="UTF-8"?>
<sequence name="tokenMediator" trace="disable" xmlns="http://ws.apache.org/ns/synapse">
<log level="full"/>
<header name="caller-token" scope="transport" value="123"/>
<header name="Content-Type" scope="transport" value="application/json"/>
<header name="Access-Control-Allow-Origin" scope="transport" value="http://onlinewebapi"/>
<header expression="$header:token" name="Authorization" scope="transport"/>
<property name="messageType" scope="axis2" type="STRING" value="application/json"/>
</sequence>
when I invoke API I get this : "TypeError: Failed to fetch"
and it don't pass WSO2 to reach my local codes.
I changed the name of parameter from token to authorization and result don't changed.

Coldfusion CFHTTP with SHA512-hmac signed REST request body

I am trying to make a signed request to the trading API at bitfloor.com (it's a REST API)
Bitfloor gives me:
1) API Key (i.e. 6bd2b780-00be-11e2-bde3-2837371c3c3a)
2) Secret Key (i.e. oaFz62YpmbWiXwseMUSod53D8pOjdyVcweNYdiab/TSQqxk6IuemDvimNaQoA==)
The following is Bitfloor's exact instructions for making the request:
Requests must be HTTPS POST requests on port 443 (https). Each request must contain the required headers (listed below). The headers identify, verify, and validate your request to prevent tampering.
headers
bitfloor-key This is the provided by bitfloor to uniquely identify your account. (i.e. 6bd2b780-00be-11e2-bde3-2837371c3c3a)
bitfloor-sign The sign field is a sha512-hmac of the request body using the secret key which corresponds to your api key.
To sign your request: base64 decode the secret key into the raw bytes (64 bytes). Use those bytes for your sha512-hmac signing of the http request body. Base64 encode the signing result and send in this header field.
bitfloor-passphrase The passphrase you specified when creating this api key. We cannot recover your passphrase if forgotten. You will need to create a new API key.
bitfloor-version The api version of the resource you are interested in. The only valid value currently is 1
After a full eight hours of trial and error and searching the internet repeatedly for any sort of insight or information, the following code is as close as I can come to what I think might be somewhere in the direction of how to construct the request properly, alas, no matter what I attmept I get "Invalid Signature" returned by their API.
Here is what I have so far...
FIRST, I found this function on the web that someone wrote to do the SHA512 signing:
<cffunction name="HMAC_SHA512" returntype="binary" access="public" output="false">
<cfargument name="signKey" type="string" required="true">
<cfargument name="signMessage" type="string" required="true">
<cfset var jMsg = JavaCast("string",arguments.signMessage).getBytes("iso-8859-1")>
<cfset var jKey = JavaCast("string",arguments.signKey).getBytes("iso-8859-1")>
<cfset var key = createObject("java","javax.crypto.spec.SecretKeySpec")>
<cfset var mac = createObject("java","javax.crypto.Mac")>
<cfset key = key.init(jKey,"HmacSHA512")>
<cfset mac = mac.getInstance(key.getAlgorithm())>
<cfset mac.init(key)>
<cfset mac.update(jMsg)>
<cfreturn mac.doFinal()>
</cffunction>
I have no idea what it does, but it seems to work and does so without error.
Here is my implementation of this function and my attempt at making the request:
NOTE: The "nonce" value is a required param that must be sent with the request.
<cffunction name="myorders">
<cfset nonce = dateDiff("s",createDateTime(2012,01,01,0,0,0),now())>
<cfset requestbody = "?nonce=#nonce#">
<cfset key = "oaFz62YpmbWiXwseMUSod53D8pOjdyVcweNYdiab/TSQqxk6IuemDvimNaQoA==">
<cfset sign = HMAC_SHA512(key,requestbody)>
<cfset signed = binaryEncode(sign,"Base64")>
<!--- HTTP REQUEST --->
<cfhttp url = "https://api.bitfloor.com/orders#requestbody#"
method = "post"
result = "bitfloor">
<!--- HEADERS --->
<cfhttpparam
type = "body"
value = requestbody>
<cfhttpparam
type = "header"
name = "bitfloor-key"
value = "6bd2b780-00be-11e2-bde3-2837371c3c3a">
<cfhttpparam
type = "header"
name = "bitfloor-sign"
value = signed>
<cfhttpparam
type = "header"
name = "bitfloor-passphrase"
value = "mysecretpassphrase">
<cfhttpparam
type = "header"
name = "bitfloor-version"
value = "1">
</cfhttp>
</cffunction>
I think most of my confusion comes from not knowing exactly what the "request body" is. I feel like I'm not signing the right thing perhaps.
I hope there is a Coldfusion programmer out there who is familiar with signed requests. I'm at my wit's end.
Please help! Namaste
I have not used that api, but I ran some tests and it seems to work with the following tweaks:
Since the secretKey value is base64 encoded, your signing function needs to use binaryDecode to properly extract the bytes. Using String.getBytes(...) produces a completely different (and wrong) result.
The expected request body value is just: nonce=#nonceValue# (without the leading "?")
It seems to require the Content-Type=application/x-www-form-urlencoded header, otherwise it fails to parse the content and the response is: {"error":"no nonce specified"}
Code
<cfset apiKey = "6bd2b780-00be-11e2-bde3-2837371c3c3a">
<cfset secretKey = "oaFz62YpmbWiXwseMUSod53D8pOjdyVcweNYdiab/TSQqxk6IuemDvimNaQoA==">
<cfset passphrase = "your secret phrase">
<cfset requestBody = "nonce="& now().getTime()>
<cfset signBytes = HMAC_SHA512(secretKey, requestbody)>
<cfset signBase64 = binaryEncode(signBytes, "base64")>
<cfhttp url="https://api.bitfloor.com/orders" method="post" port="443" result="bitfloor">
<cfhttpparam type="header" name="Content-Type" value="application/x-www-form-urlencoded">
<cfhttpparam type="header" name="bitfloor-key" value="#apiKey#">
<cfhttpparam type="header" name="bitfloor-sign" value="#signBase64#">
<cfhttpparam type="header" name="bitfloor-passphrase" value="#passphrase#">
<cfhttpparam type="header" name="bitfloor-version" value="1">
<cfhttpparam type="body" value="#requestBody#">
</cfhttp>
<cfdump var="#bitfloor#" label="Response">
<cffunction name="HMAC_SHA512" returntype="binary" access="public" output="false">
<cfargument name="base64Key" type="string" required="true">
<cfargument name="signMessage" type="string" required="true">
<cfargument name="encoding" type="string" default="UTF-8">
<cfset var messageBytes = JavaCast("string",arguments.signMessage).getBytes(arguments.encoding)>
<cfset var keyBytes = binaryDecode(arguments.base64Key, "base64")>
<cfset var key = createObject("java","javax.crypto.spec.SecretKeySpec")>
<cfset var mac = createObject("java","javax.crypto.Mac")>
<cfset key = key.init(keyBytes,"HmacSHA512")>
<cfset mac = mac.getInstance(key.getAlgorithm())>
<cfset mac.init(key)>
<cfset mac.update(messageBytes)>
<cfreturn mac.doFinal()>
</cffunction>