Handling Multiple Encrypted Fields in a Transfer Decorator
I had a recent project that had multiple fields in a particular table that needed to be encrypted for privacy and security considerations. I was using Transfer for the project. The typical way to handle the encryption is to create custom accessor methods in a decorator component. The Transfer wiki has a decent explanation of this concept on a page entitled How to Encrypt User Passwords Using a Decorator. However, it is intentionally simple, demonstrating a single property. Multiply your getter/setter methods by how many properties you will be working with, and the lines of code can add up. This framework stuff is supposed to prevent that kind of repetition! Well, utilizing onMissingMethod() and the core concept on the Transfer decorator example, we can handle this scenario efficiently.
First, let's consider the object definition in our sample Transfer configuration file.
-
<object name="Reg" table="Regs" decorator="MyProj.com.Reg">
-
<id name="ID" column="RegID" type="numeric" />
-
<property name="FName" type="string" column="RegFName" />
-
<property name="LName" type="string" column="RegLName" />
-
<property name="LastMod" type="date" column="RegLastMod" />
-
<property name="Expires" type="date" column="RegExpires" nullable="true" />
-
<property name="EncHome" type="string" column="regHome" />
-
<property name="EncWork" type="string" column="regWork" />
-
<property name="EncMobile" type="string" column="regMobile" />
-
<property name="EncSMS" type="string" column="regSMS" />
-
</object>
Note that I have prefixed the Transfer properties with "Enc". This isn't required, but will simplify our processing later on. So in this sample, we have 4 properties: Home, Work, Mobile, and SMS. The concept is that these are client phone numbers that we want to protect with encryption. So, Transfer will generate getEncHome(), getEncWork(), getEncMobile(), getEncSMS(), and corresponding setters. However, we want to provide getHome(), getWork(), getMobile(), getSMS(), and corresponding setters, that get and set the properties after encryption/decryption.
Use onMissingMethod()
To accomplish this, we might have an onMissingMethod() function in the decorator like this:
-
<cffunction name="onMissingMethod" access="public" returntype="any" output="false">
-
<cfargument name="missingMethodName" type="string" required="true">
-
<cfargument name="missingMethodArguments" type="struct" required="true">
-
<cfscript>
-
var func="";
-
var encList="Home,Mobile,SMS,Work";
-
var method=Arguments.missingMethodName;
-
var args=Arguments.missingMethodArguments;
-
var firstArg=args[ListFirst(StructKeyList(args))];
-
var prop=RemoveChars(method,1,3);
-
// Handle encryption/decryption of encrypted values:
-
if( ListFindNoCase("get,set",Left(method,3)) and
-
ListFindNoCase(encList,prop) )
-
{
-
func=variables[Insert("Enc",method,3)];
-
if( Left(method,3) is "get")
-
return decryptValue(func());
-
else
-
func(encryptValue(firstArg));
-
}
-
</cfscript>
-
</cffunction>
As a good security measure, we use an "encList" string to check that only proper methods can be called. Then we examine the method name. If it starts with "get" or "set" and ends with one of the list items, we proceed. Thus, we'll process a method like getHome(), but not a method like getFoo(). We then call getEncHome() and decrypt it for getters, encrypt and call setEncHome() for setters.
Dynamic Method Calling
But alas, how do we do this dynamically without a long if-then-else chain? We call the proper method by referencing it from the component's variables scope. Terrence Ryan describes this in his blog post Cheap and Easy Dynamic Method Calling in CFScript. We construct the method name, in our case, by inserting "Enc" after the "get" or "set" prefix of the method name passed into onMissingMethod (thus "getHome" becomes the Transfer method "getEncHome"). Assign the function to the local "func" variable, then call it as if "func" was the function. Finally, handle any encryption or decryption that you will be doing. For this sample, I have encryptValue() and decryptValue() methods handling the details of that work.
Summary
We've used two solutions to handle multiple encrypted fields in the decorator. First, we used onMissingMethod() to handle processing of all of the properties in a single method. Then, we used dynamic method calling to call the Transfer-generated accessors within onMissingMethod().
The end result is being able to use setHome(), getHome(), etc. with the encryption and decryption being handled automatically. Great!
In addition to accessors, we may sometimes want to use the object's memento with the Transfer method getMemento(). In my next blog post, I'll demonstrate how that process can also be done very efficiently.
