Q & A. V

Hi Andrey,
do you have a working example/configuration how to use Principal Authentication in Documentum?
I just read it here: http://www.emc.com/collateral/software/white-papers/h8843-dfc-session-management-wp.pdf
Thanks, Jens

Below you can find a very basic demonstration of Principal Authentication concept:

public static void main(String[] args) throws Exception {
	// this is just a demo code which writes trust.properties
	// file - perform manual configuration instead!
	ClassLoader classLoader = Test.class.getClassLoader();
	URL dfcProp = classLoader.getResource("dfc.properties");
	String trustLocation = dfcProp.getPath()
			.replace("dfc.properties", "trust.properties");
	FileOutputStream trustStream = new FileOutputStream(new File(trustLocation));
	trustStream.write("ssc_dev.username=dmadmin\n".getBytes());
	trustStream.write("ssc_dev.password=dmadmin\n".getBytes());
	trustStream.write("ssc_dev.domain=\n".getBytes());
	trustStream.close();

	IDfClientX clientX = new DfClientX();
	IDfSessionManager sessionManager = clientX.getLocalClient().newSessionManager();
	sessionManager.setPrincipalName("dmadmin");
	IDfSession session = sessionManager.getSession("ssc_dev");
	System.out.println(session.getLoginInfo().getPrincipalMode());
}

i.e. to leverage functionality of com.documentum.fc.client.DfDefaultPrincipalSupport class (default implementation of com.documentum.fc.client.IDfPrincipalSupport) we need to put into classpath file named “trust.properties” which must have following format:

# to setup credentials for all docbases
# use asterisk (*) instead of docbase name
docbase_name.username=<superuser name>
docbase_name.password=<superuser password>
docbase_name.domain=...
docbase_name.userArg1=...
docbase_name.userArg2=...
docbase_name.securityMode=...

DFC has two interfaces responsible for Principal Authentication:

  • com.documentum.fc.client.IDfPrincipalSupport – creates session for specific userprincipal, default implementation is com.documentum.fc.client.DfDefaultPrincipalSupport, custom implementation may be injected either through IDfClient.setPrincipalSupport() method or through dfc.principal.support system property, i.e. -Ddfc.principal.support=implementation_class
  • com.documentum.fc.client.IDfTrustManager – provides superuser’s IDfLoginInfo, used only by com.documentum.fc.client.DfDefaultPrincipalSupport, default implementation is com.documentum.fc.client.DfDefaultTrustManager, custom implementation may be injected through dfc.trust.manager system property, i.e. -Ddfc.trust.manager=implementation_class

I would suggest to check com.documentum.web.formext.session.UserPrincipalAuthenticationScheme class for understanding how this feature works on application server side. Also you may override com.documentum.fc.client.DfDefaultPrincipalSupport class to make some mapping between principals and docbase users, for example:

IDfPrincipalSupport ps = new DfDefaultPrincipalSupport() {

	@Override
	public IDfSession getSession(String docbaseName, String principalName) 
			throws DfPrincipalException {
		return super.getSession(docbaseName, "dmadmin");
	}
};

IDfClientX clientX = new DfClientX();
IDfClient client = clientX.getLocalClient();
client.setPrincipalSupport(ps);

IDfSessionManager sessionManager = client.newSessionManager();
sessionManager.setPrincipalName("fake_user");
IDfSession session = sessionManager.getSession("ssc_dev");
System.out.println(session.getLoginUserName());

UPD.

What is the benefit of this instead of using dmadmin credentials and creating a ticket for other users?

Both options are unreliable:

  • on the one hand, principal authentication allows you separate logic between different layers, it’s always good
  • on the other hand, by default recent DFC versions do not leverage functionality of session manager’s private session pool, this means that every IDfSessionManager#getSession() call actually performs IDfSessionManager#newSession(), that in case of “principal authentication” is accompanied by generation of new ticket and extra authentication – this is really slow
  • on the another hand, ticket concept is broken in DFC, I would even say that implementation was written by idiots and never worked properly, let’s explain. Every login ticket has expiration time, to prevent unexpected expiration when login ticket is in use (actual for long running workflow tasks) DFC does following:
    • upon successful login DFC replaces old ticket with brand new one that has expiration interval equal to max_login_ticket_timeout in dm_server_config
    • also DFC spawns special task (see com.documentum.fc.client.impl.session.TicketWatchdog) that intended to renew expiring tickets
    • code below (sorry for poor quality) demonstrates that this implementation is broken (I specially set max_login_ticket_timeout to 10 to not wait 30 days):
      /**
       * @author Andrey B. Panfilov <andrew@panfilov.tel>
       */
      public class Test {
      
          public static void main(String[] args) throws Exception {
      
              IDfClientX clientX = new DfClientX();
              IDfClient client = clientX.getLocalClient();
              IDfSessionManager sessionManager = client.newSessionManager();
              String docbase = "ssc_dev";
              sessionManager.setIdentity(docbase, new DfLoginInfo("dmadmin",
                      "dmadmin"));
              IDfSession session = sessionManager.getSession(docbase);
              int timeout = session.getServerConfig().getInt(
                      "max_login_ticket_timeout");
              String ticket = session.getLoginTicket();
              sessionManager.release(session);
              sessionManager.clearIdentities();
              sessionManager.setIdentity(docbase, new DfLoginInfo("dmadmin", ticket));
              dumpTicket(sessionManager, docbase);
              sessionManager.release(sessionManager.getSession(docbase));
              dumpTicket(sessionManager, docbase);
              while (true) {
                  try {
                      System.out.println("sleeping " + ((timeout + 1) * 60 * 1000));
                      Thread.sleep((timeout + 1) * 60 * 1000);
                      sessionManager.release(sessionManager.getSession(docbase));
                      dumpTicket(sessionManager, docbase);
                  } catch (InterruptedException ex) {
                      Thread.currentThread().interrupt();
                  }
              }
          }
      
          private static void dumpTicket(IDfSessionManager sessionManager,
                  String docbase) throws Exception {
              Object loginInfoManager = getValue(sessionManager, "m_loginInfoManager");
              if (loginInfoManager == null) {
                  return;
              }
              Method getEffectiveLoginInfo = getMethod(loginInfoManager.getClass(),
                      "getEffectiveLoginInfo", IDocbaseSpec.class);
              if (getEffectiveLoginInfo == null) {
                  return;
              }
              IDfLoginInfo loginInfo = (IDfLoginInfo) getEffectiveLoginInfo.invoke(
                      loginInfoManager, new DocbaseSpec(docbase));
              String ticket = loginInfo.getPassword();
              if (!ticket.startsWith("DM_TICKET=")) {
                  return;
              }
              String decoded = new String(new BASE64Decoder().decodeBuffer(ticket
                      .substring("DM_TICKET=".length())));
              System.out.println(extractLong(decoded, "expire_time INT S 0")
                      - extractLong(decoded, "create_time INT S 0"));
          }
      
          private static long extractLong(String string, String pattern) {
              int index = string.indexOf(pattern);
              if (index < 0) {
                  return 0;
              }
              return Long.valueOf(string.substring(index + pattern.length() + 1,
                      index + pattern.length() + 11));
          }
      
          public static Field getField(Class<?> clazz, String name) {
              Class<?> cls = clazz;
              while (true) {
                  try {
                      Field field = cls.getDeclaredField(name);
                      field.setAccessible(true);
                      return field;
                  } catch (NoSuchFieldException e) {
                      if (cls == Object.class) {
                          return null;
                      }
                      cls = cls.getSuperclass();
                  }
              }
          }
      
          public static Object getValue(Object object, String fieldName)
              throws IllegalAccessException {
              Field field = getField(object.getClass(), fieldName);
              if (field == null) {
                  return null;
              }
              return field.get(object);
          }
      
          public static Method getMethod(Class<?> clazz, String name,
                  Class<?>... parameterTypes) {
              Class<?> cls = clazz;
              while (true) {
                  try {
                      Method method = cls.getDeclaredMethod(name, parameterTypes);
                      method.setAccessible(true);
                      return method;
                  } catch (NoSuchMethodException e) {
                      if (cls == Object.class) {
                          return null;
                      }
                      cls = cls.getSuperclass();
                  }
              }
          }
      
      }
      
      

      result is:

      [dmadmin@docu70dev01 ~]$ java Test
      300
      600
      sleeping 660000
      Exception in thread "main" DfAuthenticationException:: THREAD: main; MSG: [DM_SESSION_E_AUTH_FAIL]error:  "Authentication failed for user dmadmin with docbase ssc_dev."; ERRORCODE: 100; NEXT: null
              at com.documentum.fc.client.impl.docbase.DocbaseExceptionMapper.newException(DocbaseExceptionMapper.java:52)
              at com.documentum.fc.client.impl.connection.docbase.MessageEntry.getException(MessageEntry.java:39)
              at com.documentum.fc.client.impl.connection.docbase.DocbaseMessageManager.getException(DocbaseMessageManager.java:137)
              at com.documentum.fc.client.impl.connection.docbase.netwise.NetwiseDocbaseRpcClient.checkForMessages(NetwiseDocbaseRpcClient.java:310)
              at com.documentum.fc.client.impl.connection.docbase.netwise.NetwiseDocbaseRpcClient.applyForObject(NetwiseDocbaseRpcClient.java:653)
              at com.documentum.fc.client.impl.connection.docbase.DocbaseConnection$8.evaluate(DocbaseConnection.java:1313)
              at com.documentum.fc.client.impl.connection.docbase.DocbaseConnection.evaluateRpc(DocbaseConnection.java:1072)
              at com.documentum.fc.client.impl.connection.docbase.DocbaseConnection.applyForObject(DocbaseConnection.java:1305)
              at com.documentum.fc.client.impl.docbase.DocbaseApi.authenticateUser(DocbaseApi.java:1867)
              at com.documentum.fc.client.impl.connection.docbase.DocbaseConnection.authenticate(DocbaseConnection.java:432)
              at com.documentum.fc.client.impl.connection.docbase.DocbaseConnectionManager.authenticateConnection(DocbaseConnectionManager.java:350)
              at com.documentum.fc.client.impl.connection.docbase.DocbaseConnectionManager.assignConnection(DocbaseConnectionManager.java:196)
              at com.documentum.fc.client.impl.connection.docbase.DocbaseConnectionManager.getDocbaseConnection(DocbaseConnectionManager.java:99)
              at com.documentum.fc.client.impl.session.SessionFactory.newSession(SessionFactory.java:23)
              at com.documentum.fc.client.impl.session.PrincipalAwareSessionFactory.newSession(PrincipalAwareSessionFactory.java:44)
              at com.documentum.fc.client.impl.session.PooledSessionFactory.newSession(PooledSessionFactory.java:49)
              at com.documentum.fc.client.impl.session.SessionManager.getSessionFromFactory(SessionManager.java:120)
              at com.documentum.fc.client.impl.session.SessionManager.newSession(SessionManager.java:70)
              at com.documentum.fc.client.impl.session.SessionManager.getSession(SessionManager.java:177)
              at Test.main(Test.java:43)
      
    • nobody knows about this bug because typical Documentum application never lives so long (30 days) without restart 🙂

Do we have to use the dmadmin account or any other superuser?

Any superuser account

Do we have to set the dmadmin password in plain text?

You may generate aek.key (do not use aek.key installed on CS!), put it into application’s classpath and encrypt password using this aek.key. Alternatively implement your own com.documentum.fc.client.IDfTrustManager.

2 thoughts on “Q & A. V

  1. Thanks you for providing the solution! But I don´t get it fully:

    What is the benefit of this instead of using dmadmin credentials and creating a ticket for other users?
    Do we have to use the dmadmin account or any other superuser?
    Do we have to set the dmadmin password in plain text?

    Thanks,
    Jens

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s