Best Practices for LDAP Client Applications
Table of Contents
- 1. LDAP Authorization and Populations
- 2. Public Attributes
- 3. Private Attributes
- 4. LDAP Searching
- 5. Example LDAP Search Filter Expressions
- 6. Use secure connections
- 7. Reuse connections
- 8. Use paged searches
- 9. Apply resource limits
- 10. Request only what you need
- 11. Use specific LDAP filters
- 12. Use indexed searches
- 13. Ask the directory server what it supports
- 14. Trust result codes
- 15. Avoid server-side sorting
- 16. Check Result Codes
- 17. Indexed Properties (Appendix)
- 18. LDAP Developer Notes
- 19. Resources
1. LDAP Authorization and Populations
If access to a service should be limited to a particular population, LDAP contains both public and private fields (LDAP attributes) that can be used to identify that population. Public LDAP attributes can be used with an anonymous LDAP bind (LDAP account and password); private attributes require a privileged LDAP bind.
LDAP is divided into several containers called Organizational Units (OUs). Active employees, students, and affiliates, as well as those who are expired but are still within their grace period, are in the People OU. There is also a PreSIR People OU for admitted students, an ADVCON People OU for alumni and members of the Advancement Constituents, and an Expired People OU for those with no active affiliation. Anonymous LDAP binds have access only to the People OU. Anyone needing access to other OUs must request a privileged LDAP bind.
Below are some of the LDAP attributes more commonly used for authorization purposes.
2. Public Attributes
- berkeleyEduTestIDFlag: contains true if the record is a test record.
- berkeleyEduAffiliations: a multivalued attribute that contains which affiliations a person has and whether or not any of the affiliations have expired.
- departmentNumber: contains the department code listed in UCPath as the home department under Contact Information
3. Private Attributes
- berkeleyEduEmpApptType: determine if an employee is staff, faculty, or student employee
- berkeleyEduEmpTitleCode: used to pull out a particular job category
- berkeleyEduStuCollegeCode: group students by College
- berkeleyEduStuMajorCode: group students by Major
- berkeleyEduStuUGCode: is the student a graduate (G) or undergraduate (U)
- berkeleyEduStuRegStatCode: determine a student's registration status
4. LDAP Searching
When searching LDAP, restricting the search to avoid unnecessary data will speed things up. The search base for many searches will be ou=people,dc=berkeley,dc=edu. If the search needs to search within all of the OUs, the search base should be at a higher level, namely dc=berkeley,dc=edu, but this will be a longer search.
The search scope determines how many levels down from the search base into the data the search should go. If the search base is ou=people,dc=berkeley,dc=edu, the search scope can usually be one. If the search base includes more than one sub OU, the search scope will usually need to be sub, and again the search will take longer.
An example of an anonymous search for info about a person using the uid is:
ldapsearch -H ldaps://ldap.berkeley.edu -x -s sub -b "ou=people,dc=berkeley,dc=edu" "(&(objectclass=person)(uid=3807))"
5. Example LDAP Search Filter Expressions
Below are some example LDAP search filter expressions. There is often more than one way to get the same search result.
| Search objective | LDAP filter expression |
|---|---|
| All active employees | (&(objectclass=person)(!(berkeleyEduTestIDFlag=true))(berkeleyEduAffiliations=EMPLOYEE-TYPE-*)(!(berkeleyeduAffiliations=EMPLOYEE-STATUS-EXPIRED))) |
| All active employees in a specific department | (&(objectclass=person)(!(berkeleyEduTestIDFlag=true))(employeeNumber=*)(departmentNumber=JICCS)(!(berkeleyeduempexpdate=*))) |
| All students in a major (whether they are active or in their grace period) | (&(objectclass=person)(!(berkeleyEduTestIDFlag=true))(berkeleyEduStuMajorCode=080)) |
6. Use secure connections
Always use secure connections when sending credentials for authentication, and when reading or writing any data that is not public.
For LDAP applications, either connect to the directory server's LDAPS port (636), or if possible, begin each session with the StartTLS extended operation on the (cleartext) LDAP port (389).
See https://github.com/ucidentity/ldap-client-examples
7. Reuse connections
The LDAP protocol is stateful. Typically you bind (connect), search or make an update, and then unbind (disconnect). The server maintains a context and enforces authorization decisions concerning your requests. Your application should reuse connections.
You can make multiple requests without having to set up a new connection and authenticate for every request. Many client libraries for LDAP allow your application to share connections in a pool, avoiding the overhead of setting up and tearing down connections if you use them often. This can significantly improve your application’s performance and reduce load on the directory server.
8. Use paged searches
If you suspect that the result set from a given query may be very large, you should be able to retrieve a result set in small pieces. The Simple Paged Result extended control allows this type of retrieval by allowing a one-way traversal of the result set. Options on this control allow the client to set the initial page size and reset the page size with each subsequent request to the server.
The Simple Paged Result control is also used to access all of a large result set when there is a server-side administrative limit to the number of items returned from a query. For example, CalNet LDAP Directory servers have a default server-side limit of 1000 entries as the maximum number of results that are returned in a single request. If the results of a query exceed this limit, the Paged Results control is used with a page size equal to or less than the server-side limit in order to retrieve all of the results of the query.
9. Apply resource limits
LDAP client applications can set time limits and size limits on search requests to avoid overuse of server resources. Setting limits is appropriate when the searches your application performs are not fixed, but instead partially or fully determined by user input.
The ldapsearch command has --sizeLimit and --timeLimit options.
10. Request only what you need
Request the attributes you need explicitly and request all the attributes in the same search. This reduces the number of trips to the server and improves the speed of your application.
Good Search (asks for specific attributes):
ldapsearch -H ldaps://ldap.berkeley.edu -D "$LDAP_USER" -w $LDAP_PWD -b "dc=berkeley,dc=edu" -s sub "(&(objectClass=person)(uid=2))" uid, givenName
Bad Search (asks for all attributes):
ldapsearch -H ldaps://ldap.berkeley.edu -D "$LDAP_USER" -w $LDAP_PWD -b "dc=berkeley,dc=edu" -s sub "(&(objectClass=person)(uid=2))" "*"
11. Use specific LDAP filters
The difference between a general filter like (berkeleyEduAlternateID=*@berkeley.edu) and a good, specific filter like (berkeleyEduAlternateID=agent.cooper@berkeley.edu) is significant processing time and a very large number of entries, both for the directory server and for your application. In general, try to use short, specific filters. As a rule, prefer equality filters over substring filters.
Furthermore, always use & with ! to restrict the potential result set before returning all entries that do not match part of the filter. For example:(&(berkeleyEduDept=FBI)(!(berkeleyAlternateID=agent.cooper@berkeley.edu)))
12. Use indexed searches
Some directory servers reject unindexed searches by default because unindexed searches are generally far more resource-intensive. The CalNet directory currently allows unindexed searches but that may change in the future. If your application needs to use a filter that results in an unindexed search, then work with us to find a solution, such as having the directory maintain the indexes required by your application.
See Currently Indexed Properties
13. Ask the directory server what it supports
Directory servers expose their capabilities, suffixes they support, and other information as attribute values on the root DSE. This allows your application to discover a variety of information at run time, rather than storing configuration separately. Querying the directory about its configuration and the features it supports can make your application easier to deploy and maintain.
For example, rather than hard-coding dc=berkeley,dc=edu as a suffix DN in your configuration, you can search the root DSE on DS servers for namingContexts, and then search under the naming context DNs to locate the desired entries to initialize your configuration.
14. Trust result codes
The LDAP result codes that your application gets from the directory server are reliable and consistent. For example, if you request a modified operation and you get ResultCode.SUCCESS, then consider the operation a success rather than immediately issuing a search to get the modified entry.
15. Avoid server-side sorting
Directory servers also support a resource-intensive operation called server-side sorting. When your application requests a server-side sort, the directory server retrieves all the entries matching your search, and then returns the whole set of entries in sorted order. For result sets of any size server-side sorting therefore ties up server resources that could be used elsewhere.
16. Check Result Codes
LDAP result codes are standard and clearly defined, and listed in "LDAP Result Codes". When your application receives a result, it must use the result code value to determine what action to take. When the result is not what you expect, read or at least log the additional message information.
17. Indexed Properties (Appendix)
| Property | Index Type |
|---|---|
| aci | equality, presence |
| berkeleyEduAffID | equality, presence |
| berkeleyEduAffiliations | equality, presence |
| uid | equality |
18. LDAP Developer Notes
The OpenDJ test environments may be reached at ldap-test.berkeley.edu, on TCP port 389 (cleartext and StartTLS) and port 636 (SSL/TLS). The OpenDJ production environment may be reached at ldap.berkeley.edu, on TCP port 389 (StartTLS) and port 636 (SSL/TLS).
19. Resources
Please send any questions and comments to CalNet Support at calnet-admin@berkeley.edu.