I am trying to setup my CF server with Amazon's SES but I guess I am doing something wrong...
This is what I did so far
• Created credentials from my AWS console
• Added the necessary settings (server, port, user/pass) in my CF admin
• Created a test script
• No errors of any sort appeared
• No emails received and based on my AWS SES console nothing was sent out.
Anyone ever used this service before with CF and can point me to the right direction I will appreciate it.
The below code does not use the CFMail tag. It allows for sending raw formatted emails, calendar invites, etc... using a CFHTTP POST to AWS SES without the constraints of the CFMAIL tag. If you want to use the CFMail TAG, see the AWS instructions for configuring an SMTP server.
Grab the following before implementing the below code:
Refer to <cfLove/> Sending emails with Amazon SES API SendRawEmail in ColdFusion to construct raw email message
Reference to Leigh Sv4Util.cfc for AWS v4 Signing
NOTE: in the V4 Signing cfc -- CF16 returns the AMZDate as +0000
for zulu. Find AMZDate (2 places) change to LEFT(AMZDate, 15) & "Z" to force
the to end in Z -- 20200930T222905+0000 -> 0200930T222905Z.
Refer to AWS for Sample SES Post
The sample code below assumes that attributes.mail was formatted from the sample code from CFLove and you are using the Sv4Util.cfc for signing.
<!--- This is sample code for send to AWS SES as Raw Data Send post --->
<!--- Init Signing CFC --->
<cfset LocalVar.s4 = CreateObject('component', 'Sv4Util.cfc').init(
accessKeyId = attributes.AWSKey
, secretAccessKey = attributes.AWSSecretKey
, defaultRegionName = attributes.AWSRegion
, defaultServiceName = "ses"
)>
<!---
Refer to https://docs.aws.amazon.com/ses/latest/DeveloperGuide/using-ses-api-requests.html
AWS Post Example Requirements
- Action
- Destinations.members.?
- Source
- RawMessage.Data
--->
<cfset PL = {}>
<cfset PL['Action'] = "SendRawEmail">
<!--- List of Email Addresses --->
<cfset count = 1>
<cfloop list="#attributes.deliverto#" index="i">
<cfset PL["Destinations.members.#count#"] = i>
<cfset count = count + 1>
</cfloop>
<!--- From --->
<cfset PL["Source"] = trim(attributes.from)>
<!--- The RAW MESSAGE -- Must be encoded due to BASE64 ending with an = --->
<cfset PL["RawMessage.Data"] = URLEncodedFormat(ToBase64(trim(ArrayToList(attributes.mail,chr(10)))))>
<!--- Build URL format string for posting the above struct (PLS) --->
<cfset PLS = ''>
<cfloop list="#ListSort(structKeyList(PL), 'text', 'asc')#" index="i" >
<cfset PLS = ListAppend(PLS, i & "=" & PL[i], "&")>
</cfloop>
<!--- Sign Request --->
<cfset LocalVar.Signing = LocalVar.s4.generateSignatureData(
requestMethod = 'POST' <!--- UPPER CASE --->
, hostName = 'email.#attributes.AWSRegion#.amazonaws.com'
, requestURI = ''
, requestBody = PLS
, requestHeaders = {"content-type":"application/x-www-form-urlencoded"}
, requestParams = {}
)>
<!--- Do POST to AWS --->
<!--- Required in POST
- x-amz-content-sha256
- Authorization
- x-amz-date
- content-type
- body
--->
<cfhttp url="https://email.#attributes.AWSRegion#.amazonaws.com" method="post" result="result">
<cfhttpparam type="header" name="x-amz-content-sha256" value="#LocalVar.Signing.RequestPayLoad#" />
<cfhttpparam type="header" name="Authorization" value="#LocalVar.Signing.authorizationHeader#" />
<cfhttpparam type="header" name="x-amz-date" value="#LocalVar.Signing.AMZDate#" />
<cfhttpparam type="header" name="content-type" value="application/x-www-form-urlencoded" />
<cfhttpparam type="body" value="#PLS#" />
</cfhttp>
Related
I'm in the process of trying to convert an old CF script. The only problem is I am using CommandBox and I can't seem to get Fiddler to show me what this request looks like when it's actually posted. Does anyone know what the Post request this sends out would look like?
I've tried a variety of options so far and nothing has worked.
Any help would be appreciated
<!---
Cold Fusion 4.0.1
--->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head></head>
<body>
<cfif isdefined("Form.thisid")>
<!--- Input Form Parameters --->
<cfset InputPbn = Form.thisid>
<cfoutput>
<h1>Raw XML data for #thisid#</h1>
</cfoutput>
<cfelse>
<h1>A valid thisid was not provided</h1>
<cfabort>
</cfif>
<!--- Assemble the user's input into structure --->
<CFSCRIPT>
// New request structure to pass
Request = StructNew();
Request.thisid = "#Inputthisid#";
host_url = "http://myurl/get_info_wddx.cfm";
Request["LINK_KEY"] = "1234";
</CFSCRIPT>
<!--- Convert the structure to WDDX Packet --->
<CFWDDX
INPUT="#Request#"
OUTPUT="RequestAsWDDX"
ACTION="CFML2WDDX"
>
<cfoutput>RequestAsWDDX #HTMLEditFormat(RequestAsWDDX)#<br/></cfoutput>
<!--- Post the WDDX Packet to the host page as a form field --->
<CFTRY>
<CFHTTP URL="#host_url#" METHOD="POST">
<CFHTTPPARAM
NAME="WDDXContent"
VALUE="#RequestAsWDDX#"
TYPE="FORMFIELD"
>
</CFHTTP>
<CFCATCH TYPE="Any">
WDDX - HTTP Error<BR>
<CFOUTPUT>#HTMLEditFormat(CFHTTP.FileContent)#</CFOUTPUT><BR>
<CFABORT>
</CFCATCH>
</CFTRY>
<!--- Extract recordset from WDDX Packet in host's response --->
<cfif FindNoCase("<META",CFHTTP.FileContent) >
<cfset pos = FindNoCase(">",CFHTTP.FileContent)>
<cfset wddx_strg = Right(CFHTTP.FileContent,(Len(CFHTTP.FileContent)-pos-1))>
<cfelse>
<cfset wddx_strg = CFHTTP.FileContent>
</cfif>
...
</body>
</html>
I was severely overcomplicating this ha, just had to use "WDDXContent" as the Key and the WDDXContent value as the value.
Edit: I have no code for this at this point, just was able to get fiddler to reproduce it by adding a header key "WDDXContent" and value
<wddxPacket version='1.0'><header/><data><struct><var name='link_key'><string>1234</string></var><var name='id'><string>[id here]</string></var></struct></data></wddxPacket>
I'm trying to download a .csv file on a remote server location using ColdFusion (version 2016). I used the cfhttp tag to perform this operation but I keep getting the following error:
401 UNAUTHORIZED
I checked with the server admin of the remote server to verify that I have the right domain name, userid and password and the admin confirmed it. I couldn't find anything similar on SO, hence posting this question. Hoping someone can help me out.
PS: I don't have access to the ColdFusion Administrator since it is hosted by a separate team.
Below is my code (actual values replaced with dummy data for security):
<cfhttp url="https://xxx.yyy.com/abcd/xyz/myfolder/myFile.csv">
<cfhttpparam type="header" name="Authorization"
value="Basic #ToBase64("MydomainName\Myuserid:Mypassword")#" />
</cfhttp>
<cfdump var="#cfhttp.filecontent#" label="CSV file content">
<cfabort>
It might be that your auth string is wrong. To avoid confusion and while debugging, I would suggest something like this:
<cfset myDomainName = "MydomainName" />
<cfset myUserId = "Myuserid" />
<cfset myPassword = "Mypassword" />
<cfset authString = "Basic " & ToBase64('#myDomainName#' & '\' & '#myUserId#' & ':' & '#myPassword#') />
<cfhttp url="https://xxx.yyy.com/abcd/xyz/myfolder/myFile.csv">
<cfhttpparam type="header" name="Authorization"
value="#authString#" />
</cfhttp>
<cfdump var="#cfhttp.filecontent#" label="CSV file content">
<cfabort>
I hope this helps.
I'm running Coldfusion8 and am uploading files to Amazon S3.
When displaying images, I want to check whether an image is available from S3 and if not show a fallback image. My problem is, don't know how to check for existing images.
If I list the link to an image, it's something like this:
http://s3.amazonaws.com/bucket/l_138a.jpg?AWSAccessKeyId=_key_&Expires=_exp_&Signature=_signature_
I'm trying to check for existing files like this:
<cfif fileExists("http://s3.amazonaws.com/bucket/s_" & items.filename)>
<cfdump output="e:\website\test\dump.txt" label="catch" var="found!!!">
</cfif>
Question:
Do I always have to provide accesskey, expires and signature when checking for an image? If I enter the image path without credentials in the browser, the image is loaded, so I don't understand why my fileExist is not working. Any idea?
You could use cfhttp if you have a site-wide page not found message set up.
<cfhttp url="http://a.espncdn.com/photo/2012/0813/nfl_u_flynn1x_203.jpg" method="head">
<cfdump var="#cfhttp.filecontent#">
returns object of java.io.ByteArrayOutputStream
<cfhttp url="http://a.espncdn.com/photo/20notanimage3.jpg" method="head">
<cfdump var="#cfhttp.filecontent#">
returns <html> <body> <h1>Error Processing Request</h1> </body> </html>
Can also check the statuscode returned by the server
<cfhttp url="http://a.file.exists.gif" method="head">
<cfdump var="#val(cfhttp.statuscode)#">
200 is ok, 404 is not found, etc
I've used the getObjectInfo method in the S3.cfc to see if an object exists:
<cffunction name="getObjectInfo" access="public" output="false" returntype="string"
description="Creates a bucket.">
<cfargument name="bucketName" type="string" required="yes">
<cfargument name="filekey" type="string" required="true" hint="" />
<cfset var data = "">
<cfset var content = "">
<cfset var contents = "">
<cfset var thisContent = "">
<cfset var allContents = "">
<cfset var dateTimeString = GetHTTPTimeString(Now())>
<!--- Create a canonical string to send --->
<cfset var cs = "HEAD\n\n\n#dateTimeString#\n/#arguments.bucketName#/#Arguments.filekey#">
<!--- Create a proper signature --->
<cfset var signature = createSignature(cs)>
<!--- get the bucket via REST --->
<cfhttp method="HEAD" url="http://s3.amazonaws.com/#arguments.bucketName#/#Arguments.filekey#">
<cfhttpparam type="header" name="Date" value="#dateTimeString#">
<cfhttpparam type="header" name="Authorization" value="AWS #variables.accessKeyId#:#signature#">
</cfhttp>
<cfreturn cfhttp.StatusCode />
</cffunction>
If I get a 200 status back, then I know the object exists.
I haven't used Coldfusion for a long time, but I did a quick lookup and the fileExists method seems to be for filesystem lookups, not remote URLs.
There are other Coldfusion methods for requesting URLs. One forum discussion on the subject I just quickly found is here: http://forums.adobe.com/thread/765614
But, assuming you're generating HTML to be consumed by a web browser I would suggest doing an image check / fallback in HTML/CSS/JS rather than server side. You could do this with CSS background-image tricks, or directly load and check images with JS. One question dealing with this that I found is here (there are probably a bunch of similar questions on this stuff): Inputting a default image in case the src attribute of an html <img> is not valid?
CF9 +
<cfscript>
FileExists('s3://#accessKey#:#secretKey##[your bucket]/[your file]');
</cfscript>
I'm working with ColdFusion 9.0.1 and latest (for current date) stable build of twitter4j library - twitter4j-core-2.2.4. I'm trying to create functionality which allows users to login or register at our site using their twitter accounts.
I was able to create authorization part: user click on the link on our site and system redirects him to twitter page. On this page he able to "Authorise" our application. After that system redirecting him back using callBackURL.
But I have a problem with next step. When I'm trying to setOAuthAccessToken and for that trying to instantiate AccessToken object with follow part of code:
accessToken = createObject( 'java', 'twitter4j.auth.AccessToken' ).init( 'myStoredRequestToken', 'myStoredRequestTokenSecret' );
But I have follow error:
An exception occurred while instantiating a Java object. The class
must not be an interface or an abstract class. Error: ''.
Any ideas?
Update:
The start part of stacktrace:
'coldfusion.runtime.java.JavaObjectInstantiationException: Object instantiation exception. at coldfusion.runtime.java.JavaProxy.CreateObject(JavaProxy.java:171) at coldfusion.runtime.java.JavaProxy.invoke(JavaProxy.java:80) at coldfusion.runtime.CfJspPage._invoke(CfJspPage.java:2360) at cftwitter2ecfc2084917956$funcGETUSERCREDENTIALS.runFunction(C:\inetpub\wwwroot_test\twPlayGrnd_com\twitter.cfc:36) at coldfusion.runtime.UDFMethod.invoke(UDFMethod.java:472) at coldfusion.runtime.UDFMethod$ArgumentCollectionFilter.invoke(UDFMethod.java:368) at coldfusion.filter.FunctionAccessFilter.invoke(FunctionAccessFilter.java:55) at ...
...cut here, not sure this is important...
the last part is
cfapplication2ecfc665259542$funcONREQUEST.runFunction(C:\inetpub\wwwroot_test\twPlayGrnd\application.cfc:55) ... 55 more Caused by: java.lang.IllegalArgumentException: Invalid access token format. at twitter4j.auth.AccessToken.(AccessToken.java:50) ... 60 more'
I saw the message about wrong format, but based on documentation at http://twitter4j.org it should accept two agruments (strings with keys). Am I wrong?
Update 2
*just find that out - I am sorry that I brought you into confusion with my first post and example... of course I used myStoredRequestToken, myStoredRequestTokenSecret, not a consumer key/secret *
*there are relevant parts of code I'm using for this functionality*
application.cfc ("onApplicationStart" function, instantiating components on start of application)
<cffunction name="onApplicationStart" access="public" returntype="boolean" output="false">
...
<cfset application.com.twitterInstance = server.javaloader.create("twitter4j.TwitterFactory").getInstance() />
<cfset application.com.twitter = createObject("component","_com.twitter").init() /> *<!--- cfc component which will be listed below --->*
...
</cffunction>
twitter.cfc (corresponding coldfusion component)
<cfcomponent displayname="twitter" output="false">
<cffunction name="init" access="public" output="false">
<cfreturn this>
</cffunction>
<cffunction name="authorizeTwitter" access="public" output="false">
<cfargument name="callBackURL" type="string" required="false" default="#request.twtCallBackURL#" />
<cfset var requestToken = "" />
<cfset application.com.twitterInstance.setOAuthConsumer(request.twtConsumerKey,request.twtConsumerSecret) />
<cfset requestToken = application.com.twitterInstance.getOAuthRequestToken(arguments.callBackURL) />
<cflock scope="session" type="exclusive" timeout="10">
<cfset session.oAuthRequestToken = requestToken.getToken()>
<cfset session.oAuthRequestTokenSecret = requestToken.getTokenSecret()>
</cflock>
<cflocation url="#vLocal.requestToken.getAuthorizationURL()#" addtoken="No" />
</cffunction>
<cffunction name="getUserCredentials" access="public" output="true">
<cfset var vLocal = {} />
<cfset vLocal.accessToken = "" />
<cfset vLocal.userData = "" />
<cfset vLocal.requestToken = "" />
<cfset vLocal.accessToken = server.javaloader.create("twitter4j.auth.AccessToken").init(session.oAuthRequestToken,session.oAuthRequestTokenSecret)>
<cfset application.com.twitterInstance.setOAuthAccessToken(vLocal.accessToken) />
<cfset vLocal.userData = application.com.twitterInstance.verifyCredentials() />
<cfdump var="#vLocal.userData#" label="User Credentials">
</cffunction>
First function is for first step - requesting twitter for autorization page (where user can autorize or deny application). Call back URL runs the page what calls the second function and I have problem only at this step (line for generation accessToken).
I have the same result if Im using createObject function instead of javaloader.
*So, my main question is still the same - to obtain the users unique Access Token? Please point me, what I'm doing wrong? What is a correct format for unique user's accessToken generation? Should I place oauth_verifier parameter there? if so, how?*
You are passing consumer key/secret instead of access token/secret.
You can generate your access token/secret at dev.twitter.com.
https://dev.twitter.com/apps » create my access token
Best,
Yusuke
I think I figured out what is wrong with the help of the examples 8. Sign in with Twitter and
Adding support for automated tweets with OAuth. Only tested with my own account though ..
Before you redirect to the authorization page, save the whole RequestToken object in a session variable. You will need it to extract the AccessToken. Note: I am storing the TwitterFactory in the application scope - not the instance
<cfset Twitter = application.TwitterFactory.getInstance()>
<cfset Twitter.setOAuthConsumer(application.TwitterConsumerKey, application.TwitterConsumerSecret)>
<cfset Session.RequestToken = Twitter.getOAuthRequestToken( YourCallBackURL )>
On callback, twitter adds a parameter named oauth_verifier to the URL. Use that value and the saved RequestToken to extract the AccessToken.
<cfset AccessToken = Twitter.getOAuthAccessToken(Session.RequestToken, URL.oauth_verifier)>
<cfset session.StoredAccessToken = AccessToken.getToken()>
<cfset session.StoredAccessSecret = AccessToken.getTokenSecret()>
Once you have the AccessToken/Secret you can access user details (update status,...) anywhere.
<cfset Twitter = application.TwitterFactory.getInstance()>
<cfset Twitter.setOAuthConsumer(application.TwitterConsumerKey,application.TwitterConsumerSecret)>
<cfset AccessToken = createObject("java", "twitter4j.auth.AccessToken")>
<cfset OAuthToken = AccessToken.init(session.StoredAccessToken, session.StoredAccessSecret)>
<cfset Twitter.setOAuthAccessToken(OAuthToken)>
<cfset userData = Twitter.verifyCredentials()>
<cfoutput>
id = #userData.getId()#<br>
name = #userData.getName()#<br>
followers = #userData.getFollowersCount()#<br>
friends = #userData.getFriendsCount()#<br>
</cfoutput>
If I have multiple CF8 servers, can a user login on one server, but share the login credential among all servers (no re-login required)?
Maybe question is about sharing session? This can be done using serialized J2EE sessions or using shared client variables.
For example, this can be done in following way.
Create empty database on one of servers (I've created MySQL one). Create datasources pointing to this DB on all CF servers. Use this datasource as Server Settings > Client Variables > client sessions storage with name SharedSessions (we'll use it later).
If we're using cflogin in Application.cfm on all servers, it's code can look this (simplified) way:
<cfapplication
name="shared_session_test"
sessionManagement="true"
clientmanagement="true"
clientstorage="SharedSessions" />
<cflogin>
<cfif IsDefined( "cflogin" ) and cflogin.name eq "admin" and cflogin.password eq "admin">
<cfset user_roles = "administrators" />
<cfset user_name = cflogin.name />
<cfset user_password = cflogin.password />
</cfif>
<cfif IsDefined( "user_roles" )>
<!--- push login params into shared client scope --->
<cfset CLIENT.user_roles = user_roles />
<cfset CLIENT.user_name = user_name />
<cfset CLIENT.user_password = user_password />
<cfelseif IsDefined( "CLIENT.user_roles" )>
<!--- restore login params from shared client scope --->
<cfset user_roles = CLIENT.user_roles />
<cfset user_name = CLIENT.user_name />
<cfset user_password = CLIENT.user_password />
</cfif>
<cfif IsDefined( "user_roles" )>
<cfloginuser name="#user_name#" password="#user_password#" roles="#user_roles#">
<cfelse>
<!--- authentication failed - send back 401 --->
<cfsetting enablecfoutputonly="yes" showdebugoutput="no">
<cfheader statuscode="401">
<cfheader name="WWW-Authenticate" value="Basic realm=""MySecurity""">
<cfoutput>Not authorized</cfoutput>
<cfabort />
</cfif>
</cflogin>
<cfoutput><p>other.server.com</p></cfoutput>
Now these show the same on both servers:
<cfdump var="#getAuthUser()#">
<cfdump var="#CLIENT#">
Sure, there's much to do here to make process better and more secure, just described the general idea.
Hope this helps.