/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2013 ForgeRock AS. 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
* http://forgerock.org/license/CDDLv1.0.html
* 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 http://forgerock.org/license/CDDLv1.0.html
* 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]"
*/
package org.identityconnectors.ldap;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.LdapContext;
import org.identityconnectors.common.logging.Log;
import org.identityconnectors.framework.common.exceptions.ConnectorException;
import org.identityconnectors.framework.common.objects.ObjectClass;
import org.identityconnectors.ldap.search.LdapInternalSearch;
/**
*
* @author Gael Allioux <gael.allioux@forgerock.com>
*/
/*
* This class provides static helper methods to handle
* some MS AD specific attributes over LDAP
*/
public class ADLdapUtil {
private static final Log log = Log.getLog(ADLdapUtil.class);
/*
* Maximum number of members retrieved from a group in one search
*/
public static final int GROUP_MEMBERS_MAXRANGE = 1500;
/*
* The time difference between Java and .Net for Dates
*/
public static final long DIFF_NET_JAVA_FOR_DATE_AND_TIMES = 11644473600000L;
/*
* The Pwd-Last-Set attribute represents the date and time that the password for this account was last changed.
* for read purpose, only values that can be set:
* 0 - To force a user to change his password at next logon
* -1 - To set it to current date
*/
public static final String PWD_LAST_SET = "pwdLastSet";
/*
* The date when a Microsoft Active Directory account expires.
* For read purpose
* A value of 0 or 9,223,372,036,854,775,807 indicates that the account never expires.
*/
public static final String ACCOUNT_EXPIRES = "accountExpires";
public static final String ACCOUNT_NEVER_EXPIRES = "9223372036854775807";
static String AddLeadingZero(int k) {
return (k<=0xF)?"0" + Integer.toHexString(k):Integer.toHexString(k);
}
public static String objectGUIDtoDashedString(Attribute attr){
byte[] GUID = null;
try{
GUID = (byte[])attr.get();
if (GUID.length != 16){
throw new ConnectorException(LdapConstants.MS_GUID_ATTR+" attribute has the wrong length ("+GUID.length+"). Should be 16 bytes.");
}
}
catch(NamingException e){}
StringBuilder sGUID = new StringBuilder(43);
sGUID.append("<GUID=");
sGUID.append(AddLeadingZero((int)GUID[3] & 0xFF));
sGUID.append(AddLeadingZero((int)GUID[2] & 0xFF));
sGUID.append(AddLeadingZero((int)GUID[1] & 0xFF));
sGUID.append(AddLeadingZero((int)GUID[0] & 0xFF));
sGUID.append("-");
sGUID.append(AddLeadingZero((int)GUID[5] & 0xFF));
sGUID.append(AddLeadingZero((int)GUID[4] & 0xFF));
sGUID.append("-");
sGUID.append(AddLeadingZero((int)GUID[7] & 0xFF));
sGUID.append(AddLeadingZero((int)GUID[6] & 0xFF));
sGUID.append("-");
sGUID.append(AddLeadingZero((int)GUID[8] & 0xFF));
sGUID.append(AddLeadingZero((int)GUID[9] & 0xFF));
sGUID.append("-");
sGUID.append(AddLeadingZero((int)GUID[10] & 0xFF));
sGUID.append(AddLeadingZero((int)GUID[11] & 0xFF));
sGUID.append(AddLeadingZero((int)GUID[12] & 0xFF));
sGUID.append(AddLeadingZero((int)GUID[13] & 0xFF));
sGUID.append(AddLeadingZero((int)GUID[14] & 0xFF));
sGUID.append(AddLeadingZero((int)GUID[15] & 0xFF));
sGUID.append(">");
return sGUID.toString();
}
public static String objectGUIDtoString(Attribute attr){
byte[] GUID = null;
try{
GUID = (byte[])attr.get();
if (GUID.length != 16){
throw new ConnectorException(LdapConstants.MS_GUID_ATTR+" attribute has the wrong length ("+GUID.length+"). Should be 16 bytes.");
}
}
catch(NamingException e){}
StringBuilder sGUID = new StringBuilder(39);
sGUID.append("<GUID=");
for(int i=0;i<16;i++){
sGUID.append(AddLeadingZero((int)GUID[i] & 0xFF));
}
sGUID.append(">");
return sGUID.toString();
}
public static String guidDashedStringtoByteString(String dashed){
// <GUID=ac642e6e-6ab5-425a-bcc9-9f5067d46e3f>
// [3][2][1][0]-[5][4]-[7][6]-[8][9]-[10][11][12][13][14][15]
// We reorder the bytes...
if (dashed.length() != 43){
throw new ConnectorException(LdapConstants.MS_GUID_ATTR+" attribute has the wrong length ("+dashed.length()+"). Should be 43 characters.");
}
StringBuilder bString = new StringBuilder("\\");
bString.append(dashed.substring(12,14));
bString.append("\\");
bString.append(dashed.substring(10, 12));
bString.append("\\");
bString.append(dashed.substring(8, 10));
bString.append("\\");
bString.append(dashed.substring(6, 8));
bString.append("\\");
bString.append(dashed.substring(17, 19));
bString.append("\\");
bString.append(dashed.substring(15, 17));
bString.append("\\");
bString.append(dashed.substring(22, 24));
bString.append("\\");
bString.append(dashed.substring(20, 22));
bString.append("\\");
bString.append(dashed.substring(25, 27));
bString.append("\\");
bString.append(dashed.substring(27, 29));
bString.append("\\");
bString.append(dashed.substring(30, 32));
bString.append("\\");
bString.append(dashed.substring(32, 34));
bString.append("\\");
bString.append(dashed.substring(34, 36));
bString.append("\\");
bString.append(dashed.substring(36, 38));
bString.append("\\");
bString.append(dashed.substring(38, 40));
bString.append("\\");
bString.append(dashed.substring(40, 42));
return bString.toString();
}
public static String guidStringtoByteString(String dashed){
// <GUID=2c6bfee3175c0a4e9af01182a2fb0ae1>
if (dashed.length() != 39){
throw new ConnectorException(LdapConstants.MS_GUID_ATTR+" attribute has the wrong length ("+dashed.length()+"). Should be 39 characters.");
}
StringBuilder bString = new StringBuilder();
for (int i=6;i<37;i=i+2){
bString.append("\\");
bString.append(dashed.substring(i,i+2));
}
return bString.toString();
}
public static List fetchGroupMembersByRange(LdapConnection conn, SearchResult result){
return fetchGroupMembersByRange(conn, LdapEntry.create(null, result));
}
/*
* This method returns the list of members when the group has over 1500 members.
*
*/
public static List fetchGroupMembersByRange(LdapConnection conn, LdapEntry entry){
boolean done = false;
int first = 0;
int last = GROUP_MEMBERS_MAXRANGE -1;
List members = new ArrayList();
// get the first slice (0-1499)
org.identityconnectors.framework.common.objects.Attribute range = conn.getSchemaMapping().createAttribute(ObjectClass.GROUP, String.format("member;range=%d-%d",first,last), entry, false);
if (range != null){
members.addAll(range.getValue());
first = last +1;
last = first + GROUP_MEMBERS_MAXRANGE -1;
while (!done) {
try {
SearchControls controls = LdapInternalSearch.createDefaultSearchControls();
controls.setSearchScope(SearchControls.OBJECT_SCOPE);
controls.setReturningAttributes(new String[]{String.format("member;range=%d-%d", first, last)});
LdapContext context = conn.getInitialContext().newInstance(null);
NamingEnumeration<SearchResult> entries = context.search(entry.getDN(), "objectclass=*", controls);
SearchResult res = entries.next();
if (res != null) {
range = conn.getSchemaMapping().createAttribute(ObjectClass.GROUP, String.format("member;range=%d-%d", first, last), LdapEntry.create(null,res), true);
// we hit the last slice... so the range ends with an '*'
if (range.getValue().isEmpty()) {
range = conn.getSchemaMapping().createAttribute(ObjectClass.GROUP, String.format("member;range=%d-*", first), LdapEntry.create(null,res), true);
done = true;
}
members.addAll(range.getValue());
}
} catch (NamingException e) {
log.error(e, "Error reading group member;range attribute");
}
first = last + 1;
last = first + GROUP_MEMBERS_MAXRANGE -1;
}
}
return members;
}
public static Date getJavaDateFromADTime(String adTime) {
long milliseconds = (Long.parseLong(adTime) / 10000) - DIFF_NET_JAVA_FOR_DATE_AND_TIMES;
return new Date(milliseconds);
}
public static String getADLdapDatefromJavaDate(Date date) {
SimpleDateFormat df = new SimpleDateFormat("YYYYMMddHHmmss");
df.setTimeZone(TimeZone.getTimeZone("UTC"));
return df.format(date)+".0Z";
}
}