Friday, February 2, 2018

ColdFusion and AWS SNS

How to tie ColdFusion 11 server with AWS SNS.
Let's say you have an AWS SNS topic that sends a lot of information. And you're overwhelmed and you want a more streamlined solution such as a dashboard with database backend. And for the sake of this scenario, we'll leave AWS Gateway API and Lambda out of it. Because you already have a ColdFusion just sitting around.

Step 1: Write the CFC files
  1. Go to your website's physical directory and create a new folder called "rest". You don't have to call it that, but I do just for example sake. 
  2. Create two files:
    1. application.cfc
    2. sns.cfc
  3. application.cfc
    component output="false"
    {
         this.name = "my-rest";
         this.applicationTimeout = createTimespan(0,1,0,0);
         this.datasource = "myDB";
    
         this.restSettings.skipCFCWithError = true;
    
         public boolean function onRequestStart()
         {
              request._body = toString(getHttpRequestData().content);
              return true;
         }
    
         public boolean function onRequest()
         {
              return true;
         }
    }
    
    1. It is important that you set "request._body" here. Otherwise, you won't see the content. 
    2. You can put any other functions here to override the one from parent's Application.cfc
  4. sns.cfc
    <cfcomponent rest="true" restpath="sns">
     <cffunction name="postSNSTopic" access="remote" httpMethod="POST" returntype="string" produces="application/json">
      <cfargument name="body" type="string" restArgumentSource="body"/>
      <cfscript>
       response = "y";
    
       messageType = getHttpRequestData().headers["x-amz-sns-message-type"];
       messageID   = getHttpRequestData().headers["x-amz-sns-message-id"];
       topicARN    = getHttpRequestData().headers["x-amz-sns-topic-arn"];
       subject     = "None";
       messageJson = "";
       content     = "";
    
       switch(messageType){
        case "SubscriptionConfirmation":
         //Enter subscription action here
         break;
        case "Notification":
         //Enter notification action here
         break;
        default:
         response = "n";
       }
       
       if(response IS "y"){
        content = getHttpRequestData().content;
        contentLen = len(content);
        if(contentLen GT 0){
         contentStruct = deserializeJson(toString(content));
         subject = contentStruct["Subject"];
         if(len(subject) EQ 0){
          subject = "None";
         }
         messageJson = contentStruct["Message"];
         messageLen  = len(messageJson);
         if(messageLen EQ 0){
          messageJson = "No Content";
         }
        }else{
         content = "No Content";
         //TroubleShooting
         //headers = getHttpRequestData().headers;
         //for(item in headers){
          //content = content & "." & item & ":" & getHttpRequestData().headers[item];
         //}
        }
       }
      </cfscript>
      <cfif response EQ "y">
       <cfquery name = "sns_insert" datasource="mydb">
        insert into sns_listener(messageType, messageID, topicARN, subject, messageJSON, content, timestamp) values (
         <cfqueryParam value=#messageType# cfSqlType="cf_sql_varchar" maxlength="100"/>,
         <cfqueryParam value=#messageID# cfSqlType="cf_sql_varchar" maxlength="100"/>,
         <cfqueryParam value=#topicARN# cfSqlType="cf_sql_varchar" maxlength="100"/>,
         <cfqueryParam value=#subject# cfSqlType="cf_sql_varchar" maxlength="100"/>,
         <cfqueryParam value=#messageJson# cfSqlType="cf_sql_clob" maxlength="100"/>,
         <cfqueryParam value=#content# cfSqlType="cf_sql_clob" maxlength="100"/>,
         sysdate)
       </cfquery>
      <cfelse>
       <cfquery name = "sns_insert" datasource="mydb">
        insert into sns_listener(messageType, timestamp) values ('error',sysdate)
       </cfquery>
      </cfif>
     </cffunction>
    </cfcomponent>
    
  5. Details about HTTP Header and JSON content from SNS message can be found here.
Step 2: Configuring REST Service in ColdFusion 11
  1. Log into ColdFusion Administrator
  2. Under Server Settings, click Mappings
  3. Add Logical Path as "/rest" 
  4. Add Directory Path as the actual path of your files, "E:/website/rest"
  5. Under Data & Services, click REST services
  6. Add the Logical Path you used above, "/rest"
  7. Leave Host as blank
  8. You can leave service mapping blank, it will pick this up from your Application.cfc
  9. Select "Set as default application"
  10. Add service
Step 3: Go to AWS SNS and subscribe your endpoint
  1. Your REST end point will be https://www.mywebsite.com/rest/sns
  2. "rest" says this is a REST call
  3. "sns" is the name of your cfc file that contains the POST function
  4. Once you subscribe, you can manually confirm your subscription by pulling out the subscribeURL in the message or you can write a function in the body of sns.cfc above. 
References: 
GetHttprequest function
cfqueryparam function
HTTPS SNS Issue with CA
SNS JSON format

No comments:

Post a Comment

AWS WAF log4j query

How to query AWS WAF log for log4j attacks 1. Setup your Athena table using this instruction https://docs.aws.amazon.com/athena/latest/ug/wa...