Mailgun & CfMailGun component. How to implement it - coldfusion

I'm trying to use the component [https://github.com/DominicWatson/cfmailgun][1] to send email (using SendMessage ()) through mailgun.
I am not able to use it. I have read the documentation at this address: http://dominicwatson.github.io/cfmailgun/
But I can not implement it. The author has not included a complete example.
He added only this:
mailGunClient = new MailGunClient( apiKey = myPrivateApiKey );
Can anyone help me with a more complete example?
Thank you.
Edit:
After some tests, I've tried with:
<cfscript>
myPrivateApiKey = "key-123456789";
mailGunClient = new MailGunClient( apiKey = myPrivateApiKey );
mailGunClient.sendMessage(domain = 'domain.com', from='info#domain.com', to='to#gmail.com', subject='Test', text='hello world', html='<b>hello world</b>');
</cfscript>
But I get the error:
MailGun request failure. ['from' parameter is missing]
Edit 2
I solved it (thanks to the help of the author of the script). It was a problem in the component.
Anywhere you see <cfhttpparam ... name="#key#" ..., changing that to <cfhttpparam ... name="#LCase( key )#" ...

Related

ColdFusion 2021 - How to handle SAML/SSO with multiple applications on same server

We have a server with about a dozen small applications each in their own subfolder of the server (//URL/app1, //URL/app2, etc).
I've got the basic SSO authentication round trip working. I set up my account with my IDP and have the response set to go to a common landing page (ACS URL). Since the landing page is currently shared with all the apps, it is in a separate folder distinct from the apps (//URL/sso/acsLandingPage.cfm)
I'm now working on my first app. I can detect the user is not logged in so I do a initSAMLAuthRequest(idp, sp, relayState: "CALLING_PAGE_URL") and that goes out, authenticates, then returns to the landing page.
But how do I redirect back to my target application and tell it the user is authenticated?
If I just do a <cflocation url="CALLING_PAGE_URL" /> the original app doesn't know about the SAML request.
Is there a function that I can call in the original app that will tell if the current browser/user has an open session?
Do I need to set up separate SP for each application so rather than one common landing page each app would have its own landing page so it can set session variables to pass back to the main application? (the IDP treats our apps as "one server", I can get separate keys if that is the best way to deal with this).
My current working idea for the ACS landing page is to parse the relayState URL to find out which application started the init request and then do something like this:
ACSLandingPage.cfm
<cfset response = processSAMLResponse(idp, sp) />
<cfif find(response.relaystate, 'app1')>
<cfapplication name="app1" sessionmanagement="true" />
<cfelseif find(response.relaystate, 'app2')>
<cfapplication name="app2" sessionmanagement="true" />
</cfif>
<cfset session.authenticated_username = response.nameid />
<cflocation url="#response.relaystate#" />
Not terribly ideal, but I think it might work.
I was hoping I was just overlooking something simple and really appreciate any help I can get.
Edit:
My above idea of using <cfapplication in the ACSLandingPage is not working because the <cfapplication keeps trying to assign it to a new session so that when I redirect back to the original app, it thinks it is in a different session so does not have access to the original session.authenticated-username.
Ok, here's how I ended up solving this problem. Probably not the "correct" solution, but it works for me.
The full code solution would be way too long and complicated and rely on too many local calls that would not make sense, so I'm trying to get this down to just some code snippets that will make sense to show how my solution works.
In each application, the Application.cfc looks a bit like this. Each app has a name set to the path of the Application.cfc. We do this because we often will run "training instances" of the codebase on the same server that point to an alternate DB schema so users can play around without corrupting production data.
component {
this.name = hash(getCurrentTemplatePath());
...
In the application's onRequestStart function it has something a bit like this:
cfparam(session.is_authenticated, false);
cfparam(session.auth_username, '');
cfparam(application._auth_struct, {}); // will be important later
// part 1
// there will be code in this block later in the description
// part 2
if (NOT session.is_authenticated OR session.auth_username EQ '') {
var returnURL = '#getPageContext().getRequest().getScheme()#://#cgi.server_name#/#cgi.http_url#'; // points back to this calling page
// start the call
InitSAMLAuthRequest({
'idp' : 'IDP_NAME',
'sp' : 'SP_NAME',
'relayState': returnURL
});
}
// log them in
if (session.is_authenticated AND session.auth_username NEQ '' AND NOT isUserLoggedIn()) {
... do cflogin stuff here ...
}
// throw problems if we are not logged in by this point
if (NOT isUserLoggedIn()) {
... if we don't have a logged in user by this point do error handling and redirect them somewhere safe ...
}
This initiates the SAML connection to our ID Provider. The provider does its stuff and returns the user to the file 'https://myserver/sso/ProcessSAMLResponse.cfm'.
processSAMLResponse uses the returnURL set in relayState to determine which application initiated the request so it can get a path to the app's Application.cfc.
<cfset response = ProcessSAMLResponse(idpname:"IDP_NAME", spname:"SP_NAME") />
<cfset returnURL = response.RELAYSTATE />
<cfif findNoCase("/app1", returnURL)>
<cfset appPath = "PHYSICAL_PATH_TO_APP1s_APPLICATION.CFC" />
<cfelseif findNoCase("/app2", returnURL)>
<cfset appPath = "PHYSICAL_PATH_TO_APP2s_APPLICATION.CFC" />
<cfelseif findNoCase("/app3", returnURL)>
<cfset appPath = "PHYSICAL_PATH_TO_APP3s_APPLICATION.CFC" />
...
</cfif>
<!--- initiate application --->
<cfapplication name="#hash(appPath)#" sessionmanagement="true"></cfapplication>
<!--- create a token (little more than a random string and a bit prettier than a UUID) --->
<cfset auth_token = hash(response.NAMEID & dateTimeFormat(now(), 'YYYYmmddHHnnssL'))/>
<cfset application._auth_struct[auth_token] = {
"nameid": lcase(response.NAMEID),
"expires": dateAdd('n', 5, now())
} />
<!--- append token (can also be done with a ?: if you are inclined) --->
<cfif NOT find("?", returnURL)>
<cfset returnURL &= "?auth_token=" & encodeForURL(auth_token) />
<cfelse>
<cfset returnURL &= "&auth_token=" & encodeForURL(auth_token) />
</cfif>
<!--- return to the calling page --->
<cflocation url="#returnURL#" addToken="No"/>
This throws it back to the application. So we go back into the application's onRequestStart to fill in that part 1 block from above:
cfparam(session.is_authenticated, false);
cfparam(session.auth_username, '');
// part 1
// look for an auth token
if (NOT session.is_authenticated AND session.auth_username EQ '' AND structKeyExists(URL, 'auth_token')) {
var auth_token = URL.auth_token;
// see if it exists in our auth struct (and has all fields)
if ( structKeyExists(application, "_auth_struct")
AND structKeyExists(application._auth_struct, auth_token)
AND isStruct(application._auth_struct[auth_token])
AND structKeyExists(application._auth_struct[auth_token], 'nameid')
AND structKeyExists(application._auth_struct[auth_token], 'expires')) {
// only load if not expired
if (application._auth_struct[auth_token].expires GT now()) {
session.is_authenticated = true;
session.auth_username = application._auth_struct[auth_token].nameid;
}
// remove token from struct to prevent replays
structDelete(application._auth_struct, auth_token);
} // token in auth struct?
// remove expired tokens
application._auth_struct = structFilter(application._auth_struct, function(key, value) {
return value.expires GT now();
});
} // auth_token?
// part 2
// .... from earlier
So that's how I solved the problem of multiple apps trying to use a single IDP/SP combination.
Important caveats:
This is all done on an intranet server, so my security is much more lax than it would be on a public facing server. (in particular, using an application variable to store the auth-tokens could be vulnerable to a massive DDOS type attack that would flood new sessions and fill available memory).
A subset of 1 - these apps get a few hundred users a day across all apps, if you have a site that gets thousands of hits a day, storing the tokens in application like I do may not be memory efficient enough for you.
My IDP is very constrained. It would be much nicer if I could just create distinct SP settings for each app and have the return calls go directly back to the calling app.
I skipped a few checks and error handling to keep the sample simple. You should do lots more tests on the values, especially to make sure the nameID is a valid user before the actual cflogin call.
Before calling initSAMLAuthRequest, you may want to add a session counter to prevent an infinite loop of authentication calls if something goes wrong (learned that the hard way).

Using cfpop to access gmail in CF8

So I need to use ColdFusion 8 to check a GMail account for emails, eventually I'll download the attachments. I've Googled and not found much except for the following code. What bugs me is that, that's all I've found.
So is this the best way to go?
Here is the code. I've included the link but there is not much more information.
<cfscript>
javaSystem = createObject( "java", "java.lang.System" );
jProps = javaSystem.getProperties();
jProps.setProperty( "mail.pop3.socketFactory.class", "javax.net.ssl.SSLSocketFactory" );
jProps.setproperty( "mail.pop3.port", 995 );
jProps.setProperty( "mail.pop3.socketFactory.port", 995 );
</cfscript>
<cfpop action="getheaderonly" name="rsEmail" startrow="1? maxrows="50" server="pop.gmail.com" port="995" username="your#username.com.au" password="yourpassword">
Using CFPOP to check mail on GMail
Is there a reason you need to use POP in particular? If you're on CF9, then you can use CFIMAP which ought to work with gmail. The docs even use gmail as an example.
Alternatively, you could use the javamail API directly:
<cfscript>
props = CreateObject("java","java.util.Properties").init();
props.setProperty("mail.store.protocol", "imaps");
sessClass = CreateObject("java","javax.mail.Session");
sess = sessClass.getDefaultInstance(props);
store = sess.getStore("imaps");
store.connect("imap.googlemail.com","you#gmail.com", "password");
folder = store.getFolder("inbox");
if(!folder.isOpen()){
folder.open(folder.READ_WRITE)
}
messageCount = folder.getMessageCount();
dump(messageCount);
if (!IsNull(folder) && folder.isOpen()) { folder.close(true); }
if (!IsNull(store)) { store.close(); }
</cfscript>
This will work but you should know that it sets the GLOBAL SSL socketfactory for POP3. I believe this will have a serverwide affect - which may be irrelevant to your process but it is worth noting.
You could possibly use the opensource cfgmail project on Riaforge:
http://cfgmail.riaforge.org/
It works both in CF 7 and up.

coldfusion 9 dynamically call method

I'm attempting to build a method call from strings that have been passed into an object that refer to another object.
normally when calling an object we write the code like this:
application.stObj.oNewsBusiness.getNews(argumentCollection=local.stArgs);
However what I have done is created an array that contains the object name, the method name and the argument collection.
<cfscript>
local.stArgs = {};
local.stArgs.nNewsID = 19;
local.stArgs.sAuthor = "John";
local.aData = [];
local.aData[1] = local.stArgs;
local.aData[2] = "stObj.oNewsBusiness";
local.aData[3] = "getNews";
</cfscript>
however i am struggling to recombine all this to be a method call.
UPDATE using suggestion but still with issue
While cfinvoke seems to work for:
<cfinvoke component="#application.stObj.oNewsBusiness#" method="#local.sMethod#" argumentcollection="#local.stArgs#" returnvariable="local.qData"></cfinvoke>
it doesn't work when doing something like:
<cfscript>
local.stArgs = local.aData[1];
local.sObject = local.aData[2];
local.sMethod = local.aData[3];
</cfscript>
<cfinvoke component="application.#local.sObject#" method="#local.sMethod#" argumentCollection="#local.stArgs#" returnvariable="local.qData"></cfinvoke>
it generates an error:
Could not find the ColdFusion component or interface application.stObj.oNewsBusiness
CFInvoke is generally used to handle dynamic method calls.
http://help.adobe.com/en_US/ColdFusion/9.0/CFMLRef/WSc3ff6d0ea77859461172e0811cbec22c24-7e0a.html
CFInvoke has an argumentcollection attribute so you can pass your arguments in the way you are used to.
Dan is correct CFInvoke is the way to go
<cfinvoke component="#mycomponentname#" method="get" arg1="#arg1#" arg2="#arg2#" arg3=..>
<cfinvoke component="application.#local.sObject#" method="#local.sMethod#"argumentCollection="#local.stArgs#" returnvariable="local.qData"></cfinvoke>
from your update won't work because there are no # signs around the component variable.
You could do
<cfset local.componentName = "application." & local.sObject>
<cfinvoke component="#local.componentName#" method="#local.sMethod#"argumentCollection="#local.stArgs#" returnvariable="local.qData"></cfinvoke>
There's probably an inline way of combining application. with the variable on the cfinvoke call, but I don't know off the top of my head.
Edit: Dan Wilson's comment does it better in an inline way.

How to create Java object in CFSCRIPT?

I am using ColdFusion 9.1.0
I am trying to create a java object using CFSCRIPT and I just can't get the right combination of stuff to work.
This works perfectly:
<cfobject action="create" type="Java" class="CyberSource" name="auth">
<cfset VARIABLES.ResponseString = auth.runTransaction(LOCAL.PropsFile,LOCAL.MyXML)>
When I do this, I get an error:
LOCAL.MyObject = createObject("java", "CyberSource.auth");
LOCAL.ResponseString = auth.runTransaction(LOCAL.PropsFile,LOCAL.MyXML);
This is the error I get:
Object Instantiation Exception.
Class not found: CyberSource.auth
The object is an external piece of code available to ColdFusion. I don't see what the problem is. Do you?
The classname is CyberSource and the variable you are trying to assign the instance to is "auth" in your tagbased approach. You mixed it up with "MyObject".
LOCAL.auth = createObject("java", "CyberSource");
LOCAL.ResponseString = LOCAL.auth.runTransaction(LOCAL.PropsFile,LOCAL.MyXML);
This should work.
One thing to be aware of.
The java class names are case sensitive!
// Fail
myFile = createObject( 'java', 'java.io.file' );
// Win!
myFile = createObject( 'java', 'java.io.File' );
And to call their constructor, use .init() eg.
myFile = createObject( 'java', 'java.io.File' ).init( '/Users/Mike/Dev/Test' );

Adding appointments to your calendar through email notifications

I need an example for adding events to my calendars through emails. For example when I send an email to my clients regarding their appointment, i want them to have an option in the email that will allow them to add the event on their calendar by just clicking on a button or something.
Is there anything out there that someone can direct me to it?
I need this mainly to use it with coldfusion 9.
Thanks!
I'd recommend the iCalUs UDF from http://www.cflib.org/udf/icalus
WebDH provides a great example here.
Here's an example using CF9 that I quickly put together but haven't tested yet.
<cfscript>
eventStr = {};
eventStr.organizerName = "John Doe"; //Organizer Name
eventStr.organizerEmail = "john.doe#email.com"; //Organizer Email
eventStr.startTime = ParseDateTime("12/30/2011 11:00"); //format: m/d/yyyy HH:mm OR h:mm TT -- this is Eastern time
eventStr.subject = "Demo Example";
eventStr.location = "StackOverflow.com";
eventStr.description = "Example iCalendar using CF9";
// Display in browser
//pc = getpagecontext().getresponse();
//pc.getresponse().setcontenttype('text/calendar');
//pc.setHeader("Content-Disposition","inline;filename=newAppointment.ics");
//writeOutput(iCalUS(eventStr));
//Email
m = new mail();
m.setSubject( "Event" );
m.setTo( "user#email.com" );
m.setFrom( "me#email.com" );
m.setServer( "localhost" );
//m.addParam( file="#ACCOUNT_TXT_FILE#" );
m.addPart( type="text", charset="utf-8", wraptext="72", body="Attached is a calendar event..." );
m.addPart( type="text/calendar" body="#iCalUS(eventStr)#");
m.send();
</cfscript>
Here's another example reference that shows how to email the calendar event.
Here is a very basic implementation of the ICS format. This is designed to be accessed through a browser, but it'd be fairly academic to change to create a text file with ICS extension and send it via e-mail.
<cfheader name="Content-Disposition" value="attachment; filename=event.ics" />
<cfcontent reset="true" type="text/calendar" />
<cfscript>
// handle all-day events
if (NOT isDate(starttime) OR NOT isDate(endtime)) {
dtstart=';VALUE=DATE:#dateFormat(eventdate,"yyyymmdd")#';
dtend=';VALUE=DATE:#dateFormat(dateAdd("d",1,eventdate),"yyyymmdd")#';
} else {
dtstart=';TZID="Eastern Standard Time":#dateFormat(eventdate,"yyyymmdd")#T#timeFormat(starttime,"HHmmss")#';
dtend=';TZID="Eastern Standard Time":#dateFormat(eventdate,"yyyymmdd")#T#timeFormat(endtime,"HHmmss")#';
}
</cfscript>
<cfoutput>
BEGIN:VCALENDAR
PRODID:-//Company//Source//EN
VERSION:2.0
METHOD:PUBLISH
BEGIN:VTIMEZONE
TZID:Eastern Standard Time
BEGIN:STANDARD
DTSTART:16011104T020000
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:16010311T020000
RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
END:VTIMEZONE
BEGIN:VEVENT
CLASS:PUBLIC
CREATED:#dateFormat(dateAdded,"yyyymmdd")#T#timeFormat(dateAdded,"HHmmss")#Z
DESCRIPTION:#desc#
DTEND#dtend#
DTSTAMP:#dateFormat(dateAdded,"yyyymmdd")#T#timeFormat(dateAdded,"HHmmss")#Z
DTSTART#dtstart#
LAST-MODIFIED:#dateFormat(dateApproved,"yyyymmdd")#T#timeFormat(dateApproved,"HHmmss")#Z
LOCATION:#location#
PRIORITY:5
SEQUENCE:0
SUMMARY;LANGUAGE=en-us:#title#
TRANSP:OPAQUE
UID:#dateFormat(now(),"yyyymmdd")#T#timeFormat(now(),"HHmmss")#Z##uniqueID#
BEGIN:VALARM
TRIGGER:-PT15M
ACTION:DISPLAY
DESCRIPTION:Reminder
END:VALARM
END:VEVENT
END:VCALENDAR
</cfoutput>