/*
* 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: DNMapper.java,v 1.13 2009/11/20 23:52:56 ww203982 Exp $
*
* Portions Copyrighted 2011-2015 ForgeRock AS.
*/
package com.sun.identity.sm;
import static org.forgerock.openam.ldap.LDAPUtils.rdnValue;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.StringTokenizer;
import com.sun.identity.shared.debug.Debug;
import org.forgerock.openam.ldap.LDAPUtils;
import org.forgerock.opendj.ldap.DN;
import org.forgerock.opendj.ldap.RDN;
/**
* This class is used to convert a DN to iplanet UID and vice versa.
*/
public class DNMapper {
private static Debug debug = SMSEntry.debug;
private static HashMap cache = new HashMap(2);
// Look for realmEnabled and cache the value.
protected static boolean realmEnabled = ServiceManager.isRealmEnabled();
protected static String serviceDN;
// This set is used in reversing the realm names to sdk format.
static boolean migration = false;
static {
DN dn = DN.valueOf(SMSEntry.baseDN).child(SMSEntry.SERVICES_RDN);
serviceDN = dn.toString().toLowerCase();
}
/**
* Converts orgname which is "/" seperated to DN, else if DN normalize the
* DN and return
*/
public static String orgNameToDN(String orgName) {
// Check if it is null or empty
if (orgName == null || orgName.trim().length() == 0
|| orgName.equals(SMSEntry.SLASH_STR)) {
return (SMSEntry.baseDN);
}
// Check in cache
String orgdn = (String) cache.get(orgName);
if (orgdn != null) {
return (orgdn);
}
/*
* Check if orgName is a valid DN. If so, check if realmEnabled. if
* realmEnabled, 1) Check if rest of the DN (before the baseDN) has "o"
* as the naming attribute. If not, replace it with 'o' and concat the
* value with the previous naming attribute. eg.,if orgName is
* dc=abc,l=xyz,o=coke,ou=services,dc=iplanet,dc=com then, the final
* string should be
* o=dc_abc,o=l_xyz,o=coke,ou=services,dc=iplanet,dc=com 2) Check if
* "ou=services" is present in the orgName. If not add it to the
* orgName.
*/
if (!orgName.startsWith("/")) {
DN orgdnObject = LDAPUtils.newDN(orgName);
if (orgdnObject.size() > 0) {
// If orgName is either the baseDN or root service's DN
// return the baseDN
orgdn = orgdnObject.toString();
String orgdnlc = orgdn.toLowerCase();
// Check if orgdn is a hidden internal realm, if so return
if (orgdnlc.startsWith(SMSEntry.SUN_INTERNAL_REALM_PREFIX)) {
orgdn = orgdnObject.rdn().toString() + "," + serviceDN;
// Add to cache and return
updateCache(orgName, orgdn);
return orgdn;
}
// Check for root suffix and SMS base DN
if (orgdnlc.equals(SMSEntry.baseDN) ||
orgdnlc.equals(serviceDN) ||
orgdnlc.equals(SMSEntry.amsdkbaseDN)) {
// Add to cache and return
updateCache(orgName, SMSEntry.baseDN);
return (SMSEntry.baseDN);
}
// If realm is enabled, normalize the DN and return
if (realmEnabled) {
int ndx = orgdn.indexOf(serviceDN);
if (ndx == -1) {
// Check for baseDN
ndx = orgdn.lastIndexOf(SMSEntry.baseDN);
}
if (ndx > 0) {
orgdn = orgdn.substring(0, ndx - 1);
}
int indx = orgdn.lastIndexOf(SMSEntry.COMMA);
if (indx >= 0) {
if (orgdn.substring(indx).equals(SMSEntry.COMMA)) {
orgdn = orgdn.substring(0, indx);
}
}
if (debug.messageEnabled()) {
debug.message("DNMapper.orgNameToDN():orgdn " + orgdn);
}
String answer = normalizeDN(orgdn) + serviceDN;
if (debug.messageEnabled()) {
debug.message("DNMapper.orgNameToDN(" + orgName + ")="
+ answer);
}
// Add to cache and return
updateCache(orgName, answer);
return (answer);
} else if (!migration) {
// Check if "ou=services" is present, if present remove it
orgdn = replaceString(orgdn, ",ou=services,", ",");
// Add to cache and return
updateCache(orgName, orgdn);
return (orgdn);
} else {
// When SMS Migration to 7.0 happens, the coexist mode is
// 'true' and realm is 'false'. In coexist mode, the
// 'ou=services' gets removed. But we need the new realm node
// for data migration from old DIT to new realm tree.
// So after creation of the realm during SMSMigration70,
// we set the DNMapper.migration flag to true to avoid
// removal of 'ou=services' from the newly formed realm DN
// and return the orgdn as such to the serviceconfig* class.
return (orgdn);
}
}
}
// The org name is "/" separated, construct the DN
StringBuffer buf = convertToDN(orgName);
if (realmEnabled
|| buf.toString().toLowerCase().indexOf(
SMSEntry.SUN_INTERNAL_REALM_NAME) != -1) {
buf.append(",").append(serviceDN);
} else if (SMSEntry.baseDN.length() > 0) {
buf.append(",").append(SMSEntry.baseDN);
}
if (debug.messageEnabled()) {
debug.message("DNMapper.orgNameToDN(" + orgName + ")="
+ buf.toString());
}
String answer = buf.toString();
// Add to cache
updateCache(orgName, answer);
return (answer);
}
private static void updateCache(String orgName, String realmName) {
HashMap ncache = new HashMap(cache);
// %%% TODO Need to check the size and remove least recently used
ncache.put(orgName, realmName);
cache = ncache;
}
/**
* Converts realm name to AMSDK compliant organization name
*/
public static String realmNameToAMSDKName(String realmName) {
String dn = orgNameToDN(realmName);
String dnlc = dn.toLowerCase();
if (debug.messageEnabled()) {
debug.message("DNMapper.realmNameToAMSDKName realmName ="
+ realmName);
debug.message("DNMapper.realmNameToAMSDKName orgDN =" + dn);
}
// Check for baseDN and internal hidden realm names
if ((dnlc.equals(SMSEntry.baseDN)) &&
(!dnlc.equals(SMSEntry.amsdkbaseDN))) {
return (SMSEntry.amsdkbaseDN);
}
if (dnlc.equals(SMSEntry.baseDN)
|| dnlc.startsWith(SMSEntry.SUN_INTERNAL_REALM_PREFIX)) {
return (SMSEntry.baseDN);
}
// If realm is not enabled, remove "ou=services" node
StringBuilder buf = new StringBuilder(dn.length());
String orgAttr = OrgConfigViaAMSDK.getNamingAttrForOrg();
// If orgAttr is null or is "o", return after removing "ou=services"
if (orgAttr == null
|| orgAttr.equalsIgnoreCase(SMSEntry.ORGANIZATION_RDN)) {
String answer = replaceString(dn, ",ou=services,", ",");
if (debug.messageEnabled()) {
debug.message("DNMapper.realmNameToAMSDKName sdkName ="
+ answer);
}
return (answer);
}
// Remove the baseDN and parse the DN
int index = dnlc.indexOf(serviceDN);
if (index == -1) {
// Try the baseDN
index = dnlc.indexOf(SMSEntry.baseDN);
}
String answer = (index == -1) ? dn : dn.substring(0, index - 1);
DN answerDN = DN.valueOf(answer);
for (RDN rdn : answerDN) {
buf.append(orgAttr).append(SMSEntry.EQUALS).append(rdnValue(rdn));
buf.append(',');
}
// Append baseDN and return
buf.append(SMSEntry.baseDN);
if (debug.messageEnabled()) {
debug.message("DNMapper.realmNameToAMSDKName sdkName ="
+ buf.toString());
}
return (buf.toString());
}
/**
* Returns realm name in "/" separated format for the provided
* realm/organization name in DN format.
*
* @param orgName Name of organization.
* @return DN format "/" separated realm name of organization name.
*/
public static String orgNameToRealmName(String orgName) {
if ((orgName == null) || (orgName.length() == 0)) {
return "/";
}
if (orgName.equalsIgnoreCase(SMSEntry.baseDN) ||
orgName.equalsIgnoreCase(serviceDN)
) {
return "/";
}
if (!orgName.contains("=")) {
return orgName;
}
DN orgdnObject = DN.valueOf(orgName);
StringBuilder answer = new StringBuilder(100);
answer.append("/");
Set resultSet = new HashSet(2);
resultSet.add(orgName);
// Check if orgName ends with baseDN or serviceDN
String orgdn = orgdnObject.toString();
String orgdnlc = orgdn.toLowerCase();
Set returnSet = null;
if (orgdnlc.endsWith(serviceDN)) {
returnSet = SMSEntry.parseResult(resultSet, serviceDN, true);
} else if (orgdnlc.endsWith(SMSEntry.baseDN)) {
returnSet = SMSEntry.parseResult(resultSet, serviceDN, true);
}
if (returnSet != null && !returnSet.isEmpty()) {
answer.append(returnSet.iterator().next().toString());
}
return (answer.toString());
}
/*
* Splits a string and returns the tokens.
*
* @param str original String.
* @return a String Array object of tokens after split.
*/
static String[] splitString(String str) {
String[] strArray = new String[2];
int idx = str.indexOf('=');
if (idx != -1) {
strArray[0] = str.substring(0, idx).trim();
strArray[1] = str.substring(idx + 1).trim();
}
return strArray;
}
/*
* Replaces a string with another string in a String object.
*
* @param originalString original String.
* @param token string to be replaced.
* @param newString new string to replace token.
* @return a String object after replacement.
*
*/
static String replaceString(String originalString, String token,
String newString) {
int lenToken = token.length();
int idx = originalString.indexOf(token);
if (!originalString.startsWith(SMSEntry.SLASH_STR)) {
if (idx >= 0) {
int slashndx =
originalString.substring(idx).indexOf(SMSEntry.SLASH_STR);
// This is to escape "/" embedded in realm names.
while (slashndx != -1) {
originalString = originalString.substring(0, slashndx) +
"/" + originalString.substring(slashndx+1);
slashndx =
originalString.indexOf(SMSEntry.SLASH_STR, slashndx+5);
}
}
}
while (idx != -1) {
originalString = originalString.substring(0, idx) + newString
+ originalString.substring(idx + lenToken);
idx = originalString.indexOf(token, idx + lenToken);
}
if (debug.messageEnabled()) {
debug.message("DNMapper.replaceString() " + originalString);
}
return originalString;
}
/**
* Normalized the DN as per the Realm requirements for organization name
*/
static String normalizeDN(String orgName) {
String orgAttr = "";
StringBuilder buf = new StringBuilder(orgName.length());
if (debug.messageEnabled()) {
debug.message("DNMapper.normalizeDN():orgName "+ orgName);
}
if (!realmEnabled) {
orgAttr = OrgConfigViaAMSDK.getNamingAttrForOrg();
}
String placeHold = (realmEnabled) ? SMSEntry.ORGANIZATION_RDN : orgAttr;
DN dn = DN.valueOf(orgName);
for (RDN rdn : dn) {
// Check if orgName is a hidden internal realm,if so prepend with o
if (orgName.toLowerCase().
startsWith(SMSEntry.SUN_INTERNAL_REALM_PREFIX)) {
buf.append(SMSEntry.ORGANIZATION_RDN);
} else {
buf.append(placeHold);
}
buf.append(SMSEntry.EQUALS).append(rdnValue(rdn)).append(SMSEntry.COMMA);
}
debug.message("DNMapper.normalizeDN():finalorgdn {}", buf);
return buf.toString();
}
/**
* Converts "/" separted organization names to DN
*/
static StringBuffer convertToDN(String orgName) {
StringBuffer buf = new StringBuffer();
String placeHold = (realmEnabled) ? SMSEntry.ORGANIZATION_RDN
: OrgConfigViaAMSDK.getNamingAttrForOrg();
ArrayList arr = new ArrayList();
StringTokenizer strtok = new StringTokenizer(orgName, "/");
while (strtok.hasMoreElements()) {
String token = strtok.nextToken().trim();
if (token != null && token.length() != 0) {
arr.add(token);
}
}
int size = arr.size();
for (int i = 0; i < size; i++) {
String theOrg = (String) arr.get(size - i - 1);
// Check if orgdn is a hidden internal realm, if so prepend with o
if (theOrg.toLowerCase().startsWith(
SMSEntry.SUN_INTERNAL_REALM_NAME)) {
placeHold = SMSEntry.ORGANIZATION_RDN;
}
buf.append(placeHold);
buf.append('=').append(theOrg);
if (i != size - 1) {
buf.append(',');
}
}
if (debug.messageEnabled()) {
debug.message("DNMapper.convertToDN():finalorgdn "+
buf.toString());
}
if ((buf.toString()).indexOf("&#47;") >= 0) {
String realmName = SMSSchema.unescapeName(buf.toString());
if (debug.messageEnabled()) {
debug.message("DNMapper.convertToDN():realmName "+realmName);
}
StringBuffer newBuf = new StringBuffer();
newBuf.append(realmName);
buf = newBuf;
if (debug.messageEnabled()) {
debug.message("DNMapper.convertToDN():newRealmName "+
buf.toString());
}
}
return (buf);
}
static void clearCache() {
cache = new HashMap();
realmEnabled = ServiceManager.isRealmEnabled();
}
}