Working with Security

The information in this section describes some of the tasks related to client-side security as you develop your applications.

Obtaining a LoginContext

Performing and using a Java™ Authentication and Authorization (JAAS) login consists of three steps: obtaining a LoginContext object, calling the LoginContext.login method, and impersonating the logged-in user to perform the actual work. This section provides some information and a code sample that illustrate how to perform these steps.

Obtaining a LoginContext object requires a tag to indicate some particular configuration in the JAAS login configuration file. If your login configuration file has an entry for "other", then it doesn't matter what tag you use in the call to the constructor of LoginContext, since the call will fall back to the "other" entry in the configuration file. You also need to supply a CallbackHandler to provide the credentials (username and password). If you would like to be prompted for that information, you could just provide a new instance of one of Sun's sample callback handlers, such as com.sun.security.auth.callback.DialogCallbackHandler (as shown in the example below) or TextCallbackHandler.

The login method calls the callback handler and if this is a BEA WebLogic® or IBM® WebSphere® application server the credentials are authenticated. For JBoss, credentials are not authenticated until they are used in an interaction with the server to fetch, save, or search for objects. Problems with credential authentication, such as an invalid password, can occur in different places depending on the application server you use to perform authentication. A successful call to the login method for JBoss does not mean that the credentials have been authenticated.

A UserContext object is associated with each thread that accesses the Content Engine Java API. A JAAS Subject should be associated with the thread, by calling UserContext.pushSubject, prior to making any calls that will cause the API to access the Content Engine server. Note that the UserContext class provides a helper method that can perform the JAAS login for you in the common username/password case. This approach would typically be taken when using the Content Engine Java API over the web service transport. The example in this section uses the EJB transport for all subsequent Content Engine Java API calls. To instead use the web service transport, change the JAAS configuration stanza name in the UserContext.createSubject call from "FileNetP8" to "FileNetP8WSI".

Java Example

//  Perform login
static void performLogin(
        Connection con,
    	String userName,	// Example: "username@testdom.local"
        String passWord) throws Exception 
{
    // *** Step 1: Obtain login context ***

    // Determine call back handler
    CallbackHandler handler = null;
    if (userName != null) 
    {
        // Call user-created class that implements CallbackHandler
        handler = new UserPasswordHandler(userName, passWord);
    }
    else 
    {
    	handler = new DialogCallbackHandler();
    }

    // Get login context
    LoginContext lc = new LoginContext("mysystem", handler);

    // *** Step 2: Call login method ***

    // Attempt login
    try 
    {
        lc.login();
        System.out.println("Login succeeded");
    }
    catch (Exception exc) 
    {
    	System.out.println("Login failed");
        throw exc;  
    }

    // *** Step 3: Impersonate logged-in user ***

    // Get user context
    UserContext uc = UserContext.get();
        
    // Determine subject
    Subject sub = null;
    if (userName != null) 
    {       
        // Example for EJB transport. Stanza name is FileNetP8 for EJB, FileNetP8WSI for WSI.
        sub = UserContext.createSubject(con, userName, passWord, "FileNetP8");
    }
    else 
    {
        sub = lc.getSubject();
    }

    // Associate the JAAS Subject with the UserContext
    try
    {
        uc.pushSubject(sub);
        // do work
    }
    finally
    {
        uc.popSubject();
    }
}

Setting Permissions

The annotated sample code below illustrates how to set access permissions on a new object. The access mask defines operations that the grantee (user or group) is allowed to perform. Each bit in the mask represents an operation. If the bit is set, the grantee is allowed to perform the specified operation; if the bit is not set, the operation is disallowed for that grantee. In this case, the user named "test1" is being granted full control access to a document using the group of access rights represented by the AccessLevel constant.

Java Example

  {
      //create a new permissions list
      AccessPermissionList apl = Factory.AccessPermission.createList(); 

     //create a new access permission object
     AccessPermission ap = Factory.AccessPermission.createInstance();
            
     // set access permissions  
     ap.set_GranteeName("test1");
     ap.set_AccessType(AccessType.ALLOW);            
     ap.set_AccessMask(new Integer(AccessLevel.FULL_CONTROL_DOCUMENT_AS_INT)); 

     // add the permissions to the list
     apl.add(ap);
            
     // create a document and add the permissions
     Document doc = Factory.Document.createInstance(os, ClassNames.DOCUMENT); 
     doc.set_Permissions(apl); 

     // save the document with its permissions to the object store
     doc.getProperties().putValue("DocumentTitle", "Example: Permissions Setting");
     doc.save(RefreshMode.REFRESH);
     System.out.println("Document id: " + doc.get_Id().toString());            
  }

C# Example

  // example: main snippet
  private void demoPermissionSetting(IObjectStore os)
  {
      //create a new permissions list
      IAccessPermissionList apl = Factory.AccessPermission.CreateList();

      //create a new access permission object
      IAccessPermission ap = Factory.AccessPermission.CreateInstance();

      //set access permissions
      ap.GranteeName = "test1";
      ap.AccessType = AccessType.ALLOW;
      ap.AccessMask = (int) AccessLevel.FULL_CONTROL_DOCUMENT;

      //add the permission to the list
      apl.Add(ap);

      //Create a document and add the permissions
      IDocument doc = Factory.Document.CreateInstance(os, ClassNames.DOCUMENT);
      doc.Permissions = apl;

      //Save the document with its permissions to the object store
      doc.Properties.GetProperty("DocumentTitle").SetObjectValue("Example: Permissions Setting");
      doc.Save(RefreshMode.REFRESH);
      System.Console.WriteLine("Document id: " + doc.Id.ToString());            
  }

Working with Marking Objects

The following code snippet retrieves all MarkingSet objects from the FileNet® P8 domain.

Java Example

  Domain domain = Factory.Domain.fetchInstance(conn, "Domain", null);
  domain.get_MarkingSets();

Implementing Kerberos

This section describes client-side implementation of Kerberos for single sign-on (SSO) authentication. For details about any Kerberos-related topic in this section, you can also refer to the Kerberos for Content Engine section of the Content Engine Administration documentation.

Prerequisites

A number of prerequisites must be met before you can use Kerberos for single sign-on authentication. They are listed below by system, domain/account, .Net client, and Java server categories.

System Prerequisites

Domain/Account Prerequisites

Standalone .NET Client Prerequisites

Content Engine Java Server Prerequisites

Using Kerberos on an API Client

The following is an example illustrating how to use Kerberos authentication with a Content Engine .NET API client:

Java Example

  // Java clients currently do not support Kerberos authentication

C# Example

  String strPrincipal = "FNCEWS/" + strHost + "@" + strDomain; 
  SecurityToken token = new KerberosToken(strPrincipal); 
  UserContext.SetThreadSecurityToken(token); 
  IConnection conn = Factory.Connection.GetConnection(strURI); 
  IDomain domain = Factory.Domain.GetInstance(conn, strP8Domain);

The above example uses the identity of the Windows system logged-on user for each Content Engine command. Note that this use is only supported when .NET 2.0 with WSE 3.0 is installed on the client system.

Using Kerberos on a Web Client

A pure .NET web client may also access the Content Engine by directly sending web service requests rather than making use of the Content Engine .NET API library. Here is a partial example that uses .NET 1.1 and WSE 2.0:

Java Example

  // Java clients currently do not support Kerberos authentication

C# Example

  CEWSI.FNCEWS35ServiceWse objBinding = new CEWSI.FNCEWS35ServiceWse(); 
  SoapContext objCtx = objBinding.RequestSoapContext; 
  String strPrincipal = "FNCEWS/" + strHost + "@" + strDomain; 
  SecurityToken token = new KerberosToken2(strPrincipal); 
  objCtx.Security.Tokens.Add(tok); 
  ... 
  objResponses = objBinding.GetObjects(objRequestArray); 
  ...  

When using direct web service requests, however, the KerberosToken2 object must be recreated with each web service request that is sent out and not reused or cached. If a token is reused, then there will likely be "Request is a replay" errors on the Content Engine server and the request will be rejected. (This does not apply when using the Content Engine .NET API.)

Working with Security Policies

The Java and C# examples in this section show you how to work with security policies and security templates. In the examples, you'll see how to create and assign a security policy. Other examples show you how to apply an application security template to an object, and how to retrieve permission information from a security template. The last example shows you how to remove a security policy from both an object and from an object store.

Creating a Security Policy

The following Java and C# examples show how to create a SecurityPolicy object. To start, three SecurityTemplate objects are created and added to a SecurityTemplateList object. Two of the templates are of type VersioningSecurityTemplate, and one is of type ApplicationSecurityTemplate. The VersioningSecurityTemplate objects are intended to be applied automatically to released and superseded versions of an object. The ApplicationSecurityTemplate object must be applied to an object manually (see Applying an ApplicationSecurityTemplate). For each template, the TemplatePermissions property is set to an AccessPermissionList object, which is returned by the setPermissions method (not shown).

Next, a SecurityPolicy object is created and its SecurityTemplates property is set to the SecurityTemplateList. Note that the security policy's PreserveDirectPermissions property is set to false; therefore, when the security policy is assigned to an object, the object's original direct permissions are replaced by the permissions defined in the security policy's templates.

Java Example

public void createSecurityObject(ObjectStore os)
{
   // Create security templates
   VersioningSecurityTemplate vst1 = Factory.VersioningSecurityTemplate.createInstance(os);
   VersioningSecurityTemplate vst2 = Factory.VersioningSecurityTemplate.createInstance(os);
   ApplicationSecurityTemplate vst3 = Factory.ApplicationSecurityTemplate.createInstance(os);
   SecurityTemplateList stl = Factory.SecurityTemplate.createList();
   vst1.set_ApplyStateID(VersionStatusId.RELEASED);
   vst1.set_TemplatePermissions( setPermissions("#AUTHENTICATED-USERS", AccessLevel.MAJOR_VERSION_DOCUMENT.getValue()) );
   vst1.set_DisplayName("Version Template for Released Object");
   vst1.set_IsEnabled(Boolean.TRUE);
   stl.add(vst1);
   
   vst2.set_ApplyStateID(VersionStatusId.SUPERSEDED);
   vst2.set_TemplatePermissions( setPermissions("#AUTHENTICATED-USERS", AccessLevel.VIEW.getValue()) );
   vst2.set_DisplayName("Version Template for Superseded Object");
   vst2.set_IsEnabled(Boolean.TRUE);
   stl.add(vst2);
   
   vst3.set_ApplyStateID(new Id("{21a47705-d20a-4b65-938e-2ddcefa45927}") );
   vst3.set_TemplatePermissions( setPermissions("#AUTHENTICATED-USERS", 
      AccessLevel.READ.getValue()+ AccessRight.DELETE_AS_INT ) );
   vst3.set_DisplayName("Application Template for Obsolete Objects");
   vst3.set_IsEnabled(Boolean.TRUE);
   stl.add(vst3);

   // Create security policy
   SecurityPolicy sp = Factory.SecurityPolicy.createInstance(os, ClassNames.SECURITY_POLICY);
   sp.set_SecurityTemplates(stl);
   sp.set_DisplayName("Security Policy with Version and Application Templates");
   sp.set_PreserveDirectPermissions(Boolean.FALSE);
   sp.save(RefreshMode.REFRESH);
}

C# Example

public void createSecurityObject(IObjectStore os)
{
   // Create security templates
   IVersioningSecurityTemplate vst1 = Factory.VersioningSecurityTemplate.CreateInstance(os);
   IVersioningSecurityTemplate vst2 = Factory.VersioningSecurityTemplate.CreateInstance(os);
   IApplicationSecurityTemplate vst3 = Factory.ApplicationSecurityTemplate.CreateInstance(os);
   ISecurityTemplateList stl = Factory.SecurityTemplate.CreateList();
   vst1.ApplyStateID = VersionStatusId.RELEASED;
   vst1.TemplatePermissions = setPermissions("#AUTHENTICATED-USERS", (int) AccessLevel.MAJOR_VERSION_DOCUMENT);
   vst1.DisplayName = "Version Template for Released Object";
   vst1.IsEnabled = true;
   stl.Add(vst1);

   vst2.ApplyStateID = VersionStatusId.SUPERSEDED;
   vst2.TemplatePermissions = setPermissions("#AUTHENTICATED-USERS", (int) AccessLevel.VIEW);
   vst2.DisplayName = "Version Template for Superseded Object";
   vst2.IsEnabled = true;
   stl.Add(vst2);

   vst3.ApplyStateID = new Id("{21a47705-d20a-4b65-938e-2ddcefa45927}");
   vst3.TemplatePermissions = setPermissions("#AUTHENTICATED-USERS",
         (int) AccessLevel.READ + (int) AccessRight.DELETE);
   vst3.DisplayName = "Application Template for Obsolete Objects";
   vst3.IsEnabled = true;
   stl.Add(vst3);

   // Create security policy
   ISecurityPolicy sp = Factory.SecurityPolicy.CreateInstance(os, ClassNames.SECURITY_POLICY);
   sp.SecurityTemplates = stl;
   sp.DisplayName = "Security Policy with Version and Application Templates";
   sp.PreserveDirectPermissions = false;
   sp.Save(RefreshMode.REFRESH);
}

Assigning a Security Policy

The following Java and C# examples show how to assign a security policy to a class. The Id objects for the ClassDefinition and the SecurityPolicy are passed to the method, which creates the ClassDefinition and the SecurityPolicy objects. Using the helper method getPropertyDefinition (not shown), the code retrieves the class's PropertyDefinition object for SecurityPolicy, then sets it to the SecurityPolicy object.

Note that the security policy will automatically be applied to new object instances of the class, but not to existing object instances of the class. You must explicitly set the security policy on the existing object instances of the class.

Java Example

public void assignSecurityPolicy(ObjectStore os, Id classId, Id securityPolicyId)
{
   ClassDefinition cd = Factory.ClassDefinition.fetchInstance(os, classId, null);
   SecurityPolicy sp = Factory.SecurityPolicy.getInstance(os, ClassNames.SECURITY_POLICY, securityPolicyId);
   PropertyDefinition pd = getPropertyDefinition(cd.get_PropertyDefinitions(), ClassNames.SECURITY_POLICY);
   (pd.getProperties().get(PropertyNames.PROPERTY_DEFAULT_OBJECT)).setObjectValue(sp);
   cd.save(RefreshMode.REFRESH);
}

C# Example

public void assignSecurityPolicy(IObjectStore os, Id classId, Id securityPolicyId)
{
   IClassDefinition cd = Factory.ClassDefinition.FetchInstance(os, classId, null);
   ISecurityPolicy sp = Factory.SecurityPolicy.GetInstance(os, ClassNames.SECURITY_POLICY, securityPolicyId);
   IPropertyDefinition pd = getPropertyDefinition(cd.PropertyDefinitions, ClassNames.SECURITY_POLICY);
   (pd.Properties.GetProperty(PropertyNames.PROPERTY_DEFAULT_OBJECT)).SetObjectValue(sp);
   cd.Save(RefreshMode.REFRESH);
}

Applying an Application Security Template

The following Java and C# examples show how to apply an ApplicationSecurityTemplate to an object. The examples iterate a Folder object, filtering all documents that have not been modified for over one year. For the documents that meet the criterion, an application security template with delete permission is applied. The examples assume that the documents in the folder were previously assigned a SecurityPolicy containing the application security template with delete permission.

Java Example

public void applyApplicationSecurityTemplate(ObjectStore os, Id folderId)
{
   // Create folder object
   Folder folder = Factory.Folder.fetchInstance(os, folderId, null);

   // Get all documents in folder
   DocumentSet ds = folder.get_ContainedDocuments();
   
   // Get current date to compare against dates of documents
   Calendar cal = new GregorianCalendar();
   int currYear = cal.get(Calendar.YEAR);
   int currMonth = cal.get(Calendar.MONTH);
   
   // Iterate folder documents and check last modification date.
   // If over one year, apply ApplicationSecurityTemplate with delete permission.
   Iterator iter = ds.iterator();
   while (iter.hasNext())
   {
       Document doc = (Document) iter.next();
       Date docDate = doc.get_DateLastModified();
       cal.setTime(docDate);
       if (cal.get(Calendar.YEAR) < currYear &&  cal.get(Calendar.MONTH) < currMonth )
       {
           doc.applySecurityTemplate(new Id("{21a47705-d20a-4b65-938e-2ddcefa45927}") );
           doc.save(RefreshMode.REFRESH);
       }
   }
}

C# Example

public void applyApplicationSecurityTemplate(IObjectStore os, Id folderId)
{
   // Create folder object
   IFolder folder = Factory.Folder.FetchInstance(os, folderId, null);

   // Get all documents in folder
   IDocumentSet ds = folder.ContainedDocuments;
   
   // Get current date to compare against dates of documents
   int currYear = DateTime.Now.Year;
   int currMonth = DateTime.Now.Month;
   
   // Iterate folder documents and check last modification date.
   // If over one year, apply ApplicationSecurityTemplate with delete permission.
   System.Collections.IEnumerator iter = ds.GetEnumerator();
   while (iter.MoveNext())
   {
       IDocument doc = (IDocument) iter.Current;
       DateTime docDate = (DateTime) doc.DateLastModified;
       if (docDate.Year < currYear+1 && docDate.Month < currMonth+1)
       {
           doc.ApplySecurityTemplate(new Id("{21a47705-d20a-4b65-938e-2ddcefa45927}") );
           doc.Save(RefreshMode.REFRESH);
       }
   }
}

Getting Security Template Information

You can get descriptive permission information from a security template. A SecurityTemplate object includes the TemplatePermissionDescriptions property, which contains a list of AccessPermissionDescription objects, from which you can get information about access rights.

The following Java and C# examples get permission descriptions for every SecurityTemplate object contained in a SecurityPolicy object passed to the method. Iterating the list of security templates (SecurityTemplateList), the method retrieves the TemplatePermissionDescriptions property from a security template. It then iterates the AccessPermissionDescriptionList and prints out information from each AccessPermissionDescription object.

Java Example

public void getSecurityTemplateInformation(ObjectStore os, Id secPolicyId)
{
   SecurityPolicy sp = Factory.SecurityPolicy.fetchInstance(os, secPolicyId, null );
   SecurityTemplateList stl = sp.get_SecurityTemplates();
   Iterator outerIter = stl.iterator();
   while (outerIter.hasNext())
   {
       SecurityTemplate st = (SecurityTemplate) outerIter.next();
       AccessPermissionDescriptionList apdl = st.get_TemplatePermissionDescriptions();
       Iterator innerIter = apdl.iterator();
       System.out.println("Security template is " + st.get_DisplayName());
       while (innerIter.hasNext())
       {
          AccessPermissionDescription apd = (AccessPermissionDescription) innerIter.next();
          System.out.println("Permission is " + apd.get_DescriptiveText() + "\n" + 
             "Permission type is " + apd.get_PermissionType().toString() + "\n" +
             "Access mask is " + apd.get_AccessMask()
          );
       }
       System.out.println("=============================\n");
   }
}

C# Example

public void getSecurityTemplateInformation(IObjectStore os, Id secPolicyId)
{
   ISecurityPolicy sp = Factory.SecurityPolicy.FetchInstance(os, secPolicyId, null);
   ISecurityTemplateList stl = sp.SecurityTemplates;
   System.Collections.IEnumerator outerIter = stl.GetEnumerator();
   while (outerIter.MoveNext())
   {
       ISecurityTemplate st = (ISecurityTemplate) outerIter.Current;
       IAccessPermissionDescriptionList apdl = st.TemplatePermissionDescriptions;
       System.Collections.IEnumerator innerIter = apdl.GetEnumerator();
       System.Console.WriteLine("Security template is " + st.DisplayName);
       while (innerIter.MoveNext())
       {
          IAccessPermissionDescription apd = (IAccessPermissionDescription) innerIter.Current;
          System.Console.WriteLine("Permission is " + apd.DescriptiveText + "\n" + 
            "Permission type is " + apd.PermissionType.ToString() + "\n" +
            "Access mask is " + apd.AccessMask
          );
       }
       System.Console.WriteLine("=============================\n");
    }
}

Removing a Security Policy

You can remove a security policy from an object by setting the object's SecurityPolicy property to null. If the object is a class, you set the class's PropertyDefinition object for SecurityPolicy to null. You can also remove a security policy from an object store — if no objects hold a reference to it.

The following Java and C# examples first show how to remove a security policy from a class, and then how to delete the security policy from an object store. Note that removing a security policy from a class does not remove the security policy from objects based on that class. Before you can delete a security policy from an object store, you must explicitly remove the security policy from every object that holds a reference to it.

Java Example

public void removeSecurityObject(Id classId, ObjectStore os)
{
   // Get class from which security object will be removed.
   ClassDefinition cd = Factory.ClassDefinition.fetchInstance(os, classId, null);
   
   // Get property definition for SecurityPolicy.
   // Helper method getPropertyDefinition not shown.
   PropertyDefinition pd = getPropertyDefinition(cd.get_PropertyDefinitions(), ClassNames.SECURITY_POLICY);
    
   // Get Id of SecurityObject to be removed.
   SecurityPolicy spTarget = (SecurityPolicy) (pd.getProperties().get(PropertyNames.PROPERTY_DEFAULT_OBJECT)).getObjectValue();
   Id spTargetId = spTarget.get_Id();
   
   // Remove SecurityObject from the class.
   (pd.getProperties().get(PropertyNames.PROPERTY_DEFAULT_OBJECT)).setObjectValue(null);
   cd.save(RefreshMode.REFRESH);

   // Delete SecurityObject from the object store.
   SecurityPolicySet sps = os.get_SecurityPolicies();
   Iterator outerIter = sps.iterator();
   while (outerIter.hasNext())
   {
      SecurityPolicy sp = (SecurityPolicy) outerIter.next();
      if (sp.get_Id().equals(spTargetId) )
      {
          sp.delete();
          sp.save(RefreshMode.REFRESH);
      }
   }
}

C# Example

public void removeSecurityObject(Id classId, IObjectStore os)
{
   // Get class from which security object will be removed.
   IClassDefinition cd = Factory.ClassDefinition.FetchInstance(os, classId, null);

   // Get property definition for SecurityPolicy.
   // Helper method getPropertyDefinition not shown.
      IPropertyDefinition pd = getPropertyDefinition(cd.PropertyDefinitions, ClassNames.SECURITY_POLICY);

   // Get Id of SecurityObject to be removed.
   ISecurityPolicy spTarget = (ISecurityPolicy)(pd.Properties.GetProperty(PropertyNames.PROPERTY_DEFAULT_OBJECT)).GetObjectValue();
   Id spTargetId = spTarget.Id;

   // Remove SecurityObject from the class.
   (pd.Properties.GetProperty(PropertyNames.PROPERTY_DEFAULT_OBJECT)).SetObjectValue(null);
   cd.Save(RefreshMode.REFRESH);

   // Delete SecurityObject from the object store.
   ISecurityPolicySet sps = os.SecurityPolicies;
   System.Collections.IEnumerator outerIter = sps.GetEnumerator();
   while (outerIter.MoveNext())
   {
       ISecurityPolicy sp = (ISecurityPolicy)outerIter.Current;
       if (sp.Id.Equals(spTargetId))
       {
           sp.Delete();
           sp.Save(RefreshMode.REFRESH);
       }
   }
}