GetInfoEx
When the first Get issue on an
object, ADSI ,implicitly,brings all attributes which have 1) value 2)
user has a read permission into ADSI cache. This is designed for
optimizing network traffic, server load for most cases and ease of
use- one big round trip is normally better than chatty traffics.
However, in some cases, you like to get only a small set of
attributes. GetInfoEx does exactly this. It allows you to specify only
attributes you're interested in.
Example,
Set o = GetObject("LDAP://CN=Jsmith, DC=ArcadiaBay, DC=COM")
o.GetInfoEx
Array("sn","givenName","telephoneNumber"),
0
Debug.Print o.Get("sn")
Debug.Pritn o.Get("givenName")
Back to top
Filtering
Often time you would like to
enumerate only certain types of class in a container. It's more efficient
to filter first, instead of doing comparision during the enumeration.
Example, instead of doing this
'--- Print all users in a domain
Set cont = GetObject("WinNT://ARCADIABAY")
For each obj in cont
if obj.Class = "User" then
Debug.Print obj.Name
End if
Next
Instead, you should do this:
'--- Print all users in a domain
Set cont = GetObject("WinNT://ARCADIABAY")
con.Filter = Array("user")
For each usr in cont
Debug.Print usr.Name
Next
Back to top
GetEx
If you don't if the attributes are
single or multi value in advanced, GetEx comes handy. Regardless how
many values in an attribute, you can rely on GetEx for value
enumeration.
Example,
Set usr = GetObject("LDAP://CN=Jsmith, DC=ArcadiaBay,
DC=COM")
phone = usr.Get("telephoneNumber") 'single value
while val in phone
Debug.Print val
Next
phone = usr.Get("otherTelephone")
while val in phone
Debug.Print val
Next
Back to top
Property
Caching
With IADsPropertyList, IADsPropertyEntry
and IADsPropertyValue, you will be able to manipulate ADSI caches.
Example,
Set oObject = GetObject("LDAP://CN=Andrew Anderson,OU=Sales,DC=Antipodes,DC=com")
WScript.Echo "User: " & oObject.cn
oObject.GetInfo 'Load into ADSI's caches
WScript.Echo "Property Count: " & oObject.PropertyCount
For i = 0 to oObject.PropertyCount -1
Set X = oObject.Item(i+1)
For Each Y In X.Values
WScript.Echo "Attribute: " & X.Name & " " & " (" & CStr(aProperty(Y.ADsType+1)) & ")"
Select Case Y.ADsType + 1
Case ADSTYPE_DN_STRING
WScript.Echo "Value: " & Y.DNString
Case ADSTYPE_CASE_EXACT_STRING
WScript.Echo "Value: " & Y.CaseExactString
Case ADSTYPE_CASE_IGNORE_STRING
WScript.Echo "Value: " & Y.CaseIgnoreString
Case ADSTYPE_PRINTABLE_STRING
WScript.Echo "Value: " & Y.PrintableString
Case ADSTYPE_NUMERIC_STRING
WScript.Echo "Value: " & Y.NumericString
Case ADSTYPE_BOOLEAN
WScript.Echo "Value: " & Y.Boolean
Case ADSTYPE_INTEGER
WScript.Echo "Value: " & Y.Integer
//... and
more here....
Next
Next
Back to top
WinNT:
Binding
In WinNT, you will be able to specify
the object's class as part of the ADsPath. This will speed up the bind
in most cases. Examples: "WinNT://ARCADIABAY/jsmith,user"
, "WinNT://ARCADIABAY/marketing,group"
Back to top
WinNT:
Communicating to a specific domain controller
If you specify bind to a domain or
objects in a domain, ADSI, by default, communicates to Primary Domain
Controller. In ADSI, there is a way, to select to a specific domain
controller. Specify the computer name with ',computer' as part of
ADsPath.
Example: "WinNT://bdc02,computer"
Note: if the domain controller is a
backup domain controller, all the write operations will fail.
Back to top
WinNT:
Including backup domain controllers as part of selection
ADSI 2.5 introduces a new flag (ADS_READONLY_SERVER)
for including back up domain controllers as part of selection criteria
when bind to a domain. ADSI may select either PDC or BDC.
Example,
Set dso = GetObject("WinNT:")
Set obj = dso.OpenDSObject("WinNT://ARCADIABAY/jsmith,user",
"Administrator", "secret", ADS_READONLY_SERVER )
Note: if the domain controller is
a backup domain controller, all the write operations will fail.
Back to top
Active
Directory: Searching the entire domain tree
Although it's very possible, chasing
referral is discouraged for a domain tree search. The client may have
to connect n number of domain controllers before getting the complete
result set. Global catalog is designed for enterprise, tree search.
Since not all attributes are replicated to Global Catalog, you may
want to rebind to the targeted object once you retreive its
distinguished name.
example,
Set gc = GetObject("GC://DC=Central, DC=ArcadiaBay, DC=COM")
//... do ADO search here...
Back to top
Active
Directory: Object Class vs Object Category
Object class is a multi-valued
attribute. Active Directory does not index this attribute for many
reasons. Active Directory introduces a similar concept -
objectCategory, is an indexed, single value attributes. Include
objectCategory to search a certain type of class.
Note: With the exception of (objectClass=*), you should always include
objectCategory for efficient searches.
Example,
-Find all users
(&(objectCategory=Person)(objectClass=user))
-Find all groups
(objectCategory=Group)
-Find all computers
(objectCategory=Computer)
-Find all object
(objectClass=*)
Back to top
LDAP:
Fast Bind
When you request an LDAP bind using
ADSI, one of things ADSI will do is to do a quick base search on that
object itself. This is done for the following reasons: 1) to make sure
the object exists 2) to get the object class so that ADSI will be able
to load the appropriate ADSI extensions and interfaces. By specifying
ADS_FAST_BIND, ADSI will skip this base search. This means that ADSI
does not check if object's existence, and ADSI only supports IADs
interface for this bound object.
One scenario is updating attributes on objects found in a result set.
Since the objects are included in the result set, they are highly
likely exist in the directory.
example,
Set dso = GetObject("LDAP:")
... do Search....
while rs.EOF
adsPath = rs.Fields("ADsPath")
Set obj = dso.OpenDSObject(adsPath, vbNullString,
vbNullString, ADS_SECURE_AUTHENTICATION | ADS_FAST_BIND )
rs.MoveNext
wend
Back to top
Active
Directory: Operational Attributes
Active Directory defines several
operational attributes. An operational attribute is an artifact
attribute. When one sets an operational attribute value, it, normally,
triggers some actions on the server. On the other hand, when one gets
an operational attribute value, the value itself, usually, is
calculated on the server. Like a normal attribute, an
operational attribute can be either multivalue or single value. The
operational attribute may be in read-only, write-only or read and
write mode.
Active Directory defines several
operational attributes,for examples: canonicalName, allowedChildClassesEffective,
allowedAttributes, possibleInferirors, lowestUncommitedUSN, and
many more.
To retrieve or set an operational
attribute value, you must use IDirectoryObject, or IADs::GetInfoEx
'--- Retrieve a canonical name (every object in Active Directory has a
canonicalName)
Set obj = GetObject("LDAP://CN=Cert Publishers,CN=Users,DC=Arcadiabay,DC=com")
obj.GetInfoEx Array("canonicalName"), 0
Debug.Print obj.Get("canonicalName") '---it should be:
arcadiabay.com/Users/Cert Publishers
//----- Find all allowed attributes
for a given object in the current user security context -------
IIDirectoryObject *pObject;
hr = ADsGetObject(L"LDAP://DC=activeds,DC=ntdev,DC=Microsoft,DC=Com",
IID_IDirectoryObject,
(void**) &pObject );
if (!SUCCEEDED(hr) )
{
return hr;
}
LPWSTR szAttrs[] = {
L"allowedAttributesEffective" };
PADS_ATTR_INFO pAttrEntry;
DWORD dwReturn;
hr = pObject->GetObjectAttributes( szAttrs, 1,
&pAttrEntry, &dwReturn );
if ( !SUCCEEDED(hr) )
{
pObject->Release();
return hr;
}
if ( pAttrEntry )
{
for( DWORD idx=0; idx <
pAttrEntry->dwNumValues; idx++ )
{
printf("%ws\n", pAttrEntry->pADsValues[idx].CaseIgnoreString
);
}
FreeADsMem( pAttrEntry );
}
pObject->Release();
return hr;
Back to top
Active
Directory: GUID searching
Every object in Active Directory
carries GUID which guarantees to be unique. GUID never changes even
the object is renamed or moved. ADSI support GUID binding in the form
of LDAP://<GUID=xxxxxx> or GC://<GUID=xxx>
For example of GUID binding please see Active
Directory GUID Binding
Back to top
Active
Directory: Listing Mandatory and Optional Attributes
Both mandatory and operational
attributes can be obtained from IADsClass. To get the IADsClass
interface, first you must know the class name of an object. Secondly,
compose the ADSPath from the class name and schema path. Third, bind
this object and ask for IADsClass. The MandatoryProperties and
OptionalProperties attributes contain in this interface.
Dim x As IADsClass
Dim classNams As String
Dim provider As String
className = "user"
provider = "LDAP://schema/"
Set x = GetObject( provider & className )
For Each attr In
x.MandatoryProperties
Debug.Print attr
Next
For Each attr In
x.OptionalProperties
Debug.Print attr
Next
Back to top
Active
Directory: Specifying Page Size
On subtree search, it's recommended
to specify the page size. Even if you're not expecting to find many
objects with the search. Paging allows the search to timeout in a re-startable
way, so that you can resume the search where it left off. Without
paging, the search will simply fail when it hits a time limit. Paging
also promote scalability for both clients and servers.
ADSI handles the page size transparently. No need additional
programming required, except specifying the page size in the search
preferences.
VB Example,
Command.Properties("Page Size") = 500
'--Execute and enumerate ...
VC Example,
ADS_SEARCHPREF_INFO prefInfo[ MAX_SEARCH_PREF ];
prefInfo[dwCountPref].dwSearchPref = ADS_SEARCHPREF_PAGESIZE;
prefInfo[dwCountPref].vValue.dwType = ADSTYPE_INTEGER;
prefInfo[dwCountPref].vValue.Integer = m_nPageSize;
dwCountPref++;
hr = m_pSearch->SetSearchPreference( prefInfo, dwCountPref )
Back to top
Active
Directory: Security
Active Directory Security covers in
detail in the Active
Directory Programmer's Guide.
Two common scenarios are presented here. - Organizational Unit
Delegation and Setting ACL on an attribute
Example 1 - Organizational Unit Delegation
'---- Allowing JSmith to create and
delete users in the sales organization ----------
Dim ace As New AccessControlEntry
Dim secDesc As IADsSecurityDescriptor
Dim acl As IADsAccessControlList
Set x = GetObject("LDAP://OU=Sales,DC=Arcadiay,DC=com")
Set secDesc = x.Get("ntSecurityDescriptor")
Set acl = secDesc.DiscretionaryAcl
ace.ObjectType = "{BF967ABA-0DE6-11D0-A285-00AA003049E2}" '---
This is the User Class' schemaID GUID
ace.AccessMask = ADS_RIGHT_DS_CREATE_CHILD Or ADS_RIGHT_DS_DELETE_CHILD
ace.AceType = ADS_ACETYPE_ACCESS_ALLOWED_OBJECT
ace.AceFlags = ADS_ACEFLAG_INHERIT_ACE
ace.Flags = ADS_FLAG_OBJECT_TYPE_PRESENT
ace.Trustee = "ARCADIABAY\JSmith"
acl.AddAce ace
secDesc.DiscretionaryAcl = acl
x.Put "ntSecurityDescriptor", Array(secDesc)
x.SetInfo
Example 2 - Setting ACL on an attribute
-- Deny JSmith to write Managed By attributes
Dim ace As New AccessControlEntry
Dim secDesc As IADsSecurityDescriptor
Dim acl As IADsAccessControlList
Set x = GetObject("LDAP://OU=Sales,DC=Arcadiay,DC=com")
Set secDesc = x.Get("ntSecurityDescriptor")
Set acl = secDesc.DiscretionaryAcl
ace.ObjectType = "{0296C120-40DA-11D1-A9C0-0000F80367C1} Managed By
ace.AccessMask = ADS_RIGHT_DS_WRITE_PROP
ace.AceType = ADS_ACETYPE_ACCESS_DENIED_OBJECT
ace.AceFlags = ADS_ACEFLAG_INHERIT_ACE
ace.Flags = ADS_FLAG_OBJECT_TYPE_PRESENT
ace.Trustee = "ARCADIABAY\JSmith"
acl.AddAce ace
secDesc.DiscretionaryAcl = acl
x.Put "ntSecurityDescriptor", Array(secDesc)
x.SetInfo
Back to top
|