Use Case for Decoding DACS Locks and How To Do It

When using an OpenDACS API with a Content Based Entitlement service the API, in the form of the AuthorizationAgent.checkSubscription() method, encourages the treatment of the DACS Lock as an opaque element of data to maintain a loose coupling and where possible, this is as it should be.

So why would we want to decode a DACS lock into its constituents?

Before answering that question let’s examine the anatomy of a DACS lock starting with an example extract from a RefreshMsg;

RefreshMsg
    streamId="5"
    domain="MarketPrice Domain"
    solicited
    RefreshComplete
    state="Open / Ok / None / 'All is well'"
    itemGroup="00 01"
    permissionData="03 01 01 65 62 c0"
    name="TRI.N"
    nameType="1"
    serviceId="257"
    serviceName="ELEKTRON_DD"
    Payload dataType="FieldList"
        FieldList FieldListNum="79" DictionaryId="1"
            FieldEntry fid="1" name="PROD_PERM" dataType="UInt" value="6562"
            FieldEntry fid="2" name="RDNDISPLAY" dataType="UInt" value="64"
            FieldEntry fid="3" name="DSPLY_NAME" dataType="Rmtes" value="THOMSON REUTERS"
            FieldEntry fid="4" name="RDN_EXCHID" dataType="Enum" value="2"
            FieldEntry fid="6" name="TRDPRC_1" dataType="Real" value="44.16"
…

The permissionData="03 01 01 65 62 c0" contained in the header of the RefreshMsg is the DACS lock.

The structure of a DACS Lock is described in the RFA C++ DACSLOCK API Developers Guide, the breakdown for the simple example above is:

Version

ServiceID

PE

End

03

01 01

65 62

c0

The ServiceID is encoded in hex, the value defined in the DACS Service definition and the infrastructure configuration will be in decimal: 0101 = 257

This is the originating service and therefore maps to the same service name as the request in this instance: “ELEKTRON_DD”.

The PE value is Binary Coded Decimal = 6562. For more complex DACS Locks there can be additional PE values and operators (AND/OR) that specify how to apply the requirement for the combination of PE values.

For services provided by a compound server i.e. where it combines data, adds some value or provides some derived calculation from data of another service then the DACS Lock may contain additional ServiceID and PE combinations from the originating services. This is known as a compound lock. Whenever you use the checkSubscription() method with a DACS Lock, the API will verify the users entitlement against each originating service and its associated PE values stored in the lock; this maintains correct data access control whilst reducing the administrative burden because the  user only needs to be entitled to the originating services.

The Use-case For Decoding Locks

When data is stored in another system, such as a database, one of the challenges can be how to allow search expressions on the data whilst maintaining correct data access control.

One solution is to extract the user entitlement profile data and include it in the expression so that the result-set provided is based on the stored data and the user entitlement.

For this to work we first need access to the user entitlement profile, this is available using the AuthorizationAgent::getPEList() method;


rfa::dacs::AuthorizationAgent::getPEList(
        const rfa::common::Handle & handle,
        const rfa::common::RFA_String & sServiceName,
        const rfa::common::RFA_String & sPEFilter,
        rfa::common::RFA_Vector<unsigned long> & vPEList)

 

When successful the vPEList parameter will be populated with the list of PEs for sServiceName assigned to the user that is associated with the handle.

The second part of this solution requires the PE value from the DACSLock and this is where decoding becomes useful. The decoded DACS Lock must be stored with the data it is associated with, e.g.          

DSPLY_NAME

RDN_EXCHID

TRDPRC_1

HIGH_1

Service

PE

"THOMSON REUTERS"

 

NYS (2)

44.16

44.17

ELEKTRON_DD

6562

You might consider the practice of using the PROD_PERM field to access the PE value, which as you can see in the above example holds the same value, however the PROD_PERM field is not capable of representing any variant that might occur in a DACS Lock as described above; Also the originating service in the DACS Lock does not have to match the service name or ServiceID in the request, this will depend on the configuration of the infrastructure.

Now using a table containing the data from getPEList and the DACS Lock information added to the data table as shown above a JOIN statement of the two tables can yield a result set that the user is entitled to access.

Example DACS Decoding in Java

The following code will extract the service, operator and PE values stored in a DACS Lock, it can be used within an EMA or RFA consumer. The Open DACS API is included in RFA so to use Open DACS in an EMA application you will need to include the RFA library: rfa.jar 

First, when we receive a Refresh message we have to check that there is a DACS Lock present and only then extract it:

		if( refreshMsg.hasPermissionData()) // Always check before accessing
		{
			System.out.print("hasPermissionData: ");
			// Get the raw DACS lock.
			java.nio.ByteBuffer bufPerm = refreshMsg.permissionData();
			byte[] byteBuf = new byte[bufPerm.limit()-bufPerm.position()];
			bufPerm.get(byteBuf);		

Create an AuthorizationLock using the raw DACS Lock...

try {
	String lockStr=" ";
	//Create an AuthorizationLock from the raw DACS Lock.
	AuthorizationLock authLock = new AuthorizationLock(byteBuf);
	// Get the lock with serviceIDs
	AuthorizationLockResult alr = 
		AuthorizationLockUtility.getStructuredServiceIDLock(authLock);

Check the result of using the AuthorizationLockUtility to get a StructuredServiceIDLock from the AuthorizationLock…

// Check the result
	if( AuthorizationLockResult.VALID == alr.getState() &&
	    AuthorizationLockResult.SUCCESS == alr.getStatusCode())
	{

We need to use AuthorizationLockResult .getData() to get the AuthorizationStructuredServiceIDLock, but before using a cast make sure that the AuthorizationLockResult has the correct type…

// Make sure it is a AuthorizationStructuredServiceIDLock before casting.
	if( alr.getType() == 
          AuthorizationLockResult.AS_AuthorizationStructuredServiceIDLock )
	{
		AuthorizationStructuredServiceIDLock[] aAssil = 
			(AuthorizationStructuredServiceIDLock[])alr.getData();

Now we can start to iterate over each service entry in the lock…

// for each service in the lock get its associated PEs and operator.
for( AuthorizationStructuredServiceIDLock assil : aAssil )
{ 
	lockStr = "SId=";
	lockStr += Integer.toString(assil.getServiceID());
	lockStr += " Op=";
	if ( assil.getOperator() == AuthorizationLock.OR )
		lockStr += "OR ";
	else if ( assil.getOperator() == AuthorizationLock.AND )
			lockStr += "AND ";

Once we have the serviceId and the operator for this entry, we access its list PEs…

   long[] peList = assil.getPEList();
   if( peList != null && peList.length>0 )
   {
		lockStr += "PE=";
		for( long pe : peList )
		{
			lockStr += Long.toString(pe);
			lockStr +=" ";
		}
   }
} // repeat for the next AuthorizationStructuredServiceIDLock entry

Finally, report the result…

		} // else AuthorizationLockResult indicates incorrect type
		else 
		   System.err.println("Unable to decode StructuredServiceIDLock");	
    }
	System.out.println("PermData = "+lockStr);
  } catch (AuthorizationException e) {
		System.err.println("AuthorizationException");
		e.printStackTrace();
		System.exit(-1);
  }
} // end of permissionData processing

For the example RefreshMsg given earlier, you should see the following report given by the code above for the DACS Lock:

hasPermissionData: PermData = SId=257 Op=AND PE=6562

Conclusion

Using the features of Open DACS described above and preserving the  decoded DACS Lock information it is possible to maintain access control as defined by the DACS administrator even when using SQL or search expressions to extract from a data store.