The LDAP ICE Utility

     

When Novell introduced an LDAP server for NetWare 4.1, a server-based utility called BULKLOAD.NLM was included. The function of Bulkload is similar to that of UImport: It allows you to mass-import objects into NDS/eDirectory from a data file that follows that LDAP Data Interchange Format (LDIF) standard. However, Bulkload is a lot more flexible than UImport because it works with all object classes supported by the NDS/eDirectory schema ”not just User objects, as is the case with UImport.

NOTE

LDIF is defined in RFC 2849 (see ietf.org/rfc/rfc2849.txt ).


As its name implies, Bulkload can only import data into DS and not extract information from the tree. When eDirectory 8.5 was introduced, Bulkload was renamed NDS ICE. This updated implementation allows for exporting data from the tree and migrating data between LDAP servers. The following are some of the features supported by ICE:

  • Importing data from LDIF files to an LDAP directory

  • Exporting data from the LDAP directory to an LDIF file

  • Migrating data between LDAP servers

  • Performing a schema comparison and update

  • Loading information into NDS/eDirectory by using a template

  • Importing schema from .SCH files to an LDAP directory

ICE is installed as part of the eDirectory installation. The Unix version is included in the NOVLice package.

ICE manages a collection of handlers (that is, special subroutines) that read or write data in a variety of formats. Source handlers read data; destination handlers write data. A single executable module can be both a source and a destination handler. The engine receives data from a source handler, processes the data, and then passes the data to a destination handler.

Table 12.5 shows the supported data format handlers for ICE.

Table 12.5. Supported Data Handlers for ICE

SOURCE HANDLER

DESTINATION HANDLER

LDIF

LDIF

LDAP

LDAP

DELIM

DELIM

SCH

LOAD


The DELIM source handler is for dealing with comma-delimited files, commonly known as CSV files; DELIM can also handle tab-separated data records. The SCH handler is used for importing schema files ( *.SCH ) into the tree. The LOAD handler is used to generate eDirectory information based on commands in a template file; therefore, the LOAD handler is also referred to as the Dirload (directory load) handler in some Novell documentation; ICE uses LOAD as the handler name.

SCH and LOAD cannot be used as destination handlers.

You can combine different types of source and destination handlers to suit your needs. For instance, if you want to import object information into a tree by using data stored in a CSV file, you can specify the DELIM handler as the source handler and the LDAP handler as the destination handler (see Figure 12.8).

Figure 12.8. Specifying the source handler.

graphics/12fig08.gif


TIP

You cannot use ICE by itself to create user home directories when you're importing user information into the eDirectory tree. However, you can use File System Factory in conjunction with ICE to automatically create home directories.


You can run the ICE utility from the command line, from a snap-in to ConsoleOne, or from the ICE Wizard in Novell iManager. The DELIM , SCH , and LOAD handlers are only available in the command-line version of the utility and in Novell iManager; the ConsoleOne snap-in wizard does not support these options.

NOTE

iManager 2.02 and earlier versions is not able to import a schema file by using the SCH handler; it generates the wrong command-line syntax by including a spurious -a option that is not supported by the SCH handler. The workaround is to use the command-line version of ICE . This defect should be fixed in iManager 2.10 and later.


The following steps illustrate how you can use the ICE Wizard in ConsoleOne to export object information from an eDirectory tree to an LDIF file. This example illustrates the steps of exporting all User objects and their associated attributes by using the ICE Wizard, with LDAP as the source data handler and LDIF as the destination data handler:

  1. Select NDS Import/Export from the Wizards pull-down menu.

  2. Select Export LDIF File and click Next.

  3. In the Select Source LDAP Server dialog that appears, fill in the necessary information, such as the IP address of the server and the user's DN and password that will be used for authentication (see Figure 12.9). If you set LDAP to use the default ports (389 and 686), you can leave the Port text box blank. Click Next to continue.

    Figure 12.9. Providing the necessary information to authenticate to the source LDAP server.

    graphics/12fig09.jpg


  4. In the Set Search Criteria dialog that appears, enter the container from which you want the search to start and select the search scope. If you leave the base DN blank, it will be to [Root] . You can also select the object class to export by using the Filter tab (for instance, to export User objects, you set the filter to objectlcass=user ) and select what information about an object is to be exported (see Figure 12.10). Click Next to continue.

    Figure 12.10. Selecting what information about an object is to be exported.

    graphics/12fig10.gif


  5. Enter the LDIF filename, with a path (such as F:\LDIF_FILES\myfile.ldif ) if you like, to be created. You can also use the browse button on the right to select the folder in which the file will be placed. If you don't specify a path, the file will be created in the same directory in which the ICE Wizard module is installed. Click Next to continue.

  6. Verify the information displayed. If it is correct, click Finish to start processing. Otherwise, click Back and make any necessary changes. A dialog is displayed, showing the processing results (see Figure 12.11).

    Figure 12.11. The ICE processing summary screen.
    graphics/12fig11.jpg

The following is an excerpt from the LDIF file created by using the preceding procedure:

 #This LDIF file was generated by Novell's ICE and the #LDIF destination handler. version: 1 dn: o=XYZCorp changetype: add auditFileLink: cn=AFO0_XYZCorp,o=XYZCorp o: XYZCorp objectClass: organization objectClass: ndsLoginProperties objectClass: ndsContainerLoginProperties objectClass: top ACL: 2#entry#o=XYZCorp#loginScript ACL: 2#entry#o=XYZCorp#printJobConfiguration ACL: 31#subtree#cn=Admin,ou=IS_Admins,o=XYZCorp#[Entry Rights] ACL: 47#subtree#cn=Admin,ou=IS_Admins,o=XYZCorp#[All Attributes Rights] ACL: 1#subtree#cn=Password_Admins,ou=Tree_Admins,ou=Groups, ou=IS_Admins,o=XYZCorp#[Entry graphics/ccc.gif Rights] ACL: 3#subtree#cn=Password_Admins,ou=Tree_Admins,ou=Groups, ou=IS_Admins,o=XYZCorp#[All graphics/ccc.gif Attributes Rights] ACL: 2#entry#o=XYZCorp#auditFileLink dn: ou=IS_Admins,o=XYZCorp changetype: add ou: IS_Admins objectClass: organizationalUnit objectClass: ndsLoginProperties objectClass: ndsContainerLoginProperties objectClass: top ACL: 2#entry#ou=IS_Admins,o=XYZCorp#loginScript ACL: 2#entry#ou=IS_Admins,o=XYZCorp#printJobConfiguration dn: cn=RootAdmins,ou=Groups,ou=IS_Admins,o=XYZCorp changetype: add equivalentToMe: cn=Admin,ou=IS_Admins,o=XYZCorp objectClass: groupOfNames objectClass: top uniqueMember: cn=Admin,ou=IS_Admins,o=XYZCorp cn: RootAdmins ACL: 2#entry#[Root]#uniqueMember 

WARNING

ICE automatically wraps long output lines at 79 characters (which is known as folding ). The starting position of the second line is indented by one character. This means that if you are to parse the output file for information by using a non “LDIF-aware utility, you need to know about the possible wrapping. When you're using the same LDIF file to import information back into the tree, no editing will be required of these wrapped lines. When the LDIF parser encounters a space at the beginning of a line, it knows to concatenate the rest of the data on that line with the data on the previous line. The leading space is then discarded. This behavior occurs according to RFC 2849, "The LDAP Data Interchange Format (LDIF) - Technical Specification" (see www.faqs.org/rfcs/rfc2849.html ).


TIP

Some versions of ldapsearch.exe , a command-line utility, have a flag that allows long lines to be exported without wrapping. One such implementation can be found in the Mozilla Directory ( LDAP ) SDK, which you can download for free from www.mozilla.org/directory.


To perform the preceding example from a command line, you use the following syntax:

 ice -S LDAP -s 10.6.6.1 -d cn=  admin  ,o=  org  -w  password  -F  objectclass  =user -a * -c sub -D graphics/ccc.gif LDIF -f c:\  output.ldif  

NOTE

If you are just importing data from or exporting information to LDIF files, you can also use one of the LDAP tools (such as ldapadd , ldapmodify , and ldapsearch ) included with eDirectory instead of using ICE . These tools are installed as part of ConsoleOne. For example, on a NetWare server they can be found in the SYS:PUBLIC\mgmt\ConsoleOne\1.2\bin directory.


The Dirload handler is a very powerful tool that you can use when you need to create a test tree and populate it with a large number of objects and for each object to have certain attributes filled in with values. Dirload reads from a source file whose format is very similar to that of LDIF. In the source file, you define the number of objects to create, how the objects should be named, what attributes are to be populated , and how the attribute values will be derived. In essence, the Dirload handler is an LDIF generator that works by using a template file. The following is an example of a Dirload template file:

 # Comment lines begin with "#" ================================ # Dirload example 1.00 #============================================================== # Lines beginning with "!" are control settings: #  - set the starting value of unique counter value at 300 #  - generate 2 objects using the template !COUNTER=300 !OBJECTCOUNT=2 #-------------------------------------------------------------- # ATTRIBUTE TEMPLATE # ------------------------------------------------------------- objectclass: inetorgperson # # Pick a randomly select string value from # the file called "first" and assign it as the given name. givenname: $R(first) # # Pick a randomly select string value from # the file called "initial" and assign it as the initials. initials: $R(initial) # # Pick a randomly select string value from # the file called "last" and assign it as the sn. sn: $R(lastnames) # # The cn is derived using the value in givenname plus #  the first character of the value in initials plus #  the value in sn # The context portion of the dn is derived using #  a randomly selected string value from the file "ou" plus #  the static string of "ou=test,o=XYZCorp". #  the file called "first" and assign it as the given name. # # CAUTION: DO NOT USE STRING FORMATTING IN $A() FOR ICE #          INCLUDED WITH EDIR BEFORE 8.7.3. THERE IS A #          BUG IN THE DIRLOAD HANDLER AND WILL CAUSE #          SERVER ABENDS!!! #dn: cn=$A(givenname)$A(initials,%.1s)$A(sn),ou=$R(ou), ou=test,o=XYZCorp dn: cn=$A(givenname)$A(initials)$A(sn),ou=$R(ou), ou=test,o=XYZCorp # # The telephone number is derived using #  the static string of "1-800-" plus #  a random three-digit number whose value is between #  1 and 999 plus a static string of "-" plus #  a 4-digit number based on the COUNTER control setting #  (this counter value is incremented every time after #   it is used) telephonenumber: 1-800-$N(1-999,%03d)-$C(%04d) # # Pick a randomly select string value from # the file called "titles" and assign it as the title. title: $R(titles) 

WARNING

The $A() option allows you to optionally specify a C-style formatting string to control the string data output. There is a bug in the Dirload handler for all versions of ICE from pre-eDirectory 8.7.3; when the optional formatting string is used, it results in server abends.


The files first , initials , last , ou , and titles must be located in the same directory as the template file. If those files are located elsewhere, you can specify the path as part of the filename in the $R() specification. Here's an example:

 sn: $R(f:\data_files\lastnames) 

NOTE

A set of sample first , initials , and so on files can be found in ConsoleOne's \bin\ice\ les directory.


Each record in the file must be terminated with a line-feed character.

You can process the template file by using this command:

 ice -S LOAD -f  template_filename  -D LDIF -f new.ldif 

(The username and password are not required because you are going from an input file to an output file.) This results in the generation of an LDIF file called new.ldif that looks like this:

 version: 1 dn: cn=JimAHenderson,ou=ds,ou=test,o=XYCorp changetype: add objectclass: inetorgperson givenname: Jim initials: A sn: Henderson telephonenumber: 1-800-123-0300 title: Don't do much dn: cn=PeterBKuo,ou=dev,ou=test,o=XYZCorp changetype: add objectclass: inetorgperson givenname: Peter initials: B sn: Kuo telephonenumber: 1-800-015-0301 title: Does nothing either 

After you verify the data for accuracy, you can use ICE again to import the LDIF file into the tree, using the following command syntax:

 ice -S LDIF -f new.ldif -D LDAP -s  hostname_or_ip  -d cn=  admin  ,o=  org  -w  password  

You can also directly import the data generated by Dirload into the tree without first creating an LDIF file:

 ice -S LOAD -f  template_filename  -D LDAP -s  hostname_or_ip  -d cn=  admin  ,o=  org  -w  password  

NOTE

Instead of using ICE , you can use NDS iManager by selecting Load File as the file type to be imported. From the eDirectory Maintenance task, you select Import Convert Export Wizard, Next (to import the file on disk), File Type.


You can find an in-depth description of the various command-line options for the different handlers and how the attribute specification works in the Dirload template file in the "Novell Import Conversion Export Utility" section of the eDirectory documentation.

A number of eDirectory management tasks can be performed only by using LDAP-based tools, such as ICE, but not through ConsoleOne or NetWare Administrator. For example, to create and manage dynamic groups and to manage the special [This] and [Self] ACL assignments, you must use an LDAP-based tool.

NOTE

You can use iManager to manage dynamic groups and the two special ACL assignments because iManager uses LDAP functions behind the scenes for these tasks.


Managing Dynamic Groups

Dynamic groups were introduced with eDirectory 8.6.1. Traditionally, a Group object's membership is static. That means every time a new member needs to be added or an existing member is removed, you have to manually handle the change. Therefore, these traditional groups are also known as static groups . A dynamic group, on the other hand, uses an LDAP URL to populate its Member attribute at the time the attribute is accessed. This LDAP URL is a search filter that assigns all objects, not just users, matching the search criteria to its membership list.

NOTE

The format of the LDAP URL is specified in RFC 2255 (see www.ietf.org/rfc/rfc2255.txt ). The search filter used within the URL is described in RFC 2254 (see www.ietf.org/rfc/rfc2254.txt ).


For example, you can create a dynamic group that automatically includes any object whose OU attribute ( Department , as shown in ConsoleOne or NetWare Administrator) value is Sales . If you apply a search filter for OU=Sales , the search returns a group that includes all object DNs containing the attribute value OU=Sales . Any object added to the tree that matches the OU=Sales criterion is automatically added to the group. Any object whose OU attribute value is changed from Sales to another value (or is removed from the tree) is automatically removed from the group.

TIP

Generally speaking, group members are User objects, but because any object matching the LDAP URL search criterion becomes a member of the dynamic group, you need to take care when constructing the search filter. You should either select an attribute that is populated only for User objects or take note when filling in values for attributes that are common to both User objects and other object classes. Alternatively, you can use the excludedMember attribute of the dynamic group object to block undesired objects from showing up in the membership listing. Note that excludedMember applies only to dynamic members.


NOTE

If a container (such as an OU ) becomes a dynamic member of a dynamic group, none of its subordinate objects (including any User objects) will gain any rights associated with the dynamic group. The same is true for Group objects and their members.


At a glance, it seems that one should convert all static groups to dynamic groups because they offer low management overhead, especially for sites that have many Group objects in the tree. Although there are many advantages of dynamic groups, there are also limitations, and there is merit in carefully weighing which type to use. The advantages of dynamic groups can be summarized as follows:

  • Object references ” You can create or delete User objects without having to touch all the groups that the object is a member of. This is particularly useful in the case of nonlocal members, where the overhead of external references, backlinks, and so on can be avoided. Groups can be created before the objects specified by the membership criterion are created.

  • Smaller DIB size ” Because the dynamic members are not stored in the DIB (because they are "calculated" every time the group object is queried), dynamic groups consume less storage when large member lists are involved.

  • Faster synchronization ” During synchronization, only the search filter ”rather than all the individual members ”needs to be transferred.

  • Centralized query ” Often, client applications perform a "canned," or predefined, query to obtain a list of objects. But what happens if that query ever needs to be changed? Rather than updating and distributing new client applications, a dynamic group can be employed. The client applications can use the dynamic group as the query; thus you can change it frequently without having to touch the client software.

The following are some of the disadvantages of dynamic groups:

  • Referential integrity ” The search filter holds the DN of the base of the search. Currently, this DN is not protected by referential integrity. Thus, moving, deleting, or renaming such an object can stop the dynamic groups using it from working.

  • Performance ” Because a query must take place every time, dynamic group operations occur more slowly than static group operations. Be aware that reading the membership list will cause a full search to occur, whereas comparing a specific DN to the membership list will only compare that object to the filter.

  • Lack of security- related attributes ” As discussed in the "Group Membership Recovery" section of Chapter 11, "Examples from the Real World," four changes in the DS are made when a user is added to a static group. The Group object's Member and Equivalent to Me attributes are populated with the User object's DN, and the User object's Group Membership and Security Equals properties are populated with the Group object's DN. Dynamic members do not have these assignments. As a result, the NetWare file systems and bindery are not aware of dynamic group members and thus do not recognize dynamic members when calculating access control information.

  • Application support ” Not all applications are dynamic group aware, and thus you need to maintain static groups for certain applications.

  • LDAP-only administration ” Due to lack of a snap-in at the time of this writing, ConsoleOne and NetWare Administrator are not aware of dynamic groups; therefore, dynamic group objects cannot be managed by using these tools.

TIP

If you plan to implement dynamic groups, it is best to upgrade your eDirectory to version 8.7 or higher and use the latest version of iManager. eDirectory 8.7 has optimized the "is member of" functionality. In eDirectory 8.7, the value in the GUID attribute of the entry that is being used in the search is attached to the search criteria. This is an indexed value, and it results in a significantly faster search when you're doing an "is member of" or ACL search. On the other hand, if the dynamic group is performing a member list query, the search will not be much faster.


At the time of this writing, there is no ConsoleOne or NetWare Administrator snap-in for dynamic groups. Therefore, dynamic group objects show up as unmanageable objects in ConsoleOne and NetWare Administrator. Although you can view the dynamic group membership list by using ConsoleOne (but not by using NetWare Administrator), you must create or update the membership information via LDAP. These are the general steps you need to take to create and configure a dynamic group:

  1. Create the dynamic group object.

  2. Populate the memberQuery attribute of the dynamic group object. (This is a required step.)

  3. Populate the dgIdentity attribute of the dynamic group. Ensure that the object specified has Read and Compare rights to the attributes specified in memberQuery . If a dgIdentity attribute is not specified, ensure that [Public] has Read and Compare rights to the attributes specified in memberQuery .

  4. Assign a special ACL attribute with LDAP to each of the resources being managed. Because unlike static groups, dynamic members are not security equivalent to a dynamic group, you need to add a special flag to any ACL that points to a dynamic group. This way, when a user tries to perform some privileged operation on the resource object, eDirectory will know that it must check to see whether the user is a member of the dynamic group as opposed to checking the user's security equivalences . This "special flag plus standard ACL" combination is known as dynamic ACL .

NOTE

You can also convert an existing static group to a dynamic group. The procedure to do so is described later in this section.


Before we present two examples, one on creating a dynamic group and one on converting a static group to a dynamic group, we need to first cover some of the LDIF attribute syntaxes and properties required to create and anage dynamic group objects. The search criterion for the members is specified by the attribute memberQuery . Other optional attributes that are used by the dynamic group object are excludedMember , dgIdentity , gAllowUnknown , dgTimeout , and dgAllowDuplicates (see Figure 12.12). To see these attributes on a Windows server, you can use DSBrowse. You select Tree View, Schema Tree, Schema Root, Class Definitions, Dynamic Group, Attributes.

Figure 12.12. The schema definition of the dynamicGroup class.
graphics/12fig12.gif

NOTE

Much of the Novell documentation uses the attribute name memberQueryURL . What the documentation does not tell you is that this is the LDAP attribute name and not the eDirectory attribute name. The eDirectory schema name for this attribute is memberQuery . Therefore, when you're looking for this attribute using NDS iMonitor or DSBrowse, for instance, you should look for memberQuery and not memberQueryURL .


The dgAllowDuplicates attribute indicates whether or not duplicates will be found in the member list. Duplicates may occur if an object is found in the search result of the memberQuery attribute, as well as the Member attribute. If dgAllowDuplicates is FALSE (which is the default setting), the server will eliminate the duplicates from the listing. By allowing duplicates, you can reduce the load on the server while listing dynamic group members, at the cost of having to deal with possible redundant data in the result.

The dgAllowUnknown attribute is a Boolean attribute that determines the behavior when the dynamic group members are not fully expandable for some reason. It determines the inclusion or exclusion of members in the dynamic group when the membership cannot be correctly ascertained. For example, if the search specified by memberQuery cannot fully complete because one of the replicas is not accessible, if dgAllowUnknown is set to TRUE , the object in question will be considered to be a member of the dynamic group. This is also true for dynamic ACL rights computations , and it therefore must be used carefully. In short, unless the implications of setting dgAllowUnknown to TRUE are fully understood , you should always leave it unset or set it to FALSE (which is the default).

The dgIdentity attribute holds the DN of the object that the dynamic group will use for authentication while searching. The server decides the identity to be used for a dynamic group object as follows:

  • If there is a dgIdentity attribute set on the dynamic group object, the DN specified in the dgIdentity attribute will be used as the identity for the dynamic member search.

  • If the dgIdentity attribute is not present but the dynamic group object has a password, the dynamic group itself will be used as the identity for dynamic member search.

  • If neither the dgIdentity attribute nor the password is present, the search for dynamic members will be done as [Public] (the anonymous user).

TIP

You can set a password for the dynamic group object by using an LDIF file similar to the following example:

 version: 1 dn: cn=  dynamic_group  ,ou=  org_unit  ,o=  org  changetype: add userpassword: 12345 

Or you can use setpword from JRButils to set the password (see the "JRB Utilities" section, later in this chapter).


The object specified by dgIdentity should have the necessary rights to do the search specified in the memberQuery attribute. For example, if the memberQuery value is ldap:///o=XYZCorp??sub?(title=*) , the object specified by dgIdentity , the dynamic group object itself, or [Public] should have Read and Compare rights on the attribute Title below the container O =XYZCorp . If you are to use [Public] , you should ensure that you have granted Read and Compare rights to the attribute(s) used in the search filter.

In order for the dynamic member evaluation to work correctly, the object specified by the dgIdentity attribute must be present on the same partition as the dynamic group object. The dgIdentity attribute has the schema flag DS_WRITE_MANAGED set. This means that you can specify an object as the dgIdentity attribute of a dynamic group only if it has administrative rights on that object.

The dgTimeout attribute is an integer that specifies the maximum duration a server can take, in seconds, to read or compare a member attribute before it times out. When the server exceeds this dgTimeout value, the search is terminated and any members found before the search is terminated are included in the list. At the same time, the -6016 error ( ERR_RETURNING_PARTIAL_RESULTS ) is returned, to indicate that the list may be incomplete. The default is zero, meaning that the search can take as long as is required.

The excludedMember attribute holds the DNs that are specifically excluded from the membership list of the dynamic group. You can use this information to construct exclusion lists for dynamic groups. excludedMember can only be used to exclude DNs from being dynamic members of a dynamic group. Thus, a DN is a dynamic member of a dynamic group only if it is selected by the member criteria specified by memberQuery and is not listed in excludedMember or explicitly added to the Member attribute.

The Member attribute of a dynamic group object lists ”but does not store ”all objects in the group; the exception is if a DN is explicitly added to the Member attribute. Rights assignments made to the Group object apply to all members of that group. Adding values to the Member attribute of a dynamic group adds them as static members. You can do this for specific inclusion of members.

NOTE

The LDAP server maps both the member and the uniqueMember LDAP attribute names to the Member attribute in eDirectory.


NOTE

Static members added via LDAP do not have their Group Membership and Security Equal To attributes populated. If static members are added via one of the standard management tools, such as ConsoleOne, the dynamic group object's Member and Equivalent To Me attributes are populated with the User object's DN , and the User object's Group Membership and Security Equals properties are populated with the dynamic group object's DN .


There are a number of ways to determine what are the static members in a dynamic group. You can use NDS iMonitor to view the Member attribute of the dynamic group object and the DNs that have a timestamp associated with static members (see Figure 12.13). Another method is to use DSBrowse to examine the values of the Member attribute of the dynamic group object. Member is a multivalued attribute, and each static member's DN is stored as a value; you will also find a value that is " (** Unknown) " (see Figure 12.14), and its Creation Time Stamp attribute is the same as that of the dynamic group object's. This value is a placeholder for dynamic members.

Figure 12.13. Static members' timestamps, which indicates when they were added.
graphics/12fig13.gif

Figure 12.14. Checking for static members by using DSBrowse.
graphics/12fig14.jpg

If you have extended your schema by using the dgstatic.sch schema file, you will have an additional optional attribute for the dynamic group: staticMember . As the name implies, the staticMember attribute keeps track of the static members of a dynamic group.

NOTE

For some reason, the dgstatic.sch file is included only with the Unix version of eDirectory. Novell is looking to release it as a standalone file for NetWare and Windows. But you can copy it from the Unix platform and use it on NetWare/Windows.

dgstatic.sch adds the staticMember attribute to the dynamicGroup and dynamicGroupAux classes.


The staticMember attribute is not populated statically. Rather, its value is dynamic and is determined when you query it via LDAP. This means that if you look at the dynamic group object by using ConsoleOne or DSBrowse, there is no staticMember attribute listed. When querying via LDAP, you have to ask for it explicitly ”that is, query for all attributes (that is, using * for attributes will not show staticMember ) ”as illustrated here:

 ldapsearch -h  hostname_or_ip  -b o=xyzcorp -D  cn=admin,o=xyzcorp  -w  password  graphics/ccc.gif cn=test-dynamic-group-2 staticmember dn: cn=test-dynamic-group-2,o=XYZCorp staticmember: cn=static_test_user2,o=XYZCorp 

WARNING

If you query for staticMember and some other attributes at the same time, staticMember is not returned.


The memberQuery attribute defines the set of rules that match the group members' attributes. The rules are specified in the form of an LDAP URL format, as specified in RFC 2255, and consist of the following components :

 ldap://  hostname:port  /<  baseDN  >?<  attrlist  >?<  scope  >?<  search filter  >[?<  extensions  >] 

hostname , port , and attrlist have no effect on dynamic groups because the query runs on the local server and the search is based on the search filter. Hence, the general format of the memberQuery attribute is as follows:

 ldap:///<  base dn  >??<  scope  >?<  search filter  >[?x-chain] 

< base dn > specifies the starting context for the search. If it is not specified, [Root] is assumed. < scope > indicates how the search will be performed and can be one of three values: base (only search the base DN), one (search the direct subordinates of the base DN ”the base DN itself is not searched), and sub (search the base DN and all objects in the subtree below). The default value is base . < search filter > is described in RFC 2254, and the default value is objectclass=* . At the time of this writing, x-chain is the only supported extension. If this option is specified, the search for dynamic members will chain across multiple servers. Otherwise, the search will be limited to what is in the local DIB. Depending on your network configuration and tree structure, chaining across servers can be a lengthy process; thus, the default is not to x-chain.

WARNING

If a dynamic group object does not have a memberQuery attribute, the group will not have any dynamic members.


Although memberQuery is a multivalued attribute according to its schema definition, eDirectory 8.6.1 through 8.7.2 servers use only the first value of memberQuery . For example, if there are two memberQuery values:

 ldap:///o=org1??sub?cn=* ldap:///o=org2??sub?cn=* 

eDirectory 8.6.1 through 8.7.2 servers will only use ldap:/// o=org1??sub?cn=* to compute the members of the group. They will accept more than one value, but they act on only the first query. This limitation is overcome in eDirectory 8.7.3, which computes the members based on all the memberQuery values, and the set of members is the union of the members computed using each of the memberQuery values. For the preceding example, an eDirectory 8.7.3 server will show all entries under O=org1 and O=org2 that have CN values as dynamic members.

Because the search filter in the memberQuery attribute is the same as that described in RFC 2254, the search criteria can be based on a number of different attributes that are AND'ed or OR'ed together. For instance, the filter ((title=manager)(L=Utah)) specifies that any object that has a Title attribute value of manager or an L (location) attribute value of Utah will be considered a possible dynamic member (subject to any restriction posed by excludedMember ). The type of attributes you can use in the search filter, however, is limited based on the attributes' value syntaxes. The following are the syntax types that are allowed in the search filter:

SYN_BOOLEAN

SYN_CE_STRING

SYN_CI_LIST

SYN_CI_STRING

SYN_CLASS_NAME

SYN_COUNTER

SYN_DIST_NAME

SYN_EMAIL_ADDRESS

SYN_FAX_NUMBER

SYN_INTEGER

SYN_INTERVAL

SYN_NU_STRING

SYN_PO_ADDRESS

SYN_PR_STRING

SYN_TEL_NUMBER

SYN_TIME


For eDirectory 8.7.3 and above, three additional syntax types are supported: SYN_PATH , SYN_TIMESTAMP , and SYN_TYPED_NAME .

TIP

If you are unsure of the syntax used by the attribute you want to use in the search filter, refer to Table C.2 in Appendix C, "eDirectory Classes, Objects, and Attributes." For a discussion about the various syntax types, refer to Table 2.2 in Chapter 2, "eDirectory Basics."


NOTE

Case-sensitivity of the search filter values depends on the attributes used. The syntax may be SYN_CI_STRING (which is not case-sensitive) or SYN_CE_STRING (which is case-sensitive).


The following example illustrates the steps necessary to create a dynamic group called CN=DynamicGroup.OU=ISstaff.O=XYZCorp . This group will consist of one static member, CN=Tasha.OU=ISstaff.O=XYZCorp , and all users in the OU=ISstaff.O=XYZCorp container that have Sales Support as their Title attribute value. The dynamic group will perform the LDAP queries as the Admin user. The dynamic group will be granted rights to the OU=Sales.O=XYZCorp container:

  1. Create an LDIF file to add a dynamic group to the tree, and call it dyngrp.ldif . It should look as follows:

     version: 1 dn: cn=DynamicGroup,ou=ISstaff,o=XYZCorp changetype: add cn: DynamicGroup objectclass: dynamicGroup memberQueryURL: ldap:///ou=ISstaff,o=XYZCorp??sub?(title=Sales Support) member: cn=tasha,ou=ISstaff,o=XYZCorp dgIdentity: cn=admin,o=XYZCorp 

  2. Use ICE or one of the other LDAP tools, such as ldapadd , to import the contents of the LDIF file into the tree:

     ice -S LDIF -f dyngrp.ldif -D LDAP -s  hostname_or_ip  -d cn=  admin  ,o=  org  -w  password  

  3. After the dynamic group is created, add a special ACL to the resource(s) the dynamic group is going to manage. In this example, DynamicGroup and the static member Tasha will be granted rights to the OU=Sales.O=XYZCorp container. To accomplish this, you need to add a dynamic ACL to the attributes that are to be managed. The following LDIF file, called addacl.ldif , will grant CN=DynamicGroup,OU=ISstaff,O=XYZCorp Browse rights to [Entry Rights] and Read and Compare rights to [All Attributes Rights] of the OU=Sales.O=XYZCorp container:

     version: 1 dn: ou=Sales,o=XYZCorp changetype: modify add: acl acl: 536870913#entry#cn=DynamicGroup,ou=ISstaff,o=XYZCorp#[Entry Rights] acl: 536870915#entry#cn=DynamicGroup,ou=ISstaff,o=XYZCorp#[All Attributes Rights] 

    See the "Dynamic ACL Rights Calculation" sidebar for information on how the values in the ACL entries are derived.

  4. Use ICE or one of the other LDAP tools, such as ldapmodify , to update the tree with the content of the LDIF file:

     ice -S LDIF -f addacl.ldif -D LDAP -s  hostname_or_ip  -d cn=  admin  ,o=  org  -w  password  

You can combine the contents of the two LDIF files into one LDIF file and import it into the tree in one step if you want.

REAL WORLD: Dynamic ACL Rights Calculation

The dynamic ACL is calculated by setting the privilege bit 0x20000000 along with the value with the other privileges being set. This is done by bit-wise OR'ing (or adding) 536870912 (which is 0x20000000 ) to the numeric value of the other privileges. For instance, to grant a dynamic ACL of Read and Compare, the value is determined as follows:

 Attribute privileges required = Read + Compare = 2 + 1 = 3 Dynamic ACL value = 536870912 Dynamic attribute privileges = 3 + 536870912 = 536870915 

The following table shows which privilege bits are used to specify each privilege for a dynamic ACL :

REQUIRED RIGHT

NORMAL ACL PERMISSION

DYNAMIC ACL PERMISSION

ENTRY RIGHTS

Browse

1 (0x1)

536870913 (0x20000001)

Add

2 (0x2)

536870914 (0x20000002)

Delete

4 (0x4)

536870916 (0x20000004)

Rename

8 (0x8)

536870920 (0x20000008)

Supervisor

16 (0x10)

536870928 (0x20000010)

ATTRIBUTES RIGHTS

Compare

1 (0x1)

536870913 (0x20000001)

Read

2 (0x2)

536870914 (0x20000002)

Write

4 (0x4)

536870916 (0x20000004)

Add Self

8 (0x8)

536870920 (0x20000008)

Supervisor

32 (0x20)

536870944 (0x20000020)


If you assign a dynamic group as a trustee and don't add the dynamic ACL bit setting, only the static members of the dynamic group that are added by non- LDAP means will be able to inherit those privileges due to the Security Equals relationship.


You can also use the Create Dynamic Group task under the Dynamic Group role in iManager. To do this, you specify Dynamic Group, Create Dynamic Group and then specify the group name and its context. Then you click OK and then select Modify to specify the additional attributes. If you are using the dynamic group object itself for the dgIdentity attribute value, you can set its password by using the Set Password task under the Help Desk role.

The procedure to upgrade existing static groups to dynamic groups is similar to the process for creating a dynamic group detailed earlier. Two main differences are that you need to add an auxiliary class to the static group and you need to upgrade the existing ACL assignments to include the dynamic ACL bit setting. The following are the steps to upgrade an existing static group to a dynamic group:

  1. Add the dynamicGroupAux object class and any required dynamic group object attributes (such as dgIdentity and memberQuery ) to the existing Group object.

  2. Populate the memberQuery attribute of the dynamic group object. (This is a required step.)

  3. Populate the dgIdentity attribute of the dynamic group. Ensure that the object specified has Read and Compare rights to the attributes specified in memberQuery . If a dgIdentity attribute is not specified, ensure that [Public] has Read and Compare rights to the attributes specified in memberQuery .

  4. Update the existing ACL assignments by adding the special ACL flag to each assignment with LDAP.

The following example illustrates the steps necessary to convert a static group called CN=StaticGroup.OU=ISstaff.O=XYZCorp to a dynamic group. This group consists of all users in the OU=ISstaff.O=XYZCorp container that have Sales Support as their Title attribute value. The dynamic group will perform the LDAP queries as the Admin user:

  1. Create an LDIF file, called upgrade2dyngrp.ldif , to add the dynamicGroupAux class to the static Group object. It should look as follows:

     version: 1 dn: cn=StaticGroup,ou=ISstaff,o=XYZCorp changetype: modify add: objectclass objectclass: dynamicGroup - add: memberQueryURL memberQueryURL: ldap:///ou=ISstaff,o=XYZCorp??sub?(title=Sales Support) - addd:dgIdentity dgIdentity: cn=admin,o=XYZCorp 

  2. Use ICE or one of the other LDAP tools, such as ldapmodify , to import the contents of the LDIF file into the tree:

     ice -S LDIF -f upgrade2dyngrp.ldif -D LDAP -s  hostname_or_ip  -d cn=  admin  ,o=  org  -w  password  

    NOTE

    You can use ConsoleOne to add the dynamicGroupAux auxiliary class to the existing Group object by right-clicking the object, selecting Extensions of This Object, and clicking Add Extension. However, you still have to use LDAP to add the other attributes.

  3. After the static group has been upgraded, update the existing ACL assignments with the dynamic ACL flag. Use ICE or ldapsearch to export the object's ACL assignment:

     ldapsearch -h  hostname  _  or_ip  -D cn=  admin  ,o=  org  -w  password  -s base -b ou=  org_unit  ,o=  org  graphics/ccc.gif objectclass=* acl > acl.ldif 

    or

     ice -S LDAP -s  hostname_or_ip  -d cn=  admin  ,o=  org  -w  password  -c base -b ou=  org_unit  ,o=  org  graphics/ccc.gif -F "  objectclass  =*" -a acl -D LDIF -f acl.ldif 

    This example assumes that the ACLs of interest are in the specified container only.

  4. Add the dynamic ACL flag value to the existing ACL values (using the information listed in the "Dynamic ACL Rights Calculation" sidebar) to get the new dynamic ACL values. Update acl.ldif with these new values.

  5. Use ICE or one of the other LDAP tools, such as ldapmodify , to update the tree with the content of the modified LDIF file, acl.ldif :

     ice -S LDIF -f acl.ldif -D LDAP -s  hostname_or_ip  -d cn=  admin  ,o=  org  -w  password  

You can also accomplish this task by using the Create Extended Object task under the Dynamic Group role in iManager (see Figure 12.15). To do this, you select Dynamic Group, Create Extended Object and then specify the object you want to add the dynamicGroupAux auxiliary class to. Then you click OK and select Modify to specify the additional attributes. If you are using the dynamic group object itself for the dgIdentity attribute value, you can set its password by using the Set Password task under the Help Desk role.

Figure 12.15. Upgrading a static group by using iManager.
graphics/12fig15.gif

Managing Special ACL Assignments

One of the features most often asked about by NDS/eDirectory administrators is the option to quickly and easily give users the ability to modify certain attributes in their own user objects. For instance, instead of having to have the administrator or the help desk update a user's Telephone Number attribute in DS, they want to grant the users the ability to manage that information. To address this need, eDirectory 8.6 and higher introduced two new special ACL "trustees": [Self] and [This] .

NOTE

A product called eGuide is included with eDirectory. This is a Web-based application that allows users to look up other users' information, such as telephone number, and to manage their own information (for the attributes that you have granted them rights to). The eGuide product can be found on the eDirectory Web Applications CD or, in the case of NetWare, it is located on the Products CD. You can also download eGuide from http://download.novell.com.


As an example, you can grant Read, Compare, and Write rights for each user to that user's own Telephone Number attribute. When you have many User objects in the tree, however, this becomes a very tedious process. Also, you need to remember to do this for each new User object created. You could instead use an LDIF file similar to the following Read, Compare, and Write rights to the Telephone Number attribute:

 version: 1 dn: cn=user1,ou=department1,o=XYZCorp changetype: modify add: ACL ACL: 7#entry#cn=user1,ou=department1,o=XYZCorp#telephoneNumber 

If you have a lot of users to modify, it might be easier to first generate a list of users by using NList and then use a script to parse the output and generate the necessary LDIF file. Listing 12.1 shows a sample of NList output, and Listing 12.2 is a sample awk script that converts the NList data to an LDIF file, which is shown in Listing 12.3.

Listing 12.1. An NList Listing of All Users in a Specific Context
 F:\>nlist user Object Class: User Current context: Department1.XYXCorp User name=The name of the user Dis      =Login disabled Log exp  =The login expiration date, 0 if no expiration date Pwd      =Yes if passwords are required Pwd exp  =The password expiration date, 0 if no expiration date Uni      =Yes if unique passwords are required Min      =The minimum password length, 0 if no minimum User Name                  Dis  Log Exp Pwd  Pwd Exp Uni Min --------------------------------------------------------------- admin                      No  00/00/00 No  00/00/00 No   0 Peter                      No  00/00/00 No  00/00/00 No   0 test_user                  No  00/00/00 No  00/00/00 No   0 Proxy_user                 No  00/00/00 No  00/00/00 No   0 A total of 4 User objects was found in this context. A total of 4 User objects was found. 

Listing 12.2. An awk Script That Converts the NList User Listing from Listing 12.1 to an LDIF File
 BEGIN { flag = 0         printf("version: 1\n\n"); } /Current context:/ { cx = 
 BEGIN { flag = 0 printf("version: 1\n\n"); } /Current context:/ { cx = $0 gsub("Current context:", "", cx) gsub(" ", "", cx) } # No more user information, unset flag to stop further # printing /A total of / { flag = 0 } {if (flag == 1) { printf("dn: cn=%s", $1) # assumes each level of tree is OU and topmost # level is O. count = split(cx, levels, ".") for (x=1; x< count; x++) { printf(",ou=%s", levels[x]) } printf(",o=%s\n", levels[count]) printf("changetype: modify\n") printf("add: ACL\n") printf("ACL: 7#entry#") printf("cn=%s", $1) for (x=1; x< count; x++) { printf(",ou=%s", levels[x]) } printf(",o=%s", levels[count]) printf("#telephoneNumber\n\n"); }} # The user info starts after a line of dashes, set flag # This test must be performed here else the line of # dashes is considered as user info /----------------/ { flag = 1 } END {} 
gsub("Current context:", "", cx) gsub(" ", "", cx) } # No more user information, unset flag to stop further # printing /A total of / { flag = 0 } {if (flag == 1) { printf("dn: cn=%s", ) # assumes each level of tree is OU and topmost # level is O. count = split(cx, levels, ".") for (x=1; x< count; x++) { printf(",ou=%s", levels[x]) } printf(",o=%s\n", levels[count]) printf("changetype: modify\n") printf("add: ACL\n") printf("ACL: 7#entry#") printf("cn=%s", ) for (x=1; x< count; x++) { printf(",ou=%s", levels[x]) } printf(",o=%s", levels[count]) printf("#telephoneNumber\n\n"); }} # The user info starts after a line of dashes, set flag # This test must be performed here else the line of # dashes is considered as user info /----------------/ { flag = 1 } END {}

Listing 12.3. An LDIF File Generated by the awk Script in Listing 12.2
 version: 1 dn: cn=admin,ou=Department1,o=XYXCorp changetype: modify add: ACL ACL: 7#entry#cn=admin,ou=Department1,o=XYXCorp#telephoneNumber dn: cn=Peter,ou=Department1,o=XYXCorp changetype: modify add: ACL ACL: 7#entry#cn=Peter,ou=Department1,o=XYXCorp#telephoneNumber dn: cn=test_user,ou=Department1,o=XYXCorp changetype: modify add: ACL ACL: 7#entry#cn=test_user,ou=Department1,o=XYXCorp#telephoneNumber dn: cn=Proxy_user,ou=Department1,o=XYXCorp changetype: modify add: ACL ACL: 7#entry#cn=Proxy_user,ou=Department1,o=XYXCorp#telephoneNumber 

You can make your script a little simpler by using the special [Self] ACL, as shown in Listing 12.4. The [Self] ACL is essentially a macro that refers to the object itself. Rather than using the DN of the object in the ACL, you can simply reference [Self] , and eDirectory will automatically convert that to the object's DN. The resulting LDIF file is shown in Listing 12.5.

Listing 12.4. An awk Script That Uses the [Self] ACL
 BEGIN { flag = 0         printf("version: 1\n\n"); } /Current context:/ { cx = 
 BEGIN { flag = 0 printf("version: 1\n\n"); } /Current context:/ { cx = $0 gsub("Current context:", "", cx) gsub(" ", "", cx) } # No more user information, unset flag to stop further # printing /A total of / { flag = 0 } {if (flag == 1) { printf("dn: cn=%s", $1) # assumes each level of tree is OU and topmost # level is O. count = split(cx, levels, ".") for (x=1; x< count; x++) { printf(",ou=%s", levels[x]) } printf(",o=%s\n", levels[count]) printf("changetype: modify\n") printf("add: ACL\n") # We eliminated one set of for-loop here printf("ACL: 7#entry#[self]#telephoneNumber\n\n"); }} # The user info starts after a line of dashes, set flag # This test must be performed here else the line of # dashes is considered as user info /----------------/ { flag = 1 } END {} 
gsub("Current context:", "", cx) gsub(" ", "", cx) } # No more user information, unset flag to stop further # printing /A total of / { flag = 0 } {if (flag == 1) { printf("dn: cn=%s", ) # assumes each level of tree is OU and topmost # level is O. count = split(cx, levels, ".") for (x=1; x< count; x++) { printf(",ou=%s", levels[x]) } printf(",o=%s\n", levels[count]) printf("changetype: modify\n") printf("add: ACL\n") # We eliminated one set of for-loop here printf("ACL: 7#entry#[self]#telephoneNumber\n\n"); }} # The user info starts after a line of dashes, set flag # This test must be performed here else the line of # dashes is considered as user info /----------------/ { flag = 1 } END {}

Listing 12.5. An LDIF File Generated by the awk Script in Listing 12.4
 version: 1 dn: cn=admin,ou=Department1,o=XYXCorp changetype: modify add: ACL ACL: 7#entry#[self]#telephoneNumber dn: cn=Peter,ou=Department1,o=XYXCorp changetype: modify add: ACL ACL: 7#entry#[self]#telephoneNumber dn: cn=test_user,ou=Department1,o=XYXCorp changetype: modify add: ACL ACL: 7#entry#[self]#telephoneNumber dn: cn=Proxy_user,ou=Department1,o=XYXCorp changetype: modify add: ACL ACL: 7#entry#[self]#telephoneNumber 

The LDIF files shown in Listings 12.3 and 12.5 both do the same thing, and the end result is the same. If you are generating LDIF files by using scripts, it is perhaps easier (in terms of formatting the output lines) to do it with [Self] rather than by referencing the full DN of the object.

The method shown in Listing 12.4 is not very efficient, however, because each User object will have an ACL entry added. Depending on the number of users, this can dramatically increase the size of the DIB. As mentioned earlier, any time a new user is added to the tree, this ACL needs to be manually added (or needs to be added as part of the object creation script). If you need to change the ACL setting, you need to update every User object in the tree; again, this is a very tedious and time-consuming process.

A much easier way is to grant the necessary rights somewhere high up in the tree (such as at the O= level) and let each user inherit the rights to modify this attribute in his or her own object. Prior to eDirectory 8.6, the only way to let subordinate objects inherit rights from their parent containers is to make such a container a trustee of itself, assign the necessary attribute rights, and flag the containers as Inheritable . The problem in this case is that everyone in the container and its subtree can change everyone else's Telephone Number attribute. This is where the [This] ACL comes in.

What [This] can do is allow you to set the ACL at, say, O=XYZCorp and have it inherit down so that you only modify a single ACL. However, this ACL does not grant rights to the Organization level and thereby grant rights for every user to change every other users' objects. Rather, it transparently grants each user rights to his or her own object. Here is what this would look like in LDIF syntax:

 dn: o=XYXCorp changetype: modify add: ACL ACL: 7#subtree#[this]#telephoneNumber 

Figure 12.16 shows the result of importing the preceding LDIF information into the tree. You can verify the working of the [This] ACL by looking up the effective rights of a User object to its own Telephone Number attribute (which should show Read, Compare, Write, and Add Self rights ”the Add Self right is a result of having the Write right). Then you can look up the effective rights of one User object to another User object's Telephone Number attribute (which should show only Read and Compare rights ”this is a result of O=XYZCorp having Read, Compare, Write, and Add Self effective rights to itself due to the [This] ACL assignment).

Figure 12.16. Using [This] to grant users self-management capabilities.
graphics/12fig16.gif

Because the rights are inherited from a superior container, new User objects are granted those rights automatically. This method works well if all users in the same tree branch are to be granted the rights. If there are exceptions, you need to place an Inherited Rights Filter (IRF) on the attribute for each of those User objects.



Novell's Guide to Troubleshooting eDirectory
Novells Guide to Troubleshooting eDirectory
ISBN: 0789731460
EAN: 2147483647
Year: 2003
Pages: 173

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net