Hi,

I would like to expose you a simple example of interaction with the IRM server in order to encrypt and apply the IRM security on CONTENT files of a document. The IRM configuration is out of scope of this article.
 
 
IRM
Briefly, EMC Documentum Information Rights Management (IRM): Unauthorized access prevention to secured content, enabling organizations to maintain control of information rights beyond the firewall.

  • current version 5.1 SP2,
     
  • IRM solution needs a distinct server with specific installation (1 DB, DFS Web service, load balancer, 2 instances IRM,…).
     
  • To view and interact with IRM, there are several local clients like IRM Client For PDF, IRM Client For Microsoft Office. These clients interact with IRM server when a document is IRMised and with locale tools like WORD, EXCEL, PDF READER..etc.
     
  • To administrate manage the IRM server, there is IRM Adminstration Client tool.
     
  • In DCTM side, in Content server, there are some objects configured for IRM. These elements are also accessible in DA under the node:
    [DOCBASE node]
    |- Rights Management
    |- IRM Profiles
    |- IRM Servers

    select * from irm_profile;
    080xxxxxa0	IRMDEV
    080xxxxx30	IRMConnector
    select * from irm_server; 
    080xxxxxa5	MYIRMSERVER
    select * from irm_expiration;
  • The IRM profile precise only the server to use for IRMisation.
     
  • The security is managed on document via ACL and IRM permission available when a document is IRMised. So, in ACL, it is possible to manage the IRM permission in the [-] Grant IRM Permissions section:
    o Print : Allow users to print content
    o Copy : Allow users to copy content
    o Take Offline : Allow access to content when disconnected
    o View Activity : Allow users to view IRM activity log
    o [LIST_WATERMARK] : Watermark to display on protected document
     
  • The security is also managed on IRM server document for example, it is possible to restrict all documents to nobody can print document. This security overwrites the document security.
     
  • An user who has the READ (or WRITE) basic permission in DCTM, will have the same READ (or WRITE) permission in IRM.
     
  • The R_MODIFIER and R_MODIFY_DATE are overridden:
    BEFORE IRMisation of CONTENT : i_vstamp=14 - r_modify_date=08/06/2015 11:28:31
    AFTER IRMisation of CONTENT : i_vstamp=15 - r_modify_date=01/12/2016 11:40:58
    
  • When a document has been secured via IRM, the irm_document_aspect is attached to this document,
     
  • When a document has been secured via IRM, a new format is associated irm-***** to this document => see the dm_format table:
    select * from dm_format where LOWER(name) like 'irm-%';
    irm-excel12book	IRM-protected MS Excel 2007
    irm-excel8book	IRM-protected MS Excel 8.0-2003 (Windows)
    irm-msw12	IRM-protected MS Word 2007
    irm-msw8	IRM-protected MS Word 8.0-2003 (Windows)
    irm-pdf	IRM-protected Acrobat PDF
    irm-ppt12	IRM-protected MS PowerPoint 2007
    irm-ppt8	IRM-protected MS PowerPoint 8.0-2003 (Windows)
    irm-excel14book	IRM-protected MS Excel 2010
    irm-msw14	IRM-protected MS Word 2010
    irm-ppt14	IRM-protected MS PowerPoint 2010
    
  • The apply of IRM security is done via a SBO service provided by IRM com.documentum.services.irm.IIRMService:
    IDfService irmService = DfClient.getLocalClientEx().newService("com.documentum.services.irm.IIRMService", sessMgr);
    Method method = irmService.getClass().getMethod("setObjectIRMProfile", IDfSession.class, IDfSysObject.class, IDfId.class);
    method.invoke(irmService, targetObject.getObjectSession(), 
    
  • A new dmr_content object is created for the secured content.
     
  • A new dmr_content object can not overwrite directly the previous content.
     
  • The previous dmr_content object (no-secured) is not removed and exists still in the docbase. It becomes unused and unreferenced in dm_document. The execution of standard cleaner “dm_DMClean” “dm_DMFilescan” jobs could remove the object and the file on filestore, howver, these jobs could not remove files from CENTERA.

 
 
LOCAL CONFIG
When, you use IRM on your local development machine, the following actions are necessary:

  • Adding of “emcdctmirm.jar” in the classpath of local project. Otherwise, you obtain the following error:
    29281 [main] ERROR com.documentum.services.irm.impl.irmserver.BaseIRMProxy  - java.lang.ClassNotFoundException: com.emcirm.edsdecrypt.CAPIEDS
     29286 [main] ERROR com.documentum.services.irm.impl.irmserver.PolicyServerConnectionPool  - java.lang.ClassNotFoundException: com.authentica.pvsapi.PVSAPI
     com.documentum.services.irm.impl.irmserver.PolicyServerProxy$PolicyServerException: java.lang.ClassNotFoundException: com.authentica.pvsapi.PVSAPI
  • Adding of a parameter in “VM arguments” of executed class corresponding to the IRM DLL local folders :
    -Djava.library.path="D:\Documentum\DEV\irm;D:\Documentum\DEV\irm\Common"
  • Adding of parameters in “dfc.properties” file:
    dfc.data.dir=C:/Documentum/DEV
  • Creation of new file in classpath for “dctmirm.properties”:
    irm.dir=D\:/Documentum/DEV/irm
    irm.index.enable=false
    irm.connectPool.size=5
    irm.protect.deleteOriginal=true
    irm.log.performance.enable=false
    
  • Installation of IRM Clients:
    C:\Program Files (x86)\EMC IRM:
     * Common
     * IRM Client for Microsoft Office
     * IRM Client For PDF
     * IRM Common
  • If the following error occurs, check the JRE used by your project:
    16:48:29,796 ERROR [main] com.documentum.services.irm.impl.irmserver.BaseIRMProxy - java.lang.UnsatisfiedLinkError: C:\Documentum\DEV\irm\libemcirm_mem.dll: Can't load IA 32-bit .dll on a AMD 64-bit platform
    16:48:29,812 ERROR [main] com.documentum.services.irm.impl.irmserver.PolicyServerConnectionPool - java.lang.UnsatisfiedLinkError: com.authentica.pvsapi.PVSConnHandle.nPVSConnHandle()V
    com.documentum.services.irm.impl.irmserver.PolicyServerProxy$PolicyServerException: java.lang.UnsatisfiedLinkError: com.authentica.pvsapi.PVSConnHandle.nPVSConnHandle()V
    	at com.documentum.services.irm.impl.irmserver.PolicyServerProxy$PolicyServerException.throwException(PolicyServerProxy.java:50)
    	at com.documentum.services.irm.impl.irmserver.PolicyServerProxy.newObject(PolicyServerProxy.java:89)
    

 

EXAMPLE : DOC sur FILESTORE = 090xxxxxxx3c

----------------
BEFORE : 
----------------

------ dm_document or sub-type
o a_storage_type             : filestore_01
o r_aspect_name           [0]: my_aspect_java
o r_content_size             : 9401
o r_full_content_size        : 9401
o a_content_type             : excel12book
o i_contents_id              : 060xxxxxxxxxxxxxxc
o i_vstamp                   : 14
o r_modify_date              : 08/06/2014 11:28:31
o r_modifier                 : user1

------ dmr_content or sub-type
o r_object_id                : 060xxxxxxxxxxxxxxc
o content_size               : 9401
o full_format                : excel12book
o format                     : 27xxxxxxxxxxxxx4
o full_content_size          : 9401
o i_format                [0]: 27xxxxxxxxxxxxx4
o i_full_format           [0]: excel12book
o i_vstamp                   : 1

API> getpath,c,090xxxxxxx3c
\\MYFILESERVER\data\MY_DOCBASE_DEV\content_storage_01\000XXXX5\80\02\ef\18.xls

DQL>execute get_path for '060xxxxxxxxxxxxxxc';
\\MYFILESERVER\data\MY_DOCBASE_DEV\content_storage_01\000XXXX5\80\02\ef\18.xls
----------------
AFTER: 
----------------
------ dm_document or sub-type
o a_storage_type             : filestore_01
o r_aspect_name           [0]: my_aspect_java [1]: irm_document_aspect
o r_content_size             : 14848
o r_full_content_size        : 14848
o a_content_type             : irm-excel12book
o i_contents_id              : 06xxxxxxxxxxxxxx1a
o i_vstamp                   : 15
o r_modify_date              : 01/12/2015 11:40:58
o r_modifier                 : user2
  
------ dmr_content or sub-type
o r_object_id                : 06xxxxxxxxxxxxxx1a
o content_size               : 14848
o full_format                : irm-excel12book
o format                     : 270xxxxxxxxxxxx329
o full_content_size          : 14848
o i_format                [0]: 270xxxxxxxxxxxx329
o i_full_format           [0]: irm-excel12book
o i_vstamp                   : 0

API> getpath,c,090xxxxxxx3c
\\MYFILESERVER\data\MY_DOCBASE_DEV\content_storage_01\000XXXX5\80\2a\f3\34.xls

DQL>execute get_path for '06xxxxxxxxxxxxxx1a';
\\MYFILESERVER\data\MY_DOCBASE_DEV\content_storage_01\000XXXX5\80\2a\f3\34.xls

 
Concerning the PREVIOUS dm_content (no-secured):

  • The previous dmr_content object exists still in the docbase:
    SELECT * from dmr_content where r_object_id = '060xxxxxxxxxxxxxxc';
    060xxxxxxxxxxxxxxc	0	0	6dxxxxxxxxxx28	1	0	9401	excel12book
  • The previous dmr_content becomes unused and unreferenced in dm_document:
    SELECT * from dm_sysobject where i_contents_id = '060xxxxxxxxxxxxxxc';
  • EXECUTION of “dm_DMClean” job with parameter “-clean_content TRUE”. Its logs contains:
    Content object 060xxxxxxxxxxxxxxc has parent count of zero.
    apply,c,060xxxxxxxxxxxxxxc,DESTROY_CONTENT
    getmessage,c
    close,c,q0
    ...
    28.626 0.234 N/A [main] com.documentum.dmcl.impl.DmclApiNativeAdapter@727e8a11.get("apply,c,060xxxxxxxxxxxxxxc,DESTROY_CONTENT") ==> "q0" 
    28.860 0.000 N/A [main] com.documentum.dmcl.impl.DmclApiNativeAdapter@727e8a11.exec("close,c,q0") ==> true 
    
  • The previous dmr_content object has been removed from the docbase.
     
  • The file “\\MYFILESERVER\data\MY_DOCBASE_DEV\content_storage_01\000XXXX5\80\02\ef\18.xls” has been deleted from filestore.
     
  • Importante Note: the content file on Storage has also been deleted by the use of “-clean_content TRUE” parameter otherwise it is necessary to execute the job “dm_DMFilescan”.
    The DESTROY_CONTENT administration method is used and removes a content object from the Docbase if the object has no parent_id values. It also removes the content file associated with the content object from its storage area. This administrative function is used by the dmclean utility to clean up storage areas. (Do not use this for archiving; use PURGE_CONTENT instead : PURGE_CONTENT).

    SELECT * from dmr_content where r_object_id = '060xxxxxxxxxxxxxxc';
    
    DQL> execute get_path for '060xxxxxxxxxxxxxxc'
      [DM_CONTENT_E_SECUREWRITE_POPEN]error:  "Error executing the Secure Writer: 060xxxxxxxxxxxxxxc. The popen() operation failed with status (s)."
      [DM_OBJ_MGR_E_FETCH_FAIL]error:  "attempt to fetch object with handle 060xxxxxxxxxxxxxxc failed"
    

 
 
SOURCE CODE
Finally, here the source code test class for applying IRM security:

import java.lang.reflect.Method;
import java.text.DateFormat;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.TimeZone;

import org.apache.commons.lang.exception.ExceptionUtils;

import com.documentum.fc.client.DfClient;
import com.documentum.fc.client.DfQuery;
import com.documentum.fc.client.IDfClient;
import com.documentum.fc.client.IDfCollection;
import com.documentum.fc.client.IDfDocument;
import com.documentum.fc.client.IDfQuery;
import com.documentum.fc.client.IDfService;
import com.documentum.fc.client.IDfSession;
import com.documentum.fc.client.IDfSessionManager;
import com.documentum.fc.client.IDfSysObject;
import com.documentum.fc.client.aspect.IDfAspects;
import com.documentum.fc.commands.admin.DfAdminCommand;
import com.documentum.fc.commands.admin.IDfApplyExecSQL;
import com.documentum.fc.common.DfException;
import com.documentum.fc.common.DfId;
import com.documentum.fc.common.DfLoginInfo;
import com.documentum.fc.common.IDfId;
import com.documentum.fc.common.IDfLoginInfo;
import com.documentum.fc.common.IDfTime;
import com.documentum.fc.common.IDfValue;

public class DfcApiApplyIRMSecurityTest {
	IDfSession idfSession = null;
	IDfSessionManager sessMgr = null;

	/**
	 * Constructor
	 */
	public DfcApiApplyIRMSecurityTest(String user, String passwd, String docbase) throws Exception {
		getDfSession(user, passwd, docbase);
	}

	/**
	 * MAIN METHOD
	 */
	public static void main(String[] args) throws Exception {
		long startTime = 0;
		long stopTime = 0;
		
		String user = "useradm";
		String passwd = "passwordOfuseradm";
		String docbase = "MY_DOCBASE_DEV";
		String objectId = "090xxxxxxx2f2";
		String irmProfileName = "IRMConnectorProfile";
		
		DfcApiApplyIRMSecurityTest me = new DfcApiApplyIRMSecurityTest(user, passwd, docbase);
		
		boolean isTransactionalSession = false;
		boolean noErrorWithCurrentDocument = false;
		try {
			if (!me.idfSession.isTransactionActive()) {
				me.idfSession.beginTrans();
				isTransactionalSession = true;
			}

			startTime = System.currentTimeMillis();

			// Examples : irm-excel12book
			List<String> irmFormats = me.getIRMFormats();

			IDfDocument doc = null;
			{
				doc = (IDfDocument)me.idfSession.getObject(new DfId(objectId));
				System.out.println("BEFORE IRMisation of CONTENT : i_vstamp="+doc.getInt("i_vstamp") + " - r_modify_date="+doc.getTime("r_modify_date"));
			}
			
			if(doc instanceof IDfAspects) {
				if(doc.findString("r_aspect_name", "irm_document_aspect")>=0){ // OR IDfList aspectsAttached = ((IDfAspects) doc).getAspects();
					throw new RuntimeException(MessageFormat.format("The document ({0}) has already been IRMised (associated to the \"{1}\" ASPECT)", objectId, "irm_document_aspect"));
				}
			}
			
			IDfTime previousModifyDateTime = doc.getTime("r_modify_date");
			String previousModifier = doc.getString("r_modifier");
			String aContentType = doc.getString("a_content_type"); // excel12book

			boolean isIRMableContentType = false;
			for (Iterator<String> iterator = irmFormats.iterator(); iterator.hasNext() && isIRMableContentType==false;) {
				String currIRMFormat = iterator.next();
				isIRMableContentType = currIRMFormat.equalsIgnoreCase("irm-"+aContentType);
			}//end-for

			if(isIRMableContentType==false){
				throw new RuntimeException("The content type of object ("+aContentType+") is not compliant with IRM!");
			}
			
						
			me.applyIRMSecurity(objectId, irmProfileName);	

			
			{
				doc = (IDfDocument)me.idfSession.getObject(new DfId(objectId));
				System.out.println("AFTER IRMisation of CONTENT : i_vstamp="+doc.getInt("i_vstamp")  + " - r_modify_date="+doc.getTime("r_modify_date"));
			}

			// Setting of the previous value for r_modify_date AND r_modifier because the attachment of aspect needs a SAVE action on document.
			// So, Content server overrides r_modifer and r_modify_date informations. 
			setPreviousValuesOfLastModifAttributesBySQL(objectId, me.idfSession, previousModifyDateTime, previousModifier);

			stopTime = System.currentTimeMillis();
			
			noErrorWithCurrentDocument = true;
			
		} catch(Throwable th) {
			Throwable rootCause = ExceptionUtils.getRootCause(th);
			if(rootCause!=null){
				rootCause.printStackTrace();
			}else{
				th.printStackTrace();
			}
			
		} finally {
			if(me!=null){
				if (isTransactionalSession && me.idfSession!=null) {
					if (noErrorWithCurrentDocument) {
						me.idfSession.commitTrans();
					} else {
						me.idfSession.abortTrans();
					}
				}

				// to release a docbase session
				me.releaseSession();
				
				long elapsedTime = stopTime - startTime;
				System.out.println(MessageFormat.format("Execute() total execution time : {0} ms ", elapsedTime));
			}
			
		}
	}

	
	private IDfSession getDfSession(String userName, String password, String docbaseName) throws Exception {
		IDfLoginInfo login = new DfLoginInfo();
		login.setUser(userName);
		login.setPassword(password);
		IDfClient client = DfClient.getLocalClient(); // new DfClient();
		sessMgr = client.newSessionManager();
		sessMgr.setIdentity(docbaseName, login);
		idfSession = sessMgr.getSession(docbaseName);

		if (idfSession != null){
			System.out.println("Session created successfully");
		}

		return idfSession;
	}
	
	
	private void releaseSession() throws Exception {
		sessMgr.release(idfSession);
	}

	private List<String> getIRMFormats() throws DfException {
		IDfCollection col = null;
		List<String> result = new ArrayList<String>();
		try {
			IDfQuery query = new DfQuery();
			query.setDQL("select r_object_id, name, dos_extension, mime_type from dm_format where LOWER(name) like 'irm-%'");
			col = query.execute(idfSession, IDfQuery.DF_EXEC_QUERY);
			if(col != null) {
				while(col.next()){
					// ---- dm_format
					// 27xxxxx123		irm-excel12book		xlsx	application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
					// 27xxxxxxxx456		irm-excel8book		xls		application/vnd.ms-excel
					result.add(col.getString("name"));
				}//end-while
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (col!=null) {
				col.close();
			}
		}
		return result;
	}
	
	private void applyIRMSecurity(String targetObjectId, String irmProfileName) throws Throwable {
		IDfSysObject sysObjectIRMProfile = (IDfSysObject) idfSession.getObjectByQualification("irm_profile WHERE object_name = '"+irmProfileName+"'");
		if(sysObjectIRMProfile != null) {
			DfId irmProfileId = new DfId(sysObjectIRMProfile.getString("r_object_id"));
			if(irmProfileId!=null && !irmProfileId.isNull()) {
				IDfSysObject targetObject = (IDfSysObject)idfSession.getObject(new DfId(targetObjectId));
				if(targetObject instanceof IDfAspects) {
					if(targetObject.findString("r_aspect_name", "irm_document_aspect")<0){ // OR IDfList aspectsAttached = ((IDfAspects) doc).getAspects();
						IDfService irmService = DfClient.getLocalClientEx().newService("com.documentum.services.irm.IIRMService", sessMgr);
						Method method = irmService.getClass().getMethod("setObjectIRMProfile", IDfSession.class, IDfSysObject.class, IDfId.class);
						method.invoke(irmService, targetObject.getObjectSession(), targetObject, irmProfileId);
					}else{
						String tmpStr = MessageFormat.format("The document \"{0}\" is already protected by IRM (presence of ASPECT 'irm_document_aspect')", targetObjectId);
						System.out.println(tmpStr);	
					}
				}
			}
		}
	}
	
	
	private static void setPreviousValuesOfLastModifAttributesBySQL(String currObjectId, IDfSession session, IDfTime previousModifyDateTime, String previousModifier) throws Throwable{
		StringBuilder sql = new StringBuilder();
		sql.append("UPDATE ").append(session.getDocbaseOwnerName()).append(".DM_SYSOBJECT_S");
		sql.append(" SET ");
		sql.append(" R_MODIFIER = '").append(previousModifier).append("',");
		DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
		dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
		sql.append(" R_MODIFY_DATE = to_date('").append(dateFormat.format(previousModifyDateTime.getDate())).append("', 'DD-MM-YYYY HH24:MI:SS')");
		sql.append(" WHERE R_OBJECT_ID = '").append(currObjectId).append("'");
		IDfApplyExecSQL dfApplyExecSQL = (IDfApplyExecSQL) DfAdminCommand.getCommand(DfAdminCommand.APPLY_EXEC_SQL);
		dfApplyExecSQL.setQuery(sql.toString());
		IDfCollection coll = dfApplyExecSQL.execute(session);
		try {
			if (coll.next()) {
				IDfValue dfValue = coll.getValueAt(0);
				if (!dfValue.asBoolean()) {
					throw new RuntimeException("Error in updating of R_MODIFIER and R_MODIFY_DATE fields.");
				}
			} else {
				throw new RuntimeException("Error in updating of R_MODIFIER and R_MODIFY_DATE fields.");
			}
		} finally {
			if(coll!=null){
				coll.close();
			}
		}
	}
	
}

More informations:

 

That’s all!!

Huseyin OZVEREN