Archive for the ‘ColdFusion’ Category

ColdFusion in the Enterprise, and Integration Technologies: SOTR 2008

Tuesday, June 16th, 2009

Attached is are two slightly abbreviated presentations that I did last year at SOTR (2008). The first was ColdFusion in the Enterprise, and covers

  • Scalability
  • Business Continuity
  • Single Sign On
  • Auditing
  • Localization, Globalization

The second, ColdFusion and Java, looks at integration technologies

  • JSP Tag Libraries
  • Java Libraries
  • WebServices
  • XML

And illustrates these concepts using the following

  • Globalization
  • iText
  • LiveCycle ES
  • SSO

ColdFusion Security and SOTR

Tuesday, June 9th, 2009

Scotch on the Road was a great concept; bringing the ColdFusion to the major cities in the UK at a very affordable price.

I spoke at the London event on ColdFusion security. The screen I had wasn’t really big enough for the room, so for those wanting to see the slides, I’ve attached a slightly  abridged version, without the creds.

The presentation is mainly about ColdFusion security, but also briefly covers the bigger picture, including security standards, infrastructure, and the development process.[SOTR ColdFusion Security]

SAML, SSO and ColdFusion

Monday, May 12th, 2008

Overview

The following post is intended as an overview of how a clients IT department could integrate their intranet with our CMD system to provide a seamless single sign on. There are commercial systems that can do this and we have integrated with some of them. The following shows how to use the open source Apache XMLSecurity library to the same effect.

For an overview of SSO and SAML see my earlier posting. In this post I will illustrate an implementation using the SAML Post Profile, the Apache XML Security library and ColdFusion. (Since the Apache library is a java library, the JSP/java code is almost identical). There are two parts: the creation of the SAML on the intranet server (IdP) and the interpretation of this by an external service, or to use the correct terminology, service provider (SP).

Configuring the Idp

This article assumes that you have ColdFusion 8. Alternatively you could run Tomcat and use JSP although the syntax is different, the library calls are the same.

This implementation uses the Apache XML security library. Download and place in the WEB-INF/cfusion/lib directory for ColdFusion (for java into your applications lib directory), You probably already have many of the jar files and just need serializer.jar and xmlsec-nnn.jar, where nnn is the current version number.

Creating the SAML on the IdP

The starting point is a SAML assertion from the previous article. We need to add some real user data and time information. The users identity and other information to embed in the SAML would come from Active Directory, LDAP etc. For this example I’ve hard coded some values.

<cfscript>
    ssouser=StructNew();
    ssouser["email"]=”user@tagworldwide.com”;
    nowDateTime =
        #DateFormat(DateConvert(‘local2utc’,
          Now()),’YYYY-MM-DDT’)#
        & #TimeFormat(DateConvert(‘local2utc’,
          Now()),’HH:mm:SSZ’)#;
    nowDateTimePlus1 =
        #DateFormat(DateConvert(‘local2utc’,
          DateAdd(‘n’,1,Now())),’YYYY-MM-DDT’)#
        & #TimeFormat(DateConvert(‘local2utc’,
          DateAdd(‘n’,1,Now())),’HH:mm:SSZ’)#;
</cfscript>

The CFXML tag provides an easy way to populate the SAML

<cfoutput>
<cfxml variable=”samlAssertionXML”>
<samlp:Response
  xmlns:ds=http://www.w3.org/2000/09/xmldsig#chr(35)#
  xmlns:saml=”urn:oasis:names:tc:SAML:1.0:assertion” 
  xmlns:samlp=”urn:oasis:names:tc:SAML:1.0:protocol” 
  IssueInstant=”#nowDateTime#”
  MajorVersion=”1″ MinorVersion=”1″ 
  Recipient=”http://externalService/sso/post/
  ResponseID=”#CreateUUID()#”>
    <samlp:Status>
        <samlp:StatusCode Value=”samlp:Success”/>
    </samlp:Status>
    <saml:Assertion
        xmlns:saml=”urn:oasis:names:tc:SAML:1.0:assertion”
        AssertionID=”#AssertionID#”
        IssueInstant=”#nowDateTime#”
        Issuer=”#issuer#” MajorVersion=”1″
        MinorVersion=”1″>
        <saml:Conditions NotBefore=”#nowDateTime#”
            NotOnOrAfter=”#nowDateTimePlus1#”>
            <saml:AudienceRestrictionCondition>
                <saml:Audience>
                  http://intranet/IdP
                </saml:Audience>
            </saml:AudienceRestrictionCondition>
        </saml:Conditions>
        <saml:AuthenticationStatement 
           AuthenticationInstant=”2007-11-04T13:52:42Z”>
           <saml:Subject>
             <saml:NameIdentifier>#ssouser.id#
             </saml:NameIdentifier>
           </saml:Subject>
        </saml:AuthenticationStatement>
        <saml:AttributeStatement>
         <saml:Attribute AttributeName=”emailaddress”>
         <saml:AttributeValue>#ssouser.email#
         </saml:AttributeValue> 
         </saml:Attribute>
         <saml:Attribute AttributeName=”givenname”>
         <saml:AttributeValue>
             #ssouser.givenname#
           </saml:AttributeValue> 
         </saml:Attribute>
         <saml:Attribute AttributeName=”surname”>  
         <saml:AttributeValue>
         #ssouser.surname#
         </saml:AttributeValue> 
         </saml:Attribute>
        </saml:AttributeStatement>
    </saml:Assertion>
</samlp:Response>
</cfxml>
</cfoutput>

The above uses an authentication statement for the users identity and attribute statements for additional attributes e.g. users full name, email address etc. The standard doesn’t define which attributes are used. It allows namespace attributes to be used, allowing complete flexibility.

To add a signature element, we use the XMLSecurity library. The ColdFusion variables need coaxing to extract the right java objects. The last line does the actual insertion of a signature element into the SAML.

<cfscript>
samlAssertionElement = 
  samlAssertionXML.getDocumentElement();
samlAssertionDocument =
  samlAssertionElement.GetOwnerDocument();
samlAssertion = samlAssertionDocument
  .getFirstChild();
SignatureSpecNS = CreateObject(“Java”,
  “org.apache.xml.security.utils.Constants”)
  .SigSpecNS;
Init = CreateObject(“Java”, 
   “org.apache.xml.security.Init”).Init().init();
XMLSignatureClass = CreateObject(“Java”, 
   “org.apache.xml.security.signature.XMLSignature”);
sigType =
   XMLSignatureClass.ALGO_ID_SIGNATURE_RSA_SHA1;
signature = XMLSignatureClass
   .init(samlAssertionDocument, 
    javacast(“string”,”"), sigType); 
samlAssertionElement
  .insertBefore(signature 
  .getElement(),samlAssertion.getFirstChild());

</cfscript>

Note that the signature is inside the the XML that we are signing; this is known as an enveloped signature. The following is needed by the XMLSecurity library to define that we will be using an enveloped signature.

TransformsClass = CreateObject(“Java”,
“org.apache.xml.security.transforms.Transforms”);
transformEnvStr =  
TransformsClass.TRANSFORM_ENVELOPED_SIGNATURE;
transformOmitCommentsStr =
TransformsClass.TRANSFORM_C14N_EXCL_OMIT_COMMENTS;
transforms = TransformsClass
.init(samlAssertionDocument
transforms.addTransform(transformOmitCommentsStr);
transforms.addTransform(transformEnvStr);

To sign the SAML, we need a certificate. A self self certificate can be used. Typically issuer signed certificates are used, and for this the cheapest certificate from Thawte etc is sufficient. Java typically stores these in a key store. You can follow one of the numerous guides to doing this via the command line or use Portecle which is very easy to use. The code to extract a certificate from a key store is as follows

ksfile = CreateObject(“Java”, “java.io.File”)
  .init(storepath);
inputStream = CreateObject(“Java”,
  “java.io.FileInputStream”)
  .init(ksfile);
KeyStoreClass = CreateObject(“Java”
  , “java.security.KeyStore”);        
ks = KeyStoreClass.getInstance(“JKS”); 
ks.load(inputStream,”sso”); 
key = ks.getKey(“sso”,keypw.toCharArray());
cert = ks.getCertificate(certAlias);
publickey = cert.getPublicKey();

We now need to add a digest, signature and key information.

signature.addDocument(“#chr(35)##AssertionID#”
  , transforms);
signature.addKeyInfo(cert);
signature.addKeyInfo(publickey);
signature.sign(key);

In this example we have signed the whole response. You will see examples on the web where first the saml assertion is signed, and then in addition the response is signed as well. This isn’t required when the assertion and response are created at the same time.

Finally, encode this into a HTML page that will do a post to the SP.

<html>
    <body onload=”document.forms[0].submit()”>
        <form action=
http://ServiceProvider/SSO/POST/” method=”post”>
        <input type=”hidden” name=”SAMLRequest”
        value=’#BinaryEncode(
           CharsetDecode(
           samlAssertionXML,”utf-8″)
           ,”Base64″)#’/>
        </form>
    </body>
</html>

Interpreting the XML on the External Service (SP)

On the Service Provider, we do everything in reverse.

First extract the SAML, parse the XML, and verify the signature. There are a few lines of code to set up the various java objects correctly; the last line does the work.

xmlResponse=CharsetEncode(
  BinaryDecode(form.SAMLRequest,”Base64″)
  ,”utf-8″);
docElement= XmlParse(variables.xmlResponse)
  .getDocumentElement();
SignatureConstants=CreateObject(
  “Java”, “org.apache.xml.security.utils.Constants”);
SignatureSpecNS=SignatureConstants.SignatureSpecNS; 
xmlSignatureClass = CreateObject(“Java”,
  “org.apache.xml.security.signature.XMLSignature”);
xmlSignature = xmlSignatureClass 
  .init(docElement.getElementsByTagNameNS
  (SignatureSpecNS,”Signature”)
  .item(0),”");
keyInfo=xmlSignature.getKeyInfo();
X509CertificateResolverCN = 
  “org.apache.xml.security.keys.keyresolver
  .implementations.X509CertificateResolverClass”).
keyResolver=CreateObject(“Java”,
   X509CertificateResolverCN)
  .init();
keyInfo.registerInternalKeyResolver(keyResolver);
x509cert = keyInfo.getX509Certificate();

isValid = xmlSignature.checkSignatureValue(x509cert);

Assuming the SAML has been correctly signed, we now need to establish that the request is from an approved source, is in the right time frame and isn’t a repeat. Finally extract the users identity.

ssoissuer = getSingleValue(XPathIssuer);
conditions = getSingleValue(XPathConditions);
beforeDate = DateConvertISO8601
  (conditions.XmlAttributes.NotBefore, 0);
afterDate = DateConvertISO8601
  (conditions.XmlAttributes.NotOnOrAfter, afterwindow);
certificate = getSingleValue
  (XPathCertificates).XmlText;
reference = getSingleValue(XPathUniqueReference);

// Verify Validity Info
// …

   // Extract User
   ssouser =  getSingleValue(XPathNameIdentifier).xmltext;

Much of the additional code for validity, verifying whether its a new or existing user etc will be very much application dependant.

The extras

The code above uses the function DateConvertISO8601, an implementation of this from David Satz can be found on cflib. The function getSingleValue is a wrapper around XMLSearch.

Finally, the class org.apache.xml.security.Init has an init initializer method that clashes with the ColdFusion init method; in CF 7 you need to call it via java introspection as in the following snippet (thanks to Jon for the correction on this):

InitClass = CreateObject(“Java”,
     “org.apache.xml.security.Init”);
emptyArray = ArrayNew(1);
initMethod=InitClass.GetClass()
  .GetMethod(“init”,emptyArray);
initMethod.invoke(Init,emptyArray);

<cffunction name=”getSingleValue” output=”false”
   access=”public” returntype=”ANY”>
   <!— Expects an array of values to be returned
   from XML data, but only returns the first one —>
   <cfargument name=”xpath” required=”true”>
   <cfargument name=”reqAttrib” required=”No”
     type=”boolean” default=”true”>
    <cfset values=
        XmlSearch(variables.xmlResponse,
        arguments.xpath)>
        <cfif reqAttrib >
         <cfif ArrayLen(values) EQ 0>
           <cfthrow type=”saml”
           message=”Error: No value for: #xpath#”>
        </cfif>
        </cfif>
        <cfif isArray(values)>
            <cfreturn values[1]>
        <cfelse>
            <cfreturn values >
        </cfif>
</cffunction>

More Reading

This PDF has a very clear java implementation. Phil Duba has a set of well written SAML articles on his blog. This uses a wrapper round a java implementation. You may also need to know about the differences between SAML1.1 and SAML 2.0: see saml.xml.org.

SSO with SAML

Wednesday, April 16th, 2008

Overview

We frequently get requests from clients to integrate our Tag:CMD system which a SAAS offering (Software As A Service) with their internal LDAP system. The purpose of the Blog is to describe how we approach SSO (Single Sign On)and to illustrate how it can be implemented.

The first part of the post is generic to SSO, after which, in a second post, I will illustrate an implementation using the SAML Post Profile, the Apache XML Security library and ColdFusion. (Since the Apache library is a java library, the java code is almost identical).

SSO has  a number of advantages:

  • Users want a single account and password to work on all their work systems
  • Reduces user maintenance in the application
  • Ensures user information is kept up to date
  • When users change employment their accounts are automatically deactivated

What is sometimes harder to appreciate is that its not as simple as calling their LDAP server. When evaluating a SSO solution we are looking for some key elements:

  • The clients server does the authentication to avoid
    • storing of users passwords in our database
    • forwarding of users passwords though our servers
  • The clients server sends us the following information
    • Users identity
    • Basic information about the user (e.g. Name, email etc)
    • Servers identity
    • Time stamp
  • Signatures, certificates and/or secret keys are used to protect against
    • Modification of the information
    • Replay of the information at a later point in time 
  • The system is decoupled so we don’t need a dedicated VPN or back channel to the users server

There are a number of standards for SSO including

  • OASIS: SAML
  • Liberty: ID-FF
  • OpenID
  • Etc

SAML

Many of these use SAML to exchange information. SAML looks complicated and verbose. Essentially its made up of a set of statements, called SAML Assetions. To this we add elements to provide identity and ensure the statements haven’t been modified.

The following illustrates the basic elements. Start with the user identity

<saml:AuthenticationStatement>
    <saml:Subject>
        <saml:NameIdentifier>DavidRutter</saml:NameIdentifier>
    </saml:Subject>
</saml:AuthenticationStatement>

This is incorporated into what is called a SAML Assertion; we can add time properties to this to define its validity period.

<saml:Assertion IssueInstant="2007-11-04T14:04:24Z">
    <saml:Conditions
        NotBefore="2007-11-04T13:59:24Z"
        NotOnOrAfter="2007-11-04T14:14:24Z"/>
    <saml:AuthenticationStatement>
      <saml:Subject>
        <saml:NameIdentifier>DavidRutter</saml:NameIdentifier>
      </saml:Subject>
    </saml:AuthenticationStatement>
</saml:Assertion>

The next steps involve signing the SAML assetions to verify who they came from and that they haven’t been tampered with. There are 3 parts to this:

  • The signed information
  • A signature of the signed information
  • A keyinfo to describe what was used for the signature

We create a digest of the SAML assetions using the secure SHA algorithm so ensure the SAML can’t be modified

<ds:SignedInfo>
    <ds:DigestValue>/UlguevI2sppqGHnuZQV</ds:DigestValue>
</ds:SignedInfo>

Sign the digest to prove the identity. The signing is done using the private key of a certificate.

<ds:SignatureValue>
ID0Pr3EMyqvLilnZ0YNzt3z8GEyz6029V11rq
</ds:SignatureValue>

We add a keyinfo section to identify what was used for the signature. This can be the actual certificate.

<ds:KeyInfo>
    <ds:X509Data>
        <ds:X509Certificate>MIIC6jCCAd</ds:X509Certificate>
   </ds:X509Data>
</ds:KeyInfo>

Putting the signature altogether looks something like this:

<ds:Signature>
  <ds:SignedInfo>
    <ds:DigestValue>/UlguevI2sppqGHnuZQV</ds:DigestValue>
  </ds:SignedInfo> 
  <ds:SignatureValue>
ID0Pr3EMyqvLilnZ0YNzt3z8GEyz6029V11rq
  </ds:SignatureValue>
  <ds:KeyInfo>
    <ds:X509Data>
        <ds:X509Certificate>MIIC6jCCAd…</ds:X509Certificate>
    </ds:X509Data> 
  </ds:KeyInfo>
</ds:Signature>

 

Profiles

Assuming we have a library capable of creating the SAML, the next part is to define how the user, the system creating the SAML (the issuer or IdP) and the system receiving the SAML (the service provider or SP)  interact.

There are a number of different profiles, the simplest architecturally is the Browser Post Profile.

  • User goes to an intranet site and is authenticated.
    (either automatically via, or using a username/password)
  • User clicks on a link to take them indirectly to the external service
    e.g. http://intranet/SamlRedirect?goto=ExternalServiceUrl
  • The link first goes to an intranet hosted page which generates the SAML, does a 64bit encoding, and generates a
    a HTML page with a javascript redirect to the external service
  • The users browser will automatically do a HTP POST of the SAML to the external service
  • External service decodes and verifies the SAML, and logs in the user automatically.

The html will look something like the following:

<body onload="saml.submit()" >
<form name="saml"
    method="post"action=http://serviceProvider/SSO/POST/ …>
    <input type="hidden" name="SAMLResponse" value="response" />
    …
    <input type="submit" value="Submit" />
  </form>
</body>

Implementation

This will be covered in a second post on how to do the encoding and decoding in Java and ColdFusion.

Links

See this wikipedia article for more information and links

ColdFusion, Axis, Windows .NET and Web Services

Tuesday, March 25th, 2008

We have done a number of projects integrating with clients’ systems which have required either publishing or consuming web services. In the past we did this using Apache Axis. Since CF7 we have switched to ColdFusion which provides a very convenient way of developing Web Services.

When using web services in general

  • Methods passing simple types are very straight forward; just follow the Adobe documentation.
    • ColdFusion does a good job of converting the types.
    • Use javacast() if CF gets it wrong
  • Methods that pass a single class, map to a ColdFusion structure.
  • Methods that pass a nested class, map to a nested ColdFusion structure.
    • See the following example on calling the LiveCycle Reader Extensions web service
    • You may need to use ‘javacast()’ for null values

Specifically for .Net

  • Interoperability with .Net is now generally quite good
  • If the web services is using Windows authentication or you need to use SSL:
    • You need to replace the Apache Axis authentication library with a different Apache library. See here.
    • Drop the Jakatra libraries into the cold fusion lib directory and modify the client-config.wsdd
    • See also Tom Jordahl’s musings on this topic.
    • We tried in the CF8 beta program to get the Apache libraries into the default installation for ColdFusion8; hopefully it will find its way into a future version.
  • Other Apache Axis and .net hints can be found here.

Land Rover G4 Challenge

Tuesday, January 29th, 2008

Land Rover have just started accepting applications to the next G4 Challenge.

The Land Rover G4 Challenge is an exciting global adventure competition with driving at its core, run in support of the International Federation of Red Cross and Red Crescent Societies. The programme begins with a gruelling series of selections and culminates in the three-week long Challenge Finals, when teams of two competitors from each of the 18 entered nations will compete for victory. The Challenge Finals take place in some of the world’s truly spectacular urban and wilderness locations and combine off-road driving with activities such as mountain biking, kayaking, climbing, orienteering and abseiling, pitching competitor teams against each other in tests of driving skill, initiative, strategic thinking and physical fitness.

If adventure and driving excites you or you just want to see and interesting site, check it out.

At Tag we have both an established development team and a thriving, rapidly expanding, interactive department. The G4 Challenge is one of a growing number of projects that has involved people from both teams.

The interesting technologies being used here are:

  • A flash intro consists of a number of layered images, creating a panorama effect with the landscape that follows your mouse movements.
  • The application form uses PDF, so that online matches the offline paper version.
  • Reader Extensions are used so that applicants with Acrobat Reader can save their application form and fill it in off line.
  • The back end processing is using the new forms processing capabilities of ColdFusion 8
  • We use a content distribution network to provide efficient delivery of the site worldwide.

We will be adding more interactive features over the next few months in the lead up to the main competition.

Using Reader Extensions with Shared Reviews

Sunday, January 13th, 2008

A quick summary to link the previous posts together

  • Do the configuration
  • Set up WebDAV server to store comments
  • Using Acrobat, setup a shared review using this WebDAV location.
  • Acrobat will add some JavaScript to the document which you can use as a template for the next step.
  • Create your CF script to
  • With shared annotations the configuration is stored in the PDF; the actual annotations are stored on the webdav server.

    I was asked if there was a way of applying the annotations so they are applied by the server onto the original PDF. There isn’t an out of the box solution to this, but conceptually it appears possible. There are the pdfStamper and XPAAJ java libraries that have the ability to add annotations to a PDF. The annotations stored in WebDAV are in XML.

    Acrobat Reader Extensions

    Friday, December 28th, 2007

    The following is an explanation of how to programmatically enable Acrobat Reader Extensions from ColdFusion.

    ‘Reader Extensions’ is a great enabler for pdf technologies. It allows form filling and annotation on a pdf document for users who only have the free Acrobat Reader. It is useful for general Internet applications, where Acrobat Reader is the norm. We will soon be using it on the Land Rover G4 Challenge, where we need an application form that is the same on the web and when printed.

    Another useful scenario is when engaging with large corporates, where Acrobat reader is likely to be part of their standard PC build, and where the time and business cost of getting Acrobat Professional installed for a small number of users in the organisation, can be prohibitive. This is a common scenario with some of our larger clients. Reader Extensions allows us to use Acrobat annotations when approving artwork, catalogs, DM packs etc, without all our users having to install full Acrobat.

    Acrobat Professional 8 allows you to enable reader extensions on a PDF for free. There are some limitations which are in the Acrobat EULA and discussed here. If you envisage usage beyond the EULA, have a large number of documents, or need to enable the extensions programmatically then you will need the Reader Extensions service in Adobe LiveCycle.

    To anyone unfamiliar with LiveCycle, think of it as a suite of pdf related tools, linked together within a workflow management system, that allows non programmers to define complex workflow interactions. Its a powerful system, designed (and priced) for use by large corporates. Its sold as a modular system, so you can buy just the Reader Extensions part.

    There is an article by Allen Levine on the Adobe site on how to enable reader extensions using ColdFusion. Adobe has since released LiveCycle ES, which has improved the integration of the LiveCycle components and provided a web service that can be called without the need for any additional installs or changes to the standard installation. Additionally ColdFusion 8 has made it easier to call and debug web services.

    The LiveCycle web service can accept Dime, Mime or Base64 encoded files. The example below uses Base64.

    <CFSCRIPT>   
    
    // Define username/password.
    // Change to match your installation
    creds = structnew();
      creds ['username']='administrator';
      creds ['password']='password';   
    
    // Create web service
    wsdl = "http://127.0.0.1/readerExtensions.wsdl";
    readerExtensions = createObject("webservice", wsdl, creds);   
    
    // Encode pdf
    pdfBase64 = tobinary(ToBase64(ipFile.toByteArray(),
      "iso-8859-1"));   
    
    // Create the structs needed for the web serivce
    inPDFDoc = structNew();
      inPDFDoc['contentType'] = "application/pdf";
      inPDFDoc['attachmentID'] = javacast('null','');
      inPDFDoc['remoteURL'] = javacast('null','');
      inPDFDoc['binaryData'] = pdfBase64;   
    
    usageRights = structNew();
      usageRights['enabledComments'] = TRUE|FALSE;
      usageRights['enabledCommentsOnline'] = TRUE|FALSE;
      etc   
    
    applyOptions = structNew();
      applyOptions['message'] = "";
      applyOptions['modeFinal'] = TRUE;
      applyOptions['usageRights'] = usageRights;   
    
    applyUsageRightsRequest = structNew();
      applyUsageRightsRequest['inPDFDoc'] = inPDFDoc;
      applyUsageRightsRequest['credentialAlias'] = credAlias;
      applyUsageRightsRequest['credentialPassword'] = credPassword;
      applyUsageRightsRequest['applyOptions'] = applyOptions;   
    
    // Call web service
    applyUsageRightsResponse = readerExtensions.applyUsageRights
      (inPDFDoc,credentialAlias,credentialPassword, applyOptions);   
    
    // decode blessed pdf
    pdfBinary = ToBinary(applyUsageRightsResponse.binaryData);   
    
    </CFSCRIPT>

    When you install LiveCycle Reader Extensions you will get a certificate from Adobe. You define the credentialAlias and credentialPassword values when installing the certificate.

    The only tricky part is providing structures of the correct format to the web service. There are a couple of useful flags to help track down any ambiguities or problems with the wsdl:

    creds ['saveJava']=TRUE;
    creds ['refreshWSDL']=TRUE;

    Add these to the creds struct in the example above, These ensure you have the latest wsdl and also instructs ColdFusion to save the intermediate java files that it creates. These will contain the class definitions which map to the above ColdFusion structures.

    Enabling Acrobat Shared Review using ColdFusion

    Monday, December 17th, 2007

    The PDF annotations in Acrobat are impressive, but the implementation was much more suited to an Intranet rather than an Internet environment. In Acrobat 8, Adobe introduced a new type of online annotation for PDFs, called a ‘Shared Review’. In a shared review the information for configuring the annotation store is on the PDF itself, which makes it much more flexible and robust than previously.

    Shared Reviews are normally initialed by a user from within Acrobat. The user follows a wizard, which adds some JavaScript to the PDF. To enable a Shared Review programmatically, needs a suitable library.

    ColdFusion8 has a lot of new PDF related functionality provided by the new <cfpdf> tags. Adding JavaScript isn’t one of them. JavaScript can be added, however, by using the iText library, which is included in ColdFusion, presumably to help implement the CFDocument functionality.

    The JavaScript that you need to insert into the PDF can be copied  from an existing PDF made using the manual approach, and then tweaked to suit your requirements.

    Here is a snipped showing how to insert the JavaScript using the iText PdfStamper class.

    <CFSCRIPT> 
        reader = CreateObject("java", "com.lowagie.text.pdf.PdfReader") 
    	.init(expandPath("#ipFName#"); 
        opFile = CreateObject("java", "java.io.FileOutputStream") 
    	.init(expandPath("#opFName#")); 
        stamper =CreateObject("java", "com.lowagie.text.pdf.PdfStamper") 
    	.init(reader,opFile); 
        stamper.addJavascript(#jscript#); 
        stamper.close(); 
    </CFSCRIPT>