Thursday, March 6, 2014

Scrambling Data

I was assigned this task a long time ago for a client. The goal was for the client to provide a "demo account" feature to use for demonstration. People would log in to that account and see reports with real data but scrambled attributions.
The approach taken was a non-intrusive one: if one decided to trash the "demo account" feature, there would not be much to change in order to do so.


Requirements


Must scramble only for a certain account.
Must scramble names (like first, middle and last)
Must keep the report data as is (no scrambling of numbers)
All users logged in to the same demo account must see the same scrambled data (i.e. not scrambled for each session, only scrambled once).


Architecture


Instead of changing the core code of the project, I decided to have this new feature as a plug-and-play.
One can just add this new project as a dependency and add a few lines of configuration to scramble data.

I went with Aspect Oriented Programming so I could hook up new logic to existing code and scramble whatever I needed to.

Fig. 1. AOP-based Scrambler


The framework is very basic and provides all the logic necessary to scramble data.
The implementations (Client) is as trivial.

  1. The AbstratAspect class provides all the components to scramble. To scramble data, one only needs to extends that class, provide pointcuts and implement its advice. A "scrambling aspect" will usually go with its own Scrambler but it can also re-use an existing one. It also uses the IScramblingSentry to figure out whether or not to scramble.
  2. The RoleBasedScramblingSentry will detect if the current logged in user has a ROLE_DEMO. This way, a site administrator can make any account a demo one and revoke that "privilege" any time.
  3. The AbstractScrambler class provides here with methods to help scramble data. This class is very specific to my domain (using a RandomNameGenerator) but I can extend it to scramble other kind of data (say city/state/country names).


Setup/Implementation


Using Spring, the following XML configuration will setup a scrambler and my aspect accordingly.

<?xml version="1.0" encoding="UTF-8"?>
<beans 
 xmlns="http://www.springframework.org/schema/beans" 
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 xmlns:aop="http://www.springframework.org/schema/aop"
 xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">
 
 <aop:aspectj-autoproxy/>
 
 <bean id="org.scrambler.rng.IRandomNameGenerator" class="org.scrambler.rng.MappedNameGenerator" factory-method="getInstance"/>
 
 <!-- 
 #########################################
 ## Scramblers
 #########################################
  -->
 <bean id="org.scrambler.AbstractScrambler" abstract="true">
  <property name="randomNameGenerator" ref="org.scrambler.rng.IRandomNameGenerator"/>  
 </bean>
 
 <bean id="org.scrambler.bo.PersonScrambler" class="org.scrambler.bo.PersonScrambler" parent="org.scrambler.AbstractScrambler">
  <property name="cache">
   <bean class="org.scrambler.bo.InMemoryCache" scope="session">
    <aop:scoped-proxy/>
   </bean>
  </property> 
 </bean>
  
 <!-- 
 #########################################
 ## Aspects
 #########################################
  --> 
 <bean id="org.scrambler.aspect.Pointcuts" class="org.scrambler.aspect.Pointcuts"/>
 <bean id="org.scrambler.aspect.AbstractAspect" abstract="true">
  <property name="sentry" ref="org.scrambler.bo.IScramblingSentry"/>  
 </bean>
 
 <bean id="org.scrambler.aspect.PersonDAOAspect" class="org.scrambler.aspect.PersonDAOAspect" parent="org.scrambler.aspect.AbstractAspect">
  <property name="scrambler" ref="org.scrambler.bo.PersonScrambler"/>
 </bean>
</beans>

All I need to add then in the project where scrambling needs to happen is as simple as the following:


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 xsi:schemaLocation="
  http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">

 <bean id="org.scrambler.bo.IScramblingSentry" class="org.scrambler.bo.RoleBasedScramblingSentry">
  <property name="role" value="ROLE_DEMO"/>
 </bean>
 
 <import resource="classpath:/org/scrambler/spring.xml" />
 
</beans>


Considerations


If the weaving is done at run time and there are many classes involved with scrambling, startup times and memory can be severely affected (especially when using Spring).
Also, when using transactions in Spring, it's best to use aspects only and not mix TransactionProxyFactoryBean with aspects.

There's a potential performance hit with this approach and it lies within the IScramblingSentry.scrambling() method. Even if no scrambling should happen, this method will be called. If the implementation is not quick enough, it will introduce lag when such woven classes are involved.

No comments:

Post a Comment