/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2005 Sun Microsystems Inc. All Rights Reserved
*
* The contents of this file are subject to the terms
* of the Common Development and Distribution License
* (the License). You may not use this file except in
* compliance with the License.
*
* You can obtain a copy of the License at
* https://opensso.dev.java.net/public/CDDLv1.0.html or
* opensso/legal/CDDLv1.0.txt
* See the License for the specific language governing
* permission and limitations under the License.
*
* When distributing Covered Code, include this CDDL
* Header Notice in each file and include the License file
* at opensso/legal/CDDLv1.0.txt.
* If applicable, add the following below the CDDL Header,
* with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
* $Id: COSManager.java,v 1.5 2009/01/28 05:34:51 ww203982 Exp $
*
* Portions Copyright 2015 ForgeRock AS.
*/
package com.iplanet.ums.cos;
import com.iplanet.services.ldap.Attr;
import com.iplanet.services.ldap.AttrSet;
import com.iplanet.services.util.I18n;
import com.iplanet.sso.SSOException;
import com.iplanet.sso.SSOToken;
import com.iplanet.sso.SSOTokenManager;
import com.iplanet.ums.Guid;
import com.iplanet.ums.IUMSConstants;
import com.iplanet.ums.PersistentObject;
import com.iplanet.ums.SchemaManager;
import com.iplanet.ums.SearchResults;
import com.iplanet.ums.UMSException;
import com.iplanet.ums.UMSObject;
import java.security.Principal;
import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.StringTokenizer;
import org.forgerock.opendj.ldap.DN;
import org.forgerock.opendj.ldap.LdapException;
import org.forgerock.opendj.ldap.Modification;
import org.forgerock.opendj.ldap.ModificationType;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.SearchScope;
/**
* This class has the responsibility of adding, removing and replacing COS
* definitions. It also provides search capabilities for COS definitions.
* @supported.api
*/
public class COSManager {
/**
* This constructor sets the parent Directory entry which identifies the
* location of COS definitions which will be managed. It also gets an
* instance of a SchemaManager which will be used to update schema entries
* for COS assignments.
*
* @param token Authenticated principal's single sign on token.
* @param guid The unique identifier specifying where COS definitions will
* be managed.
* @throws UMSException if the token authentication fails, or if
* the guid for the parent entry is not valid.
*/
protected COSManager(SSOToken token, Guid guid) throws UMSException {
try {
SSOTokenManager.getInstance().validateToken(token);
} catch (SSOException se) {
throw new UMSException(i18n.getString(IUMSConstants.INVALID_TOKEN),
se);
}
_parentObject = UMSObject.getObject(token, guid);
try {
_schemaManager = SchemaManager.getSchemaManager(token
.getPrincipal());
} catch (SSOException se) {
throw new UMSException("Bad Authentication Token "
+ se.getMessage());
}
}
/**
* This constructor sets the parent Directory entry which identifies the
* location of COS definitions which will be managed. It also gets an
* instance of a SchemaManager which will be used to update schema entries
* for COS assignments.
*
* @param principal
* Authenticated principal
* @param guid
* The unique identifier specifying where COS definitions will be
* managed.
*
* @throws UMSException
* The exception thrown if there is a problem determining the
* parent entry, or getting the SchemaManager instance.
*/
protected COSManager(Principal principal, Guid guid) throws UMSException {
_parentObject = UMSObject.getObject(principal, guid);
_schemaManager = SchemaManager.getSchemaManager(principal);
}
/**
* This method returns an instance of a COS Manager.
*
* @param token Authenticated principal's single sign on token.
* @param guid COS definitions will be managed under the level identified by
* this guid.
* @throws UMSException
* The exception thrown from the COSManager constructor.
* @supported.api
*/
public static COSManager getCOSManager(SSOToken token, Guid guid)
throws UMSException {
return new COSManager(token, guid);
}
/**
* This method returns an instance of a COS Manager.
*
* @param principal Authenticated principal.
* @param guid COS definitions will be managed under the level identified by
* this guid.
* @throws UMSException
* The exception thrown from the data layer.
*/
public static COSManager getCOSManager(Principal principal, Guid guid)
throws UMSException {
return new COSManager(principal, guid);
}
/**
* This method adds a COS definition to the persistent store. The definition
* is added under the specified "guid" parameter.
*
* @param cosDef
* The COS definition to be added.
*
* @throws UMSException
* The exception thrown from the data layer.
* @supported.api
*/
public void addDefinition(ICOSDefinition cosDef) throws UMSException {
if (!(cosDef instanceof DirectCOSDefinition)) {
String msg = i18n.getString(IUMSConstants.INVALID_COSDEFINITION);
throw new UMSException(msg);
}
String[] cosAttributes = cosDef.getCOSAttributes();
AbstractCollection aList = (AbstractCollection) Arrays
.asList(ICOSDefinition.qualifiers);
for (int i = 0; i < cosAttributes.length; i++) {
String cosAttribute = null;
String qualifier = null;
StringTokenizer st = new StringTokenizer(cosAttributes[i]);
if (st.hasMoreTokens()) {
cosAttribute = st.nextToken();
}
if (cosAttribute == null) {
String msg = i18n.getString(
IUMSConstants.INVALID_COS_ATTRIBUTE_QUALIFIER);
throw new UMSException(msg);
}
if (st.hasMoreTokens())
qualifier = st.nextToken();
if (qualifier == null) {
qualifier = ICOSDefinition.qualifiers[ICOSDefinition.DEFAULT];
cosDef.removeCOSAttribute(cosAttribute);
cosDef.addCOSAttribute(cosAttribute, ICOSDefinition.DEFAULT);
}
if (!aList.contains(qualifier)) {
String msg = i18n.getString(
IUMSConstants.INVALID_COS_ATTRIBUTE_QUALIFIER);
throw new UMSException(msg);
}
}
PersistentObject po = (PersistentObject) cosDef;
_parentObject.addChild(po);
}
/**
* Removes the COS definition.
*
* @param name
* The name of the definition to be removed.
*
* @throws UMSException
* The exception thrown from the data layer.
* @supported.api
*/
public void removeDefinition(String name) throws UMSException {
Guid guid = new Guid(ICOSDefinition.DEFAULT_NAMING_ATTR + "=" + name
+ "," + _parentObject.getGuid().getDn());
_parentObject.removeChild(guid);
}
/**
* Updates the contents of a COS definition with the new contents. The COS
* definition must already exist in the persistent layer, before its
* contents can be replaced.
*
* @param cosDef
* The COS definition containing new contents, which will replace
* the same definition in the persistent layer.
*
* @throws UMSException
* The exception thrown from the data layer.
* @supported.api
*/
public void updateDefinition(ICOSDefinition cosDef) throws UMSException {
PersistentObject pObject = (PersistentObject) cosDef;
if (pObject.getGuid() == null) {
String msg = i18n
.getString(IUMSConstants.REPLACE_DEFINITION_NOT_PERSISTENT);
throw new UMSException(msg);
}
pObject.save();
}
/**
* Returns COS definition given the name.
*
* @param name Name of the COS definition.
* @return A COS definition with the specified name.
* @throws UMSException if exception occurred at the data layer.
* @throws COSNotFoundException if the COS object is not found.
* @supported.api
*/
public ICOSDefinition getDefinition(String name) throws UMSException,
COSNotFoundException {
ICOSDefinition cosDef = null;
SearchResults sr = _parentObject.getChildren(
ICOSDefinition.COSSUPERDEF_NAME_SEARCH + name + ")",
DEF_ATTRIBUTE_NAMES, null);
while (sr.hasMoreElements()) {
cosDef = (ICOSDefinition) sr.next();
if (cosDef.getName().equals(name)) {
break;
} else {
cosDef = null;
}
}
if (cosDef == null) {
String msg = i18n.getString(IUMSConstants.COS_DEFINITION_NOT_FOUND);
throw new COSNotFoundException(msg);
}
sr.abandon();
return cosDef;
}
/**
* Retrieves all COS definitions for the current organization. This
* COSManager instance applies to an organization.
*
* @return A collection of COS definition objects.
*
* @throws UMSException
* The exception thrown from the data layer.
* @supported.api
*/
public Collection getDefinitions() throws UMSException {
Collection cosDefinitions = new ArrayList();
SearchResults sr = _parentObject.search(
ICOSDefinition.COSSUPERDEF_SEARCH, DEF_ATTRIBUTE_NAMES, null);
while (sr.hasMoreElements()) {
cosDefinitions.add(sr.next());
}
return cosDefinitions;
}
/**
* Assigns a COS (as defined by a COS definition) to the persistent object.
* The COS target persistent object could be a user, group, organization,
* organizationalunit, etc. The COS target object must be persistent before
* this method can be used.
*
* @param pObject
* The COS target persistent object.
* @param cosDef
* A COS definition.
* @param cosTemplate
* A COS template. This only applies for COS and Indirect COS
* definitions. For pointer COS definitions, this parameter can
* be null.
*
* @throws UMSException
* If a data layer exception occurs.
* @supported.api
*/
public void assignCOSDef(PersistentObject pObject, ICOSDefinition cosDef,
COSTemplate cosTemplate) throws UMSException {
if (pObject == null || cosDef == null) {
String msg = i18n
.getString(IUMSConstants.COS_DEF_OR_TARGET_OBJECT_NULL);
throw new UMSException(msg);
}
// Do validation....
//
if (pObject.getGuid() == null) {
String msg = i18n
.getString(IUMSConstants.COS_TARGET_OBJECT_NOT_PERSISTENT);
throw new UMSException(msg);
}
if (!(cosDef instanceof DirectCOSDefinition)) {
String msg = i18n.getString(IUMSConstants.INVALID_COSDEFINITION);
throw new UMSException(msg);
}
if (cosDef instanceof DirectCOSDefinition) {
assignDirectCOSDef(pObject, (DirectCOSDefinition) cosDef,
cosTemplate, _schemaManager);
}
}
/**
* Removes COS assignment from the persistent object. The COS target
* persistent object could be a user, group, organization,
* organizationalunit, etc. The COS target object must be persistent before
* this method can be used.
*
* @param pObject
* The COS target persistent object.
* @param cosDef
* A COS definition.
* @param cosTemplate
* A COS template.
*
* @throws UMSException
* The exception thrown if any of the following occur: o the
* target persistent object or COS definition parameter is null.
* o the target object is not persistent. o the COS definition
* is not one of the valid COS definitions. o an exception is
* propagated from any of the "remove" methods.
* @supported.api
*/
public void removeCOSAssignment(PersistentObject pObject,
ICOSDefinition cosDef, COSTemplate cosTemplate) throws UMSException
{
if (pObject == null || cosDef == null) {
String msg = i18n
.getString(IUMSConstants.COS_DEF_OR_TARGET_OBJECT_NULL);
throw new UMSException(msg);
}
// Do validation....
//
if (pObject.getGuid() == null) {
String msg = i18n
.getString(IUMSConstants.COS_TARGET_OBJECT_NOT_PERSISTENT);
throw new UMSException(msg);
}
if (!(cosDef instanceof DirectCOSDefinition)) {
String msg = i18n.getString(IUMSConstants.INVALID_COSDEFINITION);
throw new UMSException(msg);
}
if (cosDef instanceof DirectCOSDefinition) {
removeDirectCOSAssignment(pObject, (DirectCOSDefinition) cosDef,
cosTemplate, _schemaManager);
}
}
/**
* Removes a Direct COS assignment from a target persistent object. The COS
* target persistent object could be a user, group, organization,
* organizationalunit, etc. The COS target object must be persistent before
* this method can be used.
*
* @param pObject
* The COS target persistent object.
* @param cosDef
* A COS definition.
* @param sMgr
* A SchemaManager object, which is used to determine object
* classes for attributes.
*
* @throws UMSException
* The exception thrown if any of the following occur: o an
* exception occurs determining the object class for the COS
* specifier. o an exception occurs determining the object class
* for the COS attributes. o there is an exception thrown rom
* the data layer.
*/
private void removeDirectCOSAssignment(PersistentObject pObject,
DirectCOSDefinition cosDef, COSTemplate cosTemplate,
SchemaManager sMgr) throws UMSException {
ArrayList aList;
AttrSet attrSet = new AttrSet();
try {
// Include the attribute (whose name is the cosSpecifier)
// in the attribute set for removal (only if it exists).
//
if (pObject.getAttribute(cosDef.getCOSSpecifier()) != null)
attrSet.add(new Attr(cosDef.getCOSSpecifier(), cosTemplate
.getName()));
// Get cosSpecifier object class - should only be one.
// Include the cosSpecifier object class in the attribute
// set for removal (only if itt exists).
//
aList = (ArrayList) sMgr.getObjectClasses(cosDef.getCOSSpecifier());
String cosSpecObjectClass = (String) aList.get(0);
if (objectClassExists(cosSpecObjectClass, pObject)) {
attrSet.add(new Attr("objectclass", cosSpecObjectClass));
}
// Get the cos attributes from the definition (ex. mailquota).
// For each of the attributes, get the objectclass. Include the
// object classes in the attribute set for removal (if they exist).
//
String[] cosAttributes = cosDef.getCOSAttributes();
String cosAttribute = null;
for (int i = 0; i < cosAttributes.length; i++) {
// Only get the attribute - not the qualifier
//
StringTokenizer st = new StringTokenizer(cosAttributes[i]);
cosAttribute = st.nextToken();
aList = (ArrayList) sMgr.getObjectClasses(cosAttribute);
String cosAttributeObjectClass = (String) aList.get(0);
if (objectClassExists(cosAttributeObjectClass, pObject)) {
attrSet
.add(new Attr("objectclass",
cosAttributeObjectClass));
}
}
if (attrSet.size() > 0) {
pObject.modify(toModifications(ModificationType.DELETE, attrSet));
pObject.save();
}
} catch (UMSException e) {
LdapException le = (LdapException) e.getRootCause();
// Ignore anything that is not a COS generated attribute's object class
if (!ResultCode.OBJECTCLASS_VIOLATION.equals(le.getResult().getResultCode())) {
throw e;
}
}
}
/**
* Assigns a direct (Classic) COS definition to a persistent object.
*
* @param pObject
* The target persistent object.
* @param cosDef
* The direct (Classic) COS definition.
* @param cosTemplate
* A COS template belonging to the definition.
* @param sMgr
* A SchemaManager instance.
*
* @throws UMSException
* if an exception occurs
*/
private void assignDirectCOSDef(PersistentObject pObject,
DirectCOSDefinition cosDef, COSTemplate cosTemplate,
SchemaManager sMgr) throws UMSException {
// Do validation....
//
if (cosDef.getGuid() == null) {
String msg = i18n
.getString(IUMSConstants.COS_DEFINITION_NOT_PERSISTENT);
throw new UMSException(msg);
}
// Make sure target entry is in same tree as COS Def parent.
//
DN targetDN = DN.valueOf(pObject.getGuid().getDn());
DN cosParentDN = DN.valueOf(cosDef.getParentGuid().getDn());
if (!(targetDN.isInScopeOf(cosParentDN, SearchScope.SUBORDINATES))) {
String msg = i18n
.getString(IUMSConstants.COS_TARGET_OBJECT_DIFFERENT_TREE);
throw new UMSException(msg);
}
// If cosSpecifier is "nsRole", then we don't need to go
// any further (we don't need to update target entries).
//
if (cosDef.getCOSSpecifier().equalsIgnoreCase("nsrole"))
return;
ArrayList aList;
AttrSet attrSet = new AttrSet();
// Get cosSpecifier object class - should only be one.
// Update the target entry with cosSpecifier object class.
// Only add it if it doesn't already exist.
//
aList = (ArrayList) sMgr.getObjectClasses(cosDef.getCOSSpecifier());
String cosSpecObjectClass = (String) aList.get(0);
if (!objectClassExists(cosSpecObjectClass, pObject)) {
attrSet.add(new Attr("objectclass", cosSpecObjectClass));
}
// Get the cos attributes from the definition (ex. mailquota).
// For each of the attributes, get the objectclass. These
// will be used to attach to the target entry. This is only
// done if the cos attribute qualifier is not "operational"
// (you don't need to add cos attribute object classes for
// "operational" cos attribute qualifier.
//
String[] cosAttributes = cosDef.getCOSAttributes();
String qualifier = null;
Arrays.asList(ICOSDefinition.qualifiers);
Attr attr = cosTemplate.getAttribute("objectclass");
String[] cosTempObjClasses = attr.getStringValues();
for (int i = 0; i < cosAttributes.length; i++) {
StringTokenizer st = new StringTokenizer(cosAttributes[i]);
st.nextToken();
qualifier = st.nextToken();
if ((!qualifier.equals(
ICOSDefinition.qualifiers[ICOSDefinition.OPERATIONAL]))) {
for (int j = 0; j < cosTempObjClasses.length; j++) {
if (!cosTempObjClasses[j].equalsIgnoreCase("top")
&& !cosTempObjClasses[j].equalsIgnoreCase("costemplate")
&& !objectClassExists(cosTempObjClasses[j], pObject))
{
if (!attrSet.contains("objectclass",
cosTempObjClasses[j])) {
attrSet.add(new Attr("objectclass",
cosTempObjClasses[j]));
}
}
}
}
}
// Add the attribute name (cosSpecifier value) and attribute
// value (cosTemplate name) only if it doesn't exist.
//
if (pObject.getAttribute(cosDef.getCOSSpecifier()) == null)
attrSet.add(new Attr(cosDef.getCOSSpecifier(), cosTemplate
.getName()));
if (attrSet.size() > 0) {
pObject.modify(toModifications(ModificationType.ADD, attrSet));
pObject.save();
}
}
private Collection<Modification> toModifications(ModificationType type, AttrSet attrSet) {
Collection<Modification> modifications = new HashSet<>();
Enumeration<Attr> attributes = attrSet.getAttributes();
while (attributes.hasMoreElements()) {
modifications.add(new Modification(type, attributes.nextElement().toLDAPAttribute()));
}
return modifications;
}
/**
* Utility method to check if an object class exists in a persistent object.
*
* @param objectClass
* The object class.
* @param pObject
* The persistent object.
*/
private boolean objectClassExists(String objectClass,
PersistentObject pObject) {
Attr attr = pObject.getAttribute("objectclass");
String[] vals = attr.getStringValues();
for (int i = 0; i < vals.length; i++) {
if (objectClass.equalsIgnoreCase(vals[i])) {
return true;
}
}
return false;
}
//
// Definition Search Attributes
//
private static final String[] DEF_ATTRIBUTE_NAMES = { "objectclass",
ICOSDefinition.DEFAULT_NAMING_ATTR, ICOSDefinition.COSTEMPLATEDN,
ICOSDefinition.COSSPECIFIER, ICOSDefinition.COSATTRIBUTE,
ICOSDefinition.ICOSSPECIFIER };
private PersistentObject _parentObject;
private SchemaManager _schemaManager;
private static I18n i18n = I18n.getInstance(IUMSConstants.UMS_PKG);
}